Skip to content

Commit 687298f

Browse files
authored
Merge pull request #242 from WeihanLi/dev
1.0.74
2 parents 5145b55 + 2aabd19 commit 687298f

File tree

17 files changed

+395
-77
lines changed

17 files changed

+395
-77
lines changed

Directory.Packages.props

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,13 @@
22
<PropertyGroup>
33
<!-- Enable central package management -->
44
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
5+
<!-- Enable Transitive Package Pinning -->
6+
<CentralPackageTransitivePinningEnabled>true</CentralPackageTransitivePinningEnabled>
7+
<!-- https://learn.microsoft.com/en-us/nuget/concepts/auditing-packages -->
8+
<NuGetAudit>true</NuGetAudit>
9+
<NuGetAuditMode>all</NuGetAuditMode>
10+
<!-- https://learn.microsoft.com/en-us/nuget/reference/errors-and-warnings/nu1901-nu1904 -->
11+
<WarningsAsErrors>NU1901;NU1902;NU1903;NU1904</WarningsAsErrors>
512
</PropertyGroup>
613
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0' OR '$(TargetFramework)' == 'net8.0'">
714
<PackageVersion Include="Microsoft.Extensions.Configuration" Version="8.0.0" />

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
# WeihanLi.Common
22

3-
## Build status
4-
53
[![WeihanLi.Common Latest Stable](https://img.shields.io/nuget/v/WeihanLi.Common.svg)](https://www.nuget.org/packages/WeihanLi.Common/)
64

75
[![WeihanLi.Common Latest Preview](https://img.shields.io/nuget/vpre/WeihanLi.Common)](https://www.nuget.org/packages/WeihanLi.Common/absoluteLatest)
86

7+
## Build status
8+
99
[![Azure Pipelines Build Status](https://weihanli.visualstudio.com/Pipelines/_apis/build/status/WeihanLi.WeihanLi.Common?branchName=master)](https://weihanli.visualstudio.com/Pipelines/_build/latest?definitionId=16&branchName=master)
1010

1111
[![Github Actions Build Status](https://github.com/WeihanLi/WeihanLi.Common/actions/workflows/default.yml/badge.svg)](https://github.com/WeihanLi/WeihanLi.Common/actions/workflows/default.yml)

build/version.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<PropertyGroup>
33
<VersionMajor>1</VersionMajor>
44
<VersionMinor>0</VersionMinor>
5-
<VersionPatch>73</VersionPatch>
5+
<VersionPatch>74</VersionPatch>
66
<VersionPrefix>$(VersionMajor).$(VersionMinor).$(VersionPatch)</VersionPrefix>
77
</PropertyGroup>
88
</Project>

samples/AspNetCoreSample/Events/EventConsumer.cs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using WeihanLi.Common.Event;
1+
using WeihanLi.Common;
2+
using WeihanLi.Common.Event;
23
using WeihanLi.Extensions;
34

45
namespace AspNetCoreSample.Events;
@@ -7,25 +8,24 @@ public class EventConsumer
78
(IEventQueue eventQueue, IEventHandlerFactory eventHandlerFactory)
89
: BackgroundService
910
{
10-
private readonly IEventQueue _eventQueue = eventQueue;
11-
private readonly IEventHandlerFactory _eventHandlerFactory = eventHandlerFactory;
12-
1311
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
1412
{
1513
while (!stoppingToken.IsCancellationRequested)
1614
{
17-
var queues = await _eventQueue.GetQueuesAsync();
15+
var queues = await eventQueue.GetQueuesAsync();
1816
if (queues.Count > 0)
1917
{
2018
await queues.Select(async q =>
2119
{
22-
if (await _eventQueue.TryDequeueAsync(q, out var @event, out var properties))
20+
await foreach (var e in eventQueue.ReadAllAsync(q, stoppingToken))
2321
{
24-
var handlers = _eventHandlerFactory.GetHandlers(@event.GetType());
22+
var @event = e.Data;
23+
Guard.NotNull(@event);
24+
var handlers = eventHandlerFactory.GetHandlers(@event.GetType());
2525
if (handlers.Count > 0)
2626
{
2727
await handlers
28-
.Select(h => h.Handle(@event, properties))
28+
.Select(h => h.Handle(@event, e.Properties))
2929
.WhenAll()
3030
;
3131
}

samples/DotNetCoreSample/LoggerTest.cs

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,24 @@ public static void MicrosoftLoggingTest()
3737
var services = new ServiceCollection()
3838
.AddLogging(builder =>
3939
// builder.AddConsole()
40-
builder.AddFile()
40+
builder.AddFile(options => options.LogFormatter = (category, level, exception, msg, timestamp) =>
41+
$"{timestamp} - [{category}] {level} - {msg}\n{exception}")
4142
)
4243
.AddSingleton(typeof(GenericTest<>))
4344
.BuildServiceProvider();
45+
46+
var logger = services.GetRequiredService<ILoggerFactory>()
47+
.CreateLogger("test");
48+
while (!ApplicationHelper.ExitToken.IsCancellationRequested)
49+
{
50+
logger.LogInformation("Echo time: {Time}", DateTimeOffset.Now);
51+
Thread.Sleep(500);
52+
}
53+
54+
ConsoleHelper.ReadKeyWithPrompt();
55+
services.GetRequiredService<ILoggerFactory>()
56+
.CreateLogger("test")
57+
.LogInformation("test 123");
4458
services.GetRequiredService<GenericTest<int>>()
4559
.Test();
4660
services.GetRequiredService<GenericTest<string>>()
@@ -60,8 +74,6 @@ public static void MicrosoftLoggingTest()
6074

6175
private class GenericTest<T>(ILogger<GenericTest<T>> logger)
6276
{
63-
private readonly ILogger<GenericTest<T>> _logger = logger;
64-
65-
public void Test() => _logger.LogInformation("test");
77+
public void Test() => logger.LogInformation("test");
6678
}
6779
}

samples/DotNetCoreSample/Program.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -347,8 +347,8 @@
347347

348348
// InvokeHelper.TryInvoke(() => throw null, 3);
349349

350-
// InvokeHelper.TryInvoke(LoggerTest.MicrosoftLoggingTest);
351-
await InvokeHelper.TryInvokeAsync(InMemoryStreamTest.MainTest);
350+
InvokeHelper.TryInvoke(LoggerTest.MicrosoftLoggingTest);
351+
// await InvokeHelper.TryInvokeAsync(InMemoryStreamTest.MainTest);
352352

353353
ConsoleHelper.ReadKeyWithPrompt("Press any key to exit");
354354

src/Directory.Build.props

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
<PackageLicenseExpression>MIT</PackageLicenseExpression>
2626
<LangVersion>preview</LangVersion>
2727
<NoWarn>$(NoWarn);CS9216;</NoWarn>
28+
<NuGetAuditMode>direct</NuGetAuditMode>
2829
</PropertyGroup>
2930
<ItemGroup>
3031
<Using Include="System.Object" Alias="Lock" Condition="!$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net9.0'))" />
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
using System.Collections.Concurrent;
2+
using System.Runtime.CompilerServices;
3+
using WeihanLi.Common.Helpers;
4+
5+
namespace WeihanLi.Common.Event;
6+
7+
public sealed class AckQueueOptions
8+
{
9+
public TimeSpan AckTimeout { get; set; } = TimeSpan.FromMinutes(1);
10+
11+
public bool AutoRequeue { get; set; }
12+
13+
public TimeSpan RequeuePeriod { get; set; } = TimeSpan.FromMinutes(1);
14+
}
15+
16+
public sealed class AckQueue : DisposableBase
17+
{
18+
private readonly AckQueueOptions _options;
19+
private readonly ConcurrentQueue<IEvent> _queue = new();
20+
private readonly ConcurrentDictionary<string, IEvent> _unAckedMessages = new();
21+
private readonly Timer? _timer;
22+
23+
public AckQueue() : this(new()) { }
24+
25+
public AckQueue(AckQueueOptions options)
26+
{
27+
_options = options;
28+
if (options.AutoRequeue)
29+
{
30+
_timer = new Timer(_ => RequeueUnAckedMessages(), null, options.RequeuePeriod, options.RequeuePeriod);
31+
}
32+
}
33+
34+
public Task EnqueueAsync<TEvent>(TEvent @event, EventProperties? properties = null)
35+
{
36+
properties ??= new EventProperties();
37+
if (string.IsNullOrEmpty(properties.EventId))
38+
{
39+
properties.EventId = Guid.NewGuid().ToString();
40+
}
41+
42+
if (properties.EventAt == default)
43+
{
44+
properties.EventAt = DateTimeOffset.Now;
45+
}
46+
47+
var internalEvent = new EventWrapper<TEvent>
48+
{
49+
Data = @event,
50+
Properties = properties
51+
};
52+
53+
_queue.Enqueue(internalEvent);
54+
return Task.CompletedTask;
55+
}
56+
57+
public Task<IEvent<TEvent>?> DequeueAsync<TEvent>()
58+
{
59+
if (_queue.TryDequeue(out var eventWrapper))
60+
{
61+
_unAckedMessages.TryAdd(eventWrapper.Properties.EventId, eventWrapper);
62+
return Task.FromResult((IEvent<TEvent>?)eventWrapper);
63+
}
64+
65+
return Task.FromResult<IEvent<TEvent>?>(null);
66+
}
67+
68+
public Task AckMessageAsync(string eventId)
69+
{
70+
_unAckedMessages.TryRemove(eventId, out _);
71+
return Task.CompletedTask;
72+
}
73+
74+
public void RequeueUnAckedMessages()
75+
{
76+
foreach (var message in _unAckedMessages)
77+
{
78+
if (DateTimeOffset.Now - message.Value.Properties.EventAt > _options.AckTimeout)
79+
{
80+
if (_unAckedMessages.TryRemove(message.Key, out var eventWrapper)
81+
&& eventWrapper != null)
82+
{
83+
_queue.Enqueue(eventWrapper);
84+
}
85+
}
86+
}
87+
}
88+
89+
public async IAsyncEnumerable<IEvent> ReadAllAsync(
90+
[EnumeratorCancellation] CancellationToken cancellationToken = default)
91+
{
92+
while (!cancellationToken.IsCancellationRequested)
93+
{
94+
while (_queue.TryDequeue(out var eventWrapper))
95+
{
96+
_unAckedMessages.TryAdd(eventWrapper.Properties.EventId, eventWrapper);
97+
yield return eventWrapper;
98+
}
99+
100+
await Task.Delay(200, cancellationToken);
101+
}
102+
}
103+
104+
protected override void Dispose(bool disposing)
105+
{
106+
_timer?.Dispose();
107+
base.Dispose(disposing);
108+
}
109+
}

src/WeihanLi.Common/Event/EventBase.cs

Lines changed: 46 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -64,29 +64,65 @@ public interface IEvent<out T>
6464
T Data { get; }
6565
}
6666

67-
internal sealed class EventWrapper<T> : IEvent, IEvent<T>
67+
public class EventWrapper<T> : IEvent, IEvent<T>
6868
{
6969
public required T Data { get; init; }
70-
object? IEvent.Data => (object?)Data;
70+
object? IEvent.Data => Data;
7171
public required EventProperties Properties { get; init; }
7272
}
7373

74-
public static class EventBaseExtensions
74+
public static class EventExtensions
7575
{
76-
private static readonly JsonSerializerSettings EventSerializerSettings = JsonSerializeExtension.SerializerSettingsWith(s =>
77-
{
78-
s.TypeNameHandling = TypeNameHandling.Objects;
79-
});
76+
private static readonly JsonSerializerSettings EventSerializerSettings = JsonSerializeExtension
77+
.SerializerSettingsWith(s =>
78+
{
79+
s.NullValueHandling = NullValueHandling.Ignore;
80+
s.TypeNameHandling = TypeNameHandling.Objects;
81+
});
8082

81-
public static string ToEventMsg<TEvent>(this TEvent @event) where TEvent : class, IEventBase
83+
public static string ToEventMsg<TEvent>(this TEvent @event)
84+
{
85+
Guard.NotNull(@event);
86+
return GetEvent(@event).ToJson(EventSerializerSettings);
87+
}
88+
89+
public static string ToEventRawMsg<TEvent>(this TEvent @event)
8290
{
8391
Guard.NotNull(@event);
8492
return @event.ToJson(EventSerializerSettings);
8593
}
8694

87-
public static IEventBase ToEvent(this string eventMsg)
95+
private static IEvent GetEvent<TEvent>(this TEvent @event)
96+
{
97+
if (@event is IEvent eventEvent)
98+
return eventEvent;
99+
100+
if (@event is IEventBase eventBase)
101+
return new EventWrapper<TEvent>()
102+
{
103+
Data = @event,
104+
Properties = new()
105+
{
106+
EventAt = eventBase.EventAt,
107+
EventId = eventBase.EventId
108+
}
109+
};
110+
111+
return new EventWrapper<TEvent>
112+
{
113+
Data = @event,
114+
Properties = new EventProperties
115+
{
116+
EventAt = DateTimeOffset.Now
117+
}
118+
};
119+
}
120+
121+
public static TEvent ToEvent<TEvent>(this string eventMsg)
88122
{
89123
Guard.NotNull(eventMsg);
90-
return eventMsg.JsonToObject<IEventBase>(EventSerializerSettings);
124+
return eventMsg.JsonToObject<TEvent>(EventSerializerSettings);
91125
}
126+
127+
public static IEvent ToEvent(this string eventMsg) => ToEvent<IEvent>(eventMsg);
92128
}

src/WeihanLi.Common/Event/EventHandlerFactory.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,10 @@ namespace WeihanLi.Common.Event;
77

88
public sealed class DefaultEventHandlerFactory(IEventSubscriptionManager subscriptionManager) : IEventHandlerFactory
99
{
10-
private readonly IEventSubscriptionManager _subscriptionManager = subscriptionManager;
11-
1210
[RequiresUnreferencedCode("Unreferenced code may be used")]
1311
public ICollection<IEventHandler> GetHandlers(Type eventType)
1412
{
15-
var eventHandlers = _subscriptionManager.GetEventHandlers(eventType);
13+
var eventHandlers = subscriptionManager.GetEventHandlers(eventType);
1614
return eventHandlers;
1715
}
1816
}

0 commit comments

Comments
 (0)