Skip to content

Commit 348c75d

Browse files
authored
Merge pull request #16 from managedcode/massive_refactoring
Massive refactoring
2 parents 477d6dc + c3db92b commit 348c75d

120 files changed

Lines changed: 6631 additions & 3001 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/dotnet.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ jobs:
2828
run: dotnet restore
2929

3030
- name: Build
31-
run: dotnet build --no-restore
31+
run: dotnet build --no-restore --warnaserror
3232

3333
- name: Test and Collect Code Coverage
3434
run: dotnet test -p:CollectCoverage=true -p:CoverletOutput=coverage/
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
using AIBase.Abstractions.Chat.Grains;
2+
using AIBase.Abstractions.Chat.Models;
3+
using Microsoft.Extensions.Logging;
4+
5+
namespace AIBase.Core.Grains.Implementations;
6+
7+
public partial class UserGrain
8+
{
9+
public async Task<List<ChatMember>> GetChatMembershipsAsync()
10+
{
11+
if (!state.RecordExists)
12+
return new List<ChatMember>();
13+
14+
var chatMembers = new List<ChatMember>();
15+
var chatIds = state.State.ChatIds;
16+
17+
foreach (var chatId in chatIds)
18+
{
19+
var chatGrain = GrainFactory.GetGrain<IChatGrain>(chatId);
20+
var chatState = await chatGrain.GetChatStateAsync();
21+
if (chatState.Status != ChatStatus.Archived)
22+
{
23+
chatMembers.Add(new ChatMember(
24+
Id: chatId,
25+
Type: ChatMemberType.User, // Using User type for chats in user's list
26+
Name: chatId, // TODO: Store chat title in user grain
27+
JoinedAt: DateTime.UtcNow
28+
));
29+
}
30+
}
31+
32+
return chatMembers;
33+
}
34+
35+
public async Task AddChatMembershipAsync(ChatMember chatMember)
36+
{
37+
if (!state.RecordExists) return;
38+
39+
if (!state.State.ChatIds.Contains(chatMember.Id))
40+
{
41+
state.State.ChatIds.Insert(0, chatMember.Id); // Add to beginning for recent chats
42+
await state.WriteStateAsync();
43+
logger.LogInformation("Added chat {ChatId} to user {UserId}", chatMember.Id, state.State.Id);
44+
}
45+
}
46+
47+
public async Task RemoveChatMembershipAsync(string chatId)
48+
{
49+
if (!state.RecordExists) return;
50+
51+
if (state.State.ChatIds.Remove(chatId))
52+
{
53+
await state.WriteStateAsync();
54+
logger.LogInformation("Removed chat {ChatId} from user {UserId}", chatId, state.State.Id);
55+
}
56+
}
57+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
using AIBase.Abstractions.UserManagement.Models;
2+
using Microsoft.Extensions.Logging;
3+
4+
namespace AIBase.Core.Grains.Implementations;
5+
6+
public partial class UserGrain
7+
{
8+
public async Task UpdateSettingsAsync(UserSettings settings)
9+
{
10+
if (!state.RecordExists) return;
11+
12+
state.State = state.State with { Settings = settings };
13+
await state.WriteStateAsync();
14+
logger.LogInformation("Updated settings for user {UserId}", state.State.Id);
15+
}
16+
}

Directory.Build.props

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@
44
<LangVersion>13</LangVersion>
55
<EnableNETAnalyzers>true</EnableNETAnalyzers>
66
<Nullable>enable</Nullable>
7+
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
78
</PropertyGroup>
8-
9+
910
<!--NuGet-->
1011
<PropertyGroup>
1112
<Authors>ManagedCode</Authors>
@@ -24,8 +25,8 @@
2425
<RepositoryUrl>https://github.com/managedcode/Communication</RepositoryUrl>
2526
<PackageProjectUrl>https://github.com/managedcode/Communication</PackageProjectUrl>
2627
<Product>Managed Code - Communication</Product>
27-
<Version>9.0.1</Version>
28-
<PackageVersion>9.0.1</PackageVersion>
28+
<Version>9.5.0</Version>
29+
<PackageVersion>9.5.0</PackageVersion>
2930

3031
</PropertyGroup>
3132
<PropertyGroup Condition="'$(GITHUB_ACTIONS)' == 'true'">

ManagedCode.Communication.Benchmark/CreateInstanceFailBenchmark.cs

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System.Net;
12
using BenchmarkDotNet.Attributes;
23

34
namespace ManagedCode.Communication.Benchmark;
@@ -55,34 +56,26 @@ public Result ActivatorCreateInstanceGenericInt()
5556

5657

5758
[Benchmark]
58-
public object? ActivatorCreateInstanceTypeError()
59+
public Result CreateInstanceTypeError()
5960
{
60-
var result = (Result)Activator.CreateInstance(typeof(Result));
61-
result.Errors = new[] { Error.Create("oops") };
62-
return result;
61+
return Result.Fail("about:blank", "Error", HttpStatusCode.BadRequest);
6362
}
6463

6564
[Benchmark]
66-
public object? ActivatorCreateInstanceTypeIntError()
65+
public Result<int> CreateInstanceTypeIntError()
6766
{
68-
var result = (Result<int>)Activator.CreateInstance(typeof(Result<int>));
69-
result.Errors = new[] { Error.Create("oops") };
70-
return result;
67+
return Result<int>.Fail("about:blank", "Error", HttpStatusCode.BadRequest);
7168
}
7269

7370
[Benchmark]
74-
public object? ActivatorCreateInstanceTypeErrorInterface()
71+
public Result CreateInstanceTypeErrorInterface()
7572
{
76-
var result = Activator.CreateInstance(typeof(Result));
77-
(result as IResultError).AddError(Error.Create("oops"));
78-
return result;
73+
return Result.Fail("about:blank", "Error", HttpStatusCode.BadRequest);
7974
}
8075

8176
[Benchmark]
82-
public object? ActivatorCreateInstanceTypeIntErrorInterface()
77+
public Result<int> CreateInstanceTypeIntErrorInterface()
8378
{
84-
var result = Activator.CreateInstance(typeof(Result<int>));
85-
(result as IResultError).AddError(Error.Create("oops"));
86-
return result;
79+
return Result<int>.Fail("about:blank", "Error", HttpStatusCode.BadRequest);
8780
}
8881
}

ManagedCode.Communication.Benchmark/ManagedCode.Communication.Benchmark.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@
99
</PropertyGroup>
1010

1111
<ItemGroup>
12-
<PackageReference Include="BenchmarkDotNet" Version="0.14.0" />
12+
<PackageReference Include="BenchmarkDotNet" Version="0.15.2"/>
1313
</ItemGroup>
1414

1515
<ItemGroup>
16-
<ProjectReference Include="..\ManagedCode.Communication\ManagedCode.Communication.csproj" />
16+
<ProjectReference Include="..\ManagedCode.Communication\ManagedCode.Communication.csproj"/>
1717
</ItemGroup>
1818

1919
</Project>

ManagedCode.Communication.Benchmark/Program.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,12 @@ public class Program
77
{
88
public static void Main(string[] args)
99
{
10-
BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args
10+
BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly)
11+
.Run(args
1112

1213
#if DEBUG
13-
, new DebugInProcessConfig()
14+
, new DebugInProcessConfig()
1415
#endif
15-
);
16+
);
1617
}
1718
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
using System.Collections.Concurrent;
2+
using System.Globalization;
3+
using System.Net;
4+
using System.Reflection;
5+
using BenchmarkDotNet.Attributes;
6+
7+
namespace ManagedCode.Communication.Benchmark;
8+
9+
[MemoryDiagnoser]
10+
[SimpleJob(warmupCount: 3, iterationCount: 10)]
11+
public class ResultCreationBenchmark
12+
{
13+
private static readonly Exception TestException = new InvalidOperationException("Benchmark test");
14+
private static readonly Type ResultIntType = typeof(Result<int>);
15+
private static readonly ConcurrentDictionary<Type, MethodInfo> MethodCache = new();
16+
private readonly Exception _exception = TestException;
17+
private readonly object[] _exceptionArray = new object[1];
18+
19+
private MethodInfo _cachedMethod = null!;
20+
21+
[GlobalSetup]
22+
public void Setup()
23+
{
24+
_cachedMethod = ResultIntType.GetMethod(nameof(Result.Fail), [typeof(Exception), typeof(HttpStatusCode)])!;
25+
MethodCache[ResultIntType] = _cachedMethod;
26+
_exceptionArray[0] = TestException;
27+
}
28+
29+
[Benchmark(Baseline = true)]
30+
public Result<int> DirectCall()
31+
{
32+
return Result<int>.Fail(TestException);
33+
}
34+
35+
[Benchmark]
36+
public object Reflection_FindMethodEveryTime()
37+
{
38+
var method = ResultIntType.GetMethod(nameof(Result.Fail), [typeof(Exception), typeof(HttpStatusCode)]);
39+
return method!.Invoke(null, [TestException, HttpStatusCode.InternalServerError])!;
40+
}
41+
42+
[Benchmark]
43+
public object Reflection_CachedMethod()
44+
{
45+
return _cachedMethod.Invoke(null, [TestException, HttpStatusCode.InternalServerError])!;
46+
}
47+
48+
[Benchmark]
49+
public object Reflection_CachedMethod_ReuseArray()
50+
{
51+
// Can't reuse array because we need 2 parameters
52+
return _cachedMethod.Invoke(null, [TestException, HttpStatusCode.InternalServerError])!;
53+
}
54+
55+
[Benchmark]
56+
public object Reflection_ConcurrentDictionary()
57+
{
58+
var method = MethodCache.GetOrAdd(ResultIntType, type => type.GetMethod(nameof(Result.Fail), [typeof(Exception), typeof(HttpStatusCode)])!);
59+
return method.Invoke(null, [TestException, HttpStatusCode.InternalServerError])!;
60+
}
61+
62+
[Benchmark]
63+
public object Activator_TryCreateInstance()
64+
{
65+
var result = Activator.CreateInstance(ResultIntType);
66+
return result!;
67+
}
68+
69+
[Benchmark]
70+
public object Activator_WithPropertySet()
71+
{
72+
var resultType = Activator.CreateInstance(ResultIntType, BindingFlags.NonPublic | BindingFlags.Instance, null, [TestException],
73+
CultureInfo.CurrentCulture);
74+
75+
return resultType!;
76+
}
77+
}
Lines changed: 34 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,74 @@
11
namespace ManagedCode.Communication.Extensions.Constants;
22

33
/// <summary>
4-
/// Constants for Problem details to avoid string literals throughout the codebase.
4+
/// Constants for Problem details to avoid string literals throughout the codebase.
55
/// </summary>
66
public static class ProblemConstants
77
{
88
/// <summary>
9-
/// Problem titles
9+
/// Problem titles
1010
/// </summary>
1111
public static class Titles
1212
{
1313
/// <summary>
14-
/// Title for validation failure problems
14+
/// Title for validation failure problems
1515
/// </summary>
1616
public const string ValidationFailed = "Validation failed";
17-
17+
1818
/// <summary>
19-
/// Title for unexpected error problems
19+
/// Title for unexpected error problems
2020
/// </summary>
2121
public const string UnexpectedError = "An unexpected error occurred";
2222
}
23-
23+
2424
/// <summary>
25-
/// Problem extension keys
25+
/// Problem extension keys
2626
/// </summary>
2727
public static class ExtensionKeys
2828
{
2929
/// <summary>
30-
/// Key for validation errors in problem extensions
30+
/// Key for validation errors in problem extensions
3131
/// </summary>
3232
public const string ValidationErrors = "validationErrors";
33-
33+
3434
/// <summary>
35-
/// Key for trace ID in problem extensions
35+
/// Key for trace ID in problem extensions
3636
/// </summary>
3737
public const string TraceId = "traceId";
38-
38+
3939
/// <summary>
40-
/// Key for hub method name in problem extensions
40+
/// Key for hub method name in problem extensions
4141
/// </summary>
4242
public const string HubMethod = "hubMethod";
43-
43+
4444
/// <summary>
45-
/// Key for hub type name in problem extensions
45+
/// Key for hub type name in problem extensions
4646
/// </summary>
4747
public const string HubType = "hubType";
48-
48+
4949
/// <summary>
50-
/// Key for inner exception in problem extensions
50+
/// Key for inner exception in problem extensions
5151
/// </summary>
5252
public const string InnerException = "innerException";
53-
53+
5454
/// <summary>
55-
/// Key for stack trace in problem extensions
55+
/// Key for stack trace in problem extensions
5656
/// </summary>
5757
public const string StackTrace = "stackTrace";
58+
59+
/// <summary>
60+
/// Key for error type (enum type name) in problem extensions
61+
/// </summary>
62+
public const string ErrorType = "errorType";
63+
64+
/// <summary>
65+
/// Key for error code name (enum value name) in problem extensions
66+
/// </summary>
67+
public const string ErrorCodeName = "errorCodeName";
68+
69+
/// <summary>
70+
/// Key for error code value (enum numeric value) in problem extensions
71+
/// </summary>
72+
public const string ErrorCodeValue = "errorCodeValue";
5873
}
59-
}
74+
}

0 commit comments

Comments
 (0)