From 9512640bc7f8c1b8a018242f303b5625e3dca509 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Fri, 24 Dec 2021 18:25:39 +0800 Subject: [PATCH 01/17] Enable #nullable default --- CAP.sln | 4 +- src/DotNetCore.CAP/CAP.Attribute.cs | 4 +- src/DotNetCore.CAP/CAP.Options.cs | 8 +- .../Diagnostics/EventData.Cap.P.cs | 12 +- .../Diagnostics/EventData.Cap.S.cs | 16 +- src/DotNetCore.CAP/DotNetCore.CAP.csproj | 1 + src/DotNetCore.CAP/ICapPublisher.cs | 9 +- src/DotNetCore.CAP/ICapTransaction.Base.cs | 2 +- src/DotNetCore.CAP/ICapTransaction.cs | 2 +- .../Internal/ConsumerExecutedResult.cs | 6 +- .../Internal/ConsumerExecutorDescriptor.cs | 24 +- .../Internal/Filter/ExceptionContext.cs | 2 +- .../Internal/Filter/ExecutedContext.cs | 4 +- .../Internal/Filter/ExecutingContext.cs | 4 +- .../Internal/IBootstrapper.Default.cs | 11 +- .../Internal/ICapPublisher.Default.cs | 17 +- .../Internal/IConsumerRegister.Default.cs | 56 +- .../IConsumerServiceSelector.Default.cs | 20 +- .../Internal/IConsumerServiceSelector.cs | 2 +- .../Internal/IMessageSender.Default.cs | 15 +- src/DotNetCore.CAP/Internal/IMessageSender.cs | 3 +- .../Internal/ISubscribeDispatcher.Default.cs | 13 +- .../Internal/ISubscribeInvoker.Default.cs | 19 +- .../Internal/ISubscribeInvoker.cs | 3 +- .../Internal/LoggerExtensions.cs | 2 +- .../Internal/MethodMatcherCache.cs | 4 +- .../ObjectMethodExecutor/AwaitableInfo.cs | 204 +++---- .../CoercedAwaitableInfo.cs | 77 ++- .../ObjectMethodExecutor.cs | 576 +++++++++--------- .../ObjectMethodExecutorAwaitable.cs | 174 +++--- .../ObjectMethodExecutorFSharpSupport.cs | 234 +++---- .../Internal/PublisherSentFailedException.cs | 2 +- src/DotNetCore.CAP/Internal/SnowflakeId.cs | 2 +- src/DotNetCore.CAP/Internal/TopicAttribute.cs | 2 +- src/DotNetCore.CAP/Messages/FailedInfo.cs | 4 +- src/DotNetCore.CAP/Messages/Message.cs | 25 +- .../Messages/TransportMessage.cs | 16 +- src/DotNetCore.CAP/Monitoring/MessageDto.cs | 12 +- .../Monitoring/MessageQueryDto.cs | 8 +- .../Monitoring/PagedQueryResult.cs | 2 +- src/DotNetCore.CAP/OperateResult.cs | 2 +- .../Persistence/IDataStorage.cs | 2 +- .../Persistence/MediumMessage.cs | 6 +- .../Processor/IDispatcher.Default.cs | 8 +- .../Processor/IDispatcher.PerGroup.cs | 7 +- .../Processor/IProcessingServer.Cap.cs | 4 +- .../Processor/IProcessor.NeedRetry.cs | 2 +- .../Processor/ProcessingContext.cs | 6 +- .../Serialization/ISerializer.JsonUtf8.cs | 6 +- .../Serialization/ISerializer.cs | 7 +- src/DotNetCore.CAP/Transport/BrokerAddress.cs | 7 +- .../Transport/IConsumerClient.cs | 2 +- src/DotNetCore.CAP/Transport/MqLogType.cs | 2 +- 53 files changed, 826 insertions(+), 836 deletions(-) diff --git a/CAP.sln b/CAP.sln index a1de23c..8f4620b 100644 --- a/CAP.sln +++ b/CAP.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.29025.244 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31919.166 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{9B2AE124-6636-4DE9-83A3-70360DABD0C4}" EndProject diff --git a/src/DotNetCore.CAP/CAP.Attribute.cs b/src/DotNetCore.CAP/CAP.Attribute.cs index d2c886d..63eef9e 100644 --- a/src/DotNetCore.CAP/CAP.Attribute.cs +++ b/src/DotNetCore.CAP/CAP.Attribute.cs @@ -29,9 +29,9 @@ namespace DotNetCore.CAP } - public class CapHeader : ReadOnlyDictionary + public class CapHeader : ReadOnlyDictionary { - public CapHeader(IDictionary dictionary) : base(dictionary) + public CapHeader(IDictionary dictionary) : base(dictionary) { } diff --git a/src/DotNetCore.CAP/CAP.Options.cs b/src/DotNetCore.CAP/CAP.Options.cs index 3a2c8ce..735b986 100644 --- a/src/DotNetCore.CAP/CAP.Options.cs +++ b/src/DotNetCore.CAP/CAP.Options.cs @@ -40,12 +40,12 @@ namespace DotNetCore.CAP /// /// Subscriber group prefix. /// - public string GroupNamePrefix { get; set; } + public string? GroupNamePrefix { get; set; } /// /// Topic prefix. /// - public string TopicNamePrefix { get; set; } + public string? TopicNamePrefix { get; set; } /// /// The default version of the message, configured to isolate data in the same instance. The length must not exceed 20 @@ -67,7 +67,7 @@ namespace DotNetCore.CAP /// /// We’ll invoke this call-back with message type,name,content when retry failed (send or executed) messages equals times. /// - public Action FailedThresholdCallback { get; set; } + public Action? FailedThresholdCallback { get; set; } /// /// The number of message retries, the retry will stop when the threshold is reached. @@ -116,6 +116,6 @@ namespace DotNetCore.CAP /// /// Configure JSON serialization settings /// - public JsonSerializerOptions JsonSerializerOptions { get; } = new JsonSerializerOptions(); + public JsonSerializerOptions JsonSerializerOptions { get; } = new (); } } \ No newline at end of file diff --git a/src/DotNetCore.CAP/Diagnostics/EventData.Cap.P.cs b/src/DotNetCore.CAP/Diagnostics/EventData.Cap.P.cs index 2154c4c..fcd3bcd 100644 --- a/src/DotNetCore.CAP/Diagnostics/EventData.Cap.P.cs +++ b/src/DotNetCore.CAP/Diagnostics/EventData.Cap.P.cs @@ -8,27 +8,27 @@ namespace DotNetCore.CAP.Diagnostics { public long? OperationTimestamp { get; set; } - public string Operation { get; set; } + public string Operation { get; set; } = default!; - public Message Message { get; set; } + public Message Message { get; set; } = default!; public long? ElapsedTimeMs { get; set; } - public Exception Exception { get; set; } + public Exception? Exception { get; set; } } public class CapEventDataPubSend { public long? OperationTimestamp { get; set; } - public string Operation { get; set; } + public string Operation { get; set; } = default!; - public TransportMessage TransportMessage { get; set; } + public TransportMessage TransportMessage { get; set; } = default!; public BrokerAddress BrokerAddress { get; set; } public long? ElapsedTimeMs { get; set; } - public Exception Exception { get; set; } + public Exception? Exception { get; set; } } } diff --git a/src/DotNetCore.CAP/Diagnostics/EventData.Cap.S.cs b/src/DotNetCore.CAP/Diagnostics/EventData.Cap.S.cs index c8fbd58..c1ff18e 100644 --- a/src/DotNetCore.CAP/Diagnostics/EventData.Cap.S.cs +++ b/src/DotNetCore.CAP/Diagnostics/EventData.Cap.S.cs @@ -5,7 +5,6 @@ using System; using System.Reflection; using DotNetCore.CAP.Messages; using DotNetCore.CAP.Transport; -using JetBrains.Annotations; namespace DotNetCore.CAP.Diagnostics { @@ -13,30 +12,29 @@ namespace DotNetCore.CAP.Diagnostics { public long? OperationTimestamp { get; set; } - public string Operation { get; set; } + public string Operation { get; set; } = default!; - public TransportMessage TransportMessage { get; set; } + public TransportMessage TransportMessage { get; set; } = default!; public BrokerAddress BrokerAddress { get; set; } public long? ElapsedTimeMs { get; set; } - public Exception Exception { get; set; } + public Exception? Exception { get; set; } } public class CapEventDataSubExecute { public long? OperationTimestamp { get; set; } - public string Operation { get; set; } + public string Operation { get; set; } = default!; - public Message Message { get; set; } + public Message Message { get; set; } = default!; - [CanBeNull] - public MethodInfo MethodInfo { get; set; } + public MethodInfo? MethodInfo { get; set; } public long? ElapsedTimeMs { get; set; } - public Exception Exception { get; set; } + public Exception? Exception { get; set; } } } diff --git a/src/DotNetCore.CAP/DotNetCore.CAP.csproj b/src/DotNetCore.CAP/DotNetCore.CAP.csproj index 914d50d..828296d 100644 --- a/src/DotNetCore.CAP/DotNetCore.CAP.csproj +++ b/src/DotNetCore.CAP/DotNetCore.CAP.csproj @@ -2,6 +2,7 @@ netstandard2.1 + enable diff --git a/src/DotNetCore.CAP/ICapPublisher.cs b/src/DotNetCore.CAP/ICapPublisher.cs index 158417d..1cc46b5 100644 --- a/src/DotNetCore.CAP/ICapPublisher.cs +++ b/src/DotNetCore.CAP/ICapPublisher.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; -using JetBrains.Annotations; namespace DotNetCore.CAP { @@ -28,7 +27,7 @@ namespace DotNetCore.CAP /// message body content, that will be serialized. (can be null) /// callback subscriber name /// - Task PublishAsync(string name, [CanBeNull] T contentObj, string callbackName = null, CancellationToken cancellationToken = default); + Task PublishAsync(string name, T? contentObj, string? callbackName = null, CancellationToken cancellationToken = default); /// /// Asynchronous publish an object message with custom headers @@ -38,7 +37,7 @@ namespace DotNetCore.CAP /// message body content, that will be serialized. (can be null) /// message additional headers. /// - Task PublishAsync(string name, [CanBeNull] T contentObj, IDictionary headers, CancellationToken cancellationToken = default); + Task PublishAsync(string name, T? contentObj, IDictionary headers, CancellationToken cancellationToken = default); /// /// Publish an object message. @@ -46,7 +45,7 @@ namespace DotNetCore.CAP /// the topic name or exchange router key. /// message body content, that will be serialized. (can be null) /// callback subscriber name - void Publish(string name, [CanBeNull] T contentObj, string callbackName = null); + void Publish(string name, T? contentObj, string? callbackName = null); /// /// Publish an object message. @@ -54,6 +53,6 @@ namespace DotNetCore.CAP /// the topic name or exchange router key. /// message body content, that will be serialized. (can be null) /// message additional headers. - void Publish(string name, [CanBeNull] T contentObj, IDictionary headers); + void Publish(string name, T? contentObj, IDictionary headers); } } \ No newline at end of file diff --git a/src/DotNetCore.CAP/ICapTransaction.Base.cs b/src/DotNetCore.CAP/ICapTransaction.Base.cs index 5a35fca..dc774df 100644 --- a/src/DotNetCore.CAP/ICapTransaction.Base.cs +++ b/src/DotNetCore.CAP/ICapTransaction.Base.cs @@ -23,7 +23,7 @@ namespace DotNetCore.CAP public bool AutoCommit { get; set; } - public object DbTransaction { get; set; } + public object? DbTransaction { get; set; } protected internal virtual void AddToSent(MediumMessage msg) { diff --git a/src/DotNetCore.CAP/ICapTransaction.cs b/src/DotNetCore.CAP/ICapTransaction.cs index 3892ed1..304626b 100644 --- a/src/DotNetCore.CAP/ICapTransaction.cs +++ b/src/DotNetCore.CAP/ICapTransaction.cs @@ -20,7 +20,7 @@ namespace DotNetCore.CAP /// /// Database transaction object, can be converted to a specific database transaction object or IDBTransaction when used /// - object DbTransaction { get; set; } + object? DbTransaction { get; set; } /// /// Submit the transaction context of the CAP, we will send the message to the message queue at the time of submission diff --git a/src/DotNetCore.CAP/Internal/ConsumerExecutedResult.cs b/src/DotNetCore.CAP/Internal/ConsumerExecutedResult.cs index 22f6a60..8eec874 100644 --- a/src/DotNetCore.CAP/Internal/ConsumerExecutedResult.cs +++ b/src/DotNetCore.CAP/Internal/ConsumerExecutedResult.cs @@ -5,17 +5,17 @@ namespace DotNetCore.CAP.Internal { public class ConsumerExecutedResult { - public ConsumerExecutedResult(object result, string msgId, string callbackName) + public ConsumerExecutedResult(object? result, string msgId, string? callbackName) { Result = result; MessageId = msgId; CallbackName = callbackName; } - public object Result { get; set; } + public object? Result { get; set; } public string MessageId { get; set; } - public string CallbackName { get; set; } + public string? CallbackName { get; set; } } } \ No newline at end of file diff --git a/src/DotNetCore.CAP/Internal/ConsumerExecutorDescriptor.cs b/src/DotNetCore.CAP/Internal/ConsumerExecutorDescriptor.cs index 0e61ba3..c4e57cc 100644 --- a/src/DotNetCore.CAP/Internal/ConsumerExecutorDescriptor.cs +++ b/src/DotNetCore.CAP/Internal/ConsumerExecutorDescriptor.cs @@ -12,21 +12,21 @@ namespace DotNetCore.CAP.Internal /// public class ConsumerExecutorDescriptor { - public TypeInfo ServiceTypeInfo { get; set; } + public TypeInfo? ServiceTypeInfo { get; set; } - public MethodInfo MethodInfo { get; set; } + public MethodInfo MethodInfo { get; set; } = default!; - public TypeInfo ImplTypeInfo { get; set; } + public TypeInfo ImplTypeInfo { get; set; } = default!; - public TopicAttribute Attribute { get; set; } + public TopicAttribute Attribute { get; set; } = default!; - public TopicAttribute ClassAttribute { get; set; } + public TopicAttribute? ClassAttribute { get; set; } - public IList Parameters { get; set; } + public IList Parameters { get; set; } = new List(); - public string TopicNamePrefix { get; set; } + public string? TopicNamePrefix { get; set; } - private string _topicName; + private string? _topicName; /// /// Topic name based on both and . /// @@ -58,7 +58,7 @@ namespace DotNetCore.CAP.Internal public class ConsumerExecutorDescriptorComparer : IEqualityComparer { - public bool Equals(ConsumerExecutorDescriptor x, ConsumerExecutorDescriptor y) + public bool Equals(ConsumerExecutorDescriptor? x, ConsumerExecutorDescriptor? y) { //Check whether the compared objects reference the same data. if (ReferenceEquals(x, y)) @@ -77,7 +77,7 @@ namespace DotNetCore.CAP.Internal x.Attribute.Group.Equals(y.Attribute.Group, StringComparison.OrdinalIgnoreCase); } - public int GetHashCode(ConsumerExecutorDescriptor obj) + public int GetHashCode(ConsumerExecutorDescriptor? obj) { //Check whether the object is null if (obj is null) return 0; @@ -95,9 +95,9 @@ namespace DotNetCore.CAP.Internal public class ParameterDescriptor { - public string Name { get; set; } + public string Name { get; set; } = default!; - public Type ParameterType { get; set; } + public Type ParameterType { get; set; } = default!; public bool IsFromCap { get; set; } } diff --git a/src/DotNetCore.CAP/Internal/Filter/ExceptionContext.cs b/src/DotNetCore.CAP/Internal/Filter/ExceptionContext.cs index 576ca08..fa3086c 100644 --- a/src/DotNetCore.CAP/Internal/Filter/ExceptionContext.cs +++ b/src/DotNetCore.CAP/Internal/Filter/ExceptionContext.cs @@ -19,6 +19,6 @@ namespace DotNetCore.CAP.Filter public bool ExceptionHandled { get; set; } - public object Result { get; set; } + public object? Result { get; set; } } } \ No newline at end of file diff --git a/src/DotNetCore.CAP/Internal/Filter/ExecutedContext.cs b/src/DotNetCore.CAP/Internal/Filter/ExecutedContext.cs index ede0ce2..b4e771f 100644 --- a/src/DotNetCore.CAP/Internal/Filter/ExecutedContext.cs +++ b/src/DotNetCore.CAP/Internal/Filter/ExecutedContext.cs @@ -8,11 +8,11 @@ namespace DotNetCore.CAP.Filter { public class ExecutedContext : FilterContext { - public ExecutedContext(ConsumerContext context, object result) : base(context) + public ExecutedContext(ConsumerContext context, object? result) : base(context) { Result = result; } - public object Result { get; set; } + public object? Result { get; set; } } } \ No newline at end of file diff --git a/src/DotNetCore.CAP/Internal/Filter/ExecutingContext.cs b/src/DotNetCore.CAP/Internal/Filter/ExecutingContext.cs index 1eed760..a393eee 100644 --- a/src/DotNetCore.CAP/Internal/Filter/ExecutingContext.cs +++ b/src/DotNetCore.CAP/Internal/Filter/ExecutingContext.cs @@ -8,11 +8,11 @@ namespace DotNetCore.CAP.Filter { public class ExecutingContext : FilterContext { - public ExecutingContext(ConsumerContext context, object[] arguments) : base(context) + public ExecutingContext(ConsumerContext context, object?[] arguments) : base(context) { Arguments = arguments; } - public object[] Arguments { get; set; } + public object?[] Arguments { get; set; } } } \ No newline at end of file diff --git a/src/DotNetCore.CAP/Internal/IBootstrapper.Default.cs b/src/DotNetCore.CAP/Internal/IBootstrapper.Default.cs index 3b21f38..5540eaf 100644 --- a/src/DotNetCore.CAP/Internal/IBootstrapper.Default.cs +++ b/src/DotNetCore.CAP/Internal/IBootstrapper.Default.cs @@ -19,8 +19,8 @@ namespace DotNetCore.CAP.Internal { private readonly IServiceProvider _serviceProvider; private readonly ILogger _logger; - private IEnumerable _processors; - private CancellationTokenSource _cts = new CancellationTokenSource(); + private readonly CancellationTokenSource _cts = new (); + private IEnumerable _processors = default!; public Bootstrapper(IServiceProvider serviceProvider, ILogger logger) { @@ -93,9 +93,8 @@ namespace DotNetCore.CAP.Internal public override void Dispose() { - _cts?.Cancel(); - _cts?.Dispose(); - _cts = null; + _cts.Cancel(); + _cts.Dispose(); } protected override async Task ExecuteAsync(CancellationToken stoppingToken) @@ -105,7 +104,7 @@ namespace DotNetCore.CAP.Internal public override async Task StopAsync(CancellationToken cancellationToken) { - _cts?.Cancel(); + _cts.Cancel(); await base.StopAsync(cancellationToken); } diff --git a/src/DotNetCore.CAP/Internal/ICapPublisher.Default.cs b/src/DotNetCore.CAP/Internal/ICapPublisher.Default.cs index 1b9c7b4..62ffa5e 100644 --- a/src/DotNetCore.CAP/Internal/ICapPublisher.Default.cs +++ b/src/DotNetCore.CAP/Internal/ICapPublisher.Default.cs @@ -22,8 +22,7 @@ namespace DotNetCore.CAP.Internal private readonly CapOptions _capOptions; // ReSharper disable once InconsistentNaming - protected static readonly DiagnosticListener s_diagnosticListener = - new DiagnosticListener(CapDiagnosticListenerNames.DiagnosticListenerName); + protected static readonly DiagnosticListener s_diagnosticListener = new(CapDiagnosticListenerNames.DiagnosticListenerName); public CapPublisher(IServiceProvider service) { @@ -38,20 +37,20 @@ namespace DotNetCore.CAP.Internal public AsyncLocal Transaction { get; } - public Task PublishAsync(string name, T value, IDictionary headers, CancellationToken cancellationToken = default) + public Task PublishAsync(string name, T? value, IDictionary headers, CancellationToken cancellationToken = default) { return Task.Run(() => Publish(name, value, headers), cancellationToken); } - public Task PublishAsync(string name, T value, string callbackName = null, + public Task PublishAsync(string name, T? value, string? callbackName = null, CancellationToken cancellationToken = default) { return Task.Run(() => Publish(name, value, callbackName), cancellationToken); } - public void Publish(string name, T value, string callbackName = null) + public void Publish(string name, T? value, string? callbackName = null) { - var header = new Dictionary + var header = new Dictionary { {Headers.CallbackName, callbackName} }; @@ -59,7 +58,7 @@ namespace DotNetCore.CAP.Internal Publish(name, value, header); } - public void Publish(string name, T value, IDictionary headers) + public void Publish(string name, T? value, IDictionary headers) { if (string.IsNullOrEmpty(name)) { @@ -71,14 +70,12 @@ namespace DotNetCore.CAP.Internal name = $"{_capOptions.TopicNamePrefix}.{name}"; } - headers ??= new Dictionary(); - if (!headers.ContainsKey(Headers.MessageId)) { var messageId = SnowflakeId.Default().NextId().ToString(); headers.Add(Headers.MessageId, messageId); } - + if (!headers.ContainsKey(Headers.CorrelationId)) { headers.Add(Headers.CorrelationId, headers[Headers.MessageId]); diff --git a/src/DotNetCore.CAP/Internal/IConsumerRegister.Default.cs b/src/DotNetCore.CAP/Internal/IConsumerRegister.Default.cs index ebd7265..483eb91 100644 --- a/src/DotNetCore.CAP/Internal/IConsumerRegister.Default.cs +++ b/src/DotNetCore.CAP/Internal/IConsumerRegister.Default.cs @@ -25,15 +25,15 @@ namespace DotNetCore.CAP.Internal private readonly TimeSpan _pollingDelay = TimeSpan.FromSeconds(1); private readonly CapOptions _options; - private IConsumerClientFactory _consumerClientFactory; - private IDispatcher _dispatcher; - private ISerializer _serializer; - private IDataStorage _storage; + private IConsumerClientFactory _consumerClientFactory = default!; + private IDispatcher _dispatcher = default!; + private ISerializer _serializer = default!; + private IDataStorage _storage = default!; - private MethodMatcherCache _selector; + private MethodMatcherCache _selector = default!; private CancellationTokenSource _cts; private BrokerAddress _serverAddress; - private Task _compositeTask; + private Task? _compositeTask; private bool _disposed; private bool _isHealthy = true; @@ -57,13 +57,13 @@ namespace DotNetCore.CAP.Internal public void Start(CancellationToken stoppingToken) { - _selector = _serviceProvider.GetService(); - _dispatcher = _serviceProvider.GetService(); - _serializer = _serviceProvider.GetService(); - _storage = _serviceProvider.GetService(); - _consumerClientFactory = _serviceProvider.GetService(); + _selector = _serviceProvider.GetRequiredService(); + _dispatcher = _serviceProvider.GetRequiredService(); + _serializer = _serviceProvider.GetRequiredService(); + _storage = _serviceProvider.GetRequiredService(); + _consumerClientFactory = _serviceProvider.GetRequiredService(); - stoppingToken.Register(() => _cts?.Cancel()); + stoppingToken.Register(() => _cts.Cancel()); Execute(); } @@ -131,7 +131,7 @@ namespace DotNetCore.CAP.Internal if (!IsHealthy() || force) { Pulse(); - + _cts = new CancellationTokenSource(); _isHealthy = true; @@ -166,9 +166,8 @@ namespace DotNetCore.CAP.Internal public void Pulse() { - _cts?.Cancel(); - _cts?.Dispose(); - _cts = null; + _cts.Cancel(); + _cts.Dispose(); } private void RegisterMessageProcessor(IConsumerClient client) @@ -184,7 +183,7 @@ namespace DotNetCore.CAP.Internal tracingTimestamp = TracingBefore(transportMessage, _serverAddress); var name = transportMessage.GetName(); - var group = transportMessage.GetGroup(); + var group = transportMessage.GetGroup()!; Message message; @@ -201,21 +200,36 @@ namespace DotNetCore.CAP.Internal throw ex; } - var type = executor.Parameters.FirstOrDefault(x => x.IsFromCap == false)?.ParameterType; + var type = executor!.Parameters.FirstOrDefault(x => x.IsFromCap == false)?.ParameterType; message = _serializer.DeserializeAsync(transportMessage, type).GetAwaiter().GetResult(); message.RemoveException(); } catch (Exception e) { transportMessage.Headers[Headers.Exception] = e.GetType().Name + "-->" + e.Message; + string? dataUri; if (transportMessage.Headers.TryGetValue(Headers.Type, out var val)) { - var dataUri = $"data:{val};base64," + Convert.ToBase64String(transportMessage.Body); + if (transportMessage.Body != null) + { + dataUri = $"data:{val};base64," + Convert.ToBase64String(transportMessage.Body); + } + else + { + dataUri = null; + } message = new Message(transportMessage.Headers, dataUri); } else { - var dataUri = "data:UnknownType;base64," + Convert.ToBase64String(transportMessage.Body); + if (transportMessage.Body != null) + { + dataUri = "data:UnknownType;base64," + Convert.ToBase64String(transportMessage.Body); + } + else + { + dataUri = null; + } message = new Message(transportMessage.Headers, dataUri); } } @@ -255,7 +269,7 @@ namespace DotNetCore.CAP.Internal TracingAfter(tracingTimestamp, transportMessage, _serverAddress); - _dispatcher.EnqueueToExecute(mediumMessage, executor); + _dispatcher.EnqueueToExecute(mediumMessage, executor!); } } catch (Exception e) diff --git a/src/DotNetCore.CAP/Internal/IConsumerServiceSelector.Default.cs b/src/DotNetCore.CAP/Internal/IConsumerServiceSelector.Default.cs index 8939e0a..f0a7a0d 100644 --- a/src/DotNetCore.CAP/Internal/IConsumerServiceSelector.Default.cs +++ b/src/DotNetCore.CAP/Internal/IConsumerServiceSelector.Default.cs @@ -33,7 +33,7 @@ namespace DotNetCore.CAP.Internal public ConsumerServiceSelector(IServiceProvider serviceProvider) { _serviceProvider = serviceProvider; - _capOptions = serviceProvider.GetService>().Value; + _capOptions = serviceProvider.GetRequiredService>().Value; _cacheList = new ConcurrentDictionary>>(); } @@ -51,7 +51,7 @@ namespace DotNetCore.CAP.Internal return executorDescriptorList; } - public ConsumerExecutorDescriptor SelectBestCandidate(string key, IReadOnlyList executeDescriptor) + public ConsumerExecutorDescriptor? SelectBestCandidate(string key, IReadOnlyList executeDescriptor) { if (executeDescriptor.Count == 0) { @@ -107,7 +107,7 @@ namespace DotNetCore.CAP.Internal { var executorDescriptorList = new List(); - var types = Assembly.GetEntryAssembly().ExportedTypes; + var types = Assembly.GetEntryAssembly()!.ExportedTypes; foreach (var type in types) { var typeInfo = type.GetTypeInfo(); @@ -120,7 +120,7 @@ namespace DotNetCore.CAP.Internal return executorDescriptorList; } - protected IEnumerable GetTopicAttributesDescription(TypeInfo typeInfo, TypeInfo serviceTypeInfo = null) + protected IEnumerable GetTopicAttributesDescription(TypeInfo typeInfo, TypeInfo? serviceTypeInfo = null) { var topicClassAttribute = typeInfo.GetCustomAttribute(true); @@ -169,9 +169,9 @@ namespace DotNetCore.CAP.Internal TopicAttribute attr, MethodInfo methodInfo, TypeInfo implType, - TypeInfo serviceTypeInfo, + TypeInfo? serviceTypeInfo, IList parameters, - TopicAttribute classAttr = null) + TopicAttribute? classAttr = null) { var descriptor = new ConsumerExecutorDescriptor { @@ -187,7 +187,7 @@ namespace DotNetCore.CAP.Internal return descriptor; } - private ConsumerExecutorDescriptor MatchUsingName(string key, IReadOnlyList executeDescriptor) + private ConsumerExecutorDescriptor? MatchUsingName(string key, IReadOnlyList executeDescriptor) { if (key == null) { @@ -197,7 +197,7 @@ namespace DotNetCore.CAP.Internal return executeDescriptor.FirstOrDefault(x => x.TopicName.Equals(key, StringComparison.InvariantCultureIgnoreCase)); } - private ConsumerExecutorDescriptor MatchWildcardUsingRegex(string key, IReadOnlyList executeDescriptor) + private ConsumerExecutorDescriptor? MatchWildcardUsingRegex(string key, IReadOnlyList executeDescriptor) { var group = executeDescriptor.First().Attribute.Group; if (!_cacheList.TryGetValue(group, out var tmpList)) @@ -223,9 +223,9 @@ namespace DotNetCore.CAP.Internal private class RegexExecuteDescriptor { - public string Name { get; set; } + public string Name { get; set; } = default!; - public T Descriptor { get; set; } + public T Descriptor { get; set; } = default!; } } } diff --git a/src/DotNetCore.CAP/Internal/IConsumerServiceSelector.cs b/src/DotNetCore.CAP/Internal/IConsumerServiceSelector.cs index baea9af..5954a25 100644 --- a/src/DotNetCore.CAP/Internal/IConsumerServiceSelector.cs +++ b/src/DotNetCore.CAP/Internal/IConsumerServiceSelector.cs @@ -22,6 +22,6 @@ namespace DotNetCore.CAP.Internal /// /// topic or exchange router key. /// the set of candidates. - ConsumerExecutorDescriptor SelectBestCandidate(string key, IReadOnlyList candidates); + ConsumerExecutorDescriptor? SelectBestCandidate(string key, IReadOnlyList candidates); } } \ No newline at end of file diff --git a/src/DotNetCore.CAP/Internal/IMessageSender.Default.cs b/src/DotNetCore.CAP/Internal/IMessageSender.Default.cs index 76792d6..760c09b 100644 --- a/src/DotNetCore.CAP/Internal/IMessageSender.Default.cs +++ b/src/DotNetCore.CAP/Internal/IMessageSender.Default.cs @@ -26,8 +26,7 @@ namespace DotNetCore.CAP.Internal private readonly IOptions _options; // ReSharper disable once InconsistentNaming - protected static readonly DiagnosticListener s_diagnosticListener = - new DiagnosticListener(CapDiagnosticListenerNames.DiagnosticListenerName); + protected static readonly DiagnosticListener s_diagnosticListener = new(CapDiagnosticListenerNames.DiagnosticListenerName); public MessageSender( ILogger logger, @@ -36,10 +35,10 @@ namespace DotNetCore.CAP.Internal _logger = logger; _serviceProvider = serviceProvider; - _options = serviceProvider.GetService>(); - _dataStorage = serviceProvider.GetService(); - _serializer = serviceProvider.GetService(); - _transport = serviceProvider.GetService(); + _options = serviceProvider.GetRequiredService>(); + _dataStorage = serviceProvider.GetRequiredService(); + _serializer = serviceProvider.GetRequiredService(); + _transport = serviceProvider.GetRequiredService(); } public async Task SendAsync(MediumMessage message) @@ -80,9 +79,9 @@ namespace DotNetCore.CAP.Internal { TracingError(tracingTimestamp, transportMsg, _transport.BrokerAddress, result); - var needRetry = await SetFailedState(message, result.Exception); + var needRetry = await SetFailedState(message, result.Exception!); - return (needRetry, OperateResult.Failed(result.Exception)); + return (needRetry, OperateResult.Failed(result.Exception!)); } } diff --git a/src/DotNetCore.CAP/Internal/IMessageSender.cs b/src/DotNetCore.CAP/Internal/IMessageSender.cs index 94fedc0..de0143a 100644 --- a/src/DotNetCore.CAP/Internal/IMessageSender.cs +++ b/src/DotNetCore.CAP/Internal/IMessageSender.cs @@ -3,12 +3,11 @@ using System.Threading.Tasks; using DotNetCore.CAP.Persistence; -using JetBrains.Annotations; namespace DotNetCore.CAP.Internal { public interface IMessageSender { - Task SendAsync([NotNull] MediumMessage message); + Task SendAsync(MediumMessage message); } } \ No newline at end of file diff --git a/src/DotNetCore.CAP/Internal/ISubscribeDispatcher.Default.cs b/src/DotNetCore.CAP/Internal/ISubscribeDispatcher.Default.cs index 969c848..e80935e 100644 --- a/src/DotNetCore.CAP/Internal/ISubscribeDispatcher.Default.cs +++ b/src/DotNetCore.CAP/Internal/ISubscribeDispatcher.Default.cs @@ -25,8 +25,7 @@ namespace DotNetCore.CAP.Internal // diagnostics listener // ReSharper disable once InconsistentNaming - private static readonly DiagnosticListener s_diagnosticListener = - new DiagnosticListener(CapDiagnosticListenerNames.DiagnosticListenerName); + private static readonly DiagnosticListener s_diagnosticListener = new (CapDiagnosticListenerNames.DiagnosticListenerName); public SubscribeDispatcher( ILogger logger, @@ -37,8 +36,8 @@ namespace DotNetCore.CAP.Internal _logger = logger; _options = options.Value; - _dataStorage = _provider.GetService(); - Invoker = _provider.GetService(); + _dataStorage = _provider.GetRequiredService(); + Invoker = _provider.GetRequiredService(); } private ISubscribeInvoker Invoker { get; } @@ -46,7 +45,7 @@ namespace DotNetCore.CAP.Internal public Task DispatchAsync(MediumMessage message, CancellationToken cancellationToken) { var selector = _provider.GetRequiredService(); - if (!selector.TryGetTopicExecutor(message.Origin.GetName(), message.Origin.GetGroup(), out var executor)) + if (!selector.TryGetTopicExecutor(message.Origin.GetName(), message.Origin.GetGroup()!, out var executor)) { var error = $"Message (Name:{message.Origin.GetName()},Group:{message.Origin.GetGroup()}) can not be found subscriber." + $"{Environment.NewLine} see: https://github.com/dotnetcore/CAP/issues/63"; @@ -186,7 +185,7 @@ namespace DotNetCore.CAP.Internal if (!string.IsNullOrEmpty(ret.CallbackName)) { - var header = new Dictionary() + var header = new Dictionary() { [Headers.CorrelationId] = message.Origin.GetId(), [Headers.CorrelationSequence] = (message.Origin.GetCorrelationSequence() + 1).ToString() @@ -249,7 +248,7 @@ namespace DotNetCore.CAP.Internal } } - private void TracingError(long? tracingTimestamp, Message message, MethodInfo method, Exception ex) + private void TracingError(long? tracingTimestamp, Message message, MethodInfo? method, Exception ex) { if (tracingTimestamp != null && s_diagnosticListener.IsEnabled(CapDiagnosticListenerNames.ErrorSubscriberInvoke)) { diff --git a/src/DotNetCore.CAP/Internal/ISubscribeInvoker.Default.cs b/src/DotNetCore.CAP/Internal/ISubscribeInvoker.Default.cs index 2445194..f9a0523 100644 --- a/src/DotNetCore.CAP/Internal/ISubscribeInvoker.Default.cs +++ b/src/DotNetCore.CAP/Internal/ISubscribeInvoker.Default.cs @@ -12,22 +12,19 @@ using DotNetCore.CAP.Messages; using DotNetCore.CAP.Serialization; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Internal; -using Microsoft.Extensions.Logging; namespace DotNetCore.CAP.Internal { public class SubscribeInvoker : ISubscribeInvoker { - private readonly ILogger _logger; private readonly IServiceProvider _serviceProvider; private readonly ISerializer _serializer; private readonly ConcurrentDictionary _executors; - public SubscribeInvoker(ILoggerFactory loggerFactory, IServiceProvider serviceProvider, ISerializer serializer) + public SubscribeInvoker(IServiceProvider serviceProvider, ISerializer serializer) { _serviceProvider = serviceProvider; _serializer = serializer; - _logger = loggerFactory.CreateLogger(); _executors = new ConcurrentDictionary(); } @@ -36,11 +33,11 @@ namespace DotNetCore.CAP.Internal cancellationToken.ThrowIfCancellationRequested(); var methodInfo = context.ConsumerDescriptor.MethodInfo; - var reflectedTypeHandle = methodInfo.ReflectedType.TypeHandle.Value; + var reflectedTypeHandle = methodInfo.ReflectedType!.TypeHandle.Value; var methodHandle = methodInfo.MethodHandle.Value; var key = $"{reflectedTypeHandle}_{methodHandle}"; - var executor = _executors.GetOrAdd(key, x => ObjectMethodExecutor.Create(methodInfo, context.ConsumerDescriptor.ImplTypeInfo)); + var executor = _executors.GetOrAdd(key, _ => ObjectMethodExecutor.Create(methodInfo, context.ConsumerDescriptor.ImplTypeInfo)); using var scope = _serviceProvider.CreateScope(); @@ -50,7 +47,7 @@ namespace DotNetCore.CAP.Internal var message = context.DeliverMessage; var parameterDescriptors = context.ConsumerDescriptor.Parameters; - var executeParameters = new object[parameterDescriptors.Count]; + var executeParameters = new object?[parameterDescriptors.Count]; for (var i = 0; i < parameterDescriptors.Count; i++) { var parameterDescriptor = parameterDescriptors[i]; @@ -90,7 +87,7 @@ namespace DotNetCore.CAP.Internal } var filter = provider.GetService(); - object resultObj = null; + object? resultObj = null; try { if (filter != null) @@ -155,10 +152,10 @@ namespace DotNetCore.CAP.Internal var srvType = context.ConsumerDescriptor.ServiceTypeInfo?.AsType(); var implType = context.ConsumerDescriptor.ImplTypeInfo.AsType(); - object obj = null; + object? obj = null; if (srvType != null) { - obj = provider.GetServices(srvType).FirstOrDefault(o => o.GetType() == implType); + obj = provider.GetServices(srvType).FirstOrDefault(o => o?.GetType() == implType); } if (obj == null) @@ -169,7 +166,7 @@ namespace DotNetCore.CAP.Internal return obj; } - private async Task ExecuteWithParameterAsync(ObjectMethodExecutor executor, object @class, object[] parameter) + private async Task ExecuteWithParameterAsync(ObjectMethodExecutor executor, object @class, object?[] parameter) { if (executor.IsMethodAsync) { diff --git a/src/DotNetCore.CAP/Internal/ISubscribeInvoker.cs b/src/DotNetCore.CAP/Internal/ISubscribeInvoker.cs index 4f0aaab..dc55fe0 100644 --- a/src/DotNetCore.CAP/Internal/ISubscribeInvoker.cs +++ b/src/DotNetCore.CAP/Internal/ISubscribeInvoker.cs @@ -3,7 +3,6 @@ using System.Threading; using System.Threading.Tasks; -using JetBrains.Annotations; namespace DotNetCore.CAP.Internal { @@ -17,6 +16,6 @@ namespace DotNetCore.CAP.Internal /// /// consumer execute context /// The object of . - Task InvokeAsync([NotNull] ConsumerContext context, CancellationToken cancellationToken = default); + Task InvokeAsync(ConsumerContext context, CancellationToken cancellationToken = default); } } \ No newline at end of file diff --git a/src/DotNetCore.CAP/Internal/LoggerExtensions.cs b/src/DotNetCore.CAP/Internal/LoggerExtensions.cs index 19b20e9..dc4c75c 100644 --- a/src/DotNetCore.CAP/Internal/LoggerExtensions.cs +++ b/src/DotNetCore.CAP/Internal/LoggerExtensions.cs @@ -40,7 +40,7 @@ namespace DotNetCore.CAP.Internal logger.LogDebug($"Received message. id:{messageId}, name: {name}"); } - public static void MessagePublishException(this ILogger logger, string messageId, string reason, Exception ex) + public static void MessagePublishException(this ILogger logger, string? messageId, string reason, Exception? ex) { logger.LogError(ex, $"An exception occured while publishing a message, reason:{reason}. message id:{messageId}"); } diff --git a/src/DotNetCore.CAP/Internal/MethodMatcherCache.cs b/src/DotNetCore.CAP/Internal/MethodMatcherCache.cs index 2b1612f..4ae758d 100644 --- a/src/DotNetCore.CAP/Internal/MethodMatcherCache.cs +++ b/src/DotNetCore.CAP/Internal/MethodMatcherCache.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; namespace DotNetCore.CAP.Internal @@ -66,8 +67,7 @@ namespace DotNetCore.CAP.Internal /// The group name of the value to get. /// topic executor of the value. /// true if the key was found, otherwise false. - public bool TryGetTopicExecutor(string topicName, string groupName, - out ConsumerExecutorDescriptor matchTopic) + public bool TryGetTopicExecutor(string topicName, string groupName, [NotNullWhen(true)] out ConsumerExecutorDescriptor? matchTopic) { if (Entries == null) { diff --git a/src/DotNetCore.CAP/Internal/ObjectMethodExecutor/AwaitableInfo.cs b/src/DotNetCore.CAP/Internal/ObjectMethodExecutor/AwaitableInfo.cs index cdf9fc7..efbe9ab 100644 --- a/src/DotNetCore.CAP/Internal/ObjectMethodExecutor/AwaitableInfo.cs +++ b/src/DotNetCore.CAP/Internal/ObjectMethodExecutor/AwaitableInfo.cs @@ -1,128 +1,122 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#nullable disable using System; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; -// ReSharper disable once CheckNamespace -namespace Microsoft.Extensions.Internal +namespace Microsoft.Extensions.Internal; + +internal readonly struct AwaitableInfo { - internal readonly struct AwaitableInfo + public Type AwaiterType { get; } + public PropertyInfo AwaiterIsCompletedProperty { get; } + public MethodInfo AwaiterGetResultMethod { get; } + public MethodInfo AwaiterOnCompletedMethod { get; } + public MethodInfo AwaiterUnsafeOnCompletedMethod { get; } + public Type ResultType { get; } + public MethodInfo GetAwaiterMethod { get; } + + public AwaitableInfo( + Type awaiterType, + PropertyInfo awaiterIsCompletedProperty, + MethodInfo awaiterGetResultMethod, + MethodInfo awaiterOnCompletedMethod, + MethodInfo awaiterUnsafeOnCompletedMethod, + Type resultType, + MethodInfo getAwaiterMethod) { - public Type AwaiterType { get; } - public PropertyInfo AwaiterIsCompletedProperty { get; } - public MethodInfo AwaiterGetResultMethod { get; } - public MethodInfo AwaiterOnCompletedMethod { get; } - public MethodInfo AwaiterUnsafeOnCompletedMethod { get; } - public Type ResultType { get; } - public MethodInfo GetAwaiterMethod { get; } + AwaiterType = awaiterType; + AwaiterIsCompletedProperty = awaiterIsCompletedProperty; + AwaiterGetResultMethod = awaiterGetResultMethod; + AwaiterOnCompletedMethod = awaiterOnCompletedMethod; + AwaiterUnsafeOnCompletedMethod = awaiterUnsafeOnCompletedMethod; + ResultType = resultType; + GetAwaiterMethod = getAwaiterMethod; + } - public AwaitableInfo( - Type awaiterType, - PropertyInfo awaiterIsCompletedProperty, - MethodInfo awaiterGetResultMethod, - MethodInfo awaiterOnCompletedMethod, - MethodInfo awaiterUnsafeOnCompletedMethod, - Type resultType, - MethodInfo getAwaiterMethod) - { - AwaiterType = awaiterType; - AwaiterIsCompletedProperty = awaiterIsCompletedProperty; - AwaiterGetResultMethod = awaiterGetResultMethod; - AwaiterOnCompletedMethod = awaiterOnCompletedMethod; - AwaiterUnsafeOnCompletedMethod = awaiterUnsafeOnCompletedMethod; - ResultType = resultType; - GetAwaiterMethod = getAwaiterMethod; - } + public static bool IsTypeAwaitable(Type type, out AwaitableInfo awaitableInfo) + { + // Based on Roslyn code: http://source.roslyn.io/#Microsoft.CodeAnalysis.Workspaces/Shared/Extensions/ISymbolExtensions.cs,db4d48ba694b9347 - public static bool IsTypeAwaitable(Type type, out AwaitableInfo awaitableInfo) + // Awaitable must have method matching "object GetAwaiter()" + var getAwaiterMethod = type.GetRuntimeMethods().FirstOrDefault(m => + m.Name.Equals("GetAwaiter", StringComparison.OrdinalIgnoreCase) + && m.GetParameters().Length == 0 + && m.ReturnType != null); + if (getAwaiterMethod == null) { - // Based on Roslyn code: http://source.roslyn.io/#Microsoft.CodeAnalysis.Workspaces/Shared/Extensions/ISymbolExtensions.cs,db4d48ba694b9347 + awaitableInfo = default(AwaitableInfo); + return false; + } - // Awaitable must have method matching "object GetAwaiter()" - var getAwaiterMethod = type.GetRuntimeMethods().FirstOrDefault(m => - m.Name.Equals("GetAwaiter", StringComparison.OrdinalIgnoreCase) - && m.GetParameters().Length == 0); - if (getAwaiterMethod == null) - { - awaitableInfo = default(AwaitableInfo); - return false; - } + var awaiterType = getAwaiterMethod.ReturnType; - var awaiterType = getAwaiterMethod.ReturnType; + // Awaiter must have property matching "bool IsCompleted { get; }" + var isCompletedProperty = awaiterType.GetRuntimeProperties().FirstOrDefault(p => + p.Name.Equals("IsCompleted", StringComparison.OrdinalIgnoreCase) + && p.PropertyType == typeof(bool) + && p.GetMethod != null); + if (isCompletedProperty == null) + { + awaitableInfo = default(AwaitableInfo); + return false; + } - // Awaiter must have property matching "bool IsCompleted { get; }" - var isCompletedProperty = awaiterType.GetRuntimeProperties().FirstOrDefault(p => - p.Name.Equals("IsCompleted", StringComparison.OrdinalIgnoreCase) - && p.PropertyType == typeof(bool) - && p.GetMethod != null); - if (isCompletedProperty == null) - { - awaitableInfo = default(AwaitableInfo); - return false; - } + // Awaiter must implement INotifyCompletion + var awaiterInterfaces = awaiterType.GetInterfaces(); + var implementsINotifyCompletion = awaiterInterfaces.Any(t => t == typeof(INotifyCompletion)); + if (!implementsINotifyCompletion) + { + awaitableInfo = default(AwaitableInfo); + return false; + } - // Awaiter must implement INotifyCompletion - var awaiterInterfaces = awaiterType.GetInterfaces(); - var implementsINotifyCompletion = awaiterInterfaces.Any(t => t == typeof(INotifyCompletion)); - if (!implementsINotifyCompletion) - { - awaitableInfo = default(AwaitableInfo); - return false; - } + // INotifyCompletion supplies a method matching "void OnCompleted(Action action)" + var onCompletedMethod = typeof(INotifyCompletion).GetRuntimeMethods().Single(m => + m.Name.Equals("OnCompleted", StringComparison.OrdinalIgnoreCase) + && m.ReturnType == typeof(void) + && m.GetParameters().Length == 1 + && m.GetParameters()[0].ParameterType == typeof(Action)); - // INotifyCompletion supplies a method matching "void OnCompleted(Action action)" - var iNotifyCompletionMap = awaiterType - .GetTypeInfo() - .GetRuntimeInterfaceMap(typeof(INotifyCompletion)); - var onCompletedMethod = iNotifyCompletionMap.InterfaceMethods.Single(m => - m.Name.Equals("OnCompleted", StringComparison.OrdinalIgnoreCase) + // Awaiter optionally implements ICriticalNotifyCompletion + var implementsICriticalNotifyCompletion = awaiterInterfaces.Any(t => t == typeof(ICriticalNotifyCompletion)); + MethodInfo unsafeOnCompletedMethod; + if (implementsICriticalNotifyCompletion) + { + // ICriticalNotifyCompletion supplies a method matching "void UnsafeOnCompleted(Action action)" + unsafeOnCompletedMethod = typeof(ICriticalNotifyCompletion).GetRuntimeMethods().Single(m => + m.Name.Equals("UnsafeOnCompleted", StringComparison.OrdinalIgnoreCase) && m.ReturnType == typeof(void) && m.GetParameters().Length == 1 && m.GetParameters()[0].ParameterType == typeof(Action)); + } + else + { + unsafeOnCompletedMethod = null; + } - // Awaiter optionally implements ICriticalNotifyCompletion - var implementsICriticalNotifyCompletion = - awaiterInterfaces.Any(t => t == typeof(ICriticalNotifyCompletion)); - MethodInfo unsafeOnCompletedMethod; - if (implementsICriticalNotifyCompletion) - { - // ICriticalNotifyCompletion supplies a method matching "void UnsafeOnCompleted(Action action)" - var iCriticalNotifyCompletionMap = awaiterType - .GetTypeInfo() - .GetRuntimeInterfaceMap(typeof(ICriticalNotifyCompletion)); - unsafeOnCompletedMethod = iCriticalNotifyCompletionMap.InterfaceMethods.Single(m => - m.Name.Equals("UnsafeOnCompleted", StringComparison.OrdinalIgnoreCase) - && m.ReturnType == typeof(void) - && m.GetParameters().Length == 1 - && m.GetParameters()[0].ParameterType == typeof(Action)); - } - else - { - unsafeOnCompletedMethod = null; - } - - // Awaiter must have method matching "void GetResult" or "T GetResult()" - var getResultMethod = awaiterType.GetRuntimeMethods().FirstOrDefault(m => - m.Name.Equals("GetResult") - && m.GetParameters().Length == 0); - if (getResultMethod == null) - { - awaitableInfo = default(AwaitableInfo); - return false; - } - - awaitableInfo = new AwaitableInfo( - awaiterType, - isCompletedProperty, - getResultMethod, - onCompletedMethod, - unsafeOnCompletedMethod, - getResultMethod.ReturnType, - getAwaiterMethod); - return true; + // Awaiter must have method matching "void GetResult" or "T GetResult()" + var getResultMethod = awaiterType.GetRuntimeMethods().FirstOrDefault(m => + m.Name.Equals("GetResult") + && m.GetParameters().Length == 0); + if (getResultMethod == null) + { + awaitableInfo = default(AwaitableInfo); + return false; } + + awaitableInfo = new AwaitableInfo( + awaiterType, + isCompletedProperty, + getResultMethod, + onCompletedMethod, + unsafeOnCompletedMethod, + getResultMethod.ReturnType, + getAwaiterMethod); + return true; } } \ No newline at end of file diff --git a/src/DotNetCore.CAP/Internal/ObjectMethodExecutor/CoercedAwaitableInfo.cs b/src/DotNetCore.CAP/Internal/ObjectMethodExecutor/CoercedAwaitableInfo.cs index 39c3475..d9eaad1 100644 --- a/src/DotNetCore.CAP/Internal/ObjectMethodExecutor/CoercedAwaitableInfo.cs +++ b/src/DotNetCore.CAP/Internal/ObjectMethodExecutor/CoercedAwaitableInfo.cs @@ -1,57 +1,56 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#nullable disable using System; using System.Linq.Expressions; -// ReSharper disable once CheckNamespace -namespace Microsoft.Extensions.Internal +namespace Microsoft.Extensions.Internal; + +internal readonly struct CoercedAwaitableInfo { - internal readonly struct CoercedAwaitableInfo + public AwaitableInfo AwaitableInfo { get; } + public Expression CoercerExpression { get; } + public Type CoercerResultType { get; } + public bool RequiresCoercion => CoercerExpression != null; + + public CoercedAwaitableInfo(AwaitableInfo awaitableInfo) { - public AwaitableInfo AwaitableInfo { get; } - public Expression CoercerExpression { get; } - public Type CoercerResultType { get; } - public bool RequiresCoercion => CoercerExpression != null; + AwaitableInfo = awaitableInfo; + CoercerExpression = null; + CoercerResultType = null; + } - public CoercedAwaitableInfo(AwaitableInfo awaitableInfo) - { - AwaitableInfo = awaitableInfo; - CoercerExpression = null; - CoercerResultType = null; - } + public CoercedAwaitableInfo(Expression coercerExpression, Type coercerResultType, AwaitableInfo coercedAwaitableInfo) + { + CoercerExpression = coercerExpression; + CoercerResultType = coercerResultType; + AwaitableInfo = coercedAwaitableInfo; + } - public CoercedAwaitableInfo(Expression coercerExpression, Type coercerResultType, - AwaitableInfo coercedAwaitableInfo) + public static bool IsTypeAwaitable(Type type, out CoercedAwaitableInfo info) + { + if (AwaitableInfo.IsTypeAwaitable(type, out var directlyAwaitableInfo)) { - CoercerExpression = coercerExpression; - CoercerResultType = coercerResultType; - AwaitableInfo = coercedAwaitableInfo; + info = new CoercedAwaitableInfo(directlyAwaitableInfo); + return true; } - public static bool IsTypeAwaitable(Type type, out CoercedAwaitableInfo info) - { - if (AwaitableInfo.IsTypeAwaitable(type, out var directlyAwaitableInfo)) - { - info = new CoercedAwaitableInfo(directlyAwaitableInfo); - return true; - } - - // It's not directly awaitable, but maybe we can coerce it. - // Currently we support coercing FSharpAsync. - if (ObjectMethodExecutorFSharpSupport.TryBuildCoercerFromFSharpAsyncToAwaitable(type, + // It's not directly awaitable, but maybe we can coerce it. + // Currently we support coercing FSharpAsync. + if (ObjectMethodExecutorFSharpSupport.TryBuildCoercerFromFSharpAsyncToAwaitable(type, out var coercerExpression, out var coercerResultType)) + { + if (AwaitableInfo.IsTypeAwaitable(coercerResultType, out var coercedAwaitableInfo)) { - if (AwaitableInfo.IsTypeAwaitable(coercerResultType, out var coercedAwaitableInfo)) - { - info = new CoercedAwaitableInfo(coercerExpression, coercerResultType, coercedAwaitableInfo); - return true; - } + info = new CoercedAwaitableInfo(coercerExpression, coercerResultType, coercedAwaitableInfo); + return true; } - - info = default(CoercedAwaitableInfo); - return false; } + + info = default(CoercedAwaitableInfo); + return false; } } \ No newline at end of file diff --git a/src/DotNetCore.CAP/Internal/ObjectMethodExecutor/ObjectMethodExecutor.cs b/src/DotNetCore.CAP/Internal/ObjectMethodExecutor/ObjectMethodExecutor.cs index 26708a2..defdc14 100644 --- a/src/DotNetCore.CAP/Internal/ObjectMethodExecutor/ObjectMethodExecutor.cs +++ b/src/DotNetCore.CAP/Internal/ObjectMethodExecutor/ObjectMethodExecutor.cs @@ -1,338 +1,344 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#nullable enable using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq.Expressions; using System.Reflection; -// ReSharper disable once CheckNamespace -namespace Microsoft.Extensions.Internal +namespace Microsoft.Extensions.Internal; + +internal class ObjectMethodExecutor { - internal class ObjectMethodExecutor - { - // ReSharper disable once InconsistentNaming - private static readonly ConstructorInfo _objectMethodExecutorAwaitableConstructor = - typeof(ObjectMethodExecutorAwaitable).GetConstructor(new[] - { - typeof(object), // customAwaitable - typeof(Func), // getAwaiterMethod - typeof(Func), // isCompletedMethod - typeof(Func), // getResultMethod + private readonly object?[]? _parameterDefaultValues; + private readonly MethodExecutorAsync? _executorAsync; + private readonly MethodExecutor? _executor; + + private static readonly ConstructorInfo _objectMethodExecutorAwaitableConstructor = + typeof(ObjectMethodExecutorAwaitable).GetConstructor(new[] { + typeof(object), // customAwaitable + typeof(Func), // getAwaiterMethod + typeof(Func), // isCompletedMethod + typeof(Func), // getResultMethod typeof(Action), // onCompletedMethod - typeof(Action) // unsafeOnCompletedMethod - }); - - private readonly MethodExecutor _executor; - private readonly MethodExecutorAsync _executorAsync; - private readonly object[] _parameterDefaultValues; + typeof(Action) // unsafeOnCompletedMethod + })!; - private ObjectMethodExecutor(MethodInfo methodInfo, TypeInfo targetTypeInfo, object[] parameterDefaultValues) + private ObjectMethodExecutor(MethodInfo methodInfo, TypeInfo targetTypeInfo, object?[]? parameterDefaultValues) + { + if (methodInfo == null) { - if (methodInfo == null) - { - throw new ArgumentNullException(nameof(methodInfo)); - } - - MethodInfo = methodInfo; - MethodParameters = methodInfo.GetParameters(); - TargetTypeInfo = targetTypeInfo; - MethodReturnType = methodInfo.ReturnType; + throw new ArgumentNullException(nameof(methodInfo)); + } - var isAwaitable = CoercedAwaitableInfo.IsTypeAwaitable(MethodReturnType, out var coercedAwaitableInfo); + MethodInfo = methodInfo; + MethodParameters = methodInfo.GetParameters(); + TargetTypeInfo = targetTypeInfo; + MethodReturnType = methodInfo.ReturnType; - IsMethodAsync = isAwaitable; - AsyncResultType = isAwaitable ? coercedAwaitableInfo.AwaitableInfo.ResultType : null; + var isAwaitable = CoercedAwaitableInfo.IsTypeAwaitable(MethodReturnType, out var coercedAwaitableInfo); - // Upstream code may prefer to use the sync-executor even for async methods, because if it knows - // that the result is a specific Task where T is known, then it can directly cast to that type - // and await it without the extra heap allocations involved in the _executorAsync code path. - _executor = GetExecutor(methodInfo, targetTypeInfo); + IsMethodAsync = isAwaitable; + AsyncResultType = isAwaitable ? coercedAwaitableInfo.AwaitableInfo.ResultType : null; - if (IsMethodAsync) - { - _executorAsync = GetExecutorAsync(methodInfo, targetTypeInfo, coercedAwaitableInfo); - } + // Upstream code may prefer to use the sync-executor even for async methods, because if it knows + // that the result is a specific Task where T is known, then it can directly cast to that type + // and await it without the extra heap allocations involved in the _executorAsync code path. + _executor = GetExecutor(methodInfo, targetTypeInfo); - _parameterDefaultValues = parameterDefaultValues; + if (IsMethodAsync) + { + _executorAsync = GetExecutorAsync(methodInfo, targetTypeInfo, coercedAwaitableInfo); } - public MethodInfo MethodInfo { get; } + _parameterDefaultValues = parameterDefaultValues; + } + + private delegate ObjectMethodExecutorAwaitable MethodExecutorAsync(object target, object?[]? parameters); + + private delegate object? MethodExecutor(object target, object?[]? parameters); - public ParameterInfo[] MethodParameters { get; } + private delegate void VoidMethodExecutor(object target, object?[]? parameters); - public TypeInfo TargetTypeInfo { get; } + public MethodInfo MethodInfo { get; } - public Type AsyncResultType { get; } + public ParameterInfo[] MethodParameters { get; } - // This field is made internal set because it is set in unit tests. - public Type MethodReturnType { get; internal set; } + public TypeInfo TargetTypeInfo { get; } - public bool IsMethodAsync { get; } + public Type? AsyncResultType { get; } - public static ObjectMethodExecutor Create(MethodInfo methodInfo, TypeInfo targetTypeInfo) + // This field is made internal set because it is set in unit tests. + public Type MethodReturnType { get; internal set; } + + public bool IsMethodAsync { get; } + + public static ObjectMethodExecutor Create(MethodInfo methodInfo, TypeInfo targetTypeInfo) + { + return new ObjectMethodExecutor(methodInfo, targetTypeInfo, null); + } + + public static ObjectMethodExecutor Create(MethodInfo methodInfo, TypeInfo targetTypeInfo, object?[] parameterDefaultValues) + { + if (parameterDefaultValues == null) { - return new ObjectMethodExecutor(methodInfo, targetTypeInfo, null); + throw new ArgumentNullException(nameof(parameterDefaultValues)); } - public static ObjectMethodExecutor Create(MethodInfo methodInfo, TypeInfo targetTypeInfo, - object[] parameterDefaultValues) - { - if (parameterDefaultValues == null) - { - throw new ArgumentNullException(nameof(parameterDefaultValues)); - } + return new ObjectMethodExecutor(methodInfo, targetTypeInfo, parameterDefaultValues); + } - return new ObjectMethodExecutor(methodInfo, targetTypeInfo, parameterDefaultValues); - } + /// + /// Executes the configured method on . This can be used whether or not + /// the configured method is asynchronous. + /// + /// + /// Even if the target method is asynchronous, it's desirable to invoke it using Execute rather than + /// ExecuteAsync if you know at compile time what the return type is, because then you can directly + /// "await" that value (via a cast), and then the generated code will be able to reference the + /// resulting awaitable as a value-typed variable. If you use ExecuteAsync instead, the generated + /// code will have to treat the resulting awaitable as a boxed object, because it doesn't know at + /// compile time what type it would be. + /// + /// The object whose method is to be executed. + /// Parameters to pass to the method. + /// The method return value. + public object? Execute(object target, object?[]? parameters) + { + Debug.Assert(_executor != null, "Sync execution is not supported."); + return _executor(target, parameters); + } + + /// + /// Executes the configured method on . This can only be used if the configured + /// method is asynchronous. + /// + /// + /// If you don't know at compile time the type of the method's returned awaitable, you can use ExecuteAsync, + /// which supplies an awaitable-of-object. This always works, but can incur several extra heap allocations + /// as compared with using Execute and then using "await" on the result value typecasted to the known + /// awaitable type. The possible extra heap allocations are for: + /// + /// 1. The custom awaitable (though usually there's a heap allocation for this anyway, since normally + /// it's a reference type, and you normally create a new instance per call). + /// 2. The custom awaiter (whether or not it's a value type, since if it's not, you need a new instance + /// of it, and if it is, it will have to be boxed so the calling code can reference it as an object). + /// 3. The async result value, if it's a value type (it has to be boxed as an object, since the calling + /// code doesn't know what type it's going to be). + /// + /// The object whose method is to be executed. + /// Parameters to pass to the method. + /// An object that you can "await" to get the method return value. + public ObjectMethodExecutorAwaitable ExecuteAsync(object target, object?[]? parameters) + { + Debug.Assert(_executorAsync != null, "Async execution is not supported."); + return _executorAsync(target, parameters); + } - /// - /// Executes the configured method on . This can be used whether or not - /// the configured method is asynchronous. - /// - /// - /// Even if the target method is asynchronous, it's desirable to invoke it using Execute rather than - /// ExecuteAsync if you know at compile time what the return type is, because then you can directly - /// "await" that value (via a cast), and then the generated code will be able to reference the - /// resulting awaitable as a value-typed variable. If you use ExecuteAsync instead, the generated - /// code will have to treat the resulting awaitable as a boxed object, because it doesn't know at - /// compile time what type it would be. - /// - /// The object whose method is to be executed. - /// Parameters to pass to the method. - /// The method return value. - public object Execute(object target, params object[] parameters) + public object? GetDefaultValueForParameter(int index) + { + if (_parameterDefaultValues == null) { - return _executor(target, parameters); + throw new InvalidOperationException($"Cannot call {nameof(GetDefaultValueForParameter)}, because no parameter default values were supplied."); } - /// - /// Executes the configured method on . This can only be used if the configured - /// method is asynchronous. - /// - /// - /// If you don't know at compile time the type of the method's returned awaitable, you can use ExecuteAsync, - /// which supplies an awaitable-of-object. This always works, but can incur several extra heap allocations - /// as compared with using Execute and then using "await" on the result value typecasted to the known - /// awaitable type. The possible extra heap allocations are for: - /// 1. The custom awaitable (though usually there's a heap allocation for this anyway, since normally - /// it's a reference type, and you normally create a new instance per call). - /// 2. The custom awaiter (whether or not it's a value type, since if it's not, you need a new instance - /// of it, and if it is, it will have to be boxed so the calling code can reference it as an object). - /// 3. The async result value, if it's a value type (it has to be boxed as an object, since the calling - /// code doesn't know what type it's going to be). - /// - /// The object whose method is to be executed. - /// Parameters to pass to the method. - /// An object that you can "await" to get the method return value. - public ObjectMethodExecutorAwaitable ExecuteAsync(object target, params object[] parameters) + if (index < 0 || index > MethodParameters.Length - 1) { - return _executorAsync(target, parameters); + throw new ArgumentOutOfRangeException(nameof(index)); } - public object GetDefaultValueForParameter(int index) + return _parameterDefaultValues[index]; + } + + private static MethodExecutor GetExecutor(MethodInfo methodInfo, TypeInfo targetTypeInfo) + { + // Parameters to executor + var targetParameter = Expression.Parameter(typeof(object), "target"); + var parametersParameter = Expression.Parameter(typeof(object?[]), "parameters"); + + // Build parameter list + var paramInfos = methodInfo.GetParameters(); + var parameters = new List(paramInfos.Length); + for (int i = 0; i < paramInfos.Length; i++) { - if (_parameterDefaultValues == null) - { - throw new InvalidOperationException( - $"Cannot call {nameof(GetDefaultValueForParameter)}, because no parameter default values were supplied."); - } - - if (index < 0 || index > MethodParameters.Length - 1) - { - throw new ArgumentOutOfRangeException(nameof(index)); - } - - return _parameterDefaultValues[index]; + var paramInfo = paramInfos[i]; + var valueObj = Expression.ArrayIndex(parametersParameter, Expression.Constant(i)); + var valueCast = Expression.Convert(valueObj, paramInfo.ParameterType); + + // valueCast is "(Ti) parameters[i]" + parameters.Add(valueCast); } - private static MethodExecutor GetExecutor(MethodInfo methodInfo, TypeInfo targetTypeInfo) + // Call method + var instanceCast = Expression.Convert(targetParameter, targetTypeInfo.AsType()); + var methodCall = Expression.Call(instanceCast, methodInfo, parameters); + + // methodCall is "((Ttarget) target) method((T0) parameters[0], (T1) parameters[1], ...)" + // Create function + if (methodCall.Type == typeof(void)) { - // Parameters to executor - var targetParameter = Expression.Parameter(typeof(object), "target"); - var parametersParameter = Expression.Parameter(typeof(object[]), "parameters"); - - // Build parameter list - var parameters = new List(); - var paramInfos = methodInfo.GetParameters(); - for (var i = 0; i < paramInfos.Length; i++) - { - var paramInfo = paramInfos[i]; - var valueObj = Expression.ArrayIndex(parametersParameter, Expression.Constant(i)); - var valueCast = Expression.Convert(valueObj, paramInfo.ParameterType); - - // valueCast is "(Ti) parameters[i]" - parameters.Add(valueCast); - } - - // Call method - var instanceCast = Expression.Convert(targetParameter, targetTypeInfo.AsType()); - var methodCall = Expression.Call(instanceCast, methodInfo, parameters); - - // methodCall is "((Ttarget) target) method((T0) parameters[0], (T1) parameters[1], ...)" - // Create function - if (methodCall.Type == typeof(void)) - { - var lambda = Expression.Lambda(methodCall, targetParameter, parametersParameter); - var voidExecutor = lambda.Compile(); - return WrapVoidMethod(voidExecutor); - } - else - { - // must coerce methodCall to match ActionExecutor signature - var castMethodCall = Expression.Convert(methodCall, typeof(object)); - var lambda = Expression.Lambda(castMethodCall, targetParameter, parametersParameter); - return lambda.Compile(); - } + var lambda = Expression.Lambda(methodCall, targetParameter, parametersParameter); + var voidExecutor = lambda.Compile(); + return WrapVoidMethod(voidExecutor); + } + else + { + // must coerce methodCall to match ActionExecutor signature + var castMethodCall = Expression.Convert(methodCall, typeof(object)); + var lambda = Expression.Lambda(castMethodCall, targetParameter, parametersParameter); + return lambda.Compile(); } + } + + private static MethodExecutor WrapVoidMethod(VoidMethodExecutor executor) + { + return delegate (object target, object?[]? parameters) + { + executor(target, parameters); + return null; + }; + } - private static MethodExecutor WrapVoidMethod(VoidMethodExecutor executor) + private static MethodExecutorAsync GetExecutorAsync( + MethodInfo methodInfo, + TypeInfo targetTypeInfo, + CoercedAwaitableInfo coercedAwaitableInfo) + { + // Parameters to executor + var targetParameter = Expression.Parameter(typeof(object), "target"); + var parametersParameter = Expression.Parameter(typeof(object[]), "parameters"); + + // Build parameter list + var paramInfos = methodInfo.GetParameters(); + var parameters = new List(paramInfos.Length); + for (int i = 0; i < paramInfos.Length; i++) { - return delegate(object target, object[] parameters) - { - executor(target, parameters); - return null; - }; + var paramInfo = paramInfos[i]; + var valueObj = Expression.ArrayIndex(parametersParameter, Expression.Constant(i)); + var valueCast = Expression.Convert(valueObj, paramInfo.ParameterType); + + // valueCast is "(Ti) parameters[i]" + parameters.Add(valueCast); } - private static MethodExecutorAsync GetExecutorAsync( - MethodInfo methodInfo, - TypeInfo targetTypeInfo, - CoercedAwaitableInfo coercedAwaitableInfo) + // Call method + var instanceCast = Expression.Convert(targetParameter, targetTypeInfo.AsType()); + var methodCall = Expression.Call(instanceCast, methodInfo, parameters); + + // Using the method return value, construct an ObjectMethodExecutorAwaitable based on + // the info we have about its implementation of the awaitable pattern. Note that all + // the funcs/actions we construct here are precompiled, so that only one instance of + // each is preserved throughout the lifetime of the ObjectMethodExecutor. + + // var getAwaiterFunc = (object awaitable) => + // (object)((CustomAwaitableType)awaitable).GetAwaiter(); + var customAwaitableParam = Expression.Parameter(typeof(object), "awaitable"); + var awaitableInfo = coercedAwaitableInfo.AwaitableInfo; + var postCoercionMethodReturnType = coercedAwaitableInfo.CoercerResultType ?? methodInfo.ReturnType; + var getAwaiterFunc = Expression.Lambda>( + Expression.Convert( + Expression.Call( + Expression.Convert(customAwaitableParam, postCoercionMethodReturnType), + awaitableInfo.GetAwaiterMethod), + typeof(object)), + customAwaitableParam).Compile(); + + // var isCompletedFunc = (object awaiter) => + // ((CustomAwaiterType)awaiter).IsCompleted; + var isCompletedParam = Expression.Parameter(typeof(object), "awaiter"); + var isCompletedFunc = Expression.Lambda>( + Expression.MakeMemberAccess( + Expression.Convert(isCompletedParam, awaitableInfo.AwaiterType), + awaitableInfo.AwaiterIsCompletedProperty), + isCompletedParam).Compile(); + + var getResultParam = Expression.Parameter(typeof(object), "awaiter"); + Func getResultFunc; + if (awaitableInfo.ResultType == typeof(void)) { - // Parameters to executor - var targetParameter = Expression.Parameter(typeof(object), "target"); - var parametersParameter = Expression.Parameter(typeof(object[]), "parameters"); - - // Build parameter list - var parameters = new List(); - var paramInfos = methodInfo.GetParameters(); - for (var i = 0; i < paramInfos.Length; i++) - { - var paramInfo = paramInfos[i]; - var valueObj = Expression.ArrayIndex(parametersParameter, Expression.Constant(i)); - var valueCast = Expression.Convert(valueObj, paramInfo.ParameterType); - - // valueCast is "(Ti) parameters[i]" - parameters.Add(valueCast); - } - - // Call method - var instanceCast = Expression.Convert(targetParameter, targetTypeInfo.AsType()); - var methodCall = Expression.Call(instanceCast, methodInfo, parameters); - - // Using the method return value, construct an ObjectMethodExecutorAwaitable based on - // the info we have about its implementation of the awaitable pattern. Note that all - // the funcs/actions we construct here are precompiled, so that only one instance of - // each is preserved throughout the lifetime of the ObjectMethodExecutor. - - // var getAwaiterFunc = (object awaitable) => - // (object)((CustomAwaitableType)awaitable).GetAwaiter(); - var customAwaitableParam = Expression.Parameter(typeof(object), "awaitable"); - var awaitableInfo = coercedAwaitableInfo.AwaitableInfo; - var postCoercionMethodReturnType = coercedAwaitableInfo.CoercerResultType ?? methodInfo.ReturnType; - var getAwaiterFunc = Expression.Lambda>( + // var getResultFunc = (object awaiter) => + // { + // ((CustomAwaiterType)awaiter).GetResult(); // We need to invoke this to surface any exceptions + // return (object)null; + // }; + getResultFunc = Expression.Lambda>( + Expression.Block( + Expression.Call( + Expression.Convert(getResultParam, awaitableInfo.AwaiterType), + awaitableInfo.AwaiterGetResultMethod), + Expression.Constant(null) + ), + getResultParam).Compile(); + } + else + { + // var getResultFunc = (object awaiter) => + // (object)((CustomAwaiterType)awaiter).GetResult(); + getResultFunc = Expression.Lambda>( Expression.Convert( Expression.Call( - Expression.Convert(customAwaitableParam, postCoercionMethodReturnType), - awaitableInfo.GetAwaiterMethod), + Expression.Convert(getResultParam, awaitableInfo.AwaiterType), + awaitableInfo.AwaiterGetResultMethod), typeof(object)), - customAwaitableParam).Compile(); - - // var isCompletedFunc = (object awaiter) => - // ((CustomAwaiterType)awaiter).IsCompleted; - var isCompletedParam = Expression.Parameter(typeof(object), "awaiter"); - var isCompletedFunc = Expression.Lambda>( - Expression.MakeMemberAccess( - Expression.Convert(isCompletedParam, awaitableInfo.AwaiterType), - awaitableInfo.AwaiterIsCompletedProperty), - isCompletedParam).Compile(); - - var getResultParam = Expression.Parameter(typeof(object), "awaiter"); - Func getResultFunc; - if (awaitableInfo.ResultType == typeof(void)) - { - getResultFunc = Expression.Lambda>( - Expression.Block( - Expression.Call( - Expression.Convert(getResultParam, awaitableInfo.AwaiterType), - awaitableInfo.AwaiterGetResultMethod), - Expression.Constant(null) - ), - getResultParam).Compile(); - } - else - { - getResultFunc = Expression.Lambda>( - Expression.Convert( - Expression.Call( - Expression.Convert(getResultParam, awaitableInfo.AwaiterType), - awaitableInfo.AwaiterGetResultMethod), - typeof(object)), - getResultParam).Compile(); - } - - // var onCompletedFunc = (object awaiter, Action continuation) => { - // ((CustomAwaiterType)awaiter).OnCompleted(continuation); + getResultParam).Compile(); + } + + // var onCompletedFunc = (object awaiter, Action continuation) => { + // ((CustomAwaiterType)awaiter).OnCompleted(continuation); + // }; + var onCompletedParam1 = Expression.Parameter(typeof(object), "awaiter"); + var onCompletedParam2 = Expression.Parameter(typeof(Action), "continuation"); + var onCompletedFunc = Expression.Lambda>( + Expression.Call( + Expression.Convert(onCompletedParam1, awaitableInfo.AwaiterType), + awaitableInfo.AwaiterOnCompletedMethod, + onCompletedParam2), + onCompletedParam1, + onCompletedParam2).Compile(); + + Action? unsafeOnCompletedFunc = null; + if (awaitableInfo.AwaiterUnsafeOnCompletedMethod != null) + { + // var unsafeOnCompletedFunc = (object awaiter, Action continuation) => { + // ((CustomAwaiterType)awaiter).UnsafeOnCompleted(continuation); // }; - var onCompletedParam1 = Expression.Parameter(typeof(object), "awaiter"); - var onCompletedParam2 = Expression.Parameter(typeof(Action), "continuation"); - var onCompletedFunc = Expression.Lambda>( + var unsafeOnCompletedParam1 = Expression.Parameter(typeof(object), "awaiter"); + var unsafeOnCompletedParam2 = Expression.Parameter(typeof(Action), "continuation"); + unsafeOnCompletedFunc = Expression.Lambda>( Expression.Call( - Expression.Convert(onCompletedParam1, awaitableInfo.AwaiterType), - awaitableInfo.AwaiterOnCompletedMethod, - onCompletedParam2), - onCompletedParam1, - onCompletedParam2).Compile(); - - Action unsafeOnCompletedFunc = null; - if (awaitableInfo.AwaiterUnsafeOnCompletedMethod != null) - { - // var unsafeOnCompletedFunc = (object awaiter, Action continuation) => { - // ((CustomAwaiterType)awaiter).UnsafeOnCompleted(continuation); - // }; - var unsafeOnCompletedParam1 = Expression.Parameter(typeof(object), "awaiter"); - var unsafeOnCompletedParam2 = Expression.Parameter(typeof(Action), "continuation"); - unsafeOnCompletedFunc = Expression.Lambda>( - Expression.Call( - Expression.Convert(unsafeOnCompletedParam1, awaitableInfo.AwaiterType), - awaitableInfo.AwaiterUnsafeOnCompletedMethod, - unsafeOnCompletedParam2), - unsafeOnCompletedParam1, - unsafeOnCompletedParam2).Compile(); - } - - // If we need to pass the method call result through a coercer function to get an - // awaitable, then do so. - var coercedMethodCall = coercedAwaitableInfo.RequiresCoercion - ? Expression.Invoke(coercedAwaitableInfo.CoercerExpression, methodCall) - : (Expression) methodCall; - - // return new ObjectMethodExecutorAwaitable( - // (object)coercedMethodCall, - // getAwaiterFunc, - // isCompletedFunc, - // getResultFunc, - // onCompletedFunc, - // unsafeOnCompletedFunc); - var returnValueExpression = Expression.New( - _objectMethodExecutorAwaitableConstructor, - Expression.Convert(coercedMethodCall, typeof(object)), - Expression.Constant(getAwaiterFunc), - Expression.Constant(isCompletedFunc), - Expression.Constant(getResultFunc), - Expression.Constant(onCompletedFunc), - Expression.Constant(unsafeOnCompletedFunc, typeof(Action))); - - var lambda = - Expression.Lambda(returnValueExpression, targetParameter, parametersParameter); - return lambda.Compile(); + Expression.Convert(unsafeOnCompletedParam1, awaitableInfo.AwaiterType), + awaitableInfo.AwaiterUnsafeOnCompletedMethod, + unsafeOnCompletedParam2), + unsafeOnCompletedParam1, + unsafeOnCompletedParam2).Compile(); } - private delegate ObjectMethodExecutorAwaitable MethodExecutorAsync(object target, params object[] parameters); - - private delegate object MethodExecutor(object target, params object[] parameters); - - private delegate void VoidMethodExecutor(object target, object[] parameters); + // If we need to pass the method call result through a coercer function to get an + // awaitable, then do so. + var coercedMethodCall = coercedAwaitableInfo.RequiresCoercion + ? Expression.Invoke(coercedAwaitableInfo.CoercerExpression, methodCall) + : (Expression)methodCall; + + // return new ObjectMethodExecutorAwaitable( + // (object)coercedMethodCall, + // getAwaiterFunc, + // isCompletedFunc, + // getResultFunc, + // onCompletedFunc, + // unsafeOnCompletedFunc); + var returnValueExpression = Expression.New( + _objectMethodExecutorAwaitableConstructor, + Expression.Convert(coercedMethodCall, typeof(object)), + Expression.Constant(getAwaiterFunc), + Expression.Constant(isCompletedFunc), + Expression.Constant(getResultFunc), + Expression.Constant(onCompletedFunc), + Expression.Constant(unsafeOnCompletedFunc, typeof(Action))); + + var lambda = Expression.Lambda(returnValueExpression, targetParameter, parametersParameter); + return lambda.Compile(); } } \ No newline at end of file diff --git a/src/DotNetCore.CAP/Internal/ObjectMethodExecutor/ObjectMethodExecutorAwaitable.cs b/src/DotNetCore.CAP/Internal/ObjectMethodExecutor/ObjectMethodExecutorAwaitable.cs index 29012ea..d524167 100644 --- a/src/DotNetCore.CAP/Internal/ObjectMethodExecutor/ObjectMethodExecutorAwaitable.cs +++ b/src/DotNetCore.CAP/Internal/ObjectMethodExecutor/ObjectMethodExecutorAwaitable.cs @@ -1,119 +1,115 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#nullable disable using System; using System.Runtime.CompilerServices; -// ReSharper disable once CheckNamespace -namespace Microsoft.Extensions.Internal +namespace Microsoft.Extensions.Internal; + +/// +/// Provides a common awaitable structure that can +/// return, regardless of whether the underlying value is a System.Task, an FSharpAsync, or an +/// application-defined custom awaitable. +/// +internal readonly struct ObjectMethodExecutorAwaitable { - /// - /// Provides a common awaitable structure that can - /// return, regardless of whether the underlying value is a System.Task, an FSharpAsync, or an - /// application-defined custom awaitable. - /// - internal readonly struct ObjectMethodExecutorAwaitable + private readonly object _customAwaitable; + private readonly Func _getAwaiterMethod; + private readonly Func _isCompletedMethod; + private readonly Func _getResultMethod; + private readonly Action _onCompletedMethod; + private readonly Action _unsafeOnCompletedMethod; + + // Perf note: since we're requiring the customAwaitable to be supplied here as an object, + // this will trigger a further allocation if it was a value type (i.e., to box it). We can't + // fix this by making the customAwaitable type generic, because the calling code typically + // does not know the type of the awaitable/awaiter at compile-time anyway. + // + // However, we could fix it by not passing the customAwaitable here at all, and instead + // passing a func that maps directly from the target object (e.g., controller instance), + // target method (e.g., action method info), and params array to the custom awaiter in the + // GetAwaiter() method below. In effect, by delaying the actual method call until the + // upstream code calls GetAwaiter on this ObjectMethodExecutorAwaitable instance. + // This optimization is not currently implemented because: + // [1] It would make no difference when the awaitable was an object type, which is + // by far the most common scenario (e.g., System.Task). + // [2] It would be complex - we'd need some kind of object pool to track all the parameter + // arrays until we needed to use them in GetAwaiter(). + // We can reconsider this in the future if there's a need to optimize for ValueTask + // or other value-typed awaitables. + + public ObjectMethodExecutorAwaitable( + object customAwaitable, + Func getAwaiterMethod, + Func isCompletedMethod, + Func getResultMethod, + Action onCompletedMethod, + Action unsafeOnCompletedMethod) + { + _customAwaitable = customAwaitable; + _getAwaiterMethod = getAwaiterMethod; + _isCompletedMethod = isCompletedMethod; + _getResultMethod = getResultMethod; + _onCompletedMethod = onCompletedMethod; + _unsafeOnCompletedMethod = unsafeOnCompletedMethod; + } + + public Awaiter GetAwaiter() + { + var customAwaiter = _getAwaiterMethod(_customAwaitable); + return new Awaiter(customAwaiter, _isCompletedMethod, _getResultMethod, _onCompletedMethod, _unsafeOnCompletedMethod); + } + + public readonly struct Awaiter : ICriticalNotifyCompletion { - private readonly object _customAwaitable; - private readonly Func _getAwaiterMethod; + private readonly object _customAwaiter; private readonly Func _isCompletedMethod; private readonly Func _getResultMethod; private readonly Action _onCompletedMethod; private readonly Action _unsafeOnCompletedMethod; - // Perf note: since we're requiring the customAwaitable to be supplied here as an object, - // this will trigger a further allocation if it was a value type (i.e., to box it). We can't - // fix this by making the customAwaitable type generic, because the calling code typically - // does not know the type of the awaitable/awaiter at compile-time anyway. - // - // However, we could fix it by not passing the customAwaitable here at all, and instead - // passing a func that maps directly from the target object (e.g., controller instance), - // target method (e.g., action method info), and params array to the custom awaiter in the - // GetAwaiter() method below. In effect, by delaying the actual method call until the - // upstream code calls GetAwaiter on this ObjectMethodExecutorAwaitable instance. - // This optimization is not currently implemented because: - // [1] It would make no difference when the awaitable was an object type, which is - // by far the most common scenario (e.g., System.Task). - // [2] It would be complex - we'd need some kind of object pool to track all the parameter - // arrays until we needed to use them in GetAwaiter(). - // We can reconsider this in the future if there's a need to optimize for ValueTask - // or other value-typed awaitables. - - public ObjectMethodExecutorAwaitable( - object customAwaitable, - Func getAwaiterMethod, + public Awaiter( + object customAwaiter, Func isCompletedMethod, Func getResultMethod, Action onCompletedMethod, Action unsafeOnCompletedMethod) { - _customAwaitable = customAwaitable; - _getAwaiterMethod = getAwaiterMethod; + _customAwaiter = customAwaiter; _isCompletedMethod = isCompletedMethod; _getResultMethod = getResultMethod; _onCompletedMethod = onCompletedMethod; _unsafeOnCompletedMethod = unsafeOnCompletedMethod; } - public Awaiter GetAwaiter() + public bool IsCompleted => _isCompletedMethod(_customAwaiter); + + public object GetResult() => _getResultMethod(_customAwaiter); + + public void OnCompleted(Action continuation) { - var customAwaiter = _getAwaiterMethod(_customAwaitable); - return new Awaiter(customAwaiter, _isCompletedMethod, _getResultMethod, _onCompletedMethod, - _unsafeOnCompletedMethod); + _onCompletedMethod(_customAwaiter, continuation); } - public struct Awaiter : ICriticalNotifyCompletion + public void UnsafeOnCompleted(Action continuation) { - private readonly object _customAwaiter; - private readonly Func _isCompletedMethod; - private readonly Func _getResultMethod; - private readonly Action _onCompletedMethod; - private readonly Action _unsafeOnCompletedMethod; - - public Awaiter( - object customAwaiter, - Func isCompletedMethod, - Func getResultMethod, - Action onCompletedMethod, - Action unsafeOnCompletedMethod) - { - _customAwaiter = customAwaiter; - _isCompletedMethod = isCompletedMethod; - _getResultMethod = getResultMethod; - _onCompletedMethod = onCompletedMethod; - _unsafeOnCompletedMethod = unsafeOnCompletedMethod; - } - - public bool IsCompleted => _isCompletedMethod(_customAwaiter); - - public object GetResult() - { - return _getResultMethod(_customAwaiter); - } - - public void OnCompleted(Action continuation) - { - _onCompletedMethod(_customAwaiter, continuation); - } - - public void UnsafeOnCompleted(Action continuation) - { - // If the underlying awaitable implements ICriticalNotifyCompletion, use its UnsafeOnCompleted. - // If not, fall back on using its OnCompleted. - // - // Why this is safe: - // - Implementing ICriticalNotifyCompletion is a way of saying the caller can choose whether it - // needs the execution context to be preserved (which it signals by calling OnCompleted), or - // that it doesn't (which it signals by calling UnsafeOnCompleted). Obviously it's faster *not* - // to preserve and restore the context, so we prefer that where possible. - // - If a caller doesn't need the execution context to be preserved and hence calls UnsafeOnCompleted, - // there's no harm in preserving it anyway - it's just a bit of wasted cost. That's what will happen - // if a caller sees that the proxy implements ICriticalNotifyCompletion but the proxy chooses to - // pass the call on to the underlying awaitable's OnCompleted method. + // If the underlying awaitable implements ICriticalNotifyCompletion, use its UnsafeOnCompleted. + // If not, fall back on using its OnCompleted. + // + // Why this is safe: + // - Implementing ICriticalNotifyCompletion is a way of saying the caller can choose whether it + // needs the execution context to be preserved (which it signals by calling OnCompleted), or + // that it doesn't (which it signals by calling UnsafeOnCompleted). Obviously it's faster *not* + // to preserve and restore the context, so we prefer that where possible. + // - If a caller doesn't need the execution context to be preserved and hence calls UnsafeOnCompleted, + // there's no harm in preserving it anyway - it's just a bit of wasted cost. That's what will happen + // if a caller sees that the proxy implements ICriticalNotifyCompletion but the proxy chooses to + // pass the call on to the underlying awaitable's OnCompleted method. - var underlyingMethodToUse = _unsafeOnCompletedMethod ?? _onCompletedMethod; - underlyingMethodToUse(_customAwaiter, continuation); - } + var underlyingMethodToUse = _unsafeOnCompletedMethod ?? _onCompletedMethod; + underlyingMethodToUse(_customAwaiter, continuation); } } } \ No newline at end of file diff --git a/src/DotNetCore.CAP/Internal/ObjectMethodExecutor/ObjectMethodExecutorFSharpSupport.cs b/src/DotNetCore.CAP/Internal/ObjectMethodExecutor/ObjectMethodExecutorFSharpSupport.cs index 4164e35..f60c652 100644 --- a/src/DotNetCore.CAP/Internal/ObjectMethodExecutor/ObjectMethodExecutorFSharpSupport.cs +++ b/src/DotNetCore.CAP/Internal/ObjectMethodExecutor/ObjectMethodExecutorFSharpSupport.cs @@ -1,5 +1,7 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#nullable disable using System; using System.Linq; @@ -8,139 +10,141 @@ using System.Reflection; using System.Threading; using System.Threading.Tasks; -// ReSharper disable once CheckNamespace -namespace Microsoft.Extensions.Internal +namespace Microsoft.Extensions.Internal; + +/// +/// Helper for detecting whether a given type is FSharpAsync`1, and if so, supplying +/// an for mapping instances of that type to a C# awaitable. +/// +/// +/// The main design goal here is to avoid taking a compile-time dependency on +/// FSharp.Core.dll, because non-F# applications wouldn't use it. So all the references +/// to FSharp types have to be constructed dynamically at runtime. +/// +internal static class ObjectMethodExecutorFSharpSupport { - /// - /// Helper for detecting whether a given type is FSharpAsync`1, and if so, supplying - /// an for mapping instances of that type to a C# awaitable. - /// - /// - /// The main design goal here is to avoid taking a compile-time dependency on - /// FSharp.Core.dll, because non-F# applications wouldn't use it. So all the references - /// to FSharp types have to be constructed dynamically at runtime. - /// - internal static class ObjectMethodExecutorFSharpSupport + private static readonly object _fsharpValuesCacheLock = new object(); + private static Assembly _fsharpCoreAssembly; + private static MethodInfo _fsharpAsyncStartAsTaskGenericMethod; + private static PropertyInfo _fsharpOptionOfTaskCreationOptionsNoneProperty; + private static PropertyInfo _fsharpOptionOfCancellationTokenNoneProperty; + + public static bool TryBuildCoercerFromFSharpAsyncToAwaitable( + Type possibleFSharpAsyncType, + out Expression coerceToAwaitableExpression, + out Type awaitableType) { - private static readonly object _fsharpValuesCacheLock = new object(); - private static Assembly _fsharpCoreAssembly; - private static MethodInfo _fsharpAsyncStartAsTaskGenericMethod; - private static PropertyInfo _fsharpOptionOfTaskCreationOptionsNoneProperty; - private static PropertyInfo _fsharpOptionOfCancellationTokenNoneProperty; - - public static bool TryBuildCoercerFromFSharpAsyncToAwaitable( - Type possibleFSharpAsyncType, - out Expression coerceToAwaitableExpression, - out Type awaitableType) + var methodReturnGenericType = possibleFSharpAsyncType.IsGenericType + ? possibleFSharpAsyncType.GetGenericTypeDefinition() + : null; + + if (!IsFSharpAsyncOpenGenericType(methodReturnGenericType)) { - var methodReturnGenericType = possibleFSharpAsyncType.IsGenericType - ? possibleFSharpAsyncType.GetGenericTypeDefinition() - : null; + coerceToAwaitableExpression = null; + awaitableType = null; + return false; + } - if (!IsFSharpAsyncOpenGenericType(methodReturnGenericType)) - { - coerceToAwaitableExpression = null; - awaitableType = null; - return false; - } + var awaiterResultType = possibleFSharpAsyncType.GetGenericArguments().Single(); + awaitableType = typeof(Task<>).MakeGenericType(awaiterResultType); + + // coerceToAwaitableExpression = (object fsharpAsync) => + // { + // return (object)FSharpAsync.StartAsTask( + // (Microsoft.FSharp.Control.FSharpAsync)fsharpAsync, + // FSharpOption.None, + // FSharpOption.None); + // }; + var startAsTaskClosedMethod = _fsharpAsyncStartAsTaskGenericMethod + .MakeGenericMethod(awaiterResultType); + var coerceToAwaitableParam = Expression.Parameter(typeof(object)); + coerceToAwaitableExpression = Expression.Lambda( + Expression.Convert( + Expression.Call( + startAsTaskClosedMethod, + Expression.Convert(coerceToAwaitableParam, possibleFSharpAsyncType), + Expression.MakeMemberAccess(null, _fsharpOptionOfTaskCreationOptionsNoneProperty), + Expression.MakeMemberAccess(null, _fsharpOptionOfCancellationTokenNoneProperty)), + typeof(object)), + coerceToAwaitableParam); + + return true; + } - var awaiterResultType = possibleFSharpAsyncType.GetGenericArguments().Single(); - awaitableType = typeof(Task<>).MakeGenericType(awaiterResultType); - - // coerceToAwaitableExpression = (object fsharpAsync) => - // { - // return (object)FSharpAsync.StartAsTask( - // (Microsoft.FSharp.Control.FSharpAsync)fsharpAsync, - // FSharpOption.None, - // FSharpOption.None); - // }; - var startAsTaskClosedMethod = _fsharpAsyncStartAsTaskGenericMethod - .MakeGenericMethod(awaiterResultType); - var coerceToAwaitableParam = Expression.Parameter(typeof(object)); - coerceToAwaitableExpression = Expression.Lambda( - Expression.Convert( - Expression.Call( - startAsTaskClosedMethod, - Expression.Convert(coerceToAwaitableParam, possibleFSharpAsyncType), - Expression.MakeMemberAccess(null, _fsharpOptionOfTaskCreationOptionsNoneProperty), - Expression.MakeMemberAccess(null, _fsharpOptionOfCancellationTokenNoneProperty)), - typeof(object)), - coerceToAwaitableParam); - - return true; + private static bool IsFSharpAsyncOpenGenericType(Type possibleFSharpAsyncGenericType) + { + var typeFullName = possibleFSharpAsyncGenericType?.FullName; + if (!string.Equals(typeFullName, "Microsoft.FSharp.Control.FSharpAsync`1", StringComparison.Ordinal)) + { + return false; } - private static bool IsFSharpAsyncOpenGenericType(Type possibleFSharpAsyncGenericType) + lock (_fsharpValuesCacheLock) { - var typeFullName = possibleFSharpAsyncGenericType?.FullName; - if (!string.Equals(typeFullName, "Microsoft.FSharp.Control.FSharpAsync`1", StringComparison.Ordinal)) + if (_fsharpCoreAssembly != null) { - return false; + // Since we've already found the real FSharpAsync.Core assembly, we just have + // to check that the supplied FSharpAsync`1 type is the one from that assembly. + return possibleFSharpAsyncGenericType.Assembly == _fsharpCoreAssembly; } - - lock (_fsharpValuesCacheLock) + else { - if (_fsharpCoreAssembly != null) - { - return possibleFSharpAsyncGenericType.Assembly == _fsharpCoreAssembly; - } - + // We'll keep trying to find the FSharp types/values each time any type called + // FSharpAsync`1 is supplied. return TryPopulateFSharpValueCaches(possibleFSharpAsyncGenericType); } } + } - private static bool TryPopulateFSharpValueCaches(Type possibleFSharpAsyncGenericType) - { - var assembly = possibleFSharpAsyncGenericType.Assembly; - var fsharpOptionType = assembly.GetType("Microsoft.FSharp.Core.FSharpOption`1"); - var fsharpAsyncType = assembly.GetType("Microsoft.FSharp.Control.FSharpAsync"); + private static bool TryPopulateFSharpValueCaches(Type possibleFSharpAsyncGenericType) + { + var assembly = possibleFSharpAsyncGenericType.Assembly; + var fsharpOptionType = assembly.GetType("Microsoft.FSharp.Core.FSharpOption`1"); + var fsharpAsyncType = assembly.GetType("Microsoft.FSharp.Control.FSharpAsync"); - if (fsharpOptionType == null || fsharpAsyncType == null) - { - return false; - } + if (fsharpOptionType == null || fsharpAsyncType == null) + { + return false; + } - // Get a reference to FSharpOption.None - var fsharpOptionOfTaskCreationOptionsType = fsharpOptionType - .MakeGenericType(typeof(TaskCreationOptions)); - _fsharpOptionOfTaskCreationOptionsNoneProperty = fsharpOptionOfTaskCreationOptionsType - .GetTypeInfo() - .GetRuntimeProperty("None"); - - // Get a reference to FSharpOption.None - var fsharpOptionOfCancellationTokenType = fsharpOptionType - .MakeGenericType(typeof(CancellationToken)); - _fsharpOptionOfCancellationTokenNoneProperty = fsharpOptionOfCancellationTokenType - .GetTypeInfo() - .GetRuntimeProperty("None"); - - // Get a reference to FSharpAsync.StartAsTask<> - var fsharpAsyncMethods = fsharpAsyncType - .GetRuntimeMethods() - .Where(m => m.Name.Equals("StartAsTask", StringComparison.Ordinal)); - foreach (var candidateMethodInfo in fsharpAsyncMethods) + // Get a reference to FSharpOption.None + var fsharpOptionOfTaskCreationOptionsType = fsharpOptionType + .MakeGenericType(typeof(TaskCreationOptions)); + _fsharpOptionOfTaskCreationOptionsNoneProperty = fsharpOptionOfTaskCreationOptionsType + .GetRuntimeProperty("None"); + + // Get a reference to FSharpOption.None + var fsharpOptionOfCancellationTokenType = fsharpOptionType + .MakeGenericType(typeof(CancellationToken)); + _fsharpOptionOfCancellationTokenNoneProperty = fsharpOptionOfCancellationTokenType + .GetRuntimeProperty("None"); + + // Get a reference to FSharpAsync.StartAsTask<> + var fsharpAsyncMethods = fsharpAsyncType + .GetRuntimeMethods() + .Where(m => m.Name.Equals("StartAsTask", StringComparison.Ordinal)); + foreach (var candidateMethodInfo in fsharpAsyncMethods) + { + var parameters = candidateMethodInfo.GetParameters(); + if (parameters.Length == 3 + && TypesHaveSameIdentity(parameters[0].ParameterType, possibleFSharpAsyncGenericType) + && parameters[1].ParameterType == fsharpOptionOfTaskCreationOptionsType + && parameters[2].ParameterType == fsharpOptionOfCancellationTokenType) { - var parameters = candidateMethodInfo.GetParameters(); - if (parameters.Length == 3 - && TypesHaveSameIdentity(parameters[0].ParameterType, possibleFSharpAsyncGenericType) - && parameters[1].ParameterType == fsharpOptionOfTaskCreationOptionsType - && parameters[2].ParameterType == fsharpOptionOfCancellationTokenType) - { - // This really does look like the correct method (and hence assembly). - _fsharpAsyncStartAsTaskGenericMethod = candidateMethodInfo; - _fsharpCoreAssembly = assembly; - break; - } + // This really does look like the correct method (and hence assembly). + _fsharpAsyncStartAsTaskGenericMethod = candidateMethodInfo; + _fsharpCoreAssembly = assembly; + break; } - - return _fsharpCoreAssembly != null; } - private static bool TypesHaveSameIdentity(Type type1, Type type2) - { - return type1.Assembly == type2.Assembly - && string.Equals(type1.Namespace, type2.Namespace, StringComparison.Ordinal) - && string.Equals(type1.Name, type2.Name, StringComparison.Ordinal); - } + return _fsharpCoreAssembly != null; + } + + private static bool TypesHaveSameIdentity(Type type1, Type type2) + { + return type1.Assembly == type2.Assembly + && string.Equals(type1.Namespace, type2.Namespace, StringComparison.Ordinal) + && string.Equals(type1.Name, type2.Name, StringComparison.Ordinal); } } \ No newline at end of file diff --git a/src/DotNetCore.CAP/Internal/PublisherSentFailedException.cs b/src/DotNetCore.CAP/Internal/PublisherSentFailedException.cs index 9be1758..5bcdc1d 100644 --- a/src/DotNetCore.CAP/Internal/PublisherSentFailedException.cs +++ b/src/DotNetCore.CAP/Internal/PublisherSentFailedException.cs @@ -11,7 +11,7 @@ namespace DotNetCore.CAP.Internal { } - public PublisherSentFailedException(string message, Exception ex) : base(message, ex) + public PublisherSentFailedException(string message, Exception? ex) : base(message, ex) { } } diff --git a/src/DotNetCore.CAP/Internal/SnowflakeId.cs b/src/DotNetCore.CAP/Internal/SnowflakeId.cs index d8a96f9..9ea630c 100644 --- a/src/DotNetCore.CAP/Internal/SnowflakeId.cs +++ b/src/DotNetCore.CAP/Internal/SnowflakeId.cs @@ -20,7 +20,7 @@ namespace DotNetCore.CAP.Internal public const int TimestampLeftShift = SequenceBits + WorkerIdBits + DatacenterIdBits; private const long SequenceMask = -1L ^ (-1L << SequenceBits); - private static SnowflakeId _snowflakeId; + private static SnowflakeId? _snowflakeId; private readonly object _lock = new object(); private static readonly object SLock = new object(); diff --git a/src/DotNetCore.CAP/Internal/TopicAttribute.cs b/src/DotNetCore.CAP/Internal/TopicAttribute.cs index 8e462b6..2096478 100644 --- a/src/DotNetCore.CAP/Internal/TopicAttribute.cs +++ b/src/DotNetCore.CAP/Internal/TopicAttribute.cs @@ -35,6 +35,6 @@ namespace DotNetCore.CAP.Internal /// kafka --> groups.id /// rabbit MQ --> queue.name /// - public string Group { get; set; } + public string Group { get; set; } = default!; } } diff --git a/src/DotNetCore.CAP/Messages/FailedInfo.cs b/src/DotNetCore.CAP/Messages/FailedInfo.cs index 501307e..c8a9d77 100644 --- a/src/DotNetCore.CAP/Messages/FailedInfo.cs +++ b/src/DotNetCore.CAP/Messages/FailedInfo.cs @@ -4,10 +4,10 @@ namespace DotNetCore.CAP.Messages { public class FailedInfo { - public IServiceProvider ServiceProvider { get; set; } + public IServiceProvider ServiceProvider { get; set; } = default!; public MessageType MessageType { get; set; } - public Message Message { get; set; } + public Message Message { get; set; } = default!; } } diff --git a/src/DotNetCore.CAP/Messages/Message.cs b/src/DotNetCore.CAP/Messages/Message.cs index 675fdad..104c0f3 100644 --- a/src/DotNetCore.CAP/Messages/Message.cs +++ b/src/DotNetCore.CAP/Messages/Message.cs @@ -3,51 +3,50 @@ using System; using System.Collections.Generic; -using JetBrains.Annotations; namespace DotNetCore.CAP.Messages { public class Message { /// - /// System.Text.Json requires that class explicitly has a parameterless constructor + /// System.Text.Json requires that class explicitly has a parameter less constructor /// and public properties have a setter. /// - public Message() { } + public Message() + { + Headers = new Dictionary(); + } - public Message(IDictionary headers, [CanBeNull] object value) + public Message(IDictionary headers, object? value) { Headers = headers ?? throw new ArgumentNullException(nameof(headers)); Value = value; } - public IDictionary Headers { get; set; } + public IDictionary Headers { get; set; } - [CanBeNull] - public object Value { get; set; } + public object? Value { get; set; } } public static class MessageExtensions { public static string GetId(this Message message) { - message.Headers.TryGetValue(Headers.MessageId, out var value); - return value; + return message.Headers[Headers.MessageId]!; } public static string GetName(this Message message) { - message.Headers.TryGetValue(Headers.MessageName, out var value); - return value; + return message.Headers[Headers.MessageName]!; } - public static string GetCallbackName(this Message message) + public static string? GetCallbackName(this Message message) { message.Headers.TryGetValue(Headers.CallbackName, out var value); return value; } - public static string GetGroup(this Message message) + public static string? GetGroup(this Message message) { message.Headers.TryGetValue(Headers.Group, out var value); return value; diff --git a/src/DotNetCore.CAP/Messages/TransportMessage.cs b/src/DotNetCore.CAP/Messages/TransportMessage.cs index 47d5ed8..5c2776e 100644 --- a/src/DotNetCore.CAP/Messages/TransportMessage.cs +++ b/src/DotNetCore.CAP/Messages/TransportMessage.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using JetBrains.Annotations; namespace DotNetCore.CAP.Messages { @@ -13,7 +12,7 @@ namespace DotNetCore.CAP.Messages [Serializable] public class TransportMessage { - public TransportMessage(IDictionary headers, [CanBeNull] byte[] body) + public TransportMessage(IDictionary headers, byte[]? body) { Headers = headers ?? throw new ArgumentNullException(nameof(headers)); Body = body; @@ -22,30 +21,29 @@ namespace DotNetCore.CAP.Messages /// /// Gets the headers of this message /// - public IDictionary Headers { get; } + public IDictionary Headers { get; } /// /// Gets the body object of this message /// - [CanBeNull] - public byte[] Body { get; } + public byte[]? Body { get; } public string GetId() { - return Headers.TryGetValue(Messages.Headers.MessageId, out var value) ? value : null; + return Headers[Messages.Headers.MessageId]!; } public string GetName() { - return Headers.TryGetValue(Messages.Headers.MessageName, out var value) ? value : null; + return Headers[Messages.Headers.MessageName]!; } - public string GetGroup() + public string? GetGroup() { return Headers.TryGetValue(Messages.Headers.Group, out var value) ? value : null; } - public string GetCorrelationId() + public string? GetCorrelationId() { return Headers.TryGetValue(Messages.Headers.CorrelationId, out var value) ? value : null; } diff --git a/src/DotNetCore.CAP/Monitoring/MessageDto.cs b/src/DotNetCore.CAP/Monitoring/MessageDto.cs index 5f17e08..2d363b7 100644 --- a/src/DotNetCore.CAP/Monitoring/MessageDto.cs +++ b/src/DotNetCore.CAP/Monitoring/MessageDto.cs @@ -7,15 +7,15 @@ namespace DotNetCore.CAP.Monitoring { public class MessageDto { - public string Id { get; set; } + public string Id { get; set; } = default!; - public string Version { get; set; } + public string Version { get; set; } = default!; - public string Group { get; set; } + public string? Group { get; set; } - public string Name { get; set; } + public string Name { get; set; } = default!; - public string Content { get; set; } + public string? Content { get; set; } public DateTime Added { get; set; } @@ -23,6 +23,6 @@ namespace DotNetCore.CAP.Monitoring public int Retries { get; set; } - public string StatusName { get; set; } + public string StatusName { get; set; } = default!; } } \ No newline at end of file diff --git a/src/DotNetCore.CAP/Monitoring/MessageQueryDto.cs b/src/DotNetCore.CAP/Monitoring/MessageQueryDto.cs index 5f4e78e..5a19d7d 100644 --- a/src/DotNetCore.CAP/Monitoring/MessageQueryDto.cs +++ b/src/DotNetCore.CAP/Monitoring/MessageQueryDto.cs @@ -9,13 +9,13 @@ namespace DotNetCore.CAP.Monitoring { public MessageType MessageType { get; set; } - public string Group { get; set; } + public string? Group { get; set; } - public string Name { get; set; } + public string? Name { get; set; } - public string Content { get; set; } + public string? Content { get; set; } - public string StatusName { get; set; } + public string? StatusName { get; set; } public int CurrentPage { get; set; } diff --git a/src/DotNetCore.CAP/Monitoring/PagedQueryResult.cs b/src/DotNetCore.CAP/Monitoring/PagedQueryResult.cs index cd6e486..0195dd4 100644 --- a/src/DotNetCore.CAP/Monitoring/PagedQueryResult.cs +++ b/src/DotNetCore.CAP/Monitoring/PagedQueryResult.cs @@ -4,7 +4,7 @@ namespace DotNetCore.CAP.Monitoring { public class PagedQueryResult { - public IList Items { get; set; } + public IList? Items { get; set; } public long Totals { get; set; } diff --git a/src/DotNetCore.CAP/OperateResult.cs b/src/DotNetCore.CAP/OperateResult.cs index 5bd5681..461e294 100644 --- a/src/DotNetCore.CAP/OperateResult.cs +++ b/src/DotNetCore.CAP/OperateResult.cs @@ -22,7 +22,7 @@ namespace DotNetCore.CAP /// public bool Succeeded { get; set; } - public Exception Exception { get; set; } + public Exception? Exception { get; set; } /// /// An of s containing an errors diff --git a/src/DotNetCore.CAP/Persistence/IDataStorage.cs b/src/DotNetCore.CAP/Persistence/IDataStorage.cs index 73bc622..f9c2900 100644 --- a/src/DotNetCore.CAP/Persistence/IDataStorage.cs +++ b/src/DotNetCore.CAP/Persistence/IDataStorage.cs @@ -14,7 +14,7 @@ namespace DotNetCore.CAP.Persistence Task ChangeReceiveStateAsync(MediumMessage message, StatusName state); - MediumMessage StoreMessage(string name, Message content, object dbTransaction = null); + MediumMessage StoreMessage(string name, Message content, object? dbTransaction = null); void StoreReceivedExceptionMessage(string name, string group, string content); diff --git a/src/DotNetCore.CAP/Persistence/MediumMessage.cs b/src/DotNetCore.CAP/Persistence/MediumMessage.cs index f5ff86f..acb1bf0 100644 --- a/src/DotNetCore.CAP/Persistence/MediumMessage.cs +++ b/src/DotNetCore.CAP/Persistence/MediumMessage.cs @@ -5,11 +5,11 @@ namespace DotNetCore.CAP.Persistence { public class MediumMessage { - public string DbId { get; set; } + public string DbId { get; set; } = default!; - public Message Origin { get; set; } + public Message Origin { get; set; } = default!; - public string Content { get; set; } + public string Content { get; set; } = default!; public DateTime Added { get; set; } diff --git a/src/DotNetCore.CAP/Processor/IDispatcher.Default.cs b/src/DotNetCore.CAP/Processor/IDispatcher.Default.cs index f88373f..75620fb 100644 --- a/src/DotNetCore.CAP/Processor/IDispatcher.Default.cs +++ b/src/DotNetCore.CAP/Processor/IDispatcher.Default.cs @@ -21,10 +21,10 @@ namespace DotNetCore.CAP.Processor private readonly CapOptions _options; private readonly ISubscribeDispatcher _executor; private readonly ILogger _logger; - private readonly CancellationTokenSource _cts = new CancellationTokenSource(); + private readonly CancellationTokenSource _cts = new (); - private Channel _publishedChannel; - private Channel<(MediumMessage, ConsumerExecutorDescriptor)> _receivedChannel; + private Channel _publishedChannel = default!; + private Channel<(MediumMessage, ConsumerExecutorDescriptor)> _receivedChannel = default!; public Dispatcher(ILogger logger, IMessageSender sender, @@ -125,7 +125,7 @@ namespace DotNetCore.CAP.Processor var result = await _sender.SendAsync(message); if (!result.Succeeded) { - _logger.MessagePublishException(message.Origin.GetId(), result.ToString(), + _logger.MessagePublishException(message.Origin?.GetId(), result.ToString(), result.Exception); } } diff --git a/src/DotNetCore.CAP/Processor/IDispatcher.PerGroup.cs b/src/DotNetCore.CAP/Processor/IDispatcher.PerGroup.cs index 678366f..b3c7484 100644 --- a/src/DotNetCore.CAP/Processor/IDispatcher.PerGroup.cs +++ b/src/DotNetCore.CAP/Processor/IDispatcher.PerGroup.cs @@ -22,11 +22,10 @@ namespace DotNetCore.CAP.Processor private readonly CapOptions _options; private readonly ISubscribeDispatcher _executor; private readonly ILogger _logger; - private readonly CancellationTokenSource _cts = new CancellationTokenSource(); + private readonly CancellationTokenSource _cts = new (); - private Channel _publishedChannel; - // private Channel<(MediumMessage, ConsumerExecutorDescriptor)> _receivedChannel; - private ConcurrentDictionary> _receivedChannels; + private Channel _publishedChannel = default!; + private ConcurrentDictionary> _receivedChannels = default!; private CancellationToken _stoppingToken; public DispatcherPerGroup( diff --git a/src/DotNetCore.CAP/Processor/IProcessingServer.Cap.cs b/src/DotNetCore.CAP/Processor/IProcessingServer.Cap.cs index 93bb698..70da9c3 100644 --- a/src/DotNetCore.CAP/Processor/IProcessingServer.Cap.cs +++ b/src/DotNetCore.CAP/Processor/IProcessingServer.Cap.cs @@ -19,8 +19,8 @@ namespace DotNetCore.CAP.Processor private readonly ILoggerFactory _loggerFactory; private readonly IServiceProvider _provider; - private Task _compositeTask; - private ProcessingContext _context; + private Task? _compositeTask; + private ProcessingContext _context = default!; private bool _disposed; public CapProcessingServer( diff --git a/src/DotNetCore.CAP/Processor/IProcessor.NeedRetry.cs b/src/DotNetCore.CAP/Processor/IProcessor.NeedRetry.cs index 0862dd0..dd78c9a 100644 --- a/src/DotNetCore.CAP/Processor/IProcessor.NeedRetry.cs +++ b/src/DotNetCore.CAP/Processor/IProcessor.NeedRetry.cs @@ -84,7 +84,7 @@ namespace DotNetCore.CAP.Processor } catch (Exception ex) { - _logger.LogWarning(1, ex, "Get messages from storage failed. Retrying...", typeof(T).Name); + _logger.LogWarning(1, ex, "Get messages from storage failed. Retrying..."); return Enumerable.Empty(); } diff --git a/src/DotNetCore.CAP/Processor/ProcessingContext.cs b/src/DotNetCore.CAP/Processor/ProcessingContext.cs index 6a280b2..74b46af 100644 --- a/src/DotNetCore.CAP/Processor/ProcessingContext.cs +++ b/src/DotNetCore.CAP/Processor/ProcessingContext.cs @@ -10,11 +10,7 @@ namespace DotNetCore.CAP.Processor { public class ProcessingContext : IDisposable { - private IServiceScope _scope; - - public ProcessingContext() - { - } + private IServiceScope? _scope; private ProcessingContext(ProcessingContext other) { diff --git a/src/DotNetCore.CAP/Serialization/ISerializer.JsonUtf8.cs b/src/DotNetCore.CAP/Serialization/ISerializer.JsonUtf8.cs index c070711..958a698 100644 --- a/src/DotNetCore.CAP/Serialization/ISerializer.JsonUtf8.cs +++ b/src/DotNetCore.CAP/Serialization/ISerializer.JsonUtf8.cs @@ -34,7 +34,7 @@ namespace DotNetCore.CAP.Serialization return Task.FromResult(new TransportMessage(message.Headers, jsonBytes)); } - public Task DeserializeAsync(TransportMessage transportMessage, Type valueType) + public Task DeserializeAsync(TransportMessage transportMessage, Type? valueType) { if (valueType == null || transportMessage.Body == null) { @@ -51,12 +51,12 @@ namespace DotNetCore.CAP.Serialization return JsonSerializer.Serialize(message, _jsonSerializerOptions); } - public Message Deserialize(string json) + public Message? Deserialize(string json) { return JsonSerializer.Deserialize(json, _jsonSerializerOptions); } - public object Deserialize(object value, Type valueType) + public object? Deserialize(object value, Type valueType) { if (value is JsonElement jsonElement) { diff --git a/src/DotNetCore.CAP/Serialization/ISerializer.cs b/src/DotNetCore.CAP/Serialization/ISerializer.cs index da89e90..4e2342d 100644 --- a/src/DotNetCore.CAP/Serialization/ISerializer.cs +++ b/src/DotNetCore.CAP/Serialization/ISerializer.cs @@ -4,7 +4,6 @@ using System; using System.Threading.Tasks; using DotNetCore.CAP.Messages; -using JetBrains.Annotations; namespace DotNetCore.CAP.Serialization { @@ -23,17 +22,17 @@ namespace DotNetCore.CAP.Serialization /// /// Deserialize the given string into a /// - Message Deserialize(string json); + Message? Deserialize(string json); /// /// Deserialize the given back into a /// - Task DeserializeAsync(TransportMessage transportMessage, [CanBeNull] Type valueType); + Task DeserializeAsync(TransportMessage transportMessage, Type? valueType); /// /// Deserialize the given object with the given Type into an object /// - object Deserialize(object value, Type valueType); + object? Deserialize(object value, Type valueType); /// /// Check if the given object is of Json type, e.g. JToken or JsonElement diff --git a/src/DotNetCore.CAP/Transport/BrokerAddress.cs b/src/DotNetCore.CAP/Transport/BrokerAddress.cs index d208ab1..7c70f4e 100644 --- a/src/DotNetCore.CAP/Transport/BrokerAddress.cs +++ b/src/DotNetCore.CAP/Transport/BrokerAddress.cs @@ -1,11 +1,10 @@ using System.Linq; -using JetBrains.Annotations; namespace DotNetCore.CAP.Transport { public struct BrokerAddress { - public BrokerAddress([NotNull]string address) + public BrokerAddress(string address) { if (address.Contains("$")) { @@ -21,7 +20,7 @@ namespace DotNetCore.CAP.Transport } } - public BrokerAddress([NotNull]string name, [CanBeNull]string endpoint) + public BrokerAddress(string name, string? endpoint) { Name = name; Endpoint = endpoint; @@ -29,7 +28,7 @@ namespace DotNetCore.CAP.Transport public string Name { get; set; } - public string Endpoint { get; set; } + public string? Endpoint { get; set; } public override string ToString() { diff --git a/src/DotNetCore.CAP/Transport/IConsumerClient.cs b/src/DotNetCore.CAP/Transport/IConsumerClient.cs index cf1d5ed..bd0ab4a 100644 --- a/src/DotNetCore.CAP/Transport/IConsumerClient.cs +++ b/src/DotNetCore.CAP/Transport/IConsumerClient.cs @@ -47,7 +47,7 @@ namespace DotNetCore.CAP.Transport /// /// Reject message and resumption /// - void Reject([CanBeNull] object sender); + void Reject(object? sender); event EventHandler OnMessageReceived; diff --git a/src/DotNetCore.CAP/Transport/MqLogType.cs b/src/DotNetCore.CAP/Transport/MqLogType.cs index 6d95a73..59ea457 100644 --- a/src/DotNetCore.CAP/Transport/MqLogType.cs +++ b/src/DotNetCore.CAP/Transport/MqLogType.cs @@ -31,7 +31,7 @@ namespace DotNetCore.CAP.Transport public class LogMessageEventArgs : EventArgs { - public string Reason { get; set; } + public string? Reason { get; set; } public MqLogType LogType { get; set; } } From 43ada6042c51d98bddd4c123186ffd2ba89b29aa Mon Sep 17 00:00:00 2001 From: savorboard Date: Sat, 25 Dec 2021 19:01:59 +0800 Subject: [PATCH 02/17] Enable #nullable for AmazonSQS --- .../AmazonPolicyExtensions.cs | 4 +-- .../AmazonSQSConsumerClient.cs | 26 +++++++++---------- .../CAP.AmazonSQSOptions.cs | 8 +++--- .../DotNetCore.CAP.AmazonSQS.csproj | 4 +-- .../ITransport.AmazonSQS.cs | 16 ++++++------ .../SQSReceivedMessage.cs | 8 +++--- 6 files changed, 33 insertions(+), 33 deletions(-) diff --git a/src/DotNetCore.CAP.AmazonSQS/AmazonPolicyExtensions.cs b/src/DotNetCore.CAP.AmazonSQS/AmazonPolicyExtensions.cs index d3af025..1f9c652 100644 --- a/src/DotNetCore.CAP.AmazonSQS/AmazonPolicyExtensions.cs +++ b/src/DotNetCore.CAP.AmazonSQS/AmazonPolicyExtensions.cs @@ -209,7 +209,7 @@ namespace DotNetCore.CAP.AmazonSQS /// /// Source ARN /// Group prefix or null if group not present - private static string GetArnGroupPrefix(string arn) + private static string? GetArnGroupPrefix(string arn) { const char separator = '-'; if (string.IsNullOrEmpty(arn) || !arn.Contains(separator)) @@ -235,7 +235,7 @@ namespace DotNetCore.CAP.AmazonSQS /// /// Source ARN /// Group name or null if group not present - private static string GetGroupName(string arn) + private static string? GetGroupName(string arn) { const char separator = ':'; if (string.IsNullOrEmpty(arn) || !arn.Contains(separator)) diff --git a/src/DotNetCore.CAP.AmazonSQS/AmazonSQSConsumerClient.cs b/src/DotNetCore.CAP.AmazonSQS/AmazonSQSConsumerClient.cs index 9c1a463..01dca36 100644 --- a/src/DotNetCore.CAP.AmazonSQS/AmazonSQSConsumerClient.cs +++ b/src/DotNetCore.CAP.AmazonSQS/AmazonSQSConsumerClient.cs @@ -27,8 +27,8 @@ namespace DotNetCore.CAP.AmazonSQS private readonly string _groupId; private readonly AmazonSQSOptions _amazonSQSOptions; - private IAmazonSimpleNotificationService _snsClient; - private IAmazonSQS _sqsClient; + private IAmazonSimpleNotificationService? _snsClient; + private IAmazonSQS? _sqsClient; private string _queueUrl = string.Empty; public AmazonSQSConsumerClient(string groupId, IOptions options) @@ -37,9 +37,9 @@ namespace DotNetCore.CAP.AmazonSQS _amazonSQSOptions = options.Value; } - public event EventHandler OnMessageReceived; + public event EventHandler? OnMessageReceived; - public event EventHandler OnLog; + public event EventHandler? OnLog; public BrokerAddress BrokerAddress => new BrokerAddress("AmazonSQS", _queueUrl); @@ -57,7 +57,7 @@ namespace DotNetCore.CAP.AmazonSQS { var createTopicRequest = new CreateTopicRequest(topic.NormalizeForAws()); - var createTopicResponse = _snsClient.CreateTopicAsync(createTopicRequest).GetAwaiter().GetResult(); + var createTopicResponse = _snsClient!.CreateTopicAsync(createTopicRequest).GetAwaiter().GetResult(); topicArns.Add(createTopicResponse.TopicArn); } @@ -92,13 +92,13 @@ namespace DotNetCore.CAP.AmazonSQS while (true) { - var response = _sqsClient.ReceiveMessageAsync(request, cancellationToken).GetAwaiter().GetResult(); + var response = _sqsClient!.ReceiveMessageAsync(request, cancellationToken).GetAwaiter().GetResult(); if (response.Messages.Count == 1) { var messageObj = JsonSerializer.Deserialize(response.Messages[0].Body); - var header = messageObj.MessageAttributes.ToDictionary(x => x.Key, x => x.Value.Value); + var header = messageObj!.MessageAttributes.ToDictionary(x => x.Key, x => x.Value.Value); var body = messageObj.Message; var message = new TransportMessage(header, body != null ? Encoding.UTF8.GetBytes(body) : null); @@ -119,7 +119,7 @@ namespace DotNetCore.CAP.AmazonSQS { try { - _ = _sqsClient.DeleteMessageAsync(_queueUrl, (string)sender).GetAwaiter().GetResult(); + _ = _sqsClient!.DeleteMessageAsync(_queueUrl, (string)sender).GetAwaiter().GetResult(); } catch (InvalidIdFormatException ex) { @@ -127,12 +127,12 @@ namespace DotNetCore.CAP.AmazonSQS } } - public void Reject(object sender) + public void Reject(object? sender) { try { // Visible again in 3 seconds - _ = _sqsClient.ChangeMessageVisibilityAsync(_queueUrl, (string)sender, 3).GetAwaiter().GetResult(); + _ = _sqsClient!.ChangeMessageVisibilityAsync(_queueUrl, (string)sender!, 3).GetAwaiter().GetResult(); } catch (MessageNotInflightException ex) { @@ -237,7 +237,7 @@ namespace DotNetCore.CAP.AmazonSQS { Connect(initSNS: false, initSQS: true); - var queueAttributes = await _sqsClient.GetAttributesAsync(_queueUrl).ConfigureAwait(false); + var queueAttributes = await _sqsClient!.GetAttributesAsync(_queueUrl).ConfigureAwait(false); var sqsQueueArn = queueAttributes["QueueArn"]; @@ -263,12 +263,12 @@ namespace DotNetCore.CAP.AmazonSQS private async Task SubscribeToTopics(IEnumerable topics) { - var queueAttributes = await _sqsClient.GetAttributesAsync(_queueUrl).ConfigureAwait(false); + var queueAttributes = await _sqsClient!.GetAttributesAsync(_queueUrl).ConfigureAwait(false); var sqsQueueArn = queueAttributes["QueueArn"]; foreach (var topicArn in topics) { - await _snsClient.SubscribeAsync(new SubscribeRequest + await _snsClient!.SubscribeAsync(new SubscribeRequest { TopicArn = topicArn, Protocol = "sqs", diff --git a/src/DotNetCore.CAP.AmazonSQS/CAP.AmazonSQSOptions.cs b/src/DotNetCore.CAP.AmazonSQS/CAP.AmazonSQSOptions.cs index f8dcfd5..4fdf3f2 100644 --- a/src/DotNetCore.CAP.AmazonSQS/CAP.AmazonSQSOptions.cs +++ b/src/DotNetCore.CAP.AmazonSQS/CAP.AmazonSQSOptions.cs @@ -10,19 +10,19 @@ namespace DotNetCore.CAP // ReSharper disable once InconsistentNaming public class AmazonSQSOptions { - public RegionEndpoint Region { get; set; } + public RegionEndpoint Region { get; set; } = default!; - public AWSCredentials Credentials { get; set; } + public AWSCredentials? Credentials { get; set; } /// /// Overrides Service Url deduced from AWS Region. To use in local development environments like localstack. /// - public string SNSServiceUrl { get; set; } + public string? SNSServiceUrl { get; set; } /// /// Overrides Service Url deduced from AWS Region. To use in local development environments like localstack. /// - public string SQSServiceUrl { get; set; } + public string? SQSServiceUrl { get; set; } } } \ No newline at end of file diff --git a/src/DotNetCore.CAP.AmazonSQS/DotNetCore.CAP.AmazonSQS.csproj b/src/DotNetCore.CAP.AmazonSQS/DotNetCore.CAP.AmazonSQS.csproj index 6518763..9cd09e5 100644 --- a/src/DotNetCore.CAP.AmazonSQS/DotNetCore.CAP.AmazonSQS.csproj +++ b/src/DotNetCore.CAP.AmazonSQS/DotNetCore.CAP.AmazonSQS.csproj @@ -2,12 +2,12 @@ netstandard2.1 - DotNetCore.CAP.AmazonSQS + enable $(PackageTags);AmazonSQS;SQS - bin\$(Configuration)\netstandard2.1\DotNetCore.CAP.AmazonSQS.xml + true 1701;1702;1705;CS1591 diff --git a/src/DotNetCore.CAP.AmazonSQS/ITransport.AmazonSQS.cs b/src/DotNetCore.CAP.AmazonSQS/ITransport.AmazonSQS.cs index 44c8583..0bbb8b9 100644 --- a/src/DotNetCore.CAP.AmazonSQS/ITransport.AmazonSQS.cs +++ b/src/DotNetCore.CAP.AmazonSQS/ITransport.AmazonSQS.cs @@ -22,8 +22,8 @@ namespace DotNetCore.CAP.AmazonSQS private readonly ILogger _logger; private readonly IOptions _sqsOptions; private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1); - private IAmazonSimpleNotificationService _snsClient; - private IDictionary _topicArnMaps; + private IAmazonSimpleNotificationService? _snsClient; + private IDictionary? _topicArnMaps; public AmazonSQSTransport(ILogger logger, IOptions sqsOptions) { @@ -41,7 +41,7 @@ namespace DotNetCore.CAP.AmazonSQS if (TryGetOrCreateTopicArn(message.GetName().NormalizeForAws(), out var arn)) { - string bodyJson = null; + string? bodyJson = null; if (message.Body != null) { bodyJson = Encoding.UTF8.GetString(message.Body); @@ -59,7 +59,7 @@ namespace DotNetCore.CAP.AmazonSQS MessageAttributes = attributes }; - await _snsClient.PublishAsync(request); + await _snsClient!.PublishAsync(request); _logger.LogDebug($"SNS topic message [{message.GetName().NormalizeForAws()}] has been published."); return OperateResult.Success; @@ -117,7 +117,7 @@ namespace DotNetCore.CAP.AmazonSQS { _topicArnMaps = new Dictionary(); - string nextToken = null; + string? nextToken = null; do { var topics = nextToken == null @@ -143,15 +143,15 @@ namespace DotNetCore.CAP.AmazonSQS } } - private bool TryGetOrCreateTopicArn(string topicName, out string topicArn) + private bool TryGetOrCreateTopicArn(string topicName,[System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out string? topicArn) { topicArn = null; - if (_topicArnMaps.TryGetValue(topicName, out topicArn)) + if (_topicArnMaps!.TryGetValue(topicName, out topicArn)) { return true; } - var response = _snsClient.CreateTopicAsync(topicName).GetAwaiter().GetResult(); + var response = _snsClient!.CreateTopicAsync(topicName).GetAwaiter().GetResult(); if (string.IsNullOrEmpty(response.TopicArn)) { diff --git a/src/DotNetCore.CAP.AmazonSQS/SQSReceivedMessage.cs b/src/DotNetCore.CAP.AmazonSQS/SQSReceivedMessage.cs index 893b418..53fcd64 100644 --- a/src/DotNetCore.CAP.AmazonSQS/SQSReceivedMessage.cs +++ b/src/DotNetCore.CAP.AmazonSQS/SQSReceivedMessage.cs @@ -4,15 +4,15 @@ namespace DotNetCore.CAP.AmazonSQS { class SQSReceivedMessage { - public string Message { get; set; } + public string? Message { get; set; } - public Dictionary MessageAttributes { get; set; } + public Dictionary MessageAttributes { get; set; } = default!; } class SQSReceivedMessageAttributes { - public string Type { get; set; } + public string? Type { get; set; } - public string Value { get; set; } + public string? Value { get; set; } } } From c4199b5bd08887b1100b568a86f0c2f7a7cbe3bd Mon Sep 17 00:00:00 2001 From: savorboard Date: Sat, 25 Dec 2021 20:21:25 +0800 Subject: [PATCH 03/17] Fixes unit test. --- src/DotNetCore.CAP/Internal/IBootstrapper.Default.cs | 6 ++++++ src/DotNetCore.CAP/Internal/IConsumerRegister.Default.cs | 5 ++--- test/DotNetCore.CAP.Test/HelperTest.cs | 6 +++--- .../IntegrationTests/CancellationTokenSubscriberTest.cs | 1 + test/DotNetCore.CAP.Test/SubscribeInvokerTest.cs | 6 +++++- .../DotNetCore.CAP.Test/SubscribeInvokerWithCancellation.cs | 6 +++++- 6 files changed, 22 insertions(+), 8 deletions(-) diff --git a/src/DotNetCore.CAP/Internal/IBootstrapper.Default.cs b/src/DotNetCore.CAP/Internal/IBootstrapper.Default.cs index 5540eaf..9ed2cf0 100644 --- a/src/DotNetCore.CAP/Internal/IBootstrapper.Default.cs +++ b/src/DotNetCore.CAP/Internal/IBootstrapper.Default.cs @@ -20,6 +20,7 @@ namespace DotNetCore.CAP.Internal private readonly IServiceProvider _serviceProvider; private readonly ILogger _logger; private readonly CancellationTokenSource _cts = new (); + private bool _disposed; private IEnumerable _processors = default!; public Bootstrapper(IServiceProvider serviceProvider, ILogger logger) @@ -93,8 +94,13 @@ namespace DotNetCore.CAP.Internal public override void Dispose() { + if (_disposed) + { + return; + } _cts.Cancel(); _cts.Dispose(); + _disposed = true; } protected override async Task ExecuteAsync(CancellationToken stoppingToken) diff --git a/src/DotNetCore.CAP/Internal/IConsumerRegister.Default.cs b/src/DotNetCore.CAP/Internal/IConsumerRegister.Default.cs index 483eb91..b78e264 100644 --- a/src/DotNetCore.CAP/Internal/IConsumerRegister.Default.cs +++ b/src/DotNetCore.CAP/Internal/IConsumerRegister.Default.cs @@ -31,7 +31,7 @@ namespace DotNetCore.CAP.Internal private IDataStorage _storage = default!; private MethodMatcherCache _selector = default!; - private CancellationTokenSource _cts; + private CancellationTokenSource _cts = new(); private BrokerAddress _serverAddress; private Task? _compositeTask; private bool _disposed; @@ -47,7 +47,6 @@ namespace DotNetCore.CAP.Internal _logger = logger; _serviceProvider = serviceProvider; _options = serviceProvider.GetRequiredService>().Value; - _cts = new CancellationTokenSource(); } public bool IsHealthy() @@ -63,7 +62,7 @@ namespace DotNetCore.CAP.Internal _storage = _serviceProvider.GetRequiredService(); _consumerClientFactory = _serviceProvider.GetRequiredService(); - stoppingToken.Register(() => _cts.Cancel()); + stoppingToken.Register(Dispose); Execute(); } diff --git a/test/DotNetCore.CAP.Test/HelperTest.cs b/test/DotNetCore.CAP.Test/HelperTest.cs index fcb550c..2a4ddbe 100644 --- a/test/DotNetCore.CAP.Test/HelperTest.cs +++ b/test/DotNetCore.CAP.Test/HelperTest.cs @@ -11,13 +11,13 @@ namespace DotNetCore.CAP.Test public void ToTimestampTest() { //Arrange - var time = DateTime.Parse("2018-01-01 00:00:00"); + var time = DateTimeOffset.Parse("2018-01-01T00:00:00Z"); //Act - var result = Helper.ToTimestamp(time); + var result = Helper.ToTimestamp(time.DateTime); //Assert - Assert.Equal(1514764800, result); + Assert.Equal(1514736000, result); } [Fact] diff --git a/test/DotNetCore.CAP.Test/IntegrationTests/CancellationTokenSubscriberTest.cs b/test/DotNetCore.CAP.Test/IntegrationTests/CancellationTokenSubscriberTest.cs index 7cf0a6a..deb4973 100644 --- a/test/DotNetCore.CAP.Test/IntegrationTests/CancellationTokenSubscriberTest.cs +++ b/test/DotNetCore.CAP.Test/IntegrationTests/CancellationTokenSubscriberTest.cs @@ -25,6 +25,7 @@ namespace DotNetCore.CAP.Test.IntegrationTests // Explicitly stop Bootstrapper to prove the cancellation token works. var bootstrapper = Container.GetRequiredService(); + await bootstrapper.StopAsync(CancellationToken.None); var (message, token) = HandledMessages diff --git a/test/DotNetCore.CAP.Test/SubscribeInvokerTest.cs b/test/DotNetCore.CAP.Test/SubscribeInvokerTest.cs index 5db7959..4b14f9b 100644 --- a/test/DotNetCore.CAP.Test/SubscribeInvokerTest.cs +++ b/test/DotNetCore.CAP.Test/SubscribeInvokerTest.cs @@ -37,7 +37,11 @@ namespace DotNetCore.CAP.Test Parameters = new List() }; - var header = new Dictionary(); + var header = new Dictionary() + { + [Headers.MessageId] = SnowflakeId.Default().NextId().ToString(), + [Headers.MessageName] = "fake.output.integer" + }; var message = new Message(header, null); var context = new ConsumerContext(descriptor, message); diff --git a/test/DotNetCore.CAP.Test/SubscribeInvokerWithCancellation.cs b/test/DotNetCore.CAP.Test/SubscribeInvokerWithCancellation.cs index 7eb7f20..7517479 100644 --- a/test/DotNetCore.CAP.Test/SubscribeInvokerWithCancellation.cs +++ b/test/DotNetCore.CAP.Test/SubscribeInvokerWithCancellation.cs @@ -47,7 +47,11 @@ namespace DotNetCore.CAP.Test } }; - var header = new Dictionary(); + var header = new Dictionary() + { + [Headers.MessageId] = SnowflakeId.Default().NextId().ToString(), + [Headers.MessageName] = "fake.output.withcancellation" + }; var message = new Message(header, null); var context = new ConsumerContext(descriptor, message); From c3faabce067ae14e7f49885c462b4505f591b359 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Mon, 27 Dec 2021 09:46:45 +0800 Subject: [PATCH 04/17] Fixes unit test. --- src/DotNetCore.CAP/Internal/Helper.cs | 9 --------- test/DotNetCore.CAP.Test/HelperTest.cs | 13 ------------- 2 files changed, 22 deletions(-) diff --git a/src/DotNetCore.CAP/Internal/Helper.cs b/src/DotNetCore.CAP/Internal/Helper.cs index 2f0a3ed..402a007 100644 --- a/src/DotNetCore.CAP/Internal/Helper.cs +++ b/src/DotNetCore.CAP/Internal/Helper.cs @@ -10,15 +10,6 @@ namespace DotNetCore.CAP.Internal { public static class Helper { - private static readonly DateTime Epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Local) - .AddHours(TimeZoneInfo.Local.BaseUtcOffset.Hours); - - public static long ToTimestamp(DateTime value) - { - var elapsedTime = value - Epoch; - return (long)elapsedTime.TotalSeconds; - } - public static bool IsController(TypeInfo typeInfo) { if (!typeInfo.IsClass) diff --git a/test/DotNetCore.CAP.Test/HelperTest.cs b/test/DotNetCore.CAP.Test/HelperTest.cs index 2a4ddbe..c6ecccf 100644 --- a/test/DotNetCore.CAP.Test/HelperTest.cs +++ b/test/DotNetCore.CAP.Test/HelperTest.cs @@ -7,19 +7,6 @@ namespace DotNetCore.CAP.Test { public class HelperTest { - [Fact] - public void ToTimestampTest() - { - //Arrange - var time = DateTimeOffset.Parse("2018-01-01T00:00:00Z"); - - //Act - var result = Helper.ToTimestamp(time.DateTime); - - //Assert - Assert.Equal(1514736000, result); - } - [Fact] public void IsControllerTest() { From f9e46337e7fcd248ff1892d14c5014d55864ce0f Mon Sep 17 00:00:00 2001 From: Savorboard Date: Mon, 27 Dec 2021 11:08:50 +0800 Subject: [PATCH 05/17] Tweak kafka read header by index when the Key already exists. --- src/DotNetCore.CAP.Kafka/KafkaConsumerClient.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/DotNetCore.CAP.Kafka/KafkaConsumerClient.cs b/src/DotNetCore.CAP.Kafka/KafkaConsumerClient.cs index ed235c8..29680e4 100644 --- a/src/DotNetCore.CAP.Kafka/KafkaConsumerClient.cs +++ b/src/DotNetCore.CAP.Kafka/KafkaConsumerClient.cs @@ -106,7 +106,7 @@ namespace DotNetCore.CAP.Kafka var customHeaders = _kafkaOptions.CustomHeaders(consumerResult); foreach (var customHeader in customHeaders) { - headers.Add(customHeader.Key, customHeader.Value); + headers[customHeader.Key] = customHeader.Value; } } @@ -177,6 +177,6 @@ namespace DotNetCore.CAP.Kafka Reason = $"An error occurred during connect kafka --> {e.Reason}" }; OnLog?.Invoke(null, logArgs); - } + } } } \ No newline at end of file From 2537471779bc0a846116e0d0a5a527096c1b51ce Mon Sep 17 00:00:00 2001 From: Savorboard Date: Mon, 27 Dec 2021 11:22:59 +0800 Subject: [PATCH 06/17] Enable #nullable for azureservicebus. --- .../AzureServiceBusConsumerClient.cs | 20 +++++++++---------- .../AzureServiceBusConsumerCommitInput.cs | 4 ++-- .../CAP.AzureServiceBusOptions.cs | 4 ++-- .../DotNetCore.CAP.AzureServiceBus.csproj | 2 +- .../ITransport.AzureServiceBus.cs | 4 ++-- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/DotNetCore.CAP.AzureServiceBus/AzureServiceBusConsumerClient.cs b/src/DotNetCore.CAP.AzureServiceBus/AzureServiceBusConsumerClient.cs index ee5d84b..cfd1c3d 100644 --- a/src/DotNetCore.CAP.AzureServiceBus/AzureServiceBusConsumerClient.cs +++ b/src/DotNetCore.CAP.AzureServiceBus/AzureServiceBusConsumerClient.cs @@ -24,7 +24,7 @@ namespace DotNetCore.CAP.AzureServiceBus private readonly string _subscriptionName; private readonly AzureServiceBusOptions _asbOptions; - private SubscriptionClient _consumerClient; + private SubscriptionClient? _consumerClient; public AzureServiceBusConsumerClient( ILogger logger, @@ -36,11 +36,11 @@ namespace DotNetCore.CAP.AzureServiceBus _asbOptions = options.Value ?? throw new ArgumentNullException(nameof(options)); } - public event EventHandler OnMessageReceived; + public event EventHandler? OnMessageReceived; - public event EventHandler OnLog; + public event EventHandler? OnLog; - public BrokerAddress BrokerAddress => new BrokerAddress("AzureServiceBus", _asbOptions.ConnectionString); + public BrokerAddress BrokerAddress => new ("AzureServiceBus", _asbOptions.ConnectionString); public void Subscribe(IEnumerable topics) { @@ -51,7 +51,7 @@ namespace DotNetCore.CAP.AzureServiceBus ConnectAsync().GetAwaiter().GetResult(); - var allRuleNames = _consumerClient.GetRulesAsync().GetAwaiter().GetResult().Select(x => x.Name); + var allRuleNames = _consumerClient!.GetRulesAsync().GetAwaiter().GetResult().Select(x => x.Name); foreach (var newRule in topics.Except(allRuleNames)) { @@ -80,7 +80,7 @@ namespace DotNetCore.CAP.AzureServiceBus if (_asbOptions.EnableSessions) { - _consumerClient.RegisterSessionHandler(OnConsumerReceivedWithSession, + _consumerClient!.RegisterSessionHandler(OnConsumerReceivedWithSession, new SessionHandlerOptions(OnExceptionReceived) { AutoComplete = false, @@ -89,7 +89,7 @@ namespace DotNetCore.CAP.AzureServiceBus } else { - _consumerClient.RegisterMessageHandler(OnConsumerReceived, + _consumerClient!.RegisterMessageHandler(OnConsumerReceived, new MessageHandlerOptions(OnExceptionReceived) { AutoComplete = false, @@ -111,15 +111,15 @@ namespace DotNetCore.CAP.AzureServiceBus var commitInput = (AzureServiceBusConsumerCommitInput) sender; if (_asbOptions.EnableSessions) { - commitInput.Session.CompleteAsync(commitInput.LockToken); + commitInput.Session?.CompleteAsync(commitInput.LockToken); } else { - _consumerClient.CompleteAsync(commitInput.LockToken); + _consumerClient!.CompleteAsync(commitInput.LockToken); } } - public void Reject(object sender) + public void Reject(object? sender) { // ignore } diff --git a/src/DotNetCore.CAP.AzureServiceBus/AzureServiceBusConsumerCommitInput.cs b/src/DotNetCore.CAP.AzureServiceBus/AzureServiceBusConsumerCommitInput.cs index c76a360..290eda1 100644 --- a/src/DotNetCore.CAP.AzureServiceBus/AzureServiceBusConsumerCommitInput.cs +++ b/src/DotNetCore.CAP.AzureServiceBus/AzureServiceBusConsumerCommitInput.cs @@ -4,13 +4,13 @@ namespace DotNetCore.CAP.AzureServiceBus { public class AzureServiceBusConsumerCommitInput { - public AzureServiceBusConsumerCommitInput(string lockToken, IMessageSession session = null) + public AzureServiceBusConsumerCommitInput(string lockToken, IMessageSession? session = null) { LockToken = lockToken; Session = session; } - public IMessageSession Session { get; set; } + public IMessageSession? Session { get; set; } public string LockToken { get; set; } } } diff --git a/src/DotNetCore.CAP.AzureServiceBus/CAP.AzureServiceBusOptions.cs b/src/DotNetCore.CAP.AzureServiceBus/CAP.AzureServiceBusOptions.cs index 61c6d63..050bb99 100644 --- a/src/DotNetCore.CAP.AzureServiceBus/CAP.AzureServiceBusOptions.cs +++ b/src/DotNetCore.CAP.AzureServiceBus/CAP.AzureServiceBusOptions.cs @@ -20,7 +20,7 @@ namespace DotNetCore.CAP /// /// Azure Service Bus Namespace connection string. Must not contain topic information. /// - public string ConnectionString { get; set; } + public string ConnectionString { get; set; } = default!; /// /// Whether Service Bus sessions are enabled. If enabled, all messages must contain a @@ -36,6 +36,6 @@ namespace DotNetCore.CAP /// /// Represents the Azure Active Directory token provider for Azure Managed Service Identity integration. /// - public ITokenProvider ManagementTokenProvider { get; set; } + public ITokenProvider? ManagementTokenProvider { get; set; } } } \ No newline at end of file diff --git a/src/DotNetCore.CAP.AzureServiceBus/DotNetCore.CAP.AzureServiceBus.csproj b/src/DotNetCore.CAP.AzureServiceBus/DotNetCore.CAP.AzureServiceBus.csproj index 31abcb6..04bbecd 100644 --- a/src/DotNetCore.CAP.AzureServiceBus/DotNetCore.CAP.AzureServiceBus.csproj +++ b/src/DotNetCore.CAP.AzureServiceBus/DotNetCore.CAP.AzureServiceBus.csproj @@ -2,7 +2,7 @@ netstandard2.1 - DotNetCore.CAP.AzureServiceBus + enable $(PackageTags);AzureServiceBus diff --git a/src/DotNetCore.CAP.AzureServiceBus/ITransport.AzureServiceBus.cs b/src/DotNetCore.CAP.AzureServiceBus/ITransport.AzureServiceBus.cs index 3038c5d..a32d019 100644 --- a/src/DotNetCore.CAP.AzureServiceBus/ITransport.AzureServiceBus.cs +++ b/src/DotNetCore.CAP.AzureServiceBus/ITransport.AzureServiceBus.cs @@ -20,7 +20,7 @@ namespace DotNetCore.CAP.AzureServiceBus private readonly ILogger _logger; private readonly IOptions _asbOptions; - private ITopicClient _topicClient; + private ITopicClient? _topicClient; public AzureServiceBusTransport( ILogger logger, @@ -57,7 +57,7 @@ namespace DotNetCore.CAP.AzureServiceBus message.UserProperties.Add(header.Key, header.Value); } - await _topicClient.SendAsync(message); + await _topicClient!.SendAsync(message); _logger.LogDebug($"Azure Service Bus message [{transportMessage.GetName()}] has been published."); From 85a66f5b77c8b32ccae48833b4e8868b3deca2f0 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Mon, 27 Dec 2021 11:30:30 +0800 Subject: [PATCH 07/17] Enable #nullable for inmemory storage. --- .../DotNetCore.CAP.InMemoryStorage.csproj | 2 +- .../IDataStorage.InMemory.cs | 12 ++++++------ src/DotNetCore.CAP.InMemoryStorage/MemoryMessage.cs | 4 ++-- src/DotNetCore.CAP/Transport/IConsumerClient.cs | 7 +++---- 4 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/DotNetCore.CAP.InMemoryStorage/DotNetCore.CAP.InMemoryStorage.csproj b/src/DotNetCore.CAP.InMemoryStorage/DotNetCore.CAP.InMemoryStorage.csproj index 45b69b8..b404dad 100644 --- a/src/DotNetCore.CAP.InMemoryStorage/DotNetCore.CAP.InMemoryStorage.csproj +++ b/src/DotNetCore.CAP.InMemoryStorage/DotNetCore.CAP.InMemoryStorage.csproj @@ -2,7 +2,7 @@ netstandard2.1 - DotNetCore.CAP.InMemoryStorage + enable $(PackageTags);InMemory diff --git a/src/DotNetCore.CAP.InMemoryStorage/IDataStorage.InMemory.cs b/src/DotNetCore.CAP.InMemoryStorage/IDataStorage.InMemory.cs index a27821c..975bc38 100644 --- a/src/DotNetCore.CAP.InMemoryStorage/IDataStorage.InMemory.cs +++ b/src/DotNetCore.CAP.InMemoryStorage/IDataStorage.InMemory.cs @@ -26,9 +26,9 @@ namespace DotNetCore.CAP.InMemoryStorage _serializer = serializer; } - public static Dictionary PublishedMessages { get; } = new Dictionary(); + public static Dictionary PublishedMessages { get; } = new(); - public static Dictionary ReceivedMessages { get; } = new Dictionary(); + public static Dictionary ReceivedMessages { get; } = new(); public Task ChangePublishStateAsync(MediumMessage message, StatusName state) { @@ -46,7 +46,7 @@ namespace DotNetCore.CAP.InMemoryStorage return Task.CompletedTask; } - public MediumMessage StoreMessage(string name, Message content, object dbTransaction = null) + public MediumMessage StoreMessage(string name, Message content, object? dbTransaction = null) { var message = new MediumMessage { @@ -85,7 +85,7 @@ namespace DotNetCore.CAP.InMemoryStorage { DbId = id, Group = group, - Origin = null, + Origin = null!, Name = name, Content = content, Retries = _capOptions.Value.FailedRetryCount, @@ -186,7 +186,7 @@ namespace DotNetCore.CAP.InMemoryStorage foreach (var message in result) { - message.Origin = _serializer.Deserialize(message.Content); + message.Origin = _serializer.Deserialize(message.Content)!; } return Task.FromResult(result); @@ -208,7 +208,7 @@ namespace DotNetCore.CAP.InMemoryStorage foreach (var message in result) { - message.Origin = _serializer.Deserialize(message.Content); + message.Origin = _serializer.Deserialize(message.Content)!; } return Task.FromResult(result); diff --git a/src/DotNetCore.CAP.InMemoryStorage/MemoryMessage.cs b/src/DotNetCore.CAP.InMemoryStorage/MemoryMessage.cs index 53bb070..2b53c91 100644 --- a/src/DotNetCore.CAP.InMemoryStorage/MemoryMessage.cs +++ b/src/DotNetCore.CAP.InMemoryStorage/MemoryMessage.cs @@ -8,10 +8,10 @@ namespace DotNetCore.CAP.InMemoryStorage { internal class MemoryMessage : MediumMessage { - public string Name { get; set; } + public string Name { get; set; } = default!; public StatusName StatusName { get; set; } - public string Group { get; set; } + public string Group { get; set; } = default!; } } diff --git a/src/DotNetCore.CAP/Transport/IConsumerClient.cs b/src/DotNetCore.CAP/Transport/IConsumerClient.cs index bd0ab4a..d750116 100644 --- a/src/DotNetCore.CAP/Transport/IConsumerClient.cs +++ b/src/DotNetCore.CAP/Transport/IConsumerClient.cs @@ -6,7 +6,6 @@ using System.Collections.Generic; using System.Linq; using System.Threading; using DotNetCore.CAP.Messages; -using JetBrains.Annotations; namespace DotNetCore.CAP.Transport { @@ -23,7 +22,7 @@ namespace DotNetCore.CAP.Transport /// /// Names of the requested topics /// Topic identifiers - ICollection FetchTopics([NotNull] IEnumerable topicNames) + ICollection FetchTopics(IEnumerable topicNames) { return topicNames.ToList(); } @@ -32,7 +31,7 @@ namespace DotNetCore.CAP.Transport /// Subscribe to a set of topics to the message queue /// /// - void Subscribe([NotNull] IEnumerable topics); + void Subscribe(IEnumerable topics); /// /// Start listening @@ -42,7 +41,7 @@ namespace DotNetCore.CAP.Transport /// /// Manual submit message offset when the message consumption is complete /// - void Commit([NotNull] object sender); + void Commit(object sender); /// /// Reject message and resumption From 17c81c891a5f5666c92931dc96c13e4e20f0ef0e Mon Sep 17 00:00:00 2001 From: Savorboard Date: Mon, 27 Dec 2021 11:36:02 +0800 Subject: [PATCH 08/17] Enable #nullable for kafka transport. --- src/DotNetCore.CAP.Kafka/CAP.KafkaOptions.cs | 4 ++-- .../DotNetCore.CAP.Kafka.csproj | 3 +-- src/DotNetCore.CAP.Kafka/ITransport.Kafka.cs | 4 ++-- .../KafkaConsumerClient.cs | 20 +++++++++---------- 4 files changed, 15 insertions(+), 16 deletions(-) diff --git a/src/DotNetCore.CAP.Kafka/CAP.KafkaOptions.cs b/src/DotNetCore.CAP.Kafka/CAP.KafkaOptions.cs index 0faa14f..5ef9c30 100644 --- a/src/DotNetCore.CAP.Kafka/CAP.KafkaOptions.cs +++ b/src/DotNetCore.CAP.Kafka/CAP.KafkaOptions.cs @@ -37,11 +37,11 @@ namespace DotNetCore.CAP /// Initial list of brokers as a CSV list of broker host or host:port. /// /// - public string Servers { get; set; } + public string Servers { get; set; } = default!; /// /// If you need to get offset and partition and so on.., you can use this function to write additional header into /// - public Func, List>> CustomHeaders { get; set; } + public Func, List>>? CustomHeaders { get; set; } } } \ No newline at end of file diff --git a/src/DotNetCore.CAP.Kafka/DotNetCore.CAP.Kafka.csproj b/src/DotNetCore.CAP.Kafka/DotNetCore.CAP.Kafka.csproj index ad5c623..5564064 100644 --- a/src/DotNetCore.CAP.Kafka/DotNetCore.CAP.Kafka.csproj +++ b/src/DotNetCore.CAP.Kafka/DotNetCore.CAP.Kafka.csproj @@ -2,14 +2,13 @@ netstandard2.1 - DotNetCore.CAP.Kafka + enable $(PackageTags);Kafka NU1605;NU1701 NU1701;CS1591 - bin\$(Configuration)\netstandard2.1\DotNetCore.CAP.Kafka.xml diff --git a/src/DotNetCore.CAP.Kafka/ITransport.Kafka.cs b/src/DotNetCore.CAP.Kafka/ITransport.Kafka.cs index 5484193..c4af49a 100644 --- a/src/DotNetCore.CAP.Kafka/ITransport.Kafka.cs +++ b/src/DotNetCore.CAP.Kafka/ITransport.Kafka.cs @@ -43,8 +43,8 @@ namespace DotNetCore.CAP.Kafka var result = await producer.ProduceAsync(message.GetName(), new Message { Headers = headers, - Key = message.Headers.TryGetValue(KafkaHeaders.KafkaKey, out string kafkaMessageKey) && !string.IsNullOrEmpty(kafkaMessageKey) ? kafkaMessageKey : message.GetId(), - Value = message.Body + Key = message.Headers.TryGetValue(KafkaHeaders.KafkaKey, out string? kafkaMessageKey) && !string.IsNullOrEmpty(kafkaMessageKey) ? kafkaMessageKey : message.GetId(), + Value = message.Body! }); if (result.Status == PersistenceStatus.Persisted || result.Status == PersistenceStatus.PossiblyPersisted) diff --git a/src/DotNetCore.CAP.Kafka/KafkaConsumerClient.cs b/src/DotNetCore.CAP.Kafka/KafkaConsumerClient.cs index 29680e4..98901bd 100644 --- a/src/DotNetCore.CAP.Kafka/KafkaConsumerClient.cs +++ b/src/DotNetCore.CAP.Kafka/KafkaConsumerClient.cs @@ -21,7 +21,7 @@ namespace DotNetCore.CAP.Kafka private readonly string _groupId; private readonly KafkaOptions _kafkaOptions; - private IConsumer _consumerClient; + private IConsumer? _consumerClient; public KafkaConsumerClient(string groupId, IOptions options) { @@ -29,11 +29,11 @@ namespace DotNetCore.CAP.Kafka _kafkaOptions = options.Value ?? throw new ArgumentNullException(nameof(options)); } - public event EventHandler OnMessageReceived; + public event EventHandler? OnMessageReceived; - public event EventHandler OnLog; + public event EventHandler? OnLog; - public BrokerAddress BrokerAddress => new BrokerAddress("Kafka", _kafkaOptions.Servers); + public BrokerAddress BrokerAddress => new ("Kafka", _kafkaOptions.Servers); public ICollection FetchTopics(IEnumerable topicNames) { @@ -80,7 +80,7 @@ namespace DotNetCore.CAP.Kafka Connect(); - _consumerClient.Subscribe(topics); + _consumerClient!.Subscribe(topics); } public void Listening(TimeSpan timeout, CancellationToken cancellationToken) @@ -89,11 +89,11 @@ namespace DotNetCore.CAP.Kafka while (true) { - var consumerResult = _consumerClient.Consume(cancellationToken); + var consumerResult = _consumerClient!.Consume(cancellationToken); if (consumerResult.IsPartitionEOF || consumerResult.Message.Value == null) continue; - var headers = new Dictionary(consumerResult.Message.Headers.Count); + var headers = new Dictionary(consumerResult.Message.Headers.Count); foreach (var header in consumerResult.Message.Headers) { var val = header.GetValueBytes(); @@ -119,12 +119,12 @@ namespace DotNetCore.CAP.Kafka public void Commit(object sender) { - _consumerClient.Commit((ConsumeResult)sender); + _consumerClient!.Commit((ConsumeResult)sender); } - public void Reject(object sender) + public void Reject(object? sender) { - _consumerClient.Assign(_consumerClient.Assignment); + _consumerClient!.Assign(_consumerClient.Assignment); } public void Dispose() From b36fb6562dfaa0f8c5ce4cbc6d2efc3e22f12c5d Mon Sep 17 00:00:00 2001 From: Savorboard Date: Mon, 27 Dec 2021 11:40:20 +0800 Subject: [PATCH 09/17] Enable #nullable for mongo storage. --- .../CAP.MongoDBCapOptionsExtension.cs | 2 +- .../CAP.MongoDBOptions.cs | 2 +- .../DotNetCore.CAP.MongoDB.csproj | 9 ++------- .../IClientSessionHandle.CAP.cs | 8 ++++---- .../IDataStorage.MongoDB.cs | 6 +++--- src/DotNetCore.CAP.MongoDB/StorageMessage.cs | 18 +++++++++--------- 6 files changed, 20 insertions(+), 25 deletions(-) diff --git a/src/DotNetCore.CAP.MongoDB/CAP.MongoDBCapOptionsExtension.cs b/src/DotNetCore.CAP.MongoDB/CAP.MongoDBCapOptionsExtension.cs index ff9d24b..e6578b1 100644 --- a/src/DotNetCore.CAP.MongoDB/CAP.MongoDBCapOptionsExtension.cs +++ b/src/DotNetCore.CAP.MongoDB/CAP.MongoDBCapOptionsExtension.cs @@ -32,7 +32,7 @@ namespace DotNetCore.CAP.MongoDB //Try to add IMongoClient if does not exists services.TryAddSingleton(x => { - var options = x.GetService>().Value; + var options = x.GetRequiredService>().Value; return new MongoClient(options.DatabaseConnection); }); } diff --git a/src/DotNetCore.CAP.MongoDB/CAP.MongoDBOptions.cs b/src/DotNetCore.CAP.MongoDB/CAP.MongoDBOptions.cs index f753cb7..616fdfc 100644 --- a/src/DotNetCore.CAP.MongoDB/CAP.MongoDBOptions.cs +++ b/src/DotNetCore.CAP.MongoDB/CAP.MongoDBOptions.cs @@ -30,6 +30,6 @@ namespace DotNetCore.CAP.MongoDB /// public string PublishedCollection { get; set; } = "cap.published"; - internal string Version { get; set; } + internal string Version { get; set; } = default!; } } \ No newline at end of file diff --git a/src/DotNetCore.CAP.MongoDB/DotNetCore.CAP.MongoDB.csproj b/src/DotNetCore.CAP.MongoDB/DotNetCore.CAP.MongoDB.csproj index 8f975e5..0f36213 100644 --- a/src/DotNetCore.CAP.MongoDB/DotNetCore.CAP.MongoDB.csproj +++ b/src/DotNetCore.CAP.MongoDB/DotNetCore.CAP.MongoDB.csproj @@ -2,15 +2,10 @@ netstandard2.1 - DotNetCore.CAP.MongoDB + enable $(PackageTags);MongoDB - - - bin\$(Configuration)\netstandard2.1\DotNetCore.CAP.MongoDB.xml - 1701;1702;1705;CS1591 - - + diff --git a/src/DotNetCore.CAP.MongoDB/IClientSessionHandle.CAP.cs b/src/DotNetCore.CAP.MongoDB/IClientSessionHandle.CAP.cs index 177fb00..746008d 100644 --- a/src/DotNetCore.CAP.MongoDB/IClientSessionHandle.CAP.cs +++ b/src/DotNetCore.CAP.MongoDB/IClientSessionHandle.CAP.cs @@ -19,7 +19,7 @@ namespace MongoDB.Driver public CapMongoDbClientSessionHandle(ICapTransaction transaction) { _transaction = transaction; - _sessionHandle = (IClientSessionHandle)_transaction.DbTransaction; + _sessionHandle = (IClientSessionHandle)_transaction.DbTransaction!; } public void Dispose() @@ -59,7 +59,7 @@ namespace MongoDB.Driver return Task.CompletedTask; } - public void StartTransaction(TransactionOptions transactionOptions = null) + public void StartTransaction(TransactionOptions? transactionOptions = null) { _sessionHandle.StartTransaction(transactionOptions); } @@ -78,12 +78,12 @@ namespace MongoDB.Driver return _sessionHandle.Fork(); } - public TResult WithTransaction(Func callback, TransactionOptions transactionOptions = null, CancellationToken cancellationToken = default) + public TResult WithTransaction(Func callback, TransactionOptions? transactionOptions = null, CancellationToken cancellationToken = default) { return _sessionHandle.WithTransaction(callback, transactionOptions, cancellationToken); } - public Task WithTransactionAsync(Func> callbackAsync, TransactionOptions transactionOptions = null, CancellationToken cancellationToken = default) + public Task WithTransactionAsync(Func> callbackAsync, TransactionOptions? transactionOptions = null, CancellationToken cancellationToken = default) { return _sessionHandle.WithTransactionAsync(callbackAsync, transactionOptions, cancellationToken); } diff --git a/src/DotNetCore.CAP.MongoDB/IDataStorage.MongoDB.cs b/src/DotNetCore.CAP.MongoDB/IDataStorage.MongoDB.cs index f6132ed..2875293 100644 --- a/src/DotNetCore.CAP.MongoDB/IDataStorage.MongoDB.cs +++ b/src/DotNetCore.CAP.MongoDB/IDataStorage.MongoDB.cs @@ -63,7 +63,7 @@ namespace DotNetCore.CAP.MongoDB await collection.UpdateOneAsync(x => x.Id == long.Parse(message.DbId), updateDef); } - public MediumMessage StoreMessage(string name, Message content, object dbTransaction = null) + public MediumMessage StoreMessage(string name, Message content, object? dbTransaction = null) { var insertOptions = new InsertOneOptions { BypassDocumentValidation = false }; @@ -188,7 +188,7 @@ namespace DotNetCore.CAP.MongoDB return queryResult.Select(x => new MediumMessage { DbId = x.Id.ToString(), - Origin = _serializer.Deserialize(x.Content), + Origin = _serializer.Deserialize(x.Content)!, Retries = x.Retries, Added = x.Added }).ToList(); @@ -209,7 +209,7 @@ namespace DotNetCore.CAP.MongoDB return queryResult.Select(x => new MediumMessage { DbId = x.Id.ToString(), - Origin = _serializer.Deserialize(x.Content), + Origin = _serializer.Deserialize(x.Content)!, Retries = x.Retries, Added = x.Added }).ToList(); diff --git a/src/DotNetCore.CAP.MongoDB/StorageMessage.cs b/src/DotNetCore.CAP.MongoDB/StorageMessage.cs index 02cf6b8..cf3b8a4 100644 --- a/src/DotNetCore.CAP.MongoDB/StorageMessage.cs +++ b/src/DotNetCore.CAP.MongoDB/StorageMessage.cs @@ -9,13 +9,13 @@ namespace DotNetCore.CAP.MongoDB { public long Id { get; set; } - public string Version { get; set; } + public string Version { get; set; } = default!; - public string Group { get; set; } + public string Group { get; set; } = default!; - public string Name { get; set; } + public string Name { get; set; } = default!; - public string Content { get; set; } + public string Content { get; set; } = default!; public DateTime Added { get; set; } @@ -23,18 +23,18 @@ namespace DotNetCore.CAP.MongoDB public int Retries { get; set; } - public string StatusName { get; set; } + public string StatusName { get; set; } = default!; } internal class PublishedMessage { public long Id { get; set; } - public string Version { get; set; } + public string Version { get; set; } = default!; - public string Name { get; set; } + public string Name { get; set; } = default!; - public string Content { get; set; } + public string Content { get; set; } = default!; public DateTime Added { get; set; } @@ -42,6 +42,6 @@ namespace DotNetCore.CAP.MongoDB public int Retries { get; set; } - public string StatusName { get; set; } + public string StatusName { get; set; } = default!; } } \ No newline at end of file From 2e2b45f83370ea0282f24be63b44b85d2e0319d8 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Mon, 27 Dec 2021 13:53:29 +0800 Subject: [PATCH 10/17] Enable #nullable for mysql storage. --- .../IMonitoringApi.InMemory.cs | 8 ++++---- src/DotNetCore.CAP.MySql/CAP.EFOptions.cs | 2 +- src/DotNetCore.CAP.MySql/CAP.MySqlOptions.cs | 2 +- src/DotNetCore.CAP.MySql/DotNetCore.CAP.MySql.csproj | 8 +------- src/DotNetCore.CAP.MySql/IDataStorage.MySql.cs | 10 +++++----- src/DotNetCore.CAP.MySql/IDbConnection.Extensions.cs | 10 +++++----- src/DotNetCore.CAP.MySql/IDbContextTransaction.CAP.cs | 4 ++-- src/DotNetCore.CAP.MySql/IMonitoringApi.MySql.cs | 8 ++++---- src/DotNetCore.CAP/Monitoring/IMonitoringApi.cs | 4 ++-- 9 files changed, 25 insertions(+), 31 deletions(-) diff --git a/src/DotNetCore.CAP.InMemoryStorage/IMonitoringApi.InMemory.cs b/src/DotNetCore.CAP.InMemoryStorage/IMonitoringApi.InMemory.cs index eae2cbd..2dcb4cb 100644 --- a/src/DotNetCore.CAP.InMemoryStorage/IMonitoringApi.InMemory.cs +++ b/src/DotNetCore.CAP.InMemoryStorage/IMonitoringApi.InMemory.cs @@ -15,14 +15,14 @@ namespace DotNetCore.CAP.InMemoryStorage { internal class InMemoryMonitoringApi : IMonitoringApi { - public Task GetPublishedMessageAsync(long id) + public Task GetPublishedMessageAsync(long id) { - return Task.FromResult((MediumMessage)InMemoryStorage.PublishedMessages.Values.FirstOrDefault(x => x.DbId == id.ToString(CultureInfo.InvariantCulture))); + return Task.FromResult(InMemoryStorage.PublishedMessages.Values.FirstOrDefault(x => x.DbId == id.ToString(CultureInfo.InvariantCulture))); } - public Task GetReceivedMessageAsync(long id) + public Task GetReceivedMessageAsync(long id) { - return Task.FromResult((MediumMessage)InMemoryStorage.ReceivedMessages.Values.FirstOrDefault(x => x.DbId == id.ToString(CultureInfo.InvariantCulture))); + return Task.FromResult(InMemoryStorage.ReceivedMessages.Values.FirstOrDefault(x => x.DbId == id.ToString(CultureInfo.InvariantCulture))); } public StatisticsDto GetStatistics() diff --git a/src/DotNetCore.CAP.MySql/CAP.EFOptions.cs b/src/DotNetCore.CAP.MySql/CAP.EFOptions.cs index e770eb1..636dea7 100644 --- a/src/DotNetCore.CAP.MySql/CAP.EFOptions.cs +++ b/src/DotNetCore.CAP.MySql/CAP.EFOptions.cs @@ -18,7 +18,7 @@ namespace DotNetCore.CAP /// /// EF db context type. /// - internal Type DbContextType { get; set; } + internal Type? DbContextType { get; set; } /// /// Data version diff --git a/src/DotNetCore.CAP.MySql/CAP.MySqlOptions.cs b/src/DotNetCore.CAP.MySql/CAP.MySqlOptions.cs index ea1f503..a6f78f9 100644 --- a/src/DotNetCore.CAP.MySql/CAP.MySqlOptions.cs +++ b/src/DotNetCore.CAP.MySql/CAP.MySqlOptions.cs @@ -13,7 +13,7 @@ namespace DotNetCore.CAP /// /// Gets or sets the database's connection string that will be used to store database entities. /// - public string ConnectionString { get; set; } + public string ConnectionString { get; set; } = default!; } internal class ConfigureMySqlOptions : IConfigureOptions diff --git a/src/DotNetCore.CAP.MySql/DotNetCore.CAP.MySql.csproj b/src/DotNetCore.CAP.MySql/DotNetCore.CAP.MySql.csproj index 59d3685..7d0e8bf 100644 --- a/src/DotNetCore.CAP.MySql/DotNetCore.CAP.MySql.csproj +++ b/src/DotNetCore.CAP.MySql/DotNetCore.CAP.MySql.csproj @@ -2,15 +2,9 @@ net6.0;netstandard2.1 - DotNetCore.CAP.MySql + enable $(PackageTags);MySQL - - - bin\$(Configuration)\netstandard2.1\DotNetCore.CAP.MySql.xml - 1701;1702;1705;CS1591 - - diff --git a/src/DotNetCore.CAP.MySql/IDataStorage.MySql.cs b/src/DotNetCore.CAP.MySql/IDataStorage.MySql.cs index 72a0074..b2866f9 100644 --- a/src/DotNetCore.CAP.MySql/IDataStorage.MySql.cs +++ b/src/DotNetCore.CAP.MySql/IDataStorage.MySql.cs @@ -46,7 +46,7 @@ namespace DotNetCore.CAP.MySql public async Task ChangeReceiveStateAsync(MediumMessage message, StatusName state) => await ChangeMessageStateAsync(_recName, message, state); - public MediumMessage StoreMessage(string name, Message content, object dbTransaction = null) + public MediumMessage StoreMessage(string name, Message content, object? dbTransaction = null) { var sql = $"INSERT INTO `{_pubName}`(`Id`,`Version`,`Name`,`Content`,`Retries`,`Added`,`ExpiresAt`,`StatusName`)" + $" VALUES(@Id,'{_options.Value.Version}',@Name,@Content,@Retries,@Added,@ExpiresAt,@StatusName);"; @@ -68,7 +68,7 @@ namespace DotNetCore.CAP.MySql new MySqlParameter("@Content", message.Content), new MySqlParameter("@Retries", message.Retries), new MySqlParameter("@Added", message.Added), - new MySqlParameter("@ExpiresAt", message.ExpiresAt.HasValue ? (object)message.ExpiresAt.Value : DBNull.Value), + new MySqlParameter("@ExpiresAt", message.ExpiresAt.HasValue ? message.ExpiresAt.Value : DBNull.Value), new MySqlParameter("@StatusName", nameof(StatusName.Scheduled)), }; @@ -85,7 +85,7 @@ namespace DotNetCore.CAP.MySql dbTrans = dbContextTrans.GetDbTransaction(); } - var conn = dbTrans?.Connection; + var conn = dbTrans!.Connection!; conn.ExecuteNonQuery(sql, dbTrans, sqlParams); } @@ -128,7 +128,7 @@ namespace DotNetCore.CAP.MySql new MySqlParameter("@Content", _serializer.Serialize(mdMessage.Origin)), new MySqlParameter("@Retries", mdMessage.Retries), new MySqlParameter("@Added", mdMessage.Added), - new MySqlParameter("@ExpiresAt", mdMessage.ExpiresAt.HasValue ? (object) mdMessage.ExpiresAt.Value : DBNull.Value), + new MySqlParameter("@ExpiresAt", mdMessage.ExpiresAt.HasValue ? mdMessage.ExpiresAt.Value : DBNull.Value), new MySqlParameter("@StatusName", nameof(StatusName.Scheduled)) }; @@ -198,7 +198,7 @@ namespace DotNetCore.CAP.MySql messages.Add(new MediumMessage { DbId = reader.GetInt64(0).ToString(), - Origin = _serializer.Deserialize(reader.GetString(1)), + Origin = _serializer.Deserialize(reader.GetString(1))!, Retries = reader.GetInt32(2), Added = reader.GetDateTime(3) }); diff --git a/src/DotNetCore.CAP.MySql/IDbConnection.Extensions.cs b/src/DotNetCore.CAP.MySql/IDbConnection.Extensions.cs index f203025..3dcb0bf 100644 --- a/src/DotNetCore.CAP.MySql/IDbConnection.Extensions.cs +++ b/src/DotNetCore.CAP.MySql/IDbConnection.Extensions.cs @@ -9,7 +9,7 @@ namespace DotNetCore.CAP.MySql { internal static class DbConnectionExtensions { - public static int ExecuteNonQuery(this IDbConnection connection, string sql, IDbTransaction transaction = null, + public static int ExecuteNonQuery(this IDbConnection connection, string sql, IDbTransaction? transaction = null, params object[] sqlParams) { if (connection.State == ConnectionState.Closed) @@ -33,7 +33,7 @@ namespace DotNetCore.CAP.MySql return command.ExecuteNonQuery(); } - public static T ExecuteReader(this IDbConnection connection, string sql, Func readerFunc, + public static T ExecuteReader(this IDbConnection connection, string sql, Func? readerFunc, params object[] sqlParams) { if (connection.State == ConnectionState.Closed) @@ -51,7 +51,7 @@ namespace DotNetCore.CAP.MySql var reader = command.ExecuteReader(); - T result = default; + T result = default!; if (readerFunc != null) { result = readerFunc(reader); @@ -77,14 +77,14 @@ namespace DotNetCore.CAP.MySql var objValue = command.ExecuteScalar(); - T result = default; + T result = default!; if (objValue != null) { var returnType = typeof(T); var converter = TypeDescriptor.GetConverter(returnType); if (converter.CanConvertFrom(objValue.GetType())) { - result = (T)converter.ConvertFrom(objValue); + result = (T)converter.ConvertFrom(objValue)!; } else { diff --git a/src/DotNetCore.CAP.MySql/IDbContextTransaction.CAP.cs b/src/DotNetCore.CAP.MySql/IDbContextTransaction.CAP.cs index 2f89d47..015edf1 100644 --- a/src/DotNetCore.CAP.MySql/IDbContextTransaction.CAP.cs +++ b/src/DotNetCore.CAP.MySql/IDbContextTransaction.CAP.cs @@ -19,7 +19,7 @@ namespace Microsoft.EntityFrameworkCore.Storage public CapEFDbTransaction(ICapTransaction transaction) { _transaction = transaction; - var dbContextTransaction = (IDbContextTransaction)_transaction.DbTransaction; + var dbContextTransaction = (IDbContextTransaction)_transaction.DbTransaction!; TransactionId = dbContextTransaction.TransactionId; } @@ -60,7 +60,7 @@ namespace Microsoft.EntityFrameworkCore.Storage { get { - var dbContextTransaction = (IDbContextTransaction)_transaction.DbTransaction; + var dbContextTransaction = (IDbContextTransaction)_transaction.DbTransaction!; return dbContextTransaction.GetDbTransaction(); } } diff --git a/src/DotNetCore.CAP.MySql/IMonitoringApi.MySql.cs b/src/DotNetCore.CAP.MySql/IMonitoringApi.MySql.cs index 5864bdb..aaea317 100644 --- a/src/DotNetCore.CAP.MySql/IMonitoringApi.MySql.cs +++ b/src/DotNetCore.CAP.MySql/IMonitoringApi.MySql.cs @@ -247,18 +247,18 @@ WHERE `Key` >= @minKey return result; } - public async Task GetPublishedMessageAsync(long id) => await GetMessageAsync(_pubName, id); + public async Task GetPublishedMessageAsync(long id) => await GetMessageAsync(_pubName, id); - public async Task GetReceivedMessageAsync(long id) => await GetMessageAsync(_recName, id); + public async Task GetReceivedMessageAsync(long id) => await GetMessageAsync(_recName, id); - private async Task GetMessageAsync(string tableName, long id) + private async Task GetMessageAsync(string tableName, long id) { var sql = $@"SELECT `Id` as DbId, `Content`,`Added`,`ExpiresAt`,`Retries` FROM `{tableName}` WHERE Id={id};"; await using var connection = new MySqlConnection(_options.ConnectionString); var mediumMessage = connection.ExecuteReader(sql, reader => { - MediumMessage message = null; + MediumMessage? message = null; while (reader.Read()) { diff --git a/src/DotNetCore.CAP/Monitoring/IMonitoringApi.cs b/src/DotNetCore.CAP/Monitoring/IMonitoringApi.cs index 1c2343c..eb0b020 100644 --- a/src/DotNetCore.CAP/Monitoring/IMonitoringApi.cs +++ b/src/DotNetCore.CAP/Monitoring/IMonitoringApi.cs @@ -11,9 +11,9 @@ namespace DotNetCore.CAP.Monitoring { public interface IMonitoringApi { - Task GetPublishedMessageAsync(long id); + Task GetPublishedMessageAsync(long id); - Task GetReceivedMessageAsync(long id); + Task GetReceivedMessageAsync(long id); StatisticsDto GetStatistics(); From 3cf141f6aa16502f7a5fca13135c7ea7badd8920 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Mon, 27 Dec 2021 14:01:21 +0800 Subject: [PATCH 11/17] Enable #nullable for NATS transport. --- src/DotNetCore.CAP.NATS/CAP.NATSOptions.cs | 4 ++-- .../CAP.Options.Extensions.cs | 3 +-- .../DotNetCore.CAP.NATS.csproj | 8 +------- src/DotNetCore.CAP.NATS/NATSConsumerClient.cs | 20 ++++++++++--------- 4 files changed, 15 insertions(+), 20 deletions(-) diff --git a/src/DotNetCore.CAP.NATS/CAP.NATSOptions.cs b/src/DotNetCore.CAP.NATS/CAP.NATSOptions.cs index 8ba4f0a..4761244 100644 --- a/src/DotNetCore.CAP.NATS/CAP.NATSOptions.cs +++ b/src/DotNetCore.CAP.NATS/CAP.NATSOptions.cs @@ -27,9 +27,9 @@ namespace DotNetCore.CAP /// /// Used to setup all NATs client options /// - public Options Options { get; set; } + public Options? Options { get; set; } - public Action StreamOptions { get; set; } + public Action? StreamOptions { get; set; } public Func NormalizeStreamName { get; set; } = origin => origin.Split('.')[0]; } diff --git a/src/DotNetCore.CAP.NATS/CAP.Options.Extensions.cs b/src/DotNetCore.CAP.NATS/CAP.Options.Extensions.cs index 743aafd..c6be74b 100644 --- a/src/DotNetCore.CAP.NATS/CAP.Options.Extensions.cs +++ b/src/DotNetCore.CAP.NATS/CAP.Options.Extensions.cs @@ -3,7 +3,6 @@ using System; using DotNetCore.CAP; -using JetBrains.Annotations; // ReSharper disable once CheckNamespace namespace Microsoft.Extensions.DependencyInjection @@ -15,7 +14,7 @@ namespace Microsoft.Extensions.DependencyInjection /// /// CAP configuration options /// NATS bootstrap server urls. - public static CapOptions UseNATS(this CapOptions options, [CanBeNull] string bootstrapServers = null) + public static CapOptions UseNATS(this CapOptions options, string? bootstrapServers = null) { return options.UseNATS(opt => { diff --git a/src/DotNetCore.CAP.NATS/DotNetCore.CAP.NATS.csproj b/src/DotNetCore.CAP.NATS/DotNetCore.CAP.NATS.csproj index 2557ace..72b7718 100644 --- a/src/DotNetCore.CAP.NATS/DotNetCore.CAP.NATS.csproj +++ b/src/DotNetCore.CAP.NATS/DotNetCore.CAP.NATS.csproj @@ -2,16 +2,10 @@ netstandard2.1 - DotNetCore.CAP.NATS + enable $(PackageTags);NATS - - NU1605;NU1701 - NU1701;CS1591 - bin\$(Configuration)\netstandard2.1\DotNetCore.CAP.NATS.xml - - diff --git a/src/DotNetCore.CAP.NATS/NATSConsumerClient.cs b/src/DotNetCore.CAP.NATS/NATSConsumerClient.cs index f67d67a..a7ef802 100644 --- a/src/DotNetCore.CAP.NATS/NATSConsumerClient.cs +++ b/src/DotNetCore.CAP.NATS/NATSConsumerClient.cs @@ -21,7 +21,7 @@ namespace DotNetCore.CAP.NATS private readonly string _groupId; private readonly NATSOptions _natsOptions; - private IConnection _consumerClient; + private IConnection? _consumerClient; public NATSConsumerClient(string groupId, IOptions options) { @@ -29,15 +29,17 @@ namespace DotNetCore.CAP.NATS _natsOptions = options.Value ?? throw new ArgumentNullException(nameof(options)); } - public event EventHandler OnMessageReceived; + public event EventHandler? OnMessageReceived; - public event EventHandler OnLog; + public event EventHandler? OnLog; - public BrokerAddress BrokerAddress => new BrokerAddress("NATS", _natsOptions.Servers); + public BrokerAddress BrokerAddress => new ("NATS", _natsOptions.Servers); public ICollection FetchTopics(IEnumerable topicNames) { - var jsm = _consumerClient.CreateJetStreamManagementContext(); + Connect(); + + var jsm = _consumerClient!.CreateJetStreamManagementContext(); var streamGroup = topicNames.GroupBy(x => _natsOptions.NormalizeStreamName(x)); @@ -80,7 +82,7 @@ namespace DotNetCore.CAP.NATS Connect(); - var js = _consumerClient.CreateJetStreamContext(); + var js = _consumerClient!.CreateJetStreamContext(); foreach (var subject in topics) { @@ -106,7 +108,7 @@ namespace DotNetCore.CAP.NATS private void Subscription_MessageHandler(object sender, MsgHandlerEventArgs e) { - var headers = new Dictionary(); + var headers = new Dictionary(); foreach (string h in e.Message.Header.Keys) { @@ -126,7 +128,7 @@ namespace DotNetCore.CAP.NATS } } - public void Reject(object sender) + public void Reject(object? sender) { if (sender is Msg msg) { @@ -153,7 +155,7 @@ namespace DotNetCore.CAP.NATS if (_consumerClient == null) { var opts = _natsOptions.Options ?? ConnectionFactory.GetDefaultOptions(); - opts.Url = _natsOptions.Servers ?? opts.Url; + opts.Url ??= _natsOptions.Servers; opts.ClosedEventHandler = ConnectedEventHandler; opts.DisconnectedEventHandler = ConnectedEventHandler; opts.AsyncErrorEventHandler = AsyncErrorEventHandler; From bddaf525e95402a8dd7d5a0dd14c16440f980957 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Mon, 27 Dec 2021 14:07:35 +0800 Subject: [PATCH 12/17] Enable #nullable for postgresql storage. --- src/Directory.Build.props | 1 - src/DotNetCore.CAP.PostgreSql/CAP.EFOptions.cs | 4 ++-- src/DotNetCore.CAP.PostgreSql/CAP.PostgreSqlOptions.cs | 2 +- .../DotNetCore.CAP.PostgreSql.csproj | 7 +------ .../IDataStorage.PostgreSql.cs | 10 +++++----- .../IDbConnection.Extensions.cs | 10 +++++----- .../IDbContextTransaction.CAP.cs | 4 ++-- .../IMonitoringApi.PostgreSql.cs | 10 +++++----- src/DotNetCore.CAP/CAP.Builder.cs | 3 +-- 9 files changed, 22 insertions(+), 29 deletions(-) diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 44c3dd8..3efd601 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -26,7 +26,6 @@ - \ No newline at end of file diff --git a/src/DotNetCore.CAP.PostgreSql/CAP.EFOptions.cs b/src/DotNetCore.CAP.PostgreSql/CAP.EFOptions.cs index 5fa9d8b..4108988 100644 --- a/src/DotNetCore.CAP.PostgreSql/CAP.EFOptions.cs +++ b/src/DotNetCore.CAP.PostgreSql/CAP.EFOptions.cs @@ -16,11 +16,11 @@ namespace DotNetCore.CAP /// public string Schema { get; set; } = DefaultSchema; - internal Type DbContextType { get; set; } + internal Type? DbContextType { get; set; } /// /// Data version /// - internal string Version { get; set; } + internal string Version { get; set; } = default!; } } \ No newline at end of file diff --git a/src/DotNetCore.CAP.PostgreSql/CAP.PostgreSqlOptions.cs b/src/DotNetCore.CAP.PostgreSql/CAP.PostgreSqlOptions.cs index d881b2d..bcaf405 100644 --- a/src/DotNetCore.CAP.PostgreSql/CAP.PostgreSqlOptions.cs +++ b/src/DotNetCore.CAP.PostgreSql/CAP.PostgreSqlOptions.cs @@ -13,7 +13,7 @@ namespace DotNetCore.CAP /// /// Gets or sets the database's connection string that will be used to store database entities. /// - public string ConnectionString { get; set; } + public string ConnectionString { get; set; } = default!; } internal class ConfigurePostgreSqlOptions : IConfigureOptions diff --git a/src/DotNetCore.CAP.PostgreSql/DotNetCore.CAP.PostgreSql.csproj b/src/DotNetCore.CAP.PostgreSql/DotNetCore.CAP.PostgreSql.csproj index e5fe752..6dfb06c 100644 --- a/src/DotNetCore.CAP.PostgreSql/DotNetCore.CAP.PostgreSql.csproj +++ b/src/DotNetCore.CAP.PostgreSql/DotNetCore.CAP.PostgreSql.csproj @@ -2,15 +2,10 @@ net6.0;netstandard2.1 - DotNetCore.CAP.PostgreSql + enable $(PackageTags);PostgreSQL - - bin\$(Configuration)\netstandard2.1\DotNetCore.CAP.PostgreSql.xml - 1701;1702;1705;CS1591 - - diff --git a/src/DotNetCore.CAP.PostgreSql/IDataStorage.PostgreSql.cs b/src/DotNetCore.CAP.PostgreSql/IDataStorage.PostgreSql.cs index 6b60b6f..73a8610 100644 --- a/src/DotNetCore.CAP.PostgreSql/IDataStorage.PostgreSql.cs +++ b/src/DotNetCore.CAP.PostgreSql/IDataStorage.PostgreSql.cs @@ -46,7 +46,7 @@ namespace DotNetCore.CAP.PostgreSql public async Task ChangeReceiveStateAsync(MediumMessage message, StatusName state) => await ChangeMessageStateAsync(_recName, message, state); - public MediumMessage StoreMessage(string name, Message content, object dbTransaction = null) + public MediumMessage StoreMessage(string name, Message content, object? dbTransaction = null) { var sql = $"INSERT INTO {_pubName} (\"Id\",\"Version\",\"Name\",\"Content\",\"Retries\",\"Added\",\"ExpiresAt\",\"StatusName\")" + @@ -69,7 +69,7 @@ namespace DotNetCore.CAP.PostgreSql new NpgsqlParameter("@Content", message.Content), new NpgsqlParameter("@Retries", message.Retries), new NpgsqlParameter("@Added", message.Added), - new NpgsqlParameter("@ExpiresAt", message.ExpiresAt.HasValue ? (object)message.ExpiresAt.Value : DBNull.Value), + new NpgsqlParameter("@ExpiresAt", message.ExpiresAt.HasValue ? message.ExpiresAt.Value : DBNull.Value), new NpgsqlParameter("@StatusName", nameof(StatusName.Scheduled)) }; @@ -84,7 +84,7 @@ namespace DotNetCore.CAP.PostgreSql if (dbTrans == null && dbTransaction is IDbContextTransaction dbContextTrans) dbTrans = dbContextTrans.GetDbTransaction(); - var conn = dbTrans?.Connection; + var conn = dbTrans?.Connection!; conn.ExecuteNonQuery(sql, dbTrans, sqlParams); } @@ -127,7 +127,7 @@ namespace DotNetCore.CAP.PostgreSql new NpgsqlParameter("@Content", _serializer.Serialize(mdMessage.Origin)), new NpgsqlParameter("@Retries", mdMessage.Retries), new NpgsqlParameter("@Added", mdMessage.Added), - new NpgsqlParameter("@ExpiresAt", mdMessage.ExpiresAt.HasValue ? (object) mdMessage.ExpiresAt.Value : DBNull.Value), + new NpgsqlParameter("@ExpiresAt", mdMessage.ExpiresAt.HasValue ? mdMessage.ExpiresAt.Value : DBNull.Value), new NpgsqlParameter("@StatusName", nameof(StatusName.Scheduled)) }; @@ -199,7 +199,7 @@ namespace DotNetCore.CAP.PostgreSql messages.Add(new MediumMessage { DbId = reader.GetInt64(0).ToString(), - Origin = _serializer.Deserialize(reader.GetString(1)), + Origin = _serializer.Deserialize(reader.GetString(1))!, Retries = reader.GetInt32(2), Added = reader.GetDateTime(3) }); diff --git a/src/DotNetCore.CAP.PostgreSql/IDbConnection.Extensions.cs b/src/DotNetCore.CAP.PostgreSql/IDbConnection.Extensions.cs index 0a5ac1f..52f35b3 100644 --- a/src/DotNetCore.CAP.PostgreSql/IDbConnection.Extensions.cs +++ b/src/DotNetCore.CAP.PostgreSql/IDbConnection.Extensions.cs @@ -9,7 +9,7 @@ namespace DotNetCore.CAP.PostgreSql { internal static class DbConnectionExtensions { - public static int ExecuteNonQuery(this IDbConnection connection, string sql, IDbTransaction transaction = null, + public static int ExecuteNonQuery(this IDbConnection connection, string sql, IDbTransaction? transaction = null, params object[] sqlParams) { if (connection.State == ConnectionState.Closed) @@ -33,7 +33,7 @@ namespace DotNetCore.CAP.PostgreSql return command.ExecuteNonQuery(); } - public static T ExecuteReader(this IDbConnection connection, string sql, Func readerFunc, + public static T ExecuteReader(this IDbConnection connection, string sql, Func? readerFunc, params object[] sqlParams) { if (connection.State == ConnectionState.Closed) @@ -51,7 +51,7 @@ namespace DotNetCore.CAP.PostgreSql var reader = command.ExecuteReader(); - T result = default; + T result = default!; if (readerFunc != null) { result = readerFunc(reader); @@ -77,14 +77,14 @@ namespace DotNetCore.CAP.PostgreSql var objValue = command.ExecuteScalar(); - T result = default; + T result = default!; if (objValue != null) { var returnType = typeof(T); var converter = TypeDescriptor.GetConverter(returnType); if (converter.CanConvertFrom(objValue.GetType())) { - result = (T)converter.ConvertFrom(objValue); + result = (T)converter.ConvertFrom(objValue)!; } else { diff --git a/src/DotNetCore.CAP.PostgreSql/IDbContextTransaction.CAP.cs b/src/DotNetCore.CAP.PostgreSql/IDbContextTransaction.CAP.cs index 794ea2e..1354274 100644 --- a/src/DotNetCore.CAP.PostgreSql/IDbContextTransaction.CAP.cs +++ b/src/DotNetCore.CAP.PostgreSql/IDbContextTransaction.CAP.cs @@ -18,7 +18,7 @@ namespace Microsoft.EntityFrameworkCore.Storage public CapEFDbTransaction(ICapTransaction transaction) { _transaction = transaction; - var dbContextTransaction = (IDbContextTransaction)_transaction.DbTransaction; + var dbContextTransaction = (IDbContextTransaction)_transaction.DbTransaction!; TransactionId = dbContextTransaction.TransactionId; } @@ -58,7 +58,7 @@ namespace Microsoft.EntityFrameworkCore.Storage { get { - var dbContextTransaction = (IDbContextTransaction) _transaction.DbTransaction; + var dbContextTransaction = (IDbContextTransaction) _transaction.DbTransaction!; return dbContextTransaction.GetDbTransaction(); } } diff --git a/src/DotNetCore.CAP.PostgreSql/IMonitoringApi.PostgreSql.cs b/src/DotNetCore.CAP.PostgreSql/IMonitoringApi.PostgreSql.cs index 39d55db..ec98a80 100644 --- a/src/DotNetCore.CAP.PostgreSql/IMonitoringApi.PostgreSql.cs +++ b/src/DotNetCore.CAP.PostgreSql/IMonitoringApi.PostgreSql.cs @@ -27,9 +27,9 @@ namespace DotNetCore.CAP.PostgreSql _recName = initializer.GetReceivedTableName(); } - public async Task GetPublishedMessageAsync(long id) => await GetMessageAsync(_pubName, id); + public async Task GetPublishedMessageAsync(long id) => await GetMessageAsync(_pubName, id); - public async Task GetReceivedMessageAsync(long id) => await GetMessageAsync(_recName, id); + public async Task GetReceivedMessageAsync(long id) => await GetMessageAsync(_recName, id); public StatisticsDto GetStatistics() { @@ -120,7 +120,7 @@ namespace DotNetCore.CAP.PostgreSql Content = reader.GetString(index++), Retries = reader.GetInt32(index++), Added = reader.GetDateTime(index++), - ExpiresAt = reader.IsDBNull(index++) ? (DateTime?)null : reader.GetDateTime(index - 1), + ExpiresAt = reader.IsDBNull(index++) ? null : reader.GetDateTime(index - 1), StatusName = reader.GetString(index) }); } @@ -242,14 +242,14 @@ select ""Key"",""Count"" from aggr where ""Key"" >= @minKey and ""Key"" <= @maxK return result; } - private async Task GetMessageAsync(string tableName, long id) + private async Task GetMessageAsync(string tableName, long id) { var sql = $@"SELECT ""Id"" AS ""DbId"", ""Content"", ""Added"", ""ExpiresAt"", ""Retries"" FROM {tableName} WHERE ""Id""={id} FOR UPDATE SKIP LOCKED"; await using var connection = new NpgsqlConnection(_options.ConnectionString); var mediumMessage = connection.ExecuteReader(sql, reader => { - MediumMessage message = null; + MediumMessage? message = null; while (reader.Read()) { diff --git a/src/DotNetCore.CAP/CAP.Builder.cs b/src/DotNetCore.CAP/CAP.Builder.cs index 8cbf830..99c3eba 100644 --- a/src/DotNetCore.CAP/CAP.Builder.cs +++ b/src/DotNetCore.CAP/CAP.Builder.cs @@ -6,7 +6,6 @@ using System.Linq; using System.Reflection; using DotNetCore.CAP.Filter; using DotNetCore.CAP.Internal; -using JetBrains.Annotations; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; // ReSharper disable UnusedMember.Global @@ -78,7 +77,7 @@ namespace DotNetCore.CAP /// Registers subscribers from the specified types. /// /// - public CapBuilder AddSubscriberAssembly([NotNull] params Type[] handlerAssemblyMarkerTypes) + public CapBuilder AddSubscriberAssembly(params Type[] handlerAssemblyMarkerTypes) { if (handlerAssemblyMarkerTypes == null) throw new ArgumentNullException(nameof(handlerAssemblyMarkerTypes)); From 2a848af23b0d8a656116cb32bb12f3b73613c81e Mon Sep 17 00:00:00 2001 From: Savorboard Date: Mon, 27 Dec 2021 14:21:30 +0800 Subject: [PATCH 13/17] Enable #nullable for Pulsar transport. --- .../IMonitoringApi.MongoDB.cs | 4 +- .../CAP.PulsarOptions.cs | 4 +- .../DotNetCore.CAP.Pulsar.csproj | 8 +--- .../IConnectionFactory.Default.cs | 2 +- .../ITransport.Pulsar.cs | 6 +-- .../PulsarConsumerClient.cs | 39 ++++++++----------- 6 files changed, 25 insertions(+), 38 deletions(-) diff --git a/src/DotNetCore.CAP.MongoDB/IMonitoringApi.MongoDB.cs b/src/DotNetCore.CAP.MongoDB/IMonitoringApi.MongoDB.cs index 321322e..e935145 100644 --- a/src/DotNetCore.CAP.MongoDB/IMonitoringApi.MongoDB.cs +++ b/src/DotNetCore.CAP.MongoDB/IMonitoringApi.MongoDB.cs @@ -27,7 +27,7 @@ namespace DotNetCore.CAP.MongoDB _database = mongoClient.GetDatabase(_options.DatabaseName); } - public async Task GetPublishedMessageAsync(long id) + public async Task GetPublishedMessageAsync(long id) { var collection = _database.GetCollection(_options.PublishedCollection); var message = await collection.Find(x => x.Id == id).FirstOrDefaultAsync(); @@ -41,7 +41,7 @@ namespace DotNetCore.CAP.MongoDB }; } - public async Task GetReceivedMessageAsync(long id) + public async Task GetReceivedMessageAsync(long id) { var collection = _database.GetCollection(_options.ReceivedCollection); var message = await collection.Find(x => x.Id == id).FirstOrDefaultAsync(); diff --git a/src/DotNetCore.CAP.Pulsar/CAP.PulsarOptions.cs b/src/DotNetCore.CAP.Pulsar/CAP.PulsarOptions.cs index 61428c9..5e9261e 100644 --- a/src/DotNetCore.CAP.Pulsar/CAP.PulsarOptions.cs +++ b/src/DotNetCore.CAP.Pulsar/CAP.PulsarOptions.cs @@ -11,9 +11,9 @@ namespace DotNetCore.CAP /// public class PulsarOptions { - public string ServiceUrl { get; set; } + public string ServiceUrl { get; set; } = default!; - public TlsOptions TlsOptions { get; set; } + public TlsOptions? TlsOptions { get; set; } } } diff --git a/src/DotNetCore.CAP.Pulsar/DotNetCore.CAP.Pulsar.csproj b/src/DotNetCore.CAP.Pulsar/DotNetCore.CAP.Pulsar.csproj index ecc1af2..4ab2269 100644 --- a/src/DotNetCore.CAP.Pulsar/DotNetCore.CAP.Pulsar.csproj +++ b/src/DotNetCore.CAP.Pulsar/DotNetCore.CAP.Pulsar.csproj @@ -2,16 +2,10 @@ netstandard2.1 - DotNetCore.CAP.Pulsar + enable $(PackageTags);Pulsar - - NU1605;NU1701 - NU1701;CS1591 - bin\$(Configuration)\netstandard2.0\DotNetCore.CAP.Pulsar.xml - - diff --git a/src/DotNetCore.CAP.Pulsar/IConnectionFactory.Default.cs b/src/DotNetCore.CAP.Pulsar/IConnectionFactory.Default.cs index e0c55f1..8b11b02 100644 --- a/src/DotNetCore.CAP.Pulsar/IConnectionFactory.Default.cs +++ b/src/DotNetCore.CAP.Pulsar/IConnectionFactory.Default.cs @@ -13,7 +13,7 @@ namespace DotNetCore.CAP.Pulsar { public class ConnectionFactory : IConnectionFactory, IAsyncDisposable { - private PulsarClient _client; + private PulsarClient? _client; private readonly PulsarOptions _options; private readonly ConcurrentDictionary>> _topicProducers; diff --git a/src/DotNetCore.CAP.Pulsar/ITransport.Pulsar.cs b/src/DotNetCore.CAP.Pulsar/ITransport.Pulsar.cs index 55c9bb0..5594a3b 100644 --- a/src/DotNetCore.CAP.Pulsar/ITransport.Pulsar.cs +++ b/src/DotNetCore.CAP.Pulsar/ITransport.Pulsar.cs @@ -22,7 +22,7 @@ namespace DotNetCore.CAP.Pulsar _connectionFactory = connectionFactory; } - public BrokerAddress BrokerAddress => new BrokerAddress("Pulsar", _connectionFactory.ServersAddress); + public BrokerAddress BrokerAddress => new ("Pulsar", _connectionFactory.ServersAddress); public async Task SendAsync(TransportMessage message) { @@ -30,9 +30,9 @@ namespace DotNetCore.CAP.Pulsar try { - var headerDic = new Dictionary(message.Headers); + var headerDic = new Dictionary(message.Headers); headerDic.TryGetValue(PulsarHeaders.PulsarKey, out var key); - var pulsarMessage = producer.NewMessage(message.Body, key, headerDic); + var pulsarMessage = producer.NewMessage(message.Body!, key, headerDic); var result = await producer.SendAsync(pulsarMessage); if (result.Type != null) diff --git a/src/DotNetCore.CAP.Pulsar/PulsarConsumerClient.cs b/src/DotNetCore.CAP.Pulsar/PulsarConsumerClient.cs index e81b953..440d4b2 100644 --- a/src/DotNetCore.CAP.Pulsar/PulsarConsumerClient.cs +++ b/src/DotNetCore.CAP.Pulsar/PulsarConsumerClient.cs @@ -15,23 +15,23 @@ namespace DotNetCore.CAP.Pulsar { internal sealed class PulsarConsumerClient : IConsumerClient { - private static PulsarClient _client; + private readonly PulsarClient _client; private readonly string _groupId; private readonly PulsarOptions _pulsarOptions; - private IConsumer _consumerClient; + private IConsumer? _consumerClient; - public PulsarConsumerClient(PulsarClient client,string groupId, IOptions options) + public PulsarConsumerClient(PulsarClient client, string groupId, IOptions options) { - _client = client; + _client = client; _groupId = groupId; _pulsarOptions = options.Value; } - public event EventHandler OnMessageReceived; + public event EventHandler? OnMessageReceived; - public event EventHandler OnLog; + public event EventHandler? OnLog; - public BrokerAddress BrokerAddress => new BrokerAddress("Pulsar", _pulsarOptions.ServiceUrl); + public BrokerAddress BrokerAddress => new ("Pulsar", _pulsarOptions.ServiceUrl); public void Subscribe(IEnumerable topics) { @@ -47,16 +47,16 @@ namespace DotNetCore.CAP.Pulsar .SubscriptionName(_groupId) .ConsumerName(serviceName) .SubscriptionType(SubscriptionType.Shared) - .SubscribeAsync().Result; + .SubscribeAsync().GetAwaiter().GetResult(); } public void Listening(TimeSpan timeout, CancellationToken cancellationToken) { while (!cancellationToken.IsCancellationRequested) { - var consumerResult = _consumerClient.ReceiveAsync().Result; + var consumerResult = _consumerClient!.ReceiveAsync(cancellationToken).Result; - var headers = new Dictionary(consumerResult.Properties.Count); + var headers = new Dictionary(consumerResult.Properties.Count); foreach (var header in consumerResult.Properties) { headers.Add(header.Key, header.Value); @@ -72,27 +72,20 @@ namespace DotNetCore.CAP.Pulsar public void Commit(object sender) { - _consumerClient.AcknowledgeAsync((MessageId)sender); + _consumerClient!.AcknowledgeAsync((MessageId)sender); } - public void Reject(object sender) + public void Reject(object? sender) { - _consumerClient.NegativeAcknowledge((MessageId)sender); + if(sender is MessageId id) + { + _consumerClient!.NegativeAcknowledge(id); + } } public void Dispose() { _consumerClient?.DisposeAsync(); } - - private void ConsumerClient_OnConsumeError(IConsumer consumer, Exception e) - { - var logArgs = new LogMessageEventArgs - { - LogType = MqLogType.ServerConnError, - Reason = $"An error occurred during connect pulsar --> {e.Message}" - }; - OnLog?.Invoke(null, logArgs); - } } } \ No newline at end of file From a471feaaa4220deed22d0989cfbf6e858b003f3b Mon Sep 17 00:00:00 2001 From: Savorboard Date: Mon, 27 Dec 2021 14:27:40 +0800 Subject: [PATCH 14/17] Enable #nullable for RabbitMq transport. --- .../CAP.RabbiMQOptions.cs | 9 ++++---- .../DotNetCore.CAP.RabbitMQ.csproj | 7 +------ .../IConnectionChannelPool.Default.cs | 2 +- .../ITransport.RabbitMQ.cs | 6 +++--- .../RabbitMQConsumerClient.cs | 21 +++++++++---------- 5 files changed, 20 insertions(+), 25 deletions(-) diff --git a/src/DotNetCore.CAP.RabbitMQ/CAP.RabbiMQOptions.cs b/src/DotNetCore.CAP.RabbitMQ/CAP.RabbiMQOptions.cs index d642222..ebe68e5 100644 --- a/src/DotNetCore.CAP.RabbitMQ/CAP.RabbiMQOptions.cs +++ b/src/DotNetCore.CAP.RabbitMQ/CAP.RabbiMQOptions.cs @@ -74,28 +74,29 @@ namespace DotNetCore.CAP /// Optional queue arguments, also known as "x-arguments" because of their field name in the AMQP 0-9-1 protocol, /// is a map (dictionary) of arbitrary key/value pairs that can be provided by clients when a queue is declared. /// - public QueueArgumentsOptions QueueArguments { get; set; } = new QueueArgumentsOptions(); + public QueueArgumentsOptions QueueArguments { get; set; } = new (); /// /// If you need to get additional native delivery args, you can use this function to write into . /// - public Func>> CustomHeaders { get; set; } + public Func>>? CustomHeaders { get; set; } /// /// RabbitMQ native connection factory options /// - public Action ConnectionFactoryOptions { get; set; } + public Action? ConnectionFactoryOptions { get; set; } public class QueueArgumentsOptions { /// /// Gets or sets queue mode by supplying the 'x-queue-mode' declaration argument with a string specifying the desired mode. /// - public string QueueMode { get; set; } + public string QueueMode { get; set; } = default!; /// /// Gets or sets queue message automatic deletion time (in milliseconds) "x-message-ttl", Default 864000000 ms (10 days). /// + // ReSharper disable once InconsistentNaming public int MessageTTL { get; set; } = 864000000; } } diff --git a/src/DotNetCore.CAP.RabbitMQ/DotNetCore.CAP.RabbitMQ.csproj b/src/DotNetCore.CAP.RabbitMQ/DotNetCore.CAP.RabbitMQ.csproj index eb88d96..d91e004 100644 --- a/src/DotNetCore.CAP.RabbitMQ/DotNetCore.CAP.RabbitMQ.csproj +++ b/src/DotNetCore.CAP.RabbitMQ/DotNetCore.CAP.RabbitMQ.csproj @@ -2,14 +2,9 @@ netstandard2.1 - DotNetCore.CAP.RabbitMQ + enable $(PackageTags);RabbitMQ - - - bin\$(Configuration)\netstandard2.1\DotNetCore.CAP.RabbitMQ.xml - 1701;1702;1705;CS1591 - diff --git a/src/DotNetCore.CAP.RabbitMQ/IConnectionChannelPool.Default.cs b/src/DotNetCore.CAP.RabbitMQ/IConnectionChannelPool.Default.cs index 1889553..370df8e 100644 --- a/src/DotNetCore.CAP.RabbitMQ/IConnectionChannelPool.Default.cs +++ b/src/DotNetCore.CAP.RabbitMQ/IConnectionChannelPool.Default.cs @@ -18,7 +18,7 @@ namespace DotNetCore.CAP.RabbitMQ private readonly Func _connectionActivator; private readonly ILogger _logger; private readonly ConcurrentQueue _pool; - private IConnection _connection; + private IConnection? _connection; private static readonly object SLock = new object(); private int _count; diff --git a/src/DotNetCore.CAP.RabbitMQ/ITransport.RabbitMQ.cs b/src/DotNetCore.CAP.RabbitMQ/ITransport.RabbitMQ.cs index 0a19789..499b7c4 100644 --- a/src/DotNetCore.CAP.RabbitMQ/ITransport.RabbitMQ.cs +++ b/src/DotNetCore.CAP.RabbitMQ/ITransport.RabbitMQ.cs @@ -27,11 +27,11 @@ namespace DotNetCore.CAP.RabbitMQ _exchange = _connectionChannelPool.Exchange; } - public BrokerAddress BrokerAddress => new BrokerAddress("RabbitMQ", _connectionChannelPool.HostAddress); + public BrokerAddress BrokerAddress => new ("RabbitMQ", _connectionChannelPool.HostAddress); public Task SendAsync(TransportMessage message) { - IModel channel = null; + IModel? channel = null; try { channel = _connectionChannelPool.Rent(); @@ -40,7 +40,7 @@ namespace DotNetCore.CAP.RabbitMQ var props = channel.CreateBasicProperties(); props.DeliveryMode = 2; - props.Headers = message.Headers.ToDictionary(x => x.Key, x => (object)x.Value); + props.Headers = message.Headers.ToDictionary(x => x.Key, x => (object?)x.Value); channel.ExchangeDeclare(_exchange, RabbitMQOptions.ExchangeType, true); diff --git a/src/DotNetCore.CAP.RabbitMQ/RabbitMQConsumerClient.cs b/src/DotNetCore.CAP.RabbitMQ/RabbitMQConsumerClient.cs index 9624841..548001b 100644 --- a/src/DotNetCore.CAP.RabbitMQ/RabbitMQConsumerClient.cs +++ b/src/DotNetCore.CAP.RabbitMQ/RabbitMQConsumerClient.cs @@ -22,9 +22,8 @@ namespace DotNetCore.CAP.RabbitMQ private readonly string _exchangeName; private readonly string _queueName; private readonly RabbitMQOptions _rabbitMQOptions; - private IModel _channel; - - private IConnection _connection; + private IModel? _channel; + private IConnection? _connection; public RabbitMQConsumerClient(string queueName, IConnectionChannelPool connectionChannelPool, @@ -36,11 +35,11 @@ namespace DotNetCore.CAP.RabbitMQ _exchangeName = connectionChannelPool.Exchange; } - public event EventHandler OnMessageReceived; + public event EventHandler? OnMessageReceived; - public event EventHandler OnLog; + public event EventHandler? OnLog; - public BrokerAddress BrokerAddress => new BrokerAddress("RabbitMQ", _rabbitMQOptions.HostName); + public BrokerAddress BrokerAddress => new("RabbitMQ", _rabbitMQOptions.HostName); public void Subscribe(IEnumerable topics) { @@ -81,17 +80,17 @@ namespace DotNetCore.CAP.RabbitMQ public void Commit(object sender) { - if (_channel.IsOpen) + if (_channel!.IsOpen) { _channel.BasicAck((ulong)sender, false); } } - public void Reject(object sender) + public void Reject(object? sender) { - if (_channel.IsOpen) + if (_channel!.IsOpen && sender is ulong val) { - _channel.BasicReject((ulong)sender, true); + _channel.BasicReject(val, true); } } @@ -175,7 +174,7 @@ namespace DotNetCore.CAP.RabbitMQ private void OnConsumerReceived(object sender, BasicDeliverEventArgs e) { - var headers = new Dictionary(); + var headers = new Dictionary(); if (e.BasicProperties.Headers != null) { From 61942d5d3132d7213f28bce28c99a01d4cfb4339 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Mon, 27 Dec 2021 15:08:44 +0800 Subject: [PATCH 15/17] Enable #nullable for redis streams transport. --- .../CapOptions.Redis.Extensions.cs | 12 +++- .../CapOptions.Redis.cs | 6 +- .../DotNetCore.CAP.RedisStreams.csproj | 4 +- .../IConnectionPool.Default.cs | 11 ++- .../IConnectionPool.LazyConnection.cs | 2 +- .../IConsumerClient.Redis.cs | 68 +++++++++++-------- .../IRedisStream.Manager.Default.cs | 10 +-- .../IRedisStream.Manager.Extensions.cs | 6 +- .../ITransport.Redis.cs | 2 +- .../TransportMessage.Redis.cs | 17 +++-- 10 files changed, 78 insertions(+), 60 deletions(-) diff --git a/src/DotNetCore.CAP.RedisStreams/CapOptions.Redis.Extensions.cs b/src/DotNetCore.CAP.RedisStreams/CapOptions.Redis.Extensions.cs index d892867..52a26dd 100644 --- a/src/DotNetCore.CAP.RedisStreams/CapOptions.Redis.Extensions.cs +++ b/src/DotNetCore.CAP.RedisStreams/CapOptions.Redis.Extensions.cs @@ -15,12 +15,22 @@ namespace Microsoft.Extensions.DependencyInjection return options.UseRedis(_ => { }); } + /// + /// Use redis streams as the message transport. + /// + /// The . + /// The StackExchange.Redis comma-delimited configuration string. public static CapOptions UseRedis(this CapOptions options, string connection) { return options.UseRedis(opt => opt.Configuration = ConfigurationOptions.Parse(connection)); } - + /// + /// Use redis streams as the message transport. + /// + /// The . + /// The CAP redis client options. + /// is null. public static CapOptions UseRedis(this CapOptions options, Action configure) { if (configure is null) throw new ArgumentNullException(nameof(configure)); diff --git a/src/DotNetCore.CAP.RedisStreams/CapOptions.Redis.cs b/src/DotNetCore.CAP.RedisStreams/CapOptions.Redis.cs index 1b5d56a..90f0c54 100644 --- a/src/DotNetCore.CAP.RedisStreams/CapOptions.Redis.cs +++ b/src/DotNetCore.CAP.RedisStreams/CapOptions.Redis.cs @@ -9,11 +9,11 @@ namespace DotNetCore.CAP public class CapRedisOptions { /// - /// Gets or sets the options of redis connections + /// Gets or sets the native options of StackExchange.Redis /// - public ConfigurationOptions Configuration { get; set; } + public ConfigurationOptions? Configuration { get; set; } - internal string Endpoint { get; set; } + internal string Endpoint { get; set; } = default!; /// /// Gets or sets the count of entries consumed from stream diff --git a/src/DotNetCore.CAP.RedisStreams/DotNetCore.CAP.RedisStreams.csproj b/src/DotNetCore.CAP.RedisStreams/DotNetCore.CAP.RedisStreams.csproj index 168b972..37ba7e1 100644 --- a/src/DotNetCore.CAP.RedisStreams/DotNetCore.CAP.RedisStreams.csproj +++ b/src/DotNetCore.CAP.RedisStreams/DotNetCore.CAP.RedisStreams.csproj @@ -2,7 +2,7 @@ netstandard2.1 - DotNetCore.CAP.RedisStreams + enable $(PackageTags);RedisStreams @@ -16,7 +16,7 @@ - + diff --git a/src/DotNetCore.CAP.RedisStreams/IConnectionPool.Default.cs b/src/DotNetCore.CAP.RedisStreams/IConnectionPool.Default.cs index af08696..998e183 100644 --- a/src/DotNetCore.CAP.RedisStreams/IConnectionPool.Default.cs +++ b/src/DotNetCore.CAP.RedisStreams/IConnectionPool.Default.cs @@ -14,11 +14,10 @@ namespace DotNetCore.CAP.RedisStreams { internal class RedisConnectionPool : IRedisConnectionPool, IDisposable { - private readonly ConcurrentBag _connections = - new ConcurrentBag(); + private readonly ConcurrentBag _connections = new(); private readonly ILoggerFactory _loggerFactory; - private readonly SemaphoreSlim _poolLock = new SemaphoreSlim(1); + private readonly SemaphoreSlim _poolLock = new(1); private readonly CapRedisOptions _redisOptions; private bool _isDisposed; private bool _poolAlreadyConfigured; @@ -30,13 +29,11 @@ namespace DotNetCore.CAP.RedisStreams Init().GetAwaiter().GetResult(); } - private AsyncLazyRedisConnection QuietConnection + private AsyncLazyRedisConnection? QuietConnection { get { - if (_poolAlreadyConfigured) - return _connections.OrderBy(async c => (await c).ConnectionCapacity).First(); - return null; + return _poolAlreadyConfigured ? _connections.OrderBy(async c => (await c).ConnectionCapacity).First() : null; } } diff --git a/src/DotNetCore.CAP.RedisStreams/IConnectionPool.LazyConnection.cs b/src/DotNetCore.CAP.RedisStreams/IConnectionPool.LazyConnection.cs index 808bdf7..862364f 100644 --- a/src/DotNetCore.CAP.RedisStreams/IConnectionPool.LazyConnection.cs +++ b/src/DotNetCore.CAP.RedisStreams/IConnectionPool.LazyConnection.cs @@ -28,7 +28,7 @@ namespace DotNetCore.CAP.RedisStreams var redisLogger = new RedisLogger(logger); - ConnectionMultiplexer connection = null; + ConnectionMultiplexer? connection = null; while (attemp <= 5) { diff --git a/src/DotNetCore.CAP.RedisStreams/IConsumerClient.Redis.cs b/src/DotNetCore.CAP.RedisStreams/IConsumerClient.Redis.cs index 598b35c..4d2a862 100644 --- a/src/DotNetCore.CAP.RedisStreams/IConsumerClient.Redis.cs +++ b/src/DotNetCore.CAP.RedisStreams/IConsumerClient.Redis.cs @@ -18,9 +18,9 @@ namespace DotNetCore.CAP.RedisStreams { private readonly string _groupId; private readonly ILogger _logger; - private readonly IOptions _options; + private readonly IOptions _options; private readonly IRedisStreamManager _redis; - private string[] _topics; + private string[] _topics = default!; public RedisConsumerClient(string groupId, IRedisStreamManager redis, @@ -34,11 +34,11 @@ namespace DotNetCore.CAP.RedisStreams _logger = logger; } - public event EventHandler OnMessageReceived; + public event EventHandler? OnMessageReceived; - public event EventHandler OnLog; + public event EventHandler? OnLog; - public BrokerAddress BrokerAddress => new BrokerAddress("redis", _options.Value.Endpoint); + public BrokerAddress BrokerAddress => new("redis", _options.Value.Endpoint); public void Subscribe(IEnumerable topics) { @@ -59,16 +59,17 @@ namespace DotNetCore.CAP.RedisStreams cancellationToken.ThrowIfCancellationRequested(); cancellationToken.WaitHandle.WaitOne(timeout); } + // ReSharper disable once FunctionNeverReturns } public void Commit(object sender) { - var (stream, group, id) = ((string stream, string group, string id)) sender; + var (stream, group, id) = ((string stream, string group, string id))sender; _redis.Ack(stream, group, id).GetAwaiter().GetResult(); } - public void Reject(object sender) + public void Reject(object? sender) { // ignore } @@ -94,32 +95,39 @@ namespace DotNetCore.CAP.RedisStreams private async Task ConsumeMessages(IAsyncEnumerable> streamsSet, RedisValue position) { await foreach (var set in streamsSet) - foreach (var stream in set) - foreach (var entry in stream.Entries) { - if (entry.IsNull) - return; - try + foreach (var stream in set) { - var message = RedisMessage.Create(entry, _groupId); - OnMessageReceived?.Invoke((stream.Key.ToString(), _groupId, entry.Id.ToString()), message); - } - catch (Exception ex) - { - _logger.LogError(ex.Message, ex); - var logArgs = new LogMessageEventArgs + foreach (var entry in stream.Entries) { - LogType = MqLogType.ConsumeError, - Reason = ex.ToString() - }; - OnLog?.Invoke(entry, logArgs); - } - finally - { - var positionName = position == StreamPosition.Beginning - ? nameof(StreamPosition.Beginning) - : nameof(StreamPosition.NewMessages); - _logger.LogDebug($"Redis stream entry [{entry.Id}] [position : {positionName}] was delivered."); + if (entry.IsNull) + { + return; + } + + try + { + var message = RedisMessage.Create(entry, _groupId); + OnMessageReceived?.Invoke((stream.Key.ToString(), _groupId, entry.Id.ToString()), message); + } + catch (Exception ex) + { + _logger.LogError(ex.Message, ex); + var logArgs = new LogMessageEventArgs + { + LogType = MqLogType.ConsumeError, + Reason = ex.ToString() + }; + OnLog?.Invoke(entry, logArgs); + } + finally + { + var positionName = position == StreamPosition.Beginning + ? nameof(StreamPosition.Beginning) + : nameof(StreamPosition.NewMessages); + _logger.LogDebug($"Redis stream entry [{entry.Id}] [position : {positionName}] was delivered."); + } + } } } } diff --git a/src/DotNetCore.CAP.RedisStreams/IRedisStream.Manager.Default.cs b/src/DotNetCore.CAP.RedisStreams/IRedisStream.Manager.Default.cs index ba446a8..b9b16e7 100644 --- a/src/DotNetCore.CAP.RedisStreams/IRedisStream.Manager.Default.cs +++ b/src/DotNetCore.CAP.RedisStreams/IRedisStream.Manager.Default.cs @@ -18,7 +18,7 @@ namespace DotNetCore.CAP.RedisStreams private readonly IRedisConnectionPool _connectionsPool; private readonly ILogger _logger; private readonly CapRedisOptions _options; - private IConnectionMultiplexer _redis; + private IConnectionMultiplexer? _redis; public RedisStreamManager(IRedisConnectionPool connectionsPool, IOptions options, ILogger logger) @@ -33,7 +33,7 @@ namespace DotNetCore.CAP.RedisStreams await ConnectAsync(); //The object returned from GetDatabase is a cheap pass - thru object, and does not need to be stored - var database = _redis.GetDatabase(); + var database = _redis!.GetDatabase(); var streamExist = await database.KeyExistsAsync(stream); if (!streamExist) { @@ -53,7 +53,7 @@ namespace DotNetCore.CAP.RedisStreams await ConnectAsync(); //The object returned from GetDatabase is a cheap pass - thru object, and does not need to be stored - await _redis.GetDatabase().StreamAddAsync(stream, message); + await _redis!.GetDatabase().StreamAddAsync(stream, message); } public async IAsyncEnumerable> PollStreamsLatestMessagesAsync(string[] streams, @@ -98,7 +98,7 @@ namespace DotNetCore.CAP.RedisStreams { await ConnectAsync(); - await _redis.GetDatabase().StreamAcknowledgeAsync(stream, consumerGroup, messageId).ConfigureAwait(false); + await _redis!.GetDatabase().StreamAcknowledgeAsync(stream, consumerGroup, messageId).ConfigureAwait(false); } private async Task> TryReadConsumerGroup(string consumerGroup, @@ -112,7 +112,7 @@ namespace DotNetCore.CAP.RedisStreams await ConnectAsync(); - var database = _redis.GetDatabase(); + var database = _redis!.GetDatabase(); await foreach (var position in database.TryCreateConsumerGroup(positions, consumerGroup, _logger) .WithCancellation(token)) diff --git a/src/DotNetCore.CAP.RedisStreams/IRedisStream.Manager.Extensions.cs b/src/DotNetCore.CAP.RedisStreams/IRedisStream.Manager.Extensions.cs index 5250264..868ab88 100644 --- a/src/DotNetCore.CAP.RedisStreams/IRedisStream.Manager.Extensions.cs +++ b/src/DotNetCore.CAP.RedisStreams/IRedisStream.Manager.Extensions.cs @@ -11,7 +11,7 @@ namespace DotNetCore.CAP.RedisStreams { internal static class RedisStreamManagerExtensions { - public static async IAsyncEnumerable TryCreateConsumerGroup(this IDatabase database, StreamPosition[] positions, string consumerGroup, ILogger logger = null) + public static async IAsyncEnumerable TryCreateConsumerGroup(this IDatabase database, StreamPosition[] positions, string consumerGroup, ILogger logger) { foreach (var position in positions) { @@ -25,7 +25,7 @@ namespace DotNetCore.CAP.RedisStreams if (await database.StreamCreateConsumerGroupAsync(stream, consumerGroup, StreamPosition.NewMessages)) { - logger.LogInformation( + logger!.LogInformation( $"Redis stream [{position.Key}] created with consumer group [{consumerGroup}]"); created = true; } @@ -39,7 +39,7 @@ namespace DotNetCore.CAP.RedisStreams if (await database.StreamCreateConsumerGroupAsync(stream, consumerGroup, StreamPosition.NewMessages)) { - logger.LogInformation( + logger!.LogInformation( $"Redis stream [{position.Key}] created with consumer group [{consumerGroup}]"); created = true; } diff --git a/src/DotNetCore.CAP.RedisStreams/ITransport.Redis.cs b/src/DotNetCore.CAP.RedisStreams/ITransport.Redis.cs index 66c46bd..9880696 100644 --- a/src/DotNetCore.CAP.RedisStreams/ITransport.Redis.cs +++ b/src/DotNetCore.CAP.RedisStreams/ITransport.Redis.cs @@ -25,7 +25,7 @@ namespace DotNetCore.CAP.RedisStreams _logger = logger; } - public BrokerAddress BrokerAddress => new BrokerAddress("redis", _options.Endpoint); + public BrokerAddress BrokerAddress => new ("redis", _options.Endpoint); public async Task SendAsync(TransportMessage message) { diff --git a/src/DotNetCore.CAP.RedisStreams/TransportMessage.Redis.cs b/src/DotNetCore.CAP.RedisStreams/TransportMessage.Redis.cs index 243a688..ac055ad 100644 --- a/src/DotNetCore.CAP.RedisStreams/TransportMessage.Redis.cs +++ b/src/DotNetCore.CAP.RedisStreams/TransportMessage.Redis.cs @@ -23,16 +23,15 @@ namespace DotNetCore.CAP.RedisStreams }; } - public static TransportMessage Create(StreamEntry streamEntry, string groupId = null) + public static TransportMessage Create(StreamEntry streamEntry, string? groupId = null) { - if (streamEntry.IsNull) - return null; - var headersRaw = streamEntry[Headers]; if (headersRaw.IsNullOrEmpty) + { throw new ArgumentException($"Redis stream entry with id {streamEntry.Id} missing cap headers"); - - var headers = JsonSerializer.Deserialize>(headersRaw); + } + + var headers = JsonSerializer.Deserialize>(headersRaw)!; var bodyRaw = streamEntry[Body]; @@ -43,8 +42,12 @@ namespace DotNetCore.CAP.RedisStreams return new TransportMessage(headers, body); } - private static string ToJson(object obj) + private static RedisValue ToJson(object? obj) { + if (obj == null) + { + return RedisValue.Null; + } return JsonSerializer.Serialize(obj, new JsonSerializerOptions(JsonSerializerDefaults.Web)); } } From 952f42a27fa24cd0e28812736275533b4422929d Mon Sep 17 00:00:00 2001 From: Savorboard Date: Mon, 27 Dec 2021 15:19:19 +0800 Subject: [PATCH 16/17] Enable #nullable for SqlServer transport. --- src/DotNetCore.CAP.SqlServer/CAP.EFOptions.cs | 4 +- .../CAP.SqlServerOptions.cs | 2 +- .../Diagnostics/DiagnosticObserver.cs | 44 +++++++++++-------- .../DiagnosticProcessorObserver.cs | 2 + .../DotNetCore.CAP.SqlServer.csproj | 8 +--- .../ICapTransaction.SqlServer.cs | 4 +- .../IDataStorage.SqlServer.cs | 8 ++-- .../IDbConnection.Extensions.cs | 10 ++--- .../IDbContextTransaction.CAP.cs | 4 +- .../IMonitoringApi.SqlServer.cs | 10 ++--- 10 files changed, 50 insertions(+), 46 deletions(-) diff --git a/src/DotNetCore.CAP.SqlServer/CAP.EFOptions.cs b/src/DotNetCore.CAP.SqlServer/CAP.EFOptions.cs index e3d1cd2..52d1569 100644 --- a/src/DotNetCore.CAP.SqlServer/CAP.EFOptions.cs +++ b/src/DotNetCore.CAP.SqlServer/CAP.EFOptions.cs @@ -19,14 +19,14 @@ namespace DotNetCore.CAP /// /// EF DbContext /// - internal Type DbContextType { get; set; } + internal Type? DbContextType { get; set; } internal bool IsSqlServer2008 { get; set; } /// /// Data version /// - internal string Version { get; set; } + internal string Version { get; set; } = default!; public EFOptions UseSqlServer2008() { diff --git a/src/DotNetCore.CAP.SqlServer/CAP.SqlServerOptions.cs b/src/DotNetCore.CAP.SqlServer/CAP.SqlServerOptions.cs index 513e82e..d92cb42 100644 --- a/src/DotNetCore.CAP.SqlServer/CAP.SqlServerOptions.cs +++ b/src/DotNetCore.CAP.SqlServer/CAP.SqlServerOptions.cs @@ -13,7 +13,7 @@ namespace DotNetCore.CAP /// /// Gets or sets the database's connection string that will be used to store database entities. /// - public string ConnectionString { get; set; } + public string ConnectionString { get; set; } = default!; } diff --git a/src/DotNetCore.CAP.SqlServer/Diagnostics/DiagnosticObserver.cs b/src/DotNetCore.CAP.SqlServer/Diagnostics/DiagnosticObserver.cs index e74e3b0..59b4260 100644 --- a/src/DotNetCore.CAP.SqlServer/Diagnostics/DiagnosticObserver.cs +++ b/src/DotNetCore.CAP.SqlServer/Diagnostics/DiagnosticObserver.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Reflection; using DotNetCore.CAP.Persistence; using DotNetCore.CAP.Transport; @@ -11,7 +12,7 @@ using Microsoft.Data.SqlClient; namespace DotNetCore.CAP.SqlServer.Diagnostics { - internal class DiagnosticObserver : IObserver> + internal class DiagnosticObserver : IObserver> { public const string SqlAfterCommitTransactionMicrosoft = "Microsoft.Data.SqlClient.WriteTransactionCommitAfter"; public const string SqlErrorCommitTransactionMicrosoft = "Microsoft.Data.SqlClient.WriteTransactionCommitError"; @@ -36,41 +37,48 @@ namespace DotNetCore.CAP.SqlServer.Diagnostics { } - public void OnNext(KeyValuePair evt) + public void OnNext(KeyValuePair evt) { - if (evt.Key == SqlAfterCommitTransactionMicrosoft) + switch (evt.Key) { - if (!TryGetSqlConnection(evt, out SqlConnection sqlConnection)) return; - var transactionKey = sqlConnection.ClientConnectionId; - if (_bufferList.TryRemove(transactionKey, out var msgList)) + case SqlAfterCommitTransactionMicrosoft: { - foreach (var message in msgList) + if (!TryGetSqlConnection(evt, out SqlConnection? sqlConnection)) return; + var transactionKey = sqlConnection.ClientConnectionId; + if (_bufferList.TryRemove(transactionKey, out var msgList)) { - _dispatcher.EnqueueToPublish(message); + foreach (var message in msgList) + { + _dispatcher.EnqueueToPublish(message); + } } + + break; } - } - else if (evt.Key == SqlErrorCommitTransactionMicrosoft || evt.Key == SqlAfterRollbackTransactionMicrosoft || evt.Key == SqlBeforeCloseConnectionMicrosoft) - { - if (!_bufferList.IsEmpty) + case SqlErrorCommitTransactionMicrosoft or SqlAfterRollbackTransactionMicrosoft or SqlBeforeCloseConnectionMicrosoft: { - if (!TryGetSqlConnection(evt, out SqlConnection sqlConnection)) return; - var transactionKey = sqlConnection.ClientConnectionId; + if (!_bufferList.IsEmpty) + { + if (!TryGetSqlConnection(evt, out SqlConnection? sqlConnection)) return; + var transactionKey = sqlConnection.ClientConnectionId; + + _bufferList.TryRemove(transactionKey, out _); + } - _bufferList.TryRemove(transactionKey, out _); + break; } } } - private static bool TryGetSqlConnection(KeyValuePair evt, out SqlConnection sqlConnection) + private static bool TryGetSqlConnection(KeyValuePair evt, [NotNullWhen(true)] out SqlConnection? sqlConnection) { sqlConnection = GetProperty(evt.Value, "Connection") as SqlConnection; return sqlConnection != null; } - private static object GetProperty(object _this, string propertyName) + private static object? GetProperty(object? @this, string propertyName) { - return _this.GetType().GetTypeInfo().GetDeclaredProperty(propertyName)?.GetValue(_this); + return @this?.GetType().GetTypeInfo().GetDeclaredProperty(propertyName)?.GetValue(@this); } } } \ No newline at end of file diff --git a/src/DotNetCore.CAP.SqlServer/Diagnostics/DiagnosticProcessorObserver.cs b/src/DotNetCore.CAP.SqlServer/Diagnostics/DiagnosticProcessorObserver.cs index ea14ca2..8027497 100644 --- a/src/DotNetCore.CAP.SqlServer/Diagnostics/DiagnosticProcessorObserver.cs +++ b/src/DotNetCore.CAP.SqlServer/Diagnostics/DiagnosticProcessorObserver.cs @@ -34,7 +34,9 @@ namespace DotNetCore.CAP.SqlServer.Diagnostics public void OnNext(DiagnosticListener listener) { if (listener.Name == DiagnosticListenerName) + { listener.Subscribe(new DiagnosticObserver(_dispatcher, BufferList)); + } } } } \ No newline at end of file diff --git a/src/DotNetCore.CAP.SqlServer/DotNetCore.CAP.SqlServer.csproj b/src/DotNetCore.CAP.SqlServer/DotNetCore.CAP.SqlServer.csproj index 4be0173..768097e 100644 --- a/src/DotNetCore.CAP.SqlServer/DotNetCore.CAP.SqlServer.csproj +++ b/src/DotNetCore.CAP.SqlServer/DotNetCore.CAP.SqlServer.csproj @@ -2,14 +2,8 @@ net6.0;netstandard2.1 - DotNetCore.CAP.SqlServer + enable $(PackageTags);SQL Server - - - - - bin\$(Configuration)\netstandard2.1\DotNetCore.CAP.SqlServer.xml - 1701;1702;1705;CS1591 diff --git a/src/DotNetCore.CAP.SqlServer/ICapTransaction.SqlServer.cs b/src/DotNetCore.CAP.SqlServer/ICapTransaction.SqlServer.cs index eba585f..f89270c 100644 --- a/src/DotNetCore.CAP.SqlServer/ICapTransaction.SqlServer.cs +++ b/src/DotNetCore.CAP.SqlServer/ICapTransaction.SqlServer.cs @@ -46,7 +46,7 @@ namespace DotNetCore.CAP if (dbTransaction == null) throw new ArgumentNullException(nameof(DbTransaction)); } - var transactionKey = ((SqlConnection)dbTransaction.Connection).ClientConnectionId; + var transactionKey = ((SqlConnection)dbTransaction.Connection!).ClientConnectionId; if (_diagnosticProcessor.BufferList.TryGetValue(transactionKey, out var list)) { list.Add(msg); @@ -166,7 +166,7 @@ namespace DotNetCore.CAP var dbTransaction = dbConnection.BeginTransaction(); publisher.Transaction.Value = ActivatorUtilities.CreateInstance(publisher.ServiceProvider); var capTransaction = publisher.Transaction.Value.Begin(dbTransaction, autoCommit); - return (IDbTransaction)capTransaction.DbTransaction; + return (IDbTransaction)capTransaction.DbTransaction!; } /// diff --git a/src/DotNetCore.CAP.SqlServer/IDataStorage.SqlServer.cs b/src/DotNetCore.CAP.SqlServer/IDataStorage.SqlServer.cs index e2fd969..b5432cc 100644 --- a/src/DotNetCore.CAP.SqlServer/IDataStorage.SqlServer.cs +++ b/src/DotNetCore.CAP.SqlServer/IDataStorage.SqlServer.cs @@ -46,7 +46,7 @@ namespace DotNetCore.CAP.SqlServer public async Task ChangeReceiveStateAsync(MediumMessage message, StatusName state) => await ChangeMessageStateAsync(_recName, message, state); - public MediumMessage StoreMessage(string name, Message content, object dbTransaction = null) + public MediumMessage StoreMessage(string name, Message content, object? dbTransaction = null) { var sql = $"INSERT INTO {_pubName} ([Id],[Version],[Name],[Content],[Retries],[Added],[ExpiresAt],[StatusName])" + $"VALUES(@Id,'{_options.Value.Version}',@Name,@Content,@Retries,@Added,@ExpiresAt,@StatusName);"; @@ -84,7 +84,7 @@ namespace DotNetCore.CAP.SqlServer dbTrans = dbContextTrans.GetDbTransaction(); var conn = dbTrans?.Connection; - conn.ExecuteNonQuery(sql, dbTrans, sqlParams); + conn!.ExecuteNonQuery(sql, dbTrans, sqlParams); } return message; @@ -126,7 +126,7 @@ namespace DotNetCore.CAP.SqlServer new SqlParameter("@Content", _serializer.Serialize(mdMessage.Origin)), new SqlParameter("@Retries", mdMessage.Retries), new SqlParameter("@Added", mdMessage.Added), - new SqlParameter("@ExpiresAt", mdMessage.ExpiresAt.HasValue ? (object) mdMessage.ExpiresAt.Value : DBNull.Value), + new SqlParameter("@ExpiresAt", mdMessage.ExpiresAt.HasValue ? mdMessage.ExpiresAt.Value : DBNull.Value), new SqlParameter("@StatusName", nameof(StatusName.Scheduled)) }; @@ -200,7 +200,7 @@ namespace DotNetCore.CAP.SqlServer messages.Add(new MediumMessage { DbId = reader.GetInt64(0).ToString(), - Origin = _serializer.Deserialize(reader.GetString(1)), + Origin = _serializer.Deserialize(reader.GetString(1))!, Retries = reader.GetInt32(2), Added = reader.GetDateTime(3) }); diff --git a/src/DotNetCore.CAP.SqlServer/IDbConnection.Extensions.cs b/src/DotNetCore.CAP.SqlServer/IDbConnection.Extensions.cs index ba7dbce..b2a905e 100644 --- a/src/DotNetCore.CAP.SqlServer/IDbConnection.Extensions.cs +++ b/src/DotNetCore.CAP.SqlServer/IDbConnection.Extensions.cs @@ -9,7 +9,7 @@ namespace DotNetCore.CAP.SqlServer { internal static class DbConnectionExtensions { - public static int ExecuteNonQuery(this IDbConnection connection, string sql, IDbTransaction transaction = null, + public static int ExecuteNonQuery(this IDbConnection connection, string sql, IDbTransaction? transaction = null, params object[] sqlParams) { if (connection.State == ConnectionState.Closed) @@ -33,7 +33,7 @@ namespace DotNetCore.CAP.SqlServer return command.ExecuteNonQuery(); } - public static T ExecuteReader(this IDbConnection connection, string sql, Func readerFunc, + public static T ExecuteReader(this IDbConnection connection, string sql, Func? readerFunc, params object[] sqlParams) { if (connection.State == ConnectionState.Closed) @@ -51,7 +51,7 @@ namespace DotNetCore.CAP.SqlServer var reader = command.ExecuteReader(); - T result = default; + T result = default!; if (readerFunc != null) { result = readerFunc(reader); @@ -77,14 +77,14 @@ namespace DotNetCore.CAP.SqlServer var objValue = command.ExecuteScalar(); - T result = default; + T result = default!; if (objValue != null) { var returnType = typeof(T); var converter = TypeDescriptor.GetConverter(returnType); if (converter.CanConvertFrom(objValue.GetType())) { - result = (T)converter.ConvertFrom(objValue); + result = (T)converter.ConvertFrom(objValue)!; } else { diff --git a/src/DotNetCore.CAP.SqlServer/IDbContextTransaction.CAP.cs b/src/DotNetCore.CAP.SqlServer/IDbContextTransaction.CAP.cs index cbeb745..7739159 100644 --- a/src/DotNetCore.CAP.SqlServer/IDbContextTransaction.CAP.cs +++ b/src/DotNetCore.CAP.SqlServer/IDbContextTransaction.CAP.cs @@ -18,7 +18,7 @@ namespace Microsoft.EntityFrameworkCore.Storage public CapEFDbTransaction(ICapTransaction transaction) { _transaction = transaction; - var dbContextTransaction = (IDbContextTransaction)_transaction.DbTransaction; + var dbContextTransaction = (IDbContextTransaction)_transaction.DbTransaction!; TransactionId = dbContextTransaction.TransactionId; } @@ -58,7 +58,7 @@ namespace Microsoft.EntityFrameworkCore.Storage { get { - var dbContextTransaction = (IDbContextTransaction)_transaction.DbTransaction; + var dbContextTransaction = (IDbContextTransaction)_transaction.DbTransaction!; return dbContextTransaction.GetDbTransaction(); } } diff --git a/src/DotNetCore.CAP.SqlServer/IMonitoringApi.SqlServer.cs b/src/DotNetCore.CAP.SqlServer/IMonitoringApi.SqlServer.cs index 5ae9333..925b16e 100644 --- a/src/DotNetCore.CAP.SqlServer/IMonitoringApi.SqlServer.cs +++ b/src/DotNetCore.CAP.SqlServer/IMonitoringApi.SqlServer.cs @@ -131,7 +131,7 @@ SELECT Content = reader.GetString(index++), Retries = reader.GetInt32(index++), Added = reader.GetDateTime(index++), - ExpiresAt = reader.IsDBNull(index++) ? (DateTime?)null : reader.GetDateTime(index - 1), + ExpiresAt = reader.IsDBNull(index++) ? null : reader.GetDateTime(index - 1), StatusName = reader.GetString(index) }); } @@ -162,9 +162,9 @@ SELECT return GetNumberOfMessage(_recName, nameof(StatusName.Succeeded)); } - public async Task GetPublishedMessageAsync(long id) => await GetMessageAsync(_pubName, id); + public async Task GetPublishedMessageAsync(long id) => await GetMessageAsync(_pubName, id); - public async Task GetReceivedMessageAsync(long id) => await GetMessageAsync(_recName, id); + public async Task GetReceivedMessageAsync(long id) => await GetMessageAsync(_recName, id); private int GetNumberOfMessage(string tableName, string statusName) { @@ -254,14 +254,14 @@ select [Key], [Count] from aggr with (nolock) where [Key] >= @minKey and [Key] < return result; } - private async Task GetMessageAsync(string tableName, long id) + private async Task GetMessageAsync(string tableName, long id) { var sql = $@"SELECT TOP 1 Id AS DbId, Content, Added, ExpiresAt, Retries FROM {tableName} WITH (readpast) WHERE Id={id}"; await using var connection = new SqlConnection(_options.ConnectionString); var mediumMessage = connection.ExecuteReader(sql, reader => { - MediumMessage message = null; + MediumMessage? message = null; while (reader.Read()) { From 9fc0176f5469cf8c6d3d608c8a1be6295fe98d25 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Mon, 27 Dec 2021 16:20:37 +0800 Subject: [PATCH 17/17] Add EnableClientLog option for Apache Pulsar transport. --- src/DotNetCore.CAP.Pulsar/CAP.PulsarOptions.cs | 2 ++ .../DotNetCore.CAP.Pulsar.csproj | 1 + .../IConnectionFactory.Default.cs | 2 ++ src/DotNetCore.CAP.Pulsar/PulsarConsumerClient.cs | 8 ++++++-- .../PulsarConsumerClientFactory.cs | 11 +++++++++-- 5 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/DotNetCore.CAP.Pulsar/CAP.PulsarOptions.cs b/src/DotNetCore.CAP.Pulsar/CAP.PulsarOptions.cs index 5e9261e..2384439 100644 --- a/src/DotNetCore.CAP.Pulsar/CAP.PulsarOptions.cs +++ b/src/DotNetCore.CAP.Pulsar/CAP.PulsarOptions.cs @@ -13,6 +13,8 @@ namespace DotNetCore.CAP { public string ServiceUrl { get; set; } = default!; + public bool EnableClientLog { get; set; } = false; + public TlsOptions? TlsOptions { get; set; } } } diff --git a/src/DotNetCore.CAP.Pulsar/DotNetCore.CAP.Pulsar.csproj b/src/DotNetCore.CAP.Pulsar/DotNetCore.CAP.Pulsar.csproj index 4ab2269..6df7111 100644 --- a/src/DotNetCore.CAP.Pulsar/DotNetCore.CAP.Pulsar.csproj +++ b/src/DotNetCore.CAP.Pulsar/DotNetCore.CAP.Pulsar.csproj @@ -4,6 +4,7 @@ netstandard2.1 enable $(PackageTags);Pulsar + CS0067 diff --git a/src/DotNetCore.CAP.Pulsar/IConnectionFactory.Default.cs b/src/DotNetCore.CAP.Pulsar/IConnectionFactory.Default.cs index 8b11b02..822b737 100644 --- a/src/DotNetCore.CAP.Pulsar/IConnectionFactory.Default.cs +++ b/src/DotNetCore.CAP.Pulsar/IConnectionFactory.Default.cs @@ -13,12 +13,14 @@ namespace DotNetCore.CAP.Pulsar { public class ConnectionFactory : IConnectionFactory, IAsyncDisposable { + private readonly ILogger _logger; private PulsarClient? _client; private readonly PulsarOptions _options; private readonly ConcurrentDictionary>> _topicProducers; public ConnectionFactory(ILogger logger, IOptions options) { + _logger = logger; _options = options.Value; _topicProducers = new ConcurrentDictionary>>(); diff --git a/src/DotNetCore.CAP.Pulsar/PulsarConsumerClient.cs b/src/DotNetCore.CAP.Pulsar/PulsarConsumerClient.cs index 440d4b2..236204b 100644 --- a/src/DotNetCore.CAP.Pulsar/PulsarConsumerClient.cs +++ b/src/DotNetCore.CAP.Pulsar/PulsarConsumerClient.cs @@ -7,6 +7,8 @@ using System.Reflection; using System.Threading; using DotNetCore.CAP.Messages; using DotNetCore.CAP.Transport; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Options; using Pulsar.Client.Api; using Pulsar.Client.Common; @@ -41,7 +43,7 @@ namespace DotNetCore.CAP.Pulsar } var serviceName = Assembly.GetEntryAssembly()?.GetName().Name.ToLower(); - + _consumerClient = _client.NewConsumer() .Topics(topics) .SubscriptionName(_groupId) @@ -87,5 +89,7 @@ namespace DotNetCore.CAP.Pulsar { _consumerClient?.DisposeAsync(); } - } + + + } } \ No newline at end of file diff --git a/src/DotNetCore.CAP.Pulsar/PulsarConsumerClientFactory.cs b/src/DotNetCore.CAP.Pulsar/PulsarConsumerClientFactory.cs index 5771a4d..22d4fa1 100644 --- a/src/DotNetCore.CAP.Pulsar/PulsarConsumerClientFactory.cs +++ b/src/DotNetCore.CAP.Pulsar/PulsarConsumerClientFactory.cs @@ -2,7 +2,9 @@ // Licensed under the MIT License. See License.txt in the project root for license information. using DotNetCore.CAP.Transport; +using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; +using Pulsar.Client.Api; namespace DotNetCore.CAP.Pulsar { @@ -11,10 +13,15 @@ namespace DotNetCore.CAP.Pulsar private readonly IConnectionFactory _connection; private readonly IOptions _pulsarOptions; - public PulsarConsumerClientFactory(IConnectionFactory connection, IOptions pulsarOptions) + public PulsarConsumerClientFactory(IConnectionFactory connection, ILoggerFactory loggerFactory, IOptions pulsarOptions) { _connection = connection; _pulsarOptions = pulsarOptions; + + if (_pulsarOptions.Value.EnableClientLog) + { + PulsarClient.Logger = loggerFactory.CreateLogger(); + } } public IConsumerClient Create(string groupId) @@ -22,7 +29,7 @@ namespace DotNetCore.CAP.Pulsar try { var client = _connection.RentClient(); - var consumerClient = new PulsarConsumerClient(client,groupId, _pulsarOptions); + var consumerClient = new PulsarConsumerClient(client, groupId, _pulsarOptions); return consumerClient; } catch (System.Exception e)