Unverified Commit 7021dc65 authored by Savorboard's avatar Savorboard Committed by GitHub

v2.6.0 (#362)

* Code refactor

* update code format

* Add options package reference

* Rename need retry processor

* update unit tests

* Improve ICapPublisher to singleton.

* Support options pattern for MySqlOptions

* Support options pattern for PostgreSqlOptions

* Remove reference

* update namespace of SubscriberNotFoundException.cs

* update ut

* Support options pattern for CapOptions

* update version to 2.6.0

* Support options pattern for AzureServiceBusOptions

* Fix client listening bug

* update inmemory project to using  option pattern.

* Support options pattern for KafkaOptions.

* Support options pattern for MongoDbOptions.

* Fix options.

* Support options pattern for SqlServerOptions

* upgrade confluent kafka to 1.1.0

* Remove CastleCoreTest project, it is not need again

* Fix transaction dispose.

* Fix mock

* Fix mock
parent d3c88667
 
Microsoft Visual Studio Solution File, Format Version 12.00 Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15 # Visual Studio Version 16
VisualStudioVersion = 15.0.26730.15 VisualStudioVersion = 16.0.29025.244
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{9B2AE124-6636-4DE9-83A3-70360DABD0C4}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{9B2AE124-6636-4DE9-83A3-70360DABD0C4}"
EndProject EndProject
...@@ -68,8 +68,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNetCore.CAP.InMemoryStor ...@@ -68,8 +68,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNetCore.CAP.InMemoryStor
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sample.AzureServiceBus.InMemory", "samples\Sample.AzureServiceBus.InMemory\Sample.AzureServiceBus.InMemory.csproj", "{1E1E959C-3D0E-45C3-ABCA-DAAACE68AAB8}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sample.AzureServiceBus.InMemory", "samples\Sample.AzureServiceBus.InMemory\Sample.AzureServiceBus.InMemory.csproj", "{1E1E959C-3D0E-45C3-ABCA-DAAACE68AAB8}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotNetCore.CAP.CastleCoreTest", "test\DotNetCore.CAP.CastleCoreTest\DotNetCore.CAP.CastleCoreTest.csproj", "{BA499B87-77A9-43A2-98A3-89ECF5034E26}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
...@@ -144,10 +142,6 @@ Global ...@@ -144,10 +142,6 @@ Global
{1E1E959C-3D0E-45C3-ABCA-DAAACE68AAB8}.Debug|Any CPU.Build.0 = Debug|Any CPU {1E1E959C-3D0E-45C3-ABCA-DAAACE68AAB8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1E1E959C-3D0E-45C3-ABCA-DAAACE68AAB8}.Release|Any CPU.ActiveCfg = Release|Any CPU {1E1E959C-3D0E-45C3-ABCA-DAAACE68AAB8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1E1E959C-3D0E-45C3-ABCA-DAAACE68AAB8}.Release|Any CPU.Build.0 = Release|Any CPU {1E1E959C-3D0E-45C3-ABCA-DAAACE68AAB8}.Release|Any CPU.Build.0 = Release|Any CPU
{BA499B87-77A9-43A2-98A3-89ECF5034E26}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BA499B87-77A9-43A2-98A3-89ECF5034E26}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BA499B87-77A9-43A2-98A3-89ECF5034E26}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BA499B87-77A9-43A2-98A3-89ECF5034E26}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
...@@ -171,7 +165,6 @@ Global ...@@ -171,7 +165,6 @@ Global
{63B2A464-FBEA-42FB-8EFA-98AFA39FC920} = {9B2AE124-6636-4DE9-83A3-70360DABD0C4} {63B2A464-FBEA-42FB-8EFA-98AFA39FC920} = {9B2AE124-6636-4DE9-83A3-70360DABD0C4}
{58B6E829-C6C8-457C-9DD0-C600650254DF} = {9B2AE124-6636-4DE9-83A3-70360DABD0C4} {58B6E829-C6C8-457C-9DD0-C600650254DF} = {9B2AE124-6636-4DE9-83A3-70360DABD0C4}
{1E1E959C-3D0E-45C3-ABCA-DAAACE68AAB8} = {3A6B6931-A123-477A-9469-8B468B5385AF} {1E1E959C-3D0E-45C3-ABCA-DAAACE68AAB8} = {3A6B6931-A123-477A-9469-8B468B5385AF}
{BA499B87-77A9-43A2-98A3-89ECF5034E26} = {C09CDAB0-6DD4-46E9-B7F3-3EF2A4741EA0}
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {2E70565D-94CF-40B4-BFE1-AC18D5F736AB} SolutionGuid = {2E70565D-94CF-40B4-BFE1-AC18D5F736AB}
......
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=DB/@EntryIndexedValue">DB</s:String>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Mongo/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Postgre/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
\ No newline at end of file
<Project> <Project>
<PropertyGroup> <PropertyGroup>
<VersionMajor>2</VersionMajor> <VersionMajor>2</VersionMajor>
<VersionMinor>5</VersionMinor> <VersionMinor>6</VersionMinor>
<VersionPatch>2</VersionPatch> <VersionPatch>0</VersionPatch>
<VersionQuality></VersionQuality> <VersionQuality></VersionQuality>
<VersionPrefix>$(VersionMajor).$(VersionMinor).$(VersionPatch)</VersionPrefix> <VersionPrefix>$(VersionMajor).$(VersionMinor).$(VersionPatch)</VersionPrefix>
</PropertyGroup> </PropertyGroup>
......
...@@ -10,11 +10,14 @@ using System.Threading.Tasks; ...@@ -10,11 +10,14 @@ using System.Threading.Tasks;
using Microsoft.Azure.ServiceBus; using Microsoft.Azure.ServiceBus;
using Microsoft.Azure.ServiceBus.Management; using Microsoft.Azure.ServiceBus.Management;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace DotNetCore.CAP.AzureServiceBus namespace DotNetCore.CAP.AzureServiceBus
{ {
internal sealed class AzureServiceBusConsumerClient : IConsumerClient internal sealed class AzureServiceBusConsumerClient : IConsumerClient
{ {
private readonly SemaphoreSlim _connectionLock = new SemaphoreSlim(initialCount: 1, maxCount: 1);
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly string _subscriptionName; private readonly string _subscriptionName;
private readonly AzureServiceBusOptions _asbOptions; private readonly AzureServiceBusOptions _asbOptions;
...@@ -26,13 +29,11 @@ namespace DotNetCore.CAP.AzureServiceBus ...@@ -26,13 +29,11 @@ namespace DotNetCore.CAP.AzureServiceBus
public AzureServiceBusConsumerClient( public AzureServiceBusConsumerClient(
ILogger logger, ILogger logger,
string subscriptionName, string subscriptionName,
AzureServiceBusOptions options) IOptions<AzureServiceBusOptions> options)
{ {
_logger = logger; _logger = logger;
_subscriptionName = subscriptionName; _subscriptionName = subscriptionName;
_asbOptions = options ?? throw new ArgumentNullException(nameof(options)); _asbOptions = options.Value ?? throw new ArgumentNullException(nameof(options));
InitAzureServiceBusClient().GetAwaiter().GetResult();
} }
public event EventHandler<MessageContext> OnMessageReceived; public event EventHandler<MessageContext> OnMessageReceived;
...@@ -48,6 +49,8 @@ namespace DotNetCore.CAP.AzureServiceBus ...@@ -48,6 +49,8 @@ namespace DotNetCore.CAP.AzureServiceBus
throw new ArgumentNullException(nameof(topics)); throw new ArgumentNullException(nameof(topics));
} }
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)) foreach (var newRule in topics.Except(allRuleNames))
...@@ -73,6 +76,8 @@ namespace DotNetCore.CAP.AzureServiceBus ...@@ -73,6 +76,8 @@ namespace DotNetCore.CAP.AzureServiceBus
public void Listening(TimeSpan timeout, CancellationToken cancellationToken) public void Listening(TimeSpan timeout, CancellationToken cancellationToken)
{ {
ConnectAsync().GetAwaiter().GetResult();
_consumerClient.RegisterMessageHandler(OnConsumerReceived, _consumerClient.RegisterMessageHandler(OnConsumerReceived,
new MessageHandlerOptions(OnExceptionReceived) new MessageHandlerOptions(OnExceptionReceived)
{ {
...@@ -106,33 +111,50 @@ namespace DotNetCore.CAP.AzureServiceBus ...@@ -106,33 +111,50 @@ namespace DotNetCore.CAP.AzureServiceBus
#region private methods #region private methods
private async Task InitAzureServiceBusClient() private async Task ConnectAsync()
{ {
ManagementClient mClient; if (_consumerClient != null)
if (_asbOptions.ManagementTokenProvider != null)
{
mClient = new ManagementClient(new ServiceBusConnectionStringBuilder(
_asbOptions.ConnectionString), _asbOptions.ManagementTokenProvider);
}
else
{ {
mClient = new ManagementClient(_asbOptions.ConnectionString); return;
} }
if (!await mClient.TopicExistsAsync(_asbOptions.TopicPath)) _connectionLock.Wait();
try
{ {
await mClient.CreateTopicAsync(_asbOptions.TopicPath); if (_consumerClient == null)
_logger.LogInformation($"Azure Service Bus created topic: {_asbOptions.TopicPath}"); {
ManagementClient mClient;
if (_asbOptions.ManagementTokenProvider != null)
{
mClient = new ManagementClient(new ServiceBusConnectionStringBuilder(
_asbOptions.ConnectionString), _asbOptions.ManagementTokenProvider);
}
else
{
mClient = new ManagementClient(_asbOptions.ConnectionString);
}
if (!await mClient.TopicExistsAsync(_asbOptions.TopicPath))
{
await mClient.CreateTopicAsync(_asbOptions.TopicPath);
_logger.LogInformation($"Azure Service Bus created topic: {_asbOptions.TopicPath}");
}
if (!await mClient.SubscriptionExistsAsync(_asbOptions.TopicPath, _subscriptionName))
{
await mClient.CreateSubscriptionAsync(_asbOptions.TopicPath, _subscriptionName);
_logger.LogInformation($"Azure Service Bus topic {_asbOptions.TopicPath} created subscription: {_subscriptionName}");
}
_consumerClient = new SubscriptionClient(_asbOptions.ConnectionString, _asbOptions.TopicPath, _subscriptionName,
ReceiveMode.PeekLock, RetryPolicy.Default);
}
} }
finally
if (!await mClient.SubscriptionExistsAsync(_asbOptions.TopicPath, _subscriptionName))
{ {
await mClient.CreateSubscriptionAsync(_asbOptions.TopicPath, _subscriptionName); _connectionLock.Release();
_logger.LogInformation($"Azure Service Bus topic {_asbOptions.TopicPath} created subscription: {_subscriptionName}");
} }
_consumerClient = new SubscriptionClient(_asbOptions.ConnectionString, _asbOptions.TopicPath, _subscriptionName,
ReceiveMode.PeekLock, RetryPolicy.Default);
} }
private Task OnConsumerReceived(Message message, CancellationToken token) private Task OnConsumerReceived(Message message, CancellationToken token)
......
...@@ -2,17 +2,18 @@ ...@@ -2,17 +2,18 @@
// Licensed under the MIT License. See License.txt in the project root for license information. // Licensed under the MIT License. See License.txt in the project root for license information.
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace DotNetCore.CAP.AzureServiceBus namespace DotNetCore.CAP.AzureServiceBus
{ {
internal sealed class AzureServiceBusConsumerClientFactory : IConsumerClientFactory internal sealed class AzureServiceBusConsumerClientFactory : IConsumerClientFactory
{ {
private readonly ILoggerFactory _loggerFactory; private readonly ILoggerFactory _loggerFactory;
private readonly AzureServiceBusOptions _asbOptions; private readonly IOptions<AzureServiceBusOptions> _asbOptions;
public AzureServiceBusConsumerClientFactory( public AzureServiceBusConsumerClientFactory(
ILoggerFactory loggerFactory, ILoggerFactory loggerFactory,
AzureServiceBusOptions asbOptions) IOptions<AzureServiceBusOptions> asbOptions)
{ {
_loggerFactory = loggerFactory; _loggerFactory = loggerFactory;
_asbOptions = asbOptions; _asbOptions = asbOptions;
......
...@@ -21,9 +21,7 @@ namespace DotNetCore.CAP ...@@ -21,9 +21,7 @@ namespace DotNetCore.CAP
{ {
services.AddSingleton<CapMessageQueueMakerService>(); services.AddSingleton<CapMessageQueueMakerService>();
var azureServiceBusOptions = new AzureServiceBusOptions(); services.Configure(_configure);
_configure?.Invoke(azureServiceBusOptions);
services.AddSingleton(azureServiceBusOptions);
services.AddSingleton<IConsumerClientFactory, AzureServiceBusConsumerClientFactory>(); services.AddSingleton<IConsumerClientFactory, AzureServiceBusConsumerClientFactory>();
services.AddSingleton<IPublishExecutor, AzureServiceBusPublishMessageSender>(); services.AddSingleton<IPublishExecutor, AzureServiceBusPublishMessageSender>();
......
...@@ -3,40 +3,45 @@ ...@@ -3,40 +3,45 @@
using System; using System;
using System.Text; using System.Text;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using DotNetCore.CAP.Internal; using DotNetCore.CAP.Internal;
using DotNetCore.CAP.Processor.States; using DotNetCore.CAP.Processor.States;
using Microsoft.Azure.ServiceBus; using Microsoft.Azure.ServiceBus;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace DotNetCore.CAP.AzureServiceBus namespace DotNetCore.CAP.AzureServiceBus
{ {
internal class AzureServiceBusPublishMessageSender : BasePublishMessageSender internal class AzureServiceBusPublishMessageSender : BasePublishMessageSender
{ {
private readonly SemaphoreSlim _connectionLock = new SemaphoreSlim(initialCount: 1, maxCount: 1);
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly ITopicClient _topicClient; private readonly IOptions<AzureServiceBusOptions> _asbOptions;
private ITopicClient _topicClient;
public AzureServiceBusPublishMessageSender( public AzureServiceBusPublishMessageSender(
ILogger<AzureServiceBusPublishMessageSender> logger, ILogger<AzureServiceBusPublishMessageSender> logger,
CapOptions options, IOptions<CapOptions> options,
AzureServiceBusOptions asbOptions, IOptions<AzureServiceBusOptions> asbOptions,
IStateChanger stateChanger, IStateChanger stateChanger,
IStorageConnection connection) IStorageConnection connection)
: base(logger, options, connection, stateChanger) : base(logger, options, connection, stateChanger)
{ {
_logger = logger; _logger = logger;
ServersAddress = asbOptions.ConnectionString; _asbOptions = asbOptions;
_topicClient = new TopicClient(
ServersAddress,
asbOptions.TopicPath,
RetryPolicy.NoRetry);
} }
protected override string ServersAddress => _asbOptions.Value.ConnectionString;
public override async Task<OperateResult> PublishAsync(string keyName, string content) public override async Task<OperateResult> PublishAsync(string keyName, string content)
{ {
try try
{ {
Connect();
var contentBytes = Encoding.UTF8.GetBytes(content); var contentBytes = Encoding.UTF8.GetBytes(content);
var message = new Message var message = new Message
...@@ -59,5 +64,27 @@ namespace DotNetCore.CAP.AzureServiceBus ...@@ -59,5 +64,27 @@ namespace DotNetCore.CAP.AzureServiceBus
return OperateResult.Failed(wrapperEx); return OperateResult.Failed(wrapperEx);
} }
} }
private void Connect()
{
if (_topicClient != null)
{
return;
}
_connectionLock.Wait();
try
{
if (_topicClient == null)
{
_topicClient = new TopicClient(ServersAddress, _asbOptions.Value.TopicPath, RetryPolicy.NoRetry);
}
}
finally
{
_connectionLock.Release();
}
}
} }
} }
\ No newline at end of file
...@@ -17,9 +17,9 @@ namespace DotNetCore.CAP ...@@ -17,9 +17,9 @@ namespace DotNetCore.CAP
services.AddSingleton<IStorageConnection, InMemoryStorageConnection>(); services.AddSingleton<IStorageConnection, InMemoryStorageConnection>();
services.AddSingleton<ICapPublisher, InMemoryPublisher>(); services.AddSingleton<ICapPublisher, InMemoryPublisher>();
services.AddSingleton<ICallbackPublisher, InMemoryPublisher>(); services.AddSingleton<ICallbackPublisher>(x => (InMemoryPublisher)x.GetService<ICapPublisher>());
services.AddSingleton<ICollectProcessor, InMemoryCollectProcessor>();
services.AddTransient<ICollectProcessor, InMemoryCollectProcessor>();
services.AddTransient<CapTransactionBase, InMemoryCapTransaction>(); services.AddTransient<CapTransactionBase, InMemoryCapTransaction>();
} }
} }
......
...@@ -8,6 +8,7 @@ using System.Linq; ...@@ -8,6 +8,7 @@ using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using DotNetCore.CAP.Infrastructure; using DotNetCore.CAP.Infrastructure;
using DotNetCore.CAP.Models; using DotNetCore.CAP.Models;
using Microsoft.Extensions.Options;
namespace DotNetCore.CAP.InMemoryStorage namespace DotNetCore.CAP.InMemoryStorage
{ {
...@@ -15,9 +16,9 @@ namespace DotNetCore.CAP.InMemoryStorage ...@@ -15,9 +16,9 @@ namespace DotNetCore.CAP.InMemoryStorage
{ {
private readonly CapOptions _capOptions; private readonly CapOptions _capOptions;
public InMemoryStorageConnection(CapOptions capOptions) public InMemoryStorageConnection(IOptions<CapOptions> capOptions)
{ {
_capOptions = capOptions; _capOptions = capOptions.Value;
PublishedMessages = new List<CapPublishedMessage>(); PublishedMessages = new List<CapPublishedMessage>();
ReceivedMessages = new List<CapReceivedMessage>(); ReceivedMessages = new List<CapReceivedMessage>();
......
...@@ -21,9 +21,7 @@ namespace DotNetCore.CAP ...@@ -21,9 +21,7 @@ namespace DotNetCore.CAP
{ {
services.AddSingleton<CapMessageQueueMakerService>(); services.AddSingleton<CapMessageQueueMakerService>();
var kafkaOptions = new KafkaOptions(); services.Configure(_configure);
_configure?.Invoke(kafkaOptions);
services.AddSingleton(kafkaOptions);
services.AddSingleton<IConsumerClientFactory, KafkaConsumerClientFactory>(); services.AddSingleton<IConsumerClientFactory, KafkaConsumerClientFactory>();
services.AddSingleton<IPublishExecutor, KafkaPublishMessageSender>(); services.AddSingleton<IPublishExecutor, KafkaPublishMessageSender>();
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Confluent.Kafka" Version="1.0.0" /> <PackageReference Include="Confluent.Kafka" Version="1.1.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
......
...@@ -6,6 +6,7 @@ using System.Collections.Concurrent; ...@@ -6,6 +6,7 @@ using System.Collections.Concurrent;
using System.Threading; using System.Threading;
using Confluent.Kafka; using Confluent.Kafka;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Newtonsoft.Json; using Newtonsoft.Json;
namespace DotNetCore.CAP.Kafka namespace DotNetCore.CAP.Kafka
...@@ -17,18 +18,16 @@ namespace DotNetCore.CAP.Kafka ...@@ -17,18 +18,16 @@ namespace DotNetCore.CAP.Kafka
private int _pCount; private int _pCount;
private int _maxSize; private int _maxSize;
public ConnectionPool(ILogger<ConnectionPool> logger, KafkaOptions options) public ConnectionPool(ILogger<ConnectionPool> logger, IOptions<KafkaOptions> options)
{ {
ServersAddress = options.Servers; _options = options.Value;
_options = options;
_producerPool = new ConcurrentQueue<IProducer<Null, string>>(); _producerPool = new ConcurrentQueue<IProducer<Null, string>>();
_maxSize = options.ConnectionPoolSize; _maxSize = _options.ConnectionPoolSize;
logger.LogDebug("Kafka configuration of CAP :\r\n {0}", JsonConvert.SerializeObject(options.AsKafkaConfig(), Formatting.Indented)); logger.LogDebug("Kafka configuration of CAP :\r\n {0}", JsonConvert.SerializeObject(_options.AsKafkaConfig(), Formatting.Indented));
} }
public string ServersAddress { get; } public string ServersAddress => _options.Servers;
public IProducer<Null, string> RentProducer() public IProducer<Null, string> RentProducer()
{ {
......
...@@ -7,6 +7,7 @@ using Confluent.Kafka; ...@@ -7,6 +7,7 @@ using Confluent.Kafka;
using DotNetCore.CAP.Internal; using DotNetCore.CAP.Internal;
using DotNetCore.CAP.Processor.States; using DotNetCore.CAP.Processor.States;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace DotNetCore.CAP.Kafka namespace DotNetCore.CAP.Kafka
{ {
...@@ -16,15 +17,19 @@ namespace DotNetCore.CAP.Kafka ...@@ -16,15 +17,19 @@ namespace DotNetCore.CAP.Kafka
private readonly ILogger _logger; private readonly ILogger _logger;
public KafkaPublishMessageSender( public KafkaPublishMessageSender(
CapOptions options, IStateChanger stateChanger, IStorageConnection connection, ILogger<KafkaPublishMessageSender> logger,
IConnectionPool connectionPool, ILogger<KafkaPublishMessageSender> logger) IOptions<CapOptions> options,
IStorageConnection connection,
IConnectionPool connectionPool,
IStateChanger stateChanger)
: base(logger, options, connection, stateChanger) : base(logger, options, connection, stateChanger)
{ {
_logger = logger; _logger = logger;
_connectionPool = connectionPool; _connectionPool = connectionPool;
ServersAddress = _connectionPool.ServersAddress;
} }
protected override string ServersAddress => _connectionPool.ServersAddress;
public override async Task<OperateResult> PublishAsync(string keyName, string content) public override async Task<OperateResult> PublishAsync(string keyName, string content)
{ {
var producer = _connectionPool.RentProducer(); var producer = _connectionPool.RentProducer();
......
...@@ -5,25 +5,24 @@ using System; ...@@ -5,25 +5,24 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading; using System.Threading;
using Confluent.Kafka; using Confluent.Kafka;
using Microsoft.Extensions.Options;
namespace DotNetCore.CAP.Kafka namespace DotNetCore.CAP.Kafka
{ {
internal sealed class KafkaConsumerClient : IConsumerClient internal sealed class KafkaConsumerClient : IConsumerClient
{ {
private readonly SemaphoreSlim _connectionLock = new SemaphoreSlim(initialCount: 1, maxCount: 1);
private readonly string _groupId; private readonly string _groupId;
private readonly KafkaOptions _kafkaOptions; private readonly KafkaOptions _kafkaOptions;
private IConsumer<Null, string> _consumerClient; private IConsumer<Null, string> _consumerClient;
public KafkaConsumerClient(string groupId, KafkaOptions options) public KafkaConsumerClient(string groupId, IOptions<KafkaOptions> options)
{ {
_groupId = groupId; _groupId = groupId;
_kafkaOptions = options ?? throw new ArgumentNullException(nameof(options)); _kafkaOptions = options.Value ?? throw new ArgumentNullException(nameof(options));
InitKafkaClient();
} }
public IDeserializer<string> StringDeserializer { get; set; }
public event EventHandler<MessageContext> OnMessageReceived; public event EventHandler<MessageContext> OnMessageReceived;
public event EventHandler<LogMessageEventArgs> OnLog; public event EventHandler<LogMessageEventArgs> OnLog;
...@@ -37,11 +36,15 @@ namespace DotNetCore.CAP.Kafka ...@@ -37,11 +36,15 @@ namespace DotNetCore.CAP.Kafka
throw new ArgumentNullException(nameof(topics)); throw new ArgumentNullException(nameof(topics));
} }
Connect();
_consumerClient.Subscribe(topics); _consumerClient.Subscribe(topics);
} }
public void Listening(TimeSpan timeout, CancellationToken cancellationToken) public void Listening(TimeSpan timeout, CancellationToken cancellationToken)
{ {
Connect();
while (true) while (true)
{ {
var consumerResult = _consumerClient.Consume(cancellationToken); var consumerResult = _consumerClient.Consume(cancellationToken);
...@@ -77,17 +80,32 @@ namespace DotNetCore.CAP.Kafka ...@@ -77,17 +80,32 @@ namespace DotNetCore.CAP.Kafka
#region private methods #region private methods
private void InitKafkaClient() private void Connect()
{ {
lock (_kafkaOptions) if (_consumerClient != null)
{ {
_kafkaOptions.MainConfig["group.id"] = _groupId; return;
_kafkaOptions.MainConfig["auto.offset.reset"] = "earliest";
var config = _kafkaOptions.AsKafkaConfig();
_consumerClient = new ConsumerBuilder<Null, string>(config)
.SetErrorHandler(ConsumerClient_OnConsumeError)
.Build();
} }
_connectionLock.Wait();
try
{
if (_consumerClient == null)
{
_kafkaOptions.MainConfig["group.id"] = _groupId;
_kafkaOptions.MainConfig["auto.offset.reset"] = "earliest";
var config = _kafkaOptions.AsKafkaConfig();
_consumerClient = new ConsumerBuilder<Null, string>(config)
.SetErrorHandler(ConsumerClient_OnConsumeError)
.Build();
}
}
finally
{
_connectionLock.Release();
}
} }
private void ConsumerClient_OnConsumeError(IConsumer<Null, string> consumer, Error e) private void ConsumerClient_OnConsumeError(IConsumer<Null, string> consumer, Error e)
......
// Copyright (c) .NET Core Community. All rights reserved. // Copyright (c) .NET Core Community. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information. // Licensed under the MIT License. See License.txt in the project root for license information.
using Microsoft.Extensions.Options;
namespace DotNetCore.CAP.Kafka namespace DotNetCore.CAP.Kafka
{ {
internal sealed class KafkaConsumerClientFactory : IConsumerClientFactory internal sealed class KafkaConsumerClientFactory : IConsumerClientFactory
{ {
private readonly KafkaOptions _kafkaOptions; private readonly IOptions<KafkaOptions> _kafkaOptions;
public KafkaConsumerClientFactory(KafkaOptions kafkaOptions) public KafkaConsumerClientFactory(IOptions<KafkaOptions> kafkaOptions)
{ {
_kafkaOptions = kafkaOptions; _kafkaOptions = kafkaOptions;
} }
......
...@@ -5,6 +5,7 @@ using System; ...@@ -5,6 +5,7 @@ using System;
using DotNetCore.CAP.Processor; using DotNetCore.CAP.Processor;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Options;
using MongoDB.Driver; using MongoDB.Driver;
namespace DotNetCore.CAP.MongoDB namespace DotNetCore.CAP.MongoDB
...@@ -25,18 +26,20 @@ namespace DotNetCore.CAP.MongoDB ...@@ -25,18 +26,20 @@ namespace DotNetCore.CAP.MongoDB
services.AddSingleton<IStorage, MongoDBStorage>(); services.AddSingleton<IStorage, MongoDBStorage>();
services.AddSingleton<IStorageConnection, MongoDBStorageConnection>(); services.AddSingleton<IStorageConnection, MongoDBStorageConnection>();
services.AddScoped<ICapPublisher, MongoDBPublisher>(); services.AddSingleton<ICapPublisher, MongoDBPublisher>();
services.AddScoped<ICallbackPublisher, MongoDBPublisher>(); services.AddSingleton<ICallbackPublisher>(x => (MongoDBPublisher)x.GetService<ICapPublisher>());
services.AddSingleton<ICollectProcessor, MongoDBCollectProcessor>();
services.AddTransient<ICollectProcessor, MongoDBCollectProcessor>();
services.AddTransient<CapTransactionBase, MongoDBCapTransaction>(); services.AddTransient<CapTransactionBase, MongoDBCapTransaction>();
var options = new MongoDBOptions(); services.Configure(_configure);
_configure?.Invoke(options);
services.AddSingleton(options);
//Try to add IMongoClient if does not exists //Try to add IMongoClient if does not exists
services.TryAddSingleton<IMongoClient>(new MongoClient(options.DatabaseConnection)); services.TryAddSingleton<IMongoClient>(x =>
{
var options = x.GetService<IOptions<MongoDBOptions>>().Value;
return new MongoClient(options.DatabaseConnection);
});
} }
} }
} }
\ No newline at end of file
...@@ -7,6 +7,7 @@ using System.Threading.Tasks; ...@@ -7,6 +7,7 @@ using System.Threading.Tasks;
using DotNetCore.CAP.Abstractions; using DotNetCore.CAP.Abstractions;
using DotNetCore.CAP.Models; using DotNetCore.CAP.Models;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using MongoDB.Driver; using MongoDB.Driver;
namespace DotNetCore.CAP.MongoDB namespace DotNetCore.CAP.MongoDB
...@@ -16,10 +17,9 @@ namespace DotNetCore.CAP.MongoDB ...@@ -16,10 +17,9 @@ namespace DotNetCore.CAP.MongoDB
private readonly IMongoClient _client; private readonly IMongoClient _client;
private readonly MongoDBOptions _options; private readonly MongoDBOptions _options;
public MongoDBPublisher(IServiceProvider provider, MongoDBOptions options) public MongoDBPublisher(IServiceProvider provider) : base(provider)
: base(provider)
{ {
_options = options; _options = provider.GetService<IOptions<MongoDBOptions>>().Value;
_client = ServiceProvider.GetRequiredService<IMongoClient>(); _client = ServiceProvider.GetRequiredService<IMongoClient>();
} }
...@@ -31,7 +31,7 @@ namespace DotNetCore.CAP.MongoDB ...@@ -31,7 +31,7 @@ namespace DotNetCore.CAP.MongoDB
protected override Task ExecuteAsync(CapPublishedMessage message, ICapTransaction transaction, protected override Task ExecuteAsync(CapPublishedMessage message, ICapTransaction transaction,
CancellationToken cancel = default(CancellationToken)) CancellationToken cancel = default(CancellationToken))
{ {
var insertOptions = new InsertOneOptions {BypassDocumentValidation = false}; var insertOptions = new InsertOneOptions { BypassDocumentValidation = false };
var collection = _client var collection = _client
.GetDatabase(_options.DatabaseName) .GetDatabase(_options.DatabaseName)
...@@ -51,11 +51,10 @@ namespace DotNetCore.CAP.MongoDB ...@@ -51,11 +51,10 @@ namespace DotNetCore.CAP.MongoDB
if (NotUseTransaction) if (NotUseTransaction)
{ {
return collection.InsertOneAsync(store, insertOptions, cancel); return collection.InsertOneAsync(store, insertOptions, cancel);
} }
var dbTrans = (IClientSessionHandle) transaction.DbTransaction; var dbTrans = (IClientSessionHandle)transaction.DbTransaction;
return collection.InsertOneAsync(dbTrans, store, insertOptions, cancel); return collection.InsertOneAsync(dbTrans, store, insertOptions, cancel);
} }
} }
......
...@@ -39,6 +39,7 @@ namespace DotNetCore.CAP ...@@ -39,6 +39,7 @@ namespace DotNetCore.CAP
public override void Dispose() public override void Dispose()
{ {
(DbTransaction as IClientSessionHandle)?.Dispose(); (DbTransaction as IClientSessionHandle)?.Dispose();
DbTransaction = null;
} }
} }
......
...@@ -3,9 +3,9 @@ ...@@ -3,9 +3,9 @@
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using DotNetCore.CAP.Models;
using DotNetCore.CAP.Processor; using DotNetCore.CAP.Processor;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using MongoDB.Driver; using MongoDB.Driver;
namespace DotNetCore.CAP.MongoDB namespace DotNetCore.CAP.MongoDB
...@@ -17,19 +17,19 @@ namespace DotNetCore.CAP.MongoDB ...@@ -17,19 +17,19 @@ namespace DotNetCore.CAP.MongoDB
private readonly MongoDBOptions _options; private readonly MongoDBOptions _options;
private readonly TimeSpan _waitingInterval = TimeSpan.FromMinutes(5); private readonly TimeSpan _waitingInterval = TimeSpan.FromMinutes(5);
public MongoDBCollectProcessor(ILogger<MongoDBCollectProcessor> logger, public MongoDBCollectProcessor(
MongoDBOptions options, ILogger<MongoDBCollectProcessor> logger,
IOptions<MongoDBOptions> options,
IMongoClient client) IMongoClient client)
{ {
_options = options; _options = options.Value;
_logger = logger; _logger = logger;
_database = client.GetDatabase(_options.DatabaseName); _database = client.GetDatabase(_options.DatabaseName);
} }
public async Task ProcessAsync(ProcessingContext context) public async Task ProcessAsync(ProcessingContext context)
{ {
_logger.LogDebug( _logger.LogDebug($"Collecting expired data from collection [{_options.PublishedCollection}].");
$"Collecting expired data from collection [{_options.PublishedCollection}].");
var publishedCollection = _database.GetCollection<PublishedMessage>(_options.PublishedCollection); var publishedCollection = _database.GetCollection<PublishedMessage>(_options.PublishedCollection);
var receivedCollection = _database.GetCollection<ReceivedMessage>(_options.ReceivedCollection); var receivedCollection = _database.GetCollection<ReceivedMessage>(_options.ReceivedCollection);
...@@ -39,6 +39,7 @@ namespace DotNetCore.CAP.MongoDB ...@@ -39,6 +39,7 @@ namespace DotNetCore.CAP.MongoDB
new DeleteManyModel<PublishedMessage>( new DeleteManyModel<PublishedMessage>(
Builders<PublishedMessage>.Filter.Lt(x => x.ExpiresAt, DateTime.Now)) Builders<PublishedMessage>.Filter.Lt(x => x.ExpiresAt, DateTime.Now))
}); });
await receivedCollection.BulkWriteAsync(new[] await receivedCollection.BulkWriteAsync(new[]
{ {
new DeleteManyModel<ReceivedMessage>( new DeleteManyModel<ReceivedMessage>(
......
...@@ -7,6 +7,7 @@ using DotNetCore.CAP.Dashboard; ...@@ -7,6 +7,7 @@ using DotNetCore.CAP.Dashboard;
using DotNetCore.CAP.Dashboard.Monitoring; using DotNetCore.CAP.Dashboard.Monitoring;
using DotNetCore.CAP.Infrastructure; using DotNetCore.CAP.Infrastructure;
using DotNetCore.CAP.Models; using DotNetCore.CAP.Models;
using Microsoft.Extensions.Options;
using MongoDB.Bson; using MongoDB.Bson;
using MongoDB.Driver; using MongoDB.Driver;
...@@ -17,10 +18,10 @@ namespace DotNetCore.CAP.MongoDB ...@@ -17,10 +18,10 @@ namespace DotNetCore.CAP.MongoDB
private readonly IMongoDatabase _database; private readonly IMongoDatabase _database;
private readonly MongoDBOptions _options; private readonly MongoDBOptions _options;
public MongoDBMonitoringApi(IMongoClient client, MongoDBOptions options) public MongoDBMonitoringApi(IMongoClient client, IOptions<MongoDBOptions> options)
{ {
var mongoClient = client ?? throw new ArgumentNullException(nameof(client)); var mongoClient = client ?? throw new ArgumentNullException(nameof(client));
_options = options ?? throw new ArgumentNullException(nameof(options)); _options = options.Value ?? throw new ArgumentNullException(nameof(options));
_database = mongoClient.GetDatabase(_options.DatabaseName); _database = mongoClient.GetDatabase(_options.DatabaseName);
} }
...@@ -140,7 +141,7 @@ namespace DotNetCore.CAP.MongoDB ...@@ -140,7 +141,7 @@ namespace DotNetCore.CAP.MongoDB
private int GetNumberOfMessage(string collectionName, string statusName) private int GetNumberOfMessage(string collectionName, string statusName)
{ {
var collection = _database.GetCollection<BsonDocument>(collectionName); var collection = _database.GetCollection<BsonDocument>(collectionName);
var count = collection.CountDocuments(new BsonDocument {{"StatusName", statusName}}); var count = collection.CountDocuments(new BsonDocument { { "StatusName", statusName } });
return int.Parse(count.ToString()); return int.Parse(count.ToString());
} }
...@@ -199,7 +200,7 @@ namespace DotNetCore.CAP.MongoDB ...@@ -199,7 +200,7 @@ namespace DotNetCore.CAP.MongoDB
} }
}; };
var pipeline = new[] {match, groupby}; var pipeline = new[] { match, groupby };
var collection = _database.GetCollection<BsonDocument>(collectionName); var collection = _database.GetCollection<BsonDocument>(collectionName);
var result = collection.Aggregate<BsonDocument>(pipeline).ToList(); var result = collection.Aggregate<BsonDocument>(pipeline).ToList();
......
...@@ -6,19 +6,21 @@ using System.Threading; ...@@ -6,19 +6,21 @@ using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using DotNetCore.CAP.Dashboard; using DotNetCore.CAP.Dashboard;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using MongoDB.Driver; using MongoDB.Driver;
namespace DotNetCore.CAP.MongoDB namespace DotNetCore.CAP.MongoDB
{ {
public class MongoDBStorage : IStorage public class MongoDBStorage : IStorage
{ {
private readonly CapOptions _capOptions; private readonly IOptions<CapOptions> _capOptions;
private readonly IMongoClient _client; private readonly IMongoClient _client;
private readonly ILogger<MongoDBStorage> _logger; private readonly ILogger<MongoDBStorage> _logger;
private readonly MongoDBOptions _options; private readonly IOptions<MongoDBOptions> _options;
public MongoDBStorage(CapOptions capOptions, public MongoDBStorage(
MongoDBOptions options, IOptions<CapOptions> capOptions,
IOptions<MongoDBOptions> options,
IMongoClient client, IMongoClient client,
ILogger<MongoDBStorage> logger) ILogger<MongoDBStorage> logger)
{ {
...@@ -45,31 +47,32 @@ namespace DotNetCore.CAP.MongoDB ...@@ -45,31 +47,32 @@ namespace DotNetCore.CAP.MongoDB
return; return;
} }
var database = _client.GetDatabase(_options.DatabaseName); var options = _options.Value;
var names = (await database.ListCollectionNamesAsync(cancellationToken: cancellationToken))?.ToList(); var database = _client.GetDatabase(options.DatabaseName);
var names = (await database.ListCollectionNamesAsync(cancellationToken: cancellationToken)).ToList();
if (names.All(n => n != _options.ReceivedCollection)) if (names.All(n => n != options.ReceivedCollection))
{ {
await database.CreateCollectionAsync(_options.ReceivedCollection, cancellationToken: cancellationToken); await database.CreateCollectionAsync(options.ReceivedCollection, cancellationToken: cancellationToken);
} }
if (names.All(n => n != _options.PublishedCollection)) if (names.All(n => n != options.PublishedCollection))
{ {
await database.CreateCollectionAsync(_options.PublishedCollection, await database.CreateCollectionAsync(options.PublishedCollection,
cancellationToken: cancellationToken); cancellationToken: cancellationToken);
} }
var receivedMessageIndexNames = new string[] { var receivedMessageIndexNames = new[] {
nameof(ReceivedMessage.Name), nameof(ReceivedMessage.Added), nameof(ReceivedMessage.ExpiresAt), nameof(ReceivedMessage.Name), nameof(ReceivedMessage.Added), nameof(ReceivedMessage.ExpiresAt),
nameof(ReceivedMessage.StatusName), nameof(ReceivedMessage.Retries), nameof(ReceivedMessage.Version) }; nameof(ReceivedMessage.StatusName), nameof(ReceivedMessage.Retries), nameof(ReceivedMessage.Version) };
var publishedMessageIndexNames = new string[] { var publishedMessageIndexNames = new[] {
nameof(PublishedMessage.Name), nameof(PublishedMessage.Added), nameof(PublishedMessage.ExpiresAt), nameof(PublishedMessage.Name), nameof(PublishedMessage.Added), nameof(PublishedMessage.ExpiresAt),
nameof(PublishedMessage.StatusName), nameof(PublishedMessage.Retries), nameof(PublishedMessage.Version) }; nameof(PublishedMessage.StatusName), nameof(PublishedMessage.Retries), nameof(PublishedMessage.Version) };
await Task.WhenAll( await Task.WhenAll(
TryCreateIndexesAsync<ReceivedMessage>(_options.ReceivedCollection, receivedMessageIndexNames), TryCreateIndexesAsync<ReceivedMessage>(options.ReceivedCollection, receivedMessageIndexNames),
TryCreateIndexesAsync<PublishedMessage>(_options.PublishedCollection, publishedMessageIndexNames) TryCreateIndexesAsync<PublishedMessage>(options.PublishedCollection, publishedMessageIndexNames)
); );
_logger.LogDebug("Ensuring all create database tables script are applied."); _logger.LogDebug("Ensuring all create database tables script are applied.");
...@@ -87,15 +90,15 @@ namespace DotNetCore.CAP.MongoDB ...@@ -87,15 +90,15 @@ namespace DotNetCore.CAP.MongoDB
if (indexNames.Any() == false) if (indexNames.Any() == false)
return; return;
var indexes = indexNames.Select(index_name => var indexes = indexNames.Select(indexName =>
{ {
var indexOptions = new CreateIndexOptions var indexOptions = new CreateIndexOptions
{ {
Name = index_name, Name = indexName,
Background = true, Background = true,
}; };
var indexBuilder = Builders<T>.IndexKeys; var indexBuilder = Builders<T>.IndexKeys;
return new CreateIndexModel<T>(indexBuilder.Descending(index_name), indexOptions); return new CreateIndexModel<T>(indexBuilder.Descending(indexName), indexOptions);
}).ToArray(); }).ToArray();
await col.Indexes.CreateManyAsync(indexes, cancellationToken); await col.Indexes.CreateManyAsync(indexes, cancellationToken);
......
...@@ -6,6 +6,7 @@ using System.Collections.Generic; ...@@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using DotNetCore.CAP.Infrastructure; using DotNetCore.CAP.Infrastructure;
using DotNetCore.CAP.Models; using DotNetCore.CAP.Models;
using Microsoft.Extensions.Options;
using MongoDB.Driver; using MongoDB.Driver;
namespace DotNetCore.CAP.MongoDB namespace DotNetCore.CAP.MongoDB
...@@ -17,10 +18,13 @@ namespace DotNetCore.CAP.MongoDB ...@@ -17,10 +18,13 @@ namespace DotNetCore.CAP.MongoDB
private readonly IMongoDatabase _database; private readonly IMongoDatabase _database;
private readonly MongoDBOptions _options; private readonly MongoDBOptions _options;
public MongoDBStorageConnection(CapOptions capOptions, MongoDBOptions options, IMongoClient client) public MongoDBStorageConnection(
IOptions<CapOptions> capOptions,
IOptions<MongoDBOptions> options,
IMongoClient client)
{ {
_capOptions = capOptions; _capOptions = capOptions.Value;
_options = options; _options = options.Value;
_client = client; _client = client;
_database = _client.GetDatabase(_options.DatabaseName); _database = _client.GetDatabase(_options.DatabaseName);
} }
......
...@@ -17,7 +17,7 @@ namespace DotNetCore.CAP.MongoDB ...@@ -17,7 +17,7 @@ namespace DotNetCore.CAP.MongoDB
public MongoDBStorageTransaction(IMongoClient client, MongoDBOptions options) public MongoDBStorageTransaction(IMongoClient client, MongoDBOptions options)
{ {
_options = options; _options = options;
_database = client.GetDatabase(options.DatabaseName); _database = client.GetDatabase(_options.DatabaseName);
_session = client.StartSession(); _session = client.StartSession();
_session.StartTransaction(); _session.StartTransaction();
} }
......
...@@ -4,8 +4,8 @@ ...@@ -4,8 +4,8 @@
using System; using System;
using DotNetCore.CAP.MySql; using DotNetCore.CAP.MySql;
using DotNetCore.CAP.Processor; using DotNetCore.CAP.Processor;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
// ReSharper disable once CheckNamespace // ReSharper disable once CheckNamespace
namespace DotNetCore.CAP namespace DotNetCore.CAP
...@@ -24,39 +24,15 @@ namespace DotNetCore.CAP ...@@ -24,39 +24,15 @@ namespace DotNetCore.CAP
services.AddSingleton<CapStorageMarkerService>(); services.AddSingleton<CapStorageMarkerService>();
services.AddSingleton<IStorage, MySqlStorage>(); services.AddSingleton<IStorage, MySqlStorage>();
services.AddSingleton<IStorageConnection, MySqlStorageConnection>(); services.AddSingleton<IStorageConnection, MySqlStorageConnection>();
services.AddSingleton<ICapPublisher, MySqlPublisher>();
services.AddSingleton<ICallbackPublisher>(provider => (MySqlPublisher)provider.GetService<ICapPublisher>());
services.AddSingleton<ICollectProcessor, MySqlCollectProcessor>();
services.AddScoped<ICapPublisher, MySqlPublisher>();
services.AddScoped<ICallbackPublisher, MySqlPublisher>();
services.AddTransient<ICollectProcessor, MySqlCollectProcessor>();
services.AddTransient<CapTransactionBase, MySqlCapTransaction>(); services.AddTransient<CapTransactionBase, MySqlCapTransaction>();
AddSingletionMySqlOptions(services); //Add MySqlOptions
} services.Configure(_configure);
services.AddSingleton<IConfigureOptions<MySqlOptions>, ConfigureMySqlOptions>();
private void AddSingletionMySqlOptions(IServiceCollection services) }
{
var mysqlOptions = new MySqlOptions();
_configure(mysqlOptions);
if (mysqlOptions.DbContextType != null)
{
services.AddSingleton(x =>
{
using (var scope = x.CreateScope())
{
var provider = scope.ServiceProvider;
var dbContext = (DbContext) provider.GetService(mysqlOptions.DbContextType);
mysqlOptions.ConnectionString = dbContext.Database.GetDbConnection().ConnectionString;
return mysqlOptions;
}
});
}
else
{
services.AddSingleton(mysqlOptions);
}
}
} }
} }
\ No newline at end of file
// Copyright (c) .NET Core Community. All rights reserved. // Copyright (c) .NET Core Community. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information. // Licensed under the MIT License. See License.txt in the project root for license information.
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
namespace DotNetCore.CAP namespace DotNetCore.CAP
{ {
public class MySqlOptions : EFOptions public class MySqlOptions : EFOptions
...@@ -10,4 +14,29 @@ namespace DotNetCore.CAP ...@@ -10,4 +14,29 @@ namespace DotNetCore.CAP
/// </summary> /// </summary>
public string ConnectionString { get; set; } public string ConnectionString { get; set; }
} }
internal class ConfigureMySqlOptions : IConfigureOptions<MySqlOptions>
{
private readonly IServiceScopeFactory _serviceScopeFactory;
public ConfigureMySqlOptions(IServiceScopeFactory serviceScopeFactory)
{
_serviceScopeFactory = serviceScopeFactory;
}
public void Configure(MySqlOptions options)
{
if (options.DbContextType != null)
{
using (var scope = _serviceScopeFactory.CreateScope())
{
var provider = scope.ServiceProvider;
using (var dbContext = (DbContext)provider.GetRequiredService(options.DbContextType))
{
options.ConnectionString = dbContext.Database.GetDbConnection().ConnectionString;
}
}
}
}
}
} }
\ No newline at end of file
...@@ -10,6 +10,7 @@ using DotNetCore.CAP.Abstractions; ...@@ -10,6 +10,7 @@ using DotNetCore.CAP.Abstractions;
using DotNetCore.CAP.Models; using DotNetCore.CAP.Models;
using Microsoft.EntityFrameworkCore.Storage; using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using MySql.Data.MySqlClient; using MySql.Data.MySqlClient;
namespace DotNetCore.CAP.MySql namespace DotNetCore.CAP.MySql
...@@ -20,7 +21,7 @@ namespace DotNetCore.CAP.MySql ...@@ -20,7 +21,7 @@ namespace DotNetCore.CAP.MySql
public MySqlPublisher(IServiceProvider provider) : base(provider) public MySqlPublisher(IServiceProvider provider) : base(provider)
{ {
_options = provider.GetService<MySqlOptions>(); _options = provider.GetService<IOptions<MySqlOptions>>().Value;
} }
public async Task PublishCallbackAsync(CapPublishedMessage message) public async Task PublishCallbackAsync(CapPublishedMessage message)
......
...@@ -50,6 +50,7 @@ namespace DotNetCore.CAP ...@@ -50,6 +50,7 @@ namespace DotNetCore.CAP
public override void Dispose() public override void Dispose()
{ {
(DbTransaction as IDbTransaction)?.Dispose(); (DbTransaction as IDbTransaction)?.Dispose();
DbTransaction = null;
} }
} }
......
...@@ -6,6 +6,7 @@ using System.Threading.Tasks; ...@@ -6,6 +6,7 @@ using System.Threading.Tasks;
using Dapper; using Dapper;
using DotNetCore.CAP.Processor; using DotNetCore.CAP.Processor;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using MySql.Data.MySqlClient; using MySql.Data.MySqlClient;
namespace DotNetCore.CAP.MySql namespace DotNetCore.CAP.MySql
...@@ -18,11 +19,10 @@ namespace DotNetCore.CAP.MySql ...@@ -18,11 +19,10 @@ namespace DotNetCore.CAP.MySql
private readonly MySqlOptions _options; private readonly MySqlOptions _options;
private readonly TimeSpan _waitingInterval = TimeSpan.FromMinutes(5); private readonly TimeSpan _waitingInterval = TimeSpan.FromMinutes(5);
public MySqlCollectProcessor(ILogger<MySqlCollectProcessor> logger, public MySqlCollectProcessor(ILogger<MySqlCollectProcessor> logger, IOptions<MySqlOptions> mysqlOptions)
MySqlOptions mysqlOptions)
{ {
_logger = logger; _logger = logger;
_options = mysqlOptions; _options = mysqlOptions.Value;
} }
public async Task ProcessAsync(ProcessingContext context) public async Task ProcessAsync(ProcessingContext context)
...@@ -44,7 +44,7 @@ namespace DotNetCore.CAP.MySql ...@@ -44,7 +44,7 @@ namespace DotNetCore.CAP.MySql
{ {
removedCount = await connection.ExecuteAsync( removedCount = await connection.ExecuteAsync(
$@"DELETE FROM `{table}` WHERE ExpiresAt < @now limit @count;", $@"DELETE FROM `{table}` WHERE ExpiresAt < @now limit @count;",
new {now = DateTime.Now, count = MaxBatch}); new { now = DateTime.Now, count = MaxBatch });
} }
if (removedCount != 0) if (removedCount != 0)
......
...@@ -10,6 +10,7 @@ using DotNetCore.CAP.Dashboard; ...@@ -10,6 +10,7 @@ using DotNetCore.CAP.Dashboard;
using DotNetCore.CAP.Dashboard.Monitoring; using DotNetCore.CAP.Dashboard.Monitoring;
using DotNetCore.CAP.Infrastructure; using DotNetCore.CAP.Infrastructure;
using DotNetCore.CAP.Models; using DotNetCore.CAP.Models;
using Microsoft.Extensions.Options;
namespace DotNetCore.CAP.MySql namespace DotNetCore.CAP.MySql
{ {
...@@ -18,10 +19,10 @@ namespace DotNetCore.CAP.MySql ...@@ -18,10 +19,10 @@ namespace DotNetCore.CAP.MySql
private readonly string _prefix; private readonly string _prefix;
private readonly MySqlStorage _storage; private readonly MySqlStorage _storage;
public MySqlMonitoringApi(IStorage storage, MySqlOptions options) public MySqlMonitoringApi(IStorage storage, IOptions<MySqlOptions> options)
{ {
_storage = storage as MySqlStorage ?? throw new ArgumentNullException(nameof(storage)); _storage = storage as MySqlStorage ?? throw new ArgumentNullException(nameof(storage));
_prefix = options?.TableNamePrefix ?? throw new ArgumentNullException(nameof(options)); _prefix = options.Value.TableNamePrefix ?? throw new ArgumentNullException(nameof(options));
} }
public StatisticsDto GetStatistics() public StatisticsDto GetStatistics()
...@@ -126,7 +127,7 @@ select count(Id) from `{0}.received` where StatusName = N'Failed';", _prefix); ...@@ -126,7 +127,7 @@ select count(Id) from `{0}.received` where StatusName = N'Failed';", _prefix);
{ {
var sqlQuery = $"select count(Id) from `{_prefix}.{tableName}` where StatusName = @state"; var sqlQuery = $"select count(Id) from `{_prefix}.{tableName}` where StatusName = @state";
var count = connection.ExecuteScalar<int>(sqlQuery, new {state = statusName}); var count = connection.ExecuteScalar<int>(sqlQuery, new { state = statusName });
return count; return count;
} }
...@@ -169,7 +170,7 @@ select aggr.* from ( ...@@ -169,7 +170,7 @@ select aggr.* from (
var valuesMap = connection.Query<TimelineCounter>( var valuesMap = connection.Query<TimelineCounter>(
sqlQuery, sqlQuery,
new {keys = keyMaps.Keys, statusName}) new { keys = keyMaps.Keys, statusName })
.ToDictionary(x => x.Key, x => x.Count); .ToDictionary(x => x.Key, x => x.Count);
foreach (var key in keyMaps.Keys) foreach (var key in keyMaps.Keys)
......
...@@ -8,20 +8,22 @@ using System.Threading.Tasks; ...@@ -8,20 +8,22 @@ using System.Threading.Tasks;
using Dapper; using Dapper;
using DotNetCore.CAP.Dashboard; using DotNetCore.CAP.Dashboard;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using MySql.Data.MySqlClient; using MySql.Data.MySqlClient;
namespace DotNetCore.CAP.MySql namespace DotNetCore.CAP.MySql
{ {
public class MySqlStorage : IStorage public class MySqlStorage : IStorage
{ {
private readonly CapOptions _capOptions; private readonly IOptions<CapOptions> _capOptions;
private readonly IOptions<MySqlOptions> _options;
private readonly IDbConnection _existingConnection = null; private readonly IDbConnection _existingConnection = null;
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly MySqlOptions _options;
public MySqlStorage(ILogger<MySqlStorage> logger, public MySqlStorage(
MySqlOptions options, ILogger<MySqlStorage> logger,
CapOptions capOptions) IOptions<MySqlOptions> options,
IOptions<CapOptions> capOptions)
{ {
_options = options; _options = options;
_capOptions = capOptions; _capOptions = capOptions;
...@@ -45,10 +47,10 @@ namespace DotNetCore.CAP.MySql ...@@ -45,10 +47,10 @@ namespace DotNetCore.CAP.MySql
return; return;
} }
var sql = CreateDbTablesScript(_options.TableNamePrefix); var sql = CreateDbTablesScript(_options.Value.TableNamePrefix);
using (var connection = new MySqlConnection(_options.ConnectionString)) using (var connection = new MySqlConnection(_options.Value.ConnectionString))
{ {
await connection.ExecuteAsync(sql); await connection.ExecuteAsync(sql);
} }
_logger.LogDebug("Ensuring all create database tables script are applied."); _logger.LogDebug("Ensuring all create database tables script are applied.");
...@@ -103,7 +105,7 @@ CREATE TABLE IF NOT EXISTS `{prefix}.published` ( ...@@ -103,7 +105,7 @@ CREATE TABLE IF NOT EXISTS `{prefix}.published` (
internal IDbConnection CreateAndOpenConnection() internal IDbConnection CreateAndOpenConnection()
{ {
var connection = _existingConnection ?? new MySqlConnection(_options.ConnectionString); var connection = _existingConnection ?? new MySqlConnection(_options.Value.ConnectionString);
if (connection.State == ConnectionState.Closed) if (connection.State == ConnectionState.Closed)
{ {
......
...@@ -7,6 +7,7 @@ using System.Threading.Tasks; ...@@ -7,6 +7,7 @@ using System.Threading.Tasks;
using Dapper; using Dapper;
using DotNetCore.CAP.Infrastructure; using DotNetCore.CAP.Infrastructure;
using DotNetCore.CAP.Models; using DotNetCore.CAP.Models;
using Microsoft.Extensions.Options;
using MySql.Data.MySqlClient; using MySql.Data.MySqlClient;
namespace DotNetCore.CAP.MySql namespace DotNetCore.CAP.MySql
...@@ -14,16 +15,17 @@ namespace DotNetCore.CAP.MySql ...@@ -14,16 +15,17 @@ namespace DotNetCore.CAP.MySql
public class MySqlStorageConnection : IStorageConnection public class MySqlStorageConnection : IStorageConnection
{ {
private readonly CapOptions _capOptions; private readonly CapOptions _capOptions;
private readonly IOptions<MySqlOptions> _options;
private readonly string _prefix; private readonly string _prefix;
public MySqlStorageConnection(MySqlOptions options, CapOptions capOptions) public MySqlStorageConnection(IOptions<MySqlOptions> options, IOptions<CapOptions> capOptions)
{ {
_capOptions = capOptions; _options = options;
Options = options; _capOptions = capOptions.Value;
_prefix = Options.TableNamePrefix; _prefix = options.Value.TableNamePrefix;
} }
public MySqlOptions Options { get; } public MySqlOptions Options => _options.Value;
public IStorageTransaction CreateTransaction() public IStorageTransaction CreateTransaction()
{ {
......
...@@ -4,8 +4,8 @@ ...@@ -4,8 +4,8 @@
using System; using System;
using DotNetCore.CAP.PostgreSql; using DotNetCore.CAP.PostgreSql;
using DotNetCore.CAP.Processor; using DotNetCore.CAP.Processor;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
// ReSharper disable once CheckNamespace // ReSharper disable once CheckNamespace
namespace DotNetCore.CAP namespace DotNetCore.CAP
...@@ -24,38 +24,14 @@ namespace DotNetCore.CAP ...@@ -24,38 +24,14 @@ namespace DotNetCore.CAP
services.AddSingleton<CapStorageMarkerService>(); services.AddSingleton<CapStorageMarkerService>();
services.AddSingleton<IStorage, PostgreSqlStorage>(); services.AddSingleton<IStorage, PostgreSqlStorage>();
services.AddSingleton<IStorageConnection, PostgreSqlStorageConnection>(); services.AddSingleton<IStorageConnection, PostgreSqlStorageConnection>();
services.AddSingleton<ICapPublisher, PostgreSqlPublisher>();
services.AddSingleton<ICallbackPublisher>(provider => (PostgreSqlPublisher)provider.GetService<ICapPublisher>());
services.AddSingleton<ICollectProcessor, PostgreSqlCollectProcessor>();
services.AddScoped<ICapPublisher, PostgreSqlPublisher>();
services.AddScoped<ICallbackPublisher, PostgreSqlPublisher>();
services.AddTransient<ICollectProcessor, PostgreSqlCollectProcessor>();
services.AddTransient<CapTransactionBase, PostgreSqlCapTransaction>(); services.AddTransient<CapTransactionBase, PostgreSqlCapTransaction>();
AddSingletonPostgreSqlOptions(services); services.Configure(_configure);
} services.AddSingleton<IConfigureOptions<PostgreSqlOptions>, ConfigurePostgreSqlOptions>();
private void AddSingletonPostgreSqlOptions(IServiceCollection services)
{
var postgreSqlOptions = new PostgreSqlOptions();
_configure(postgreSqlOptions);
if (postgreSqlOptions.DbContextType != null)
{
services.AddSingleton(x =>
{
using (var scope = x.CreateScope())
{
var provider = scope.ServiceProvider;
var dbContext = (DbContext) provider.GetService(postgreSqlOptions.DbContextType);
postgreSqlOptions.ConnectionString = dbContext.Database.GetDbConnection().ConnectionString;
return postgreSqlOptions;
}
});
}
else
{
services.AddSingleton(postgreSqlOptions);
}
} }
} }
} }
\ No newline at end of file
// Copyright (c) .NET Core Community. All rights reserved. // Copyright (c) .NET Core Community. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information. // Licensed under the MIT License. See License.txt in the project root for license information.
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
// ReSharper disable once CheckNamespace
namespace DotNetCore.CAP namespace DotNetCore.CAP
{ {
public class PostgreSqlOptions : EFOptions public class PostgreSqlOptions : EFOptions
...@@ -10,4 +15,29 @@ namespace DotNetCore.CAP ...@@ -10,4 +15,29 @@ namespace DotNetCore.CAP
/// </summary> /// </summary>
public string ConnectionString { get; set; } public string ConnectionString { get; set; }
} }
internal class ConfigurePostgreSqlOptions : IConfigureOptions<PostgreSqlOptions>
{
private readonly IServiceScopeFactory _serviceScopeFactory;
public ConfigurePostgreSqlOptions(IServiceScopeFactory serviceScopeFactory)
{
_serviceScopeFactory = serviceScopeFactory;
}
public void Configure(PostgreSqlOptions options)
{
if (options.DbContextType != null)
{
using (var scope = _serviceScopeFactory.CreateScope())
{
var provider = scope.ServiceProvider;
using (var dbContext = (DbContext)provider.GetRequiredService(options.DbContextType))
{
options.ConnectionString = dbContext.Database.GetDbConnection().ConnectionString;
}
}
}
}
}
} }
\ No newline at end of file
...@@ -10,6 +10,7 @@ using DotNetCore.CAP.Abstractions; ...@@ -10,6 +10,7 @@ using DotNetCore.CAP.Abstractions;
using DotNetCore.CAP.Models; using DotNetCore.CAP.Models;
using Microsoft.EntityFrameworkCore.Storage; using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Npgsql; using Npgsql;
namespace DotNetCore.CAP.PostgreSql namespace DotNetCore.CAP.PostgreSql
...@@ -20,7 +21,7 @@ namespace DotNetCore.CAP.PostgreSql ...@@ -20,7 +21,7 @@ namespace DotNetCore.CAP.PostgreSql
public PostgreSqlPublisher(IServiceProvider provider) : base(provider) public PostgreSqlPublisher(IServiceProvider provider) : base(provider)
{ {
_options = provider.GetService<PostgreSqlOptions>(); _options = provider.GetService<IOptions<PostgreSqlOptions>>().Value;
} }
public async Task PublishCallbackAsync(CapPublishedMessage message) public async Task PublishCallbackAsync(CapPublishedMessage message)
......
...@@ -50,6 +50,7 @@ namespace DotNetCore.CAP ...@@ -50,6 +50,7 @@ namespace DotNetCore.CAP
public override void Dispose() public override void Dispose()
{ {
(DbTransaction as IDbTransaction)?.Dispose(); (DbTransaction as IDbTransaction)?.Dispose();
DbTransaction = null;
} }
} }
......
...@@ -6,6 +6,7 @@ using System.Threading.Tasks; ...@@ -6,6 +6,7 @@ using System.Threading.Tasks;
using Dapper; using Dapper;
using DotNetCore.CAP.Processor; using DotNetCore.CAP.Processor;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Npgsql; using Npgsql;
namespace DotNetCore.CAP.PostgreSql namespace DotNetCore.CAP.PostgreSql
...@@ -25,10 +26,10 @@ namespace DotNetCore.CAP.PostgreSql ...@@ -25,10 +26,10 @@ namespace DotNetCore.CAP.PostgreSql
private readonly TimeSpan _waitingInterval = TimeSpan.FromMinutes(5); private readonly TimeSpan _waitingInterval = TimeSpan.FromMinutes(5);
public PostgreSqlCollectProcessor(ILogger<PostgreSqlCollectProcessor> logger, public PostgreSqlCollectProcessor(ILogger<PostgreSqlCollectProcessor> logger,
PostgreSqlOptions sqlServerOptions) IOptions<PostgreSqlOptions> sqlServerOptions)
{ {
_logger = logger; _logger = logger;
_options = sqlServerOptions; _options = sqlServerOptions.Value;
} }
public async Task ProcessAsync(ProcessingContext context) public async Task ProcessAsync(ProcessingContext context)
...@@ -44,7 +45,7 @@ namespace DotNetCore.CAP.PostgreSql ...@@ -44,7 +45,7 @@ namespace DotNetCore.CAP.PostgreSql
{ {
removedCount = await connection.ExecuteAsync( removedCount = await connection.ExecuteAsync(
$"DELETE FROM \"{_options.Schema}\".\"{table}\" WHERE \"ExpiresAt\" < @now AND \"Id\" IN (SELECT \"Id\" FROM \"{_options.Schema}\".\"{table}\" LIMIT @count);", $"DELETE FROM \"{_options.Schema}\".\"{table}\" WHERE \"ExpiresAt\" < @now AND \"Id\" IN (SELECT \"Id\" FROM \"{_options.Schema}\".\"{table}\" LIMIT @count);",
new {now = DateTime.Now, count = MaxBatch}); new { now = DateTime.Now, count = MaxBatch });
} }
if (removedCount != 0) if (removedCount != 0)
......
...@@ -10,6 +10,7 @@ using DotNetCore.CAP.Dashboard; ...@@ -10,6 +10,7 @@ using DotNetCore.CAP.Dashboard;
using DotNetCore.CAP.Dashboard.Monitoring; using DotNetCore.CAP.Dashboard.Monitoring;
using DotNetCore.CAP.Infrastructure; using DotNetCore.CAP.Infrastructure;
using DotNetCore.CAP.Models; using DotNetCore.CAP.Models;
using Microsoft.Extensions.Options;
namespace DotNetCore.CAP.PostgreSql namespace DotNetCore.CAP.PostgreSql
{ {
...@@ -18,9 +19,9 @@ namespace DotNetCore.CAP.PostgreSql ...@@ -18,9 +19,9 @@ namespace DotNetCore.CAP.PostgreSql
private readonly PostgreSqlOptions _options; private readonly PostgreSqlOptions _options;
private readonly PostgreSqlStorage _storage; private readonly PostgreSqlStorage _storage;
public PostgreSqlMonitoringApi(IStorage storage, PostgreSqlOptions options) public PostgreSqlMonitoringApi(IStorage storage, IOptions<PostgreSqlOptions> options)
{ {
_options = options ?? throw new ArgumentNullException(nameof(options)); _options = options.Value ?? throw new ArgumentNullException(nameof(options));
_storage = storage as PostgreSqlStorage ?? throw new ArgumentNullException(nameof(storage)); _storage = storage as PostgreSqlStorage ?? throw new ArgumentNullException(nameof(storage));
} }
......
...@@ -8,20 +8,21 @@ using System.Threading.Tasks; ...@@ -8,20 +8,21 @@ using System.Threading.Tasks;
using Dapper; using Dapper;
using DotNetCore.CAP.Dashboard; using DotNetCore.CAP.Dashboard;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Npgsql; using Npgsql;
namespace DotNetCore.CAP.PostgreSql namespace DotNetCore.CAP.PostgreSql
{ {
public class PostgreSqlStorage : IStorage public class PostgreSqlStorage : IStorage
{ {
private readonly CapOptions _capOptions; private readonly IOptions<CapOptions> _capOptions;
private readonly IDbConnection _existingConnection = null; private readonly IDbConnection _existingConnection = null;
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly PostgreSqlOptions _options; private readonly IOptions<PostgreSqlOptions> _options;
public PostgreSqlStorage(ILogger<PostgreSqlStorage> logger, public PostgreSqlStorage(ILogger<PostgreSqlStorage> logger,
CapOptions capOptions, IOptions<CapOptions> capOptions,
PostgreSqlOptions options) IOptions<PostgreSqlOptions> options)
{ {
_options = options; _options = options;
_logger = logger; _logger = logger;
...@@ -45,9 +46,9 @@ namespace DotNetCore.CAP.PostgreSql ...@@ -45,9 +46,9 @@ namespace DotNetCore.CAP.PostgreSql
return; return;
} }
var sql = CreateDbTablesScript(_options.Schema); var sql = CreateDbTablesScript(_options.Value.Schema);
using (var connection = new NpgsqlConnection(_options.ConnectionString)) using (var connection = new NpgsqlConnection(_options.Value.ConnectionString))
{ {
await connection.ExecuteAsync(sql); await connection.ExecuteAsync(sql);
} }
...@@ -72,7 +73,7 @@ namespace DotNetCore.CAP.PostgreSql ...@@ -72,7 +73,7 @@ namespace DotNetCore.CAP.PostgreSql
internal IDbConnection CreateAndOpenConnection() internal IDbConnection CreateAndOpenConnection()
{ {
var connection = _existingConnection ?? new NpgsqlConnection(_options.ConnectionString); var connection = _existingConnection ?? new NpgsqlConnection(_options.Value.ConnectionString);
if (connection.State == ConnectionState.Closed) if (connection.State == ConnectionState.Closed)
{ {
......
...@@ -7,6 +7,7 @@ using System.Threading.Tasks; ...@@ -7,6 +7,7 @@ using System.Threading.Tasks;
using Dapper; using Dapper;
using DotNetCore.CAP.Infrastructure; using DotNetCore.CAP.Infrastructure;
using DotNetCore.CAP.Models; using DotNetCore.CAP.Models;
using Microsoft.Extensions.Options;
using Npgsql; using Npgsql;
namespace DotNetCore.CAP.PostgreSql namespace DotNetCore.CAP.PostgreSql
...@@ -15,10 +16,12 @@ namespace DotNetCore.CAP.PostgreSql ...@@ -15,10 +16,12 @@ namespace DotNetCore.CAP.PostgreSql
{ {
private readonly CapOptions _capOptions; private readonly CapOptions _capOptions;
public PostgreSqlStorageConnection(PostgreSqlOptions options, CapOptions capOptions) public PostgreSqlStorageConnection(
IOptions<PostgreSqlOptions> options,
IOptions<CapOptions> capOptions)
{ {
_capOptions = capOptions; _capOptions = capOptions.Value;
Options = options; Options = options.Value;
} }
public PostgreSqlOptions Options { get; } public PostgreSqlOptions Options { get; }
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
using System; using System;
using RabbitMQ.Client; using RabbitMQ.Client;
// ReSharper disable once CheckNamespace
namespace DotNetCore.CAP namespace DotNetCore.CAP
{ {
public class RabbitMQOptions public class RabbitMQOptions
......
...@@ -20,11 +20,8 @@ namespace DotNetCore.CAP ...@@ -20,11 +20,8 @@ namespace DotNetCore.CAP
public void AddServices(IServiceCollection services) public void AddServices(IServiceCollection services)
{ {
services.AddSingleton<CapMessageQueueMakerService>(); services.AddSingleton<CapMessageQueueMakerService>();
var options = new RabbitMQOptions(); services.Configure(_configure);
_configure?.Invoke(options);
services.AddSingleton(options);
services.AddSingleton<IConsumerClientFactory, RabbitMQConsumerClientFactory>(); services.AddSingleton<IConsumerClientFactory, RabbitMQConsumerClientFactory>();
services.AddSingleton<IConnectionChannelPool, ConnectionChannelPool>(); services.AddSingleton<IConnectionChannelPool, ConnectionChannelPool>();
services.AddSingleton<IPublishExecutor, RabbitMQPublishMessageSender>(); services.AddSingleton<IPublishExecutor, RabbitMQPublishMessageSender>();
......
...@@ -7,6 +7,7 @@ using System.Diagnostics; ...@@ -7,6 +7,7 @@ using System.Diagnostics;
using System.Reflection; using System.Reflection;
using System.Threading; using System.Threading;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using RabbitMQ.Client; using RabbitMQ.Client;
namespace DotNetCore.CAP.RabbitMQ namespace DotNetCore.CAP.RabbitMQ
...@@ -18,37 +19,34 @@ namespace DotNetCore.CAP.RabbitMQ ...@@ -18,37 +19,34 @@ namespace DotNetCore.CAP.RabbitMQ
private readonly ILogger<ConnectionChannelPool> _logger; private readonly ILogger<ConnectionChannelPool> _logger;
private readonly ConcurrentQueue<IModel> _pool; private readonly ConcurrentQueue<IModel> _pool;
private IConnection _connection; private IConnection _connection;
private static readonly object s_lock = new object(); private static readonly object SLock = new object();
private int _count; private int _count;
private int _maxSize; private int _maxSize;
public ConnectionChannelPool(ILogger<ConnectionChannelPool> logger, public ConnectionChannelPool(
CapOptions capOptions, ILogger<ConnectionChannelPool> logger,
RabbitMQOptions options) IOptions<CapOptions> capOptionsAccessor,
IOptions<RabbitMQOptions> optionsAccessor)
{ {
_logger = logger; _logger = logger;
_maxSize = DefaultPoolSize; _maxSize = DefaultPoolSize;
_pool = new ConcurrentQueue<IModel>(); _pool = new ConcurrentQueue<IModel>();
_connectionActivator = CreateConnection(options);
HostAddress = options.HostName + ":" + options.Port; var capOptions = capOptionsAccessor.Value;
var options = optionsAccessor.Value;
if (CapOptions.DefaultVersion == capOptions.Version) _connectionActivator = CreateConnection(options);
{
Exchange = options.ExchangeName; HostAddress = $"{options.HostName}:{options.Port}";
} Exchange = CapOptions.DefaultVersion == capOptions.Version ? options.ExchangeName : $"{options.ExchangeName}.{capOptions.Version}";
else
{
Exchange = options.ExchangeName + "." + capOptions.Version;
}
_logger.LogDebug($"RabbitMQ configuration:'HostName:{options.HostName}, Port:{options.Port}, UserName:{options.UserName}, Password:{options.Password}, ExchangeName:{options.ExchangeName}'"); _logger.LogDebug($"RabbitMQ configuration:'HostName:{options.HostName}, Port:{options.Port}, UserName:{options.UserName}, Password:{options.Password}, ExchangeName:{options.ExchangeName}'");
} }
IModel IConnectionChannelPool.Rent() IModel IConnectionChannelPool.Rent()
{ {
lock (s_lock) lock (SLock)
{ {
while (_count > _maxSize) while (_count > _maxSize)
{ {
...@@ -100,14 +98,14 @@ namespace DotNetCore.CAP.RabbitMQ ...@@ -100,14 +98,14 @@ namespace DotNetCore.CAP.RabbitMQ
Password = options.Password, Password = options.Password,
VirtualHost = options.VirtualHost VirtualHost = options.VirtualHost
}; };
if (options.HostName.Contains(",")) if (options.HostName.Contains(","))
{ {
options.ConnectionFactoryOptions?.Invoke(factory); options.ConnectionFactoryOptions?.Invoke(factory);
return () => factory.CreateConnection( return () => factory.CreateConnection(
options.HostName.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries), serviceName); options.HostName.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries), serviceName);
} }
factory.HostName = options.HostName; factory.HostName = options.HostName;
options.ConnectionFactoryOptions?.Invoke(factory); options.ConnectionFactoryOptions?.Invoke(factory);
return () => factory.CreateConnection(serviceName); return () => factory.CreateConnection(serviceName);
...@@ -135,7 +133,7 @@ namespace DotNetCore.CAP.RabbitMQ ...@@ -135,7 +133,7 @@ namespace DotNetCore.CAP.RabbitMQ
} }
catch (Exception e) catch (Exception e)
{ {
_logger.LogError(e,"RabbitMQ channel model create failed!"); _logger.LogError(e, "RabbitMQ channel model create failed!");
Console.WriteLine(e); Console.WriteLine(e);
throw; throw;
} }
......
...@@ -7,6 +7,7 @@ using System.Threading.Tasks; ...@@ -7,6 +7,7 @@ using System.Threading.Tasks;
using DotNetCore.CAP.Internal; using DotNetCore.CAP.Internal;
using DotNetCore.CAP.Processor.States; using DotNetCore.CAP.Processor.States;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using RabbitMQ.Client; using RabbitMQ.Client;
using RabbitMQ.Client.Framing; using RabbitMQ.Client.Framing;
...@@ -18,16 +19,21 @@ namespace DotNetCore.CAP.RabbitMQ ...@@ -18,16 +19,21 @@ namespace DotNetCore.CAP.RabbitMQ
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly string _exchange; private readonly string _exchange;
public RabbitMQPublishMessageSender(ILogger<RabbitMQPublishMessageSender> logger, CapOptions options, public RabbitMQPublishMessageSender(
IStorageConnection connection, IConnectionChannelPool connectionChannelPool, IStateChanger stateChanger) ILogger<RabbitMQPublishMessageSender> logger,
IOptions<CapOptions> options,
IStorageConnection connection,
IConnectionChannelPool connectionChannelPool,
IStateChanger stateChanger)
: base(logger, options, connection, stateChanger) : base(logger, options, connection, stateChanger)
{ {
_logger = logger; _logger = logger;
_connectionChannelPool = connectionChannelPool; _connectionChannelPool = connectionChannelPool;
_exchange = _connectionChannelPool.Exchange; _exchange = _connectionChannelPool.Exchange;
ServersAddress = _connectionChannelPool.HostAddress;
} }
protected override string ServersAddress => _connectionChannelPool.HostAddress;
public override Task<OperateResult> PublishAsync(string keyName, string content) public override Task<OperateResult> PublishAsync(string keyName, string content)
{ {
var channel = _connectionChannelPool.Rent(); var channel = _connectionChannelPool.Rent();
...@@ -48,14 +54,14 @@ namespace DotNetCore.CAP.RabbitMQ ...@@ -48,14 +54,14 @@ namespace DotNetCore.CAP.RabbitMQ
} }
catch (Exception ex) catch (Exception ex)
{ {
var wapperEx = new PublisherSentFailedException(ex.Message, ex); var wrapperEx = new PublisherSentFailedException(ex.Message, ex);
var errors = new OperateError var errors = new OperateError
{ {
Code = ex.HResult.ToString(), Code = ex.HResult.ToString(),
Description = ex.Message Description = ex.Message
}; };
return Task.FromResult(OperateResult.Failed(wapperEx, errors)); return Task.FromResult(OperateResult.Failed(wrapperEx, errors));
} }
finally finally
{ {
......
...@@ -5,6 +5,7 @@ using System; ...@@ -5,6 +5,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using Microsoft.Extensions.Options;
using RabbitMQ.Client; using RabbitMQ.Client;
using RabbitMQ.Client.Events; using RabbitMQ.Client.Events;
...@@ -12,6 +13,8 @@ namespace DotNetCore.CAP.RabbitMQ ...@@ -12,6 +13,8 @@ namespace DotNetCore.CAP.RabbitMQ
{ {
internal sealed class RabbitMQConsumerClient : IConsumerClient internal sealed class RabbitMQConsumerClient : IConsumerClient
{ {
private readonly SemaphoreSlim _connectionLock = new SemaphoreSlim(initialCount: 1, maxCount: 1);
private readonly IConnectionChannelPool _connectionChannelPool; private readonly IConnectionChannelPool _connectionChannelPool;
private readonly string _exchangeName; private readonly string _exchangeName;
private readonly string _queueName; private readonly string _queueName;
...@@ -23,14 +26,12 @@ namespace DotNetCore.CAP.RabbitMQ ...@@ -23,14 +26,12 @@ namespace DotNetCore.CAP.RabbitMQ
public RabbitMQConsumerClient(string queueName, public RabbitMQConsumerClient(string queueName,
IConnectionChannelPool connectionChannelPool, IConnectionChannelPool connectionChannelPool,
RabbitMQOptions options) IOptions<RabbitMQOptions> options)
{ {
_queueName = queueName; _queueName = queueName;
_connectionChannelPool = connectionChannelPool; _connectionChannelPool = connectionChannelPool;
_rabbitMQOptions = options; _rabbitMQOptions = options.Value;
_exchangeName = connectionChannelPool.Exchange; _exchangeName = connectionChannelPool.Exchange;
InitClient();
} }
public event EventHandler<MessageContext> OnMessageReceived; public event EventHandler<MessageContext> OnMessageReceived;
...@@ -46,6 +47,8 @@ namespace DotNetCore.CAP.RabbitMQ ...@@ -46,6 +47,8 @@ namespace DotNetCore.CAP.RabbitMQ
throw new ArgumentNullException(nameof(topics)); throw new ArgumentNullException(nameof(topics));
} }
Connect();
foreach (var topic in topics) foreach (var topic in topics)
{ {
_channel.QueueBind(_queueName, _exchangeName, topic); _channel.QueueBind(_queueName, _exchangeName, topic);
...@@ -54,6 +57,8 @@ namespace DotNetCore.CAP.RabbitMQ ...@@ -54,6 +57,8 @@ namespace DotNetCore.CAP.RabbitMQ
public void Listening(TimeSpan timeout, CancellationToken cancellationToken) public void Listening(TimeSpan timeout, CancellationToken cancellationToken)
{ {
Connect();
var consumer = new EventingBasicConsumer(_channel); var consumer = new EventingBasicConsumer(_channel);
consumer.Received += OnConsumerReceived; consumer.Received += OnConsumerReceived;
consumer.Shutdown += OnConsumerShutdown; consumer.Shutdown += OnConsumerShutdown;
...@@ -88,25 +93,39 @@ namespace DotNetCore.CAP.RabbitMQ ...@@ -88,25 +93,39 @@ namespace DotNetCore.CAP.RabbitMQ
_connection.Dispose(); _connection.Dispose();
} }
private void InitClient() #region events
{
_connection = _connectionChannelPool.GetConnection();
_channel = _connection.CreateModel(); private void Connect()
{
if (_connection != null)
{
return;
}
_channel.ExchangeDeclare( _connectionLock.Wait();
_exchangeName,
RabbitMQOptions.ExchangeType,
true);
var arguments = new Dictionary<string, object> try
{ {
{"x-message-ttl", _rabbitMQOptions.QueueMessageExpires} if (_connection == null)
}; {
_channel.QueueDeclare(_queueName, durable: true, exclusive: false, autoDelete: false, arguments: arguments); _connection = _connectionChannelPool.GetConnection();
}
#region events _channel = _connection.CreateModel();
_channel.ExchangeDeclare(_exchangeName, RabbitMQOptions.ExchangeType, true);
var arguments = new Dictionary<string, object>
{
{"x-message-ttl", _rabbitMQOptions.QueueMessageExpires}
};
_channel.QueueDeclare(_queueName, durable: true, exclusive: false, autoDelete: false, arguments: arguments);
}
}
finally
{
_connectionLock.Release();
}
}
private void OnConsumerConsumerCancelled(object sender, ConsumerEventArgs e) private void OnConsumerConsumerCancelled(object sender, ConsumerEventArgs e)
{ {
......
// Copyright (c) .NET Core Community. All rights reserved. // Copyright (c) .NET Core Community. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information. // Licensed under the MIT License. See License.txt in the project root for license information.
using Microsoft.Extensions.Options;
namespace DotNetCore.CAP.RabbitMQ namespace DotNetCore.CAP.RabbitMQ
{ {
internal sealed class RabbitMQConsumerClientFactory : IConsumerClientFactory internal sealed class RabbitMQConsumerClientFactory : IConsumerClientFactory
{ {
private readonly IConnectionChannelPool _connectionChannelPool; private readonly IConnectionChannelPool _connectionChannelPool;
private readonly RabbitMQOptions _rabbitMQOptions; private readonly IOptions<RabbitMQOptions> _rabbitMQOptions;
public RabbitMQConsumerClientFactory(RabbitMQOptions rabbitMQOptions, IConnectionChannelPool channelPool) public RabbitMQConsumerClientFactory(IOptions<RabbitMQOptions> rabbitMQOptions, IConnectionChannelPool channelPool)
{ {
_rabbitMQOptions = rabbitMQOptions; _rabbitMQOptions = rabbitMQOptions;
_connectionChannelPool = channelPool; _connectionChannelPool = channelPool;
......
...@@ -5,8 +5,8 @@ using System; ...@@ -5,8 +5,8 @@ using System;
using DotNetCore.CAP.Processor; using DotNetCore.CAP.Processor;
using DotNetCore.CAP.SqlServer; using DotNetCore.CAP.SqlServer;
using DotNetCore.CAP.SqlServer.Diagnostics; using DotNetCore.CAP.SqlServer.Diagnostics;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
// ReSharper disable once CheckNamespace // ReSharper disable once CheckNamespace
namespace DotNetCore.CAP namespace DotNetCore.CAP
...@@ -23,42 +23,18 @@ namespace DotNetCore.CAP ...@@ -23,42 +23,18 @@ namespace DotNetCore.CAP
public void AddServices(IServiceCollection services) public void AddServices(IServiceCollection services)
{ {
services.AddSingleton<CapStorageMarkerService>(); services.AddSingleton<CapStorageMarkerService>();
services.AddSingleton<DiagnosticProcessorObserver>(); services.AddSingleton<DiagnosticProcessorObserver>();
services.AddSingleton<IStorage, SqlServerStorage>(); services.AddSingleton<IStorage, SqlServerStorage>();
services.AddSingleton<IStorageConnection, SqlServerStorageConnection>(); services.AddSingleton<IStorageConnection, SqlServerStorageConnection>();
services.AddSingleton<ICapPublisher, SqlServerPublisher>();
services.AddSingleton<ICallbackPublisher>(x => (SqlServerPublisher)x.GetService<ICapPublisher>());
services.AddSingleton<ICollectProcessor, SqlServerCollectProcessor>();
services.AddScoped<ICapPublisher, SqlServerPublisher>();
services.AddScoped<ICallbackPublisher, SqlServerPublisher>();
services.AddTransient<ICollectProcessor, SqlServerCollectProcessor>();
services.AddTransient<CapTransactionBase, SqlServerCapTransaction>(); services.AddTransient<CapTransactionBase, SqlServerCapTransaction>();
AddSqlServerOptions(services); services.Configure(_configure);
} services.AddSingleton<IConfigureOptions<SqlServerOptions>, ConfigureSqlServerOptions>();
private void AddSqlServerOptions(IServiceCollection services)
{
var sqlServerOptions = new SqlServerOptions();
_configure(sqlServerOptions);
if (sqlServerOptions.DbContextType != null)
{
services.AddSingleton(x =>
{
using (var scope = x.CreateScope())
{
var provider = scope.ServiceProvider;
var dbContext = (DbContext) provider.GetService(sqlServerOptions.DbContextType);
sqlServerOptions.ConnectionString = dbContext.Database.GetDbConnection().ConnectionString;
return sqlServerOptions;
}
});
}
else
{
services.AddSingleton(sqlServerOptions);
}
} }
} }
} }
\ No newline at end of file
// Copyright (c) .NET Core Community. All rights reserved. // Copyright (c) .NET Core Community. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information. // Licensed under the MIT License. See License.txt in the project root for license information.
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
// ReSharper disable once CheckNamespace
namespace DotNetCore.CAP namespace DotNetCore.CAP
{ {
public class SqlServerOptions : EFOptions public class SqlServerOptions : EFOptions
...@@ -10,4 +15,30 @@ namespace DotNetCore.CAP ...@@ -10,4 +15,30 @@ namespace DotNetCore.CAP
/// </summary> /// </summary>
public string ConnectionString { get; set; } public string ConnectionString { get; set; }
} }
internal class ConfigureSqlServerOptions : IConfigureOptions<SqlServerOptions>
{
private readonly IServiceScopeFactory _serviceScopeFactory;
public ConfigureSqlServerOptions(IServiceScopeFactory serviceScopeFactory)
{
_serviceScopeFactory = serviceScopeFactory;
}
public void Configure(SqlServerOptions options)
{
if (options.DbContextType != null)
{
using (var scope = _serviceScopeFactory.CreateScope())
{
var provider = scope.ServiceProvider;
using (var dbContext = (DbContext)provider.GetRequiredService(options.DbContextType))
{
options.ConnectionString = dbContext.Database.GetDbConnection().ConnectionString;
}
}
}
}
}
} }
\ No newline at end of file
...@@ -11,6 +11,7 @@ using DotNetCore.CAP.Abstractions; ...@@ -11,6 +11,7 @@ using DotNetCore.CAP.Abstractions;
using DotNetCore.CAP.Models; using DotNetCore.CAP.Models;
using Microsoft.EntityFrameworkCore.Storage; using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
namespace DotNetCore.CAP.SqlServer namespace DotNetCore.CAP.SqlServer
{ {
...@@ -20,7 +21,7 @@ namespace DotNetCore.CAP.SqlServer ...@@ -20,7 +21,7 @@ namespace DotNetCore.CAP.SqlServer
public SqlServerPublisher(IServiceProvider provider) : base(provider) public SqlServerPublisher(IServiceProvider provider) : base(provider)
{ {
_options = ServiceProvider.GetService<SqlServerOptions>(); _options = ServiceProvider.GetService<IOptions<SqlServerOptions>>().Value;
} }
public async Task PublishCallbackAsync(CapPublishedMessage message) public async Task PublishCallbackAsync(CapPublishedMessage message)
......
...@@ -12,6 +12,7 @@ using Microsoft.EntityFrameworkCore; ...@@ -12,6 +12,7 @@ using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage; using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
// ReSharper disable once CheckNamespace // ReSharper disable once CheckNamespace
namespace DotNetCore.CAP namespace DotNetCore.CAP
...@@ -23,9 +24,9 @@ namespace DotNetCore.CAP ...@@ -23,9 +24,9 @@ namespace DotNetCore.CAP
public SqlServerCapTransaction( public SqlServerCapTransaction(
IDispatcher dispatcher, IDispatcher dispatcher,
SqlServerOptions sqlServerOptions,
IServiceProvider serviceProvider) : base(dispatcher) IServiceProvider serviceProvider) : base(dispatcher)
{ {
var sqlServerOptions = serviceProvider.GetService<IOptions<SqlServerOptions>>().Value;
if (sqlServerOptions.DbContextType != null) if (sqlServerOptions.DbContextType != null)
{ {
_dbContext = serviceProvider.GetService(sqlServerOptions.DbContextType) as DbContext; _dbContext = serviceProvider.GetService(sqlServerOptions.DbContextType) as DbContext;
...@@ -56,14 +57,14 @@ namespace DotNetCore.CAP ...@@ -56,14 +57,14 @@ namespace DotNetCore.CAP
} }
} }
var transactionKey = ((SqlConnection) dbTransaction.Connection).ClientConnectionId; var transactionKey = ((SqlConnection)dbTransaction.Connection).ClientConnectionId;
if (_diagnosticProcessor.BufferList.TryGetValue(transactionKey, out var list)) if (_diagnosticProcessor.BufferList.TryGetValue(transactionKey, out var list))
{ {
list.Add(msg); list.Add(msg);
} }
else else
{ {
var msgList = new List<CapPublishedMessage>(1) {msg}; var msgList = new List<CapPublishedMessage>(1) { msg };
_diagnosticProcessor.BufferList.TryAdd(transactionKey, msgList); _diagnosticProcessor.BufferList.TryAdd(transactionKey, msgList);
} }
} }
...@@ -109,6 +110,7 @@ namespace DotNetCore.CAP ...@@ -109,6 +110,7 @@ namespace DotNetCore.CAP
dbContextTransaction.Dispose(); dbContextTransaction.Dispose();
break; break;
} }
DbTransaction = null;
} }
} }
...@@ -149,7 +151,7 @@ namespace DotNetCore.CAP ...@@ -149,7 +151,7 @@ namespace DotNetCore.CAP
var dbTransaction = dbConnection.BeginTransaction(); var dbTransaction = dbConnection.BeginTransaction();
var capTransaction = publisher.Transaction.Begin(dbTransaction, autoCommit); var capTransaction = publisher.Transaction.Begin(dbTransaction, autoCommit);
return (IDbTransaction) capTransaction.DbTransaction; return (IDbTransaction)capTransaction.DbTransaction;
} }
/// <summary> /// <summary>
......
...@@ -7,6 +7,7 @@ using System.Threading.Tasks; ...@@ -7,6 +7,7 @@ using System.Threading.Tasks;
using Dapper; using Dapper;
using DotNetCore.CAP.Processor; using DotNetCore.CAP.Processor;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace DotNetCore.CAP.SqlServer namespace DotNetCore.CAP.SqlServer
{ {
...@@ -25,10 +26,10 @@ namespace DotNetCore.CAP.SqlServer ...@@ -25,10 +26,10 @@ namespace DotNetCore.CAP.SqlServer
private readonly TimeSpan _waitingInterval = TimeSpan.FromMinutes(5); private readonly TimeSpan _waitingInterval = TimeSpan.FromMinutes(5);
public SqlServerCollectProcessor(ILogger<SqlServerCollectProcessor> logger, public SqlServerCollectProcessor(ILogger<SqlServerCollectProcessor> logger,
SqlServerOptions sqlServerOptions) IOptions<SqlServerOptions> sqlServerOptions)
{ {
_logger = logger; _logger = logger;
_options = sqlServerOptions; _options = sqlServerOptions.Value;
} }
public async Task ProcessAsync(ProcessingContext context) public async Task ProcessAsync(ProcessingContext context)
......
...@@ -11,6 +11,7 @@ using Dapper; ...@@ -11,6 +11,7 @@ using Dapper;
using DotNetCore.CAP.Dashboard; using DotNetCore.CAP.Dashboard;
using DotNetCore.CAP.SqlServer.Diagnostics; using DotNetCore.CAP.SqlServer.Diagnostics;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace DotNetCore.CAP.SqlServer namespace DotNetCore.CAP.SqlServer
{ {
...@@ -23,14 +24,14 @@ namespace DotNetCore.CAP.SqlServer ...@@ -23,14 +24,14 @@ namespace DotNetCore.CAP.SqlServer
private readonly SqlServerOptions _options; private readonly SqlServerOptions _options;
public SqlServerStorage(ILogger<SqlServerStorage> logger, public SqlServerStorage(ILogger<SqlServerStorage> logger,
CapOptions capOptions, IOptions<CapOptions> capOptions,
SqlServerOptions options, IOptions<SqlServerOptions> options,
DiagnosticProcessorObserver diagnosticProcessorObserver) DiagnosticProcessorObserver diagnosticProcessorObserver)
{ {
_options = options; _options = options.Value;
_diagnosticProcessorObserver = diagnosticProcessorObserver; _diagnosticProcessorObserver = diagnosticProcessorObserver;
_logger = logger; _logger = logger;
_capOptions = capOptions; _capOptions = capOptions.Value;
} }
public IStorageConnection GetConnection() public IStorageConnection GetConnection()
......
...@@ -15,9 +15,9 @@ namespace DotNetCore.CAP.Abstractions ...@@ -15,9 +15,9 @@ namespace DotNetCore.CAP.Abstractions
{ {
public abstract class CapPublisherBase : ICapPublisher public abstract class CapPublisherBase : ICapPublisher
{ {
private readonly CapTransactionBase _transaction;
private readonly IMessagePacker _msgPacker; private readonly IMessagePacker _msgPacker;
private readonly IContentSerializer _serializer; private readonly IContentSerializer _serializer;
private CapTransactionBase _transaction;
protected bool NotUseTransaction; protected bool NotUseTransaction;
...@@ -28,14 +28,27 @@ namespace DotNetCore.CAP.Abstractions ...@@ -28,14 +28,27 @@ namespace DotNetCore.CAP.Abstractions
protected CapPublisherBase(IServiceProvider service) protected CapPublisherBase(IServiceProvider service)
{ {
ServiceProvider = service; ServiceProvider = service;
_transaction = service.GetRequiredService<CapTransactionBase>();
_msgPacker = service.GetRequiredService<IMessagePacker>(); _msgPacker = service.GetRequiredService<IMessagePacker>();
_serializer = service.GetRequiredService<IContentSerializer>(); _serializer = service.GetRequiredService<IContentSerializer>();
} }
protected IServiceProvider ServiceProvider { get; } protected IServiceProvider ServiceProvider { get; }
public ICapTransaction Transaction => _transaction; public ICapTransaction Transaction
{
get
{
if (_transaction == null)
{
using (var scope = ServiceProvider.CreateScope())
{
_transaction = scope.ServiceProvider.GetRequiredService<CapTransactionBase>();
}
}
return _transaction;
}
}
public void Publish<T>(string name, T contentObj, string callbackName = null) public void Publish<T>(string name, T contentObj, string callbackName = null)
{ {
...@@ -99,7 +112,7 @@ namespace DotNetCore.CAP.Abstractions ...@@ -99,7 +112,7 @@ namespace DotNetCore.CAP.Abstractions
{ {
if (NotUseTransaction || Transaction.AutoCommit) if (NotUseTransaction || Transaction.AutoCommit)
{ {
_transaction.Dispose(); _transaction?.Dispose();
} }
} }
} }
......
...@@ -2,7 +2,6 @@ ...@@ -2,7 +2,6 @@
// Licensed under the MIT License. See License.txt in the project root for license information. // Licensed under the MIT License. See License.txt in the project root for license information.
using System; using System;
using System.Collections.Generic;
using DotNetCore.CAP; using DotNetCore.CAP;
using DotNetCore.CAP.Abstractions; using DotNetCore.CAP.Abstractions;
using DotNetCore.CAP.Internal; using DotNetCore.CAP.Internal;
...@@ -11,7 +10,6 @@ using DotNetCore.CAP.Processor.States; ...@@ -11,7 +10,6 @@ using DotNetCore.CAP.Processor.States;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
// ReSharper disable once CheckNamespace // ReSharper disable once CheckNamespace
namespace Microsoft.Extensions.DependencyInjection namespace Microsoft.Extensions.DependencyInjection
...@@ -58,7 +56,7 @@ namespace Microsoft.Extensions.DependencyInjection ...@@ -58,7 +56,7 @@ namespace Microsoft.Extensions.DependencyInjection
services.TryAddSingleton<IStateChanger, StateChanger>(); services.TryAddSingleton<IStateChanger, StateChanger>();
//Queue's message processor //Queue's message processor
services.TryAddSingleton<NeedRetryMessageProcessor>(); services.TryAddSingleton<MessageNeedToRetryProcessor>();
services.TryAddSingleton<TransportCheckProcessor>(); services.TryAddSingleton<TransportCheckProcessor>();
//Sender and Executors //Sender and Executors
...@@ -73,11 +71,11 @@ namespace Microsoft.Extensions.DependencyInjection ...@@ -73,11 +71,11 @@ namespace Microsoft.Extensions.DependencyInjection
{ {
serviceExtension.AddServices(services); serviceExtension.AddServices(services);
} }
services.AddSingleton(options); services.Configure(setupAction);
//Startup and Middleware //Startup and Hosted
services.AddTransient<IHostedService, DefaultBootstrapper>();
services.AddTransient<IStartupFilter, CapStartupFilter>(); services.AddTransient<IStartupFilter, CapStartupFilter>();
services.AddHostedService<DefaultBootstrapper>();
return new CapBuilder(services); return new CapBuilder(services);
} }
......
...@@ -33,11 +33,9 @@ ...@@ -33,11 +33,9 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Consul" Version="0.7.2.6" /> <PackageReference Include="Consul" Version="0.7.2.6" />
<PackageReference Include="Microsoft.AspNetCore.Hosting.Abstractions" Version="2.2.0" /> <PackageReference Include="Microsoft.AspNetCore.Hosting.Abstractions" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="2.2.0" /> <PackageReference Include="Microsoft.Extensions.Options" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="2.2.0" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" /> <PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
<PackageReference Include="System.Diagnostics.DiagnosticSource" Version="4.5.1" /> <PackageReference Include="System.Diagnostics.DiagnosticSource" Version="4.5.1" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.2.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
......
// Copyright (c) .NET Core Community. All rights reserved. // Copyright (c) .NET Core Community. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information. // Licensed under the MIT License. See License.txt in the project root for license information.
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using DotNetCore.CAP.Abstractions; using DotNetCore.CAP.Abstractions;
using DotNetCore.CAP.Infrastructure; using DotNetCore.CAP.Infrastructure;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using Microsoft.Extensions.Options;
namespace DotNetCore.CAP
{ namespace DotNetCore.CAP
/// <inheritdoc /> {
/// <summary> /// <inheritdoc />
/// A default <see cref="T:DotNetCore.CAP.Abstractions.IConsumerServiceSelector" /> implementation. /// <summary>
/// </summary> /// A default <see cref="T:DotNetCore.CAP.Abstractions.IConsumerServiceSelector" /> implementation.
public class DefaultConsumerServiceSelector : IConsumerServiceSelector /// </summary>
{ public class DefaultConsumerServiceSelector : IConsumerServiceSelector
private readonly CapOptions _capOptions; {
private readonly IServiceProvider _serviceProvider; private readonly CapOptions _capOptions;
private readonly IServiceProvider _serviceProvider;
/// <summary>
/// since this class be designed as a Singleton service,the following two list must be thread safe! /// <summary>
/// </summary> /// since this class be designed as a Singleton service,the following two list must be thread safe!
private readonly ConcurrentDictionary<string, List<RegexExecuteDescriptor<ConsumerExecutorDescriptor>>> _asteriskList; /// </summary>
private readonly ConcurrentDictionary<string, List<RegexExecuteDescriptor<ConsumerExecutorDescriptor>>> _poundList; private readonly ConcurrentDictionary<string, List<RegexExecuteDescriptor<ConsumerExecutorDescriptor>>> _asteriskList;
private readonly ConcurrentDictionary<string, List<RegexExecuteDescriptor<ConsumerExecutorDescriptor>>> _poundList;
/// <summary>
/// Creates a new <see cref="DefaultConsumerServiceSelector" />. /// <summary>
/// </summary> /// Creates a new <see cref="DefaultConsumerServiceSelector" />.
public DefaultConsumerServiceSelector(IServiceProvider serviceProvider, CapOptions capOptions) /// </summary>
{ public DefaultConsumerServiceSelector(IServiceProvider serviceProvider)
_serviceProvider = serviceProvider; {
_capOptions = capOptions; _serviceProvider = serviceProvider;
_capOptions = serviceProvider.GetService<IOptions<CapOptions>>().Value;
_asteriskList = new ConcurrentDictionary<string, List<RegexExecuteDescriptor<ConsumerExecutorDescriptor>>>();
_poundList = new ConcurrentDictionary<string, List<RegexExecuteDescriptor<ConsumerExecutorDescriptor>>>(); _asteriskList = new ConcurrentDictionary<string, List<RegexExecuteDescriptor<ConsumerExecutorDescriptor>>>();
} _poundList = new ConcurrentDictionary<string, List<RegexExecuteDescriptor<ConsumerExecutorDescriptor>>>();
}
public IReadOnlyList<ConsumerExecutorDescriptor> SelectCandidates()
{ public IReadOnlyList<ConsumerExecutorDescriptor> SelectCandidates()
var executorDescriptorList = new List<ConsumerExecutorDescriptor>(); {
var executorDescriptorList = new List<ConsumerExecutorDescriptor>();
executorDescriptorList.AddRange(FindConsumersFromInterfaceTypes(_serviceProvider));
executorDescriptorList.AddRange(FindConsumersFromInterfaceTypes(_serviceProvider));
executorDescriptorList.AddRange(FindConsumersFromControllerTypes());
executorDescriptorList.AddRange(FindConsumersFromControllerTypes());
return executorDescriptorList;
} return executorDescriptorList;
}
public ConsumerExecutorDescriptor SelectBestCandidate(string key, IReadOnlyList<ConsumerExecutorDescriptor> executeDescriptor)
{ public ConsumerExecutorDescriptor SelectBestCandidate(string key, IReadOnlyList<ConsumerExecutorDescriptor> executeDescriptor)
var result = MatchUsingName(key, executeDescriptor); {
if (result != null) var result = MatchUsingName(key, executeDescriptor);
{ if (result != null)
return result; {
} return result;
}
//[*] match with regex, i.e. foo.*.abc
result = MatchAsteriskUsingRegex(key, executeDescriptor); //[*] match with regex, i.e. foo.*.abc
if (result != null) result = MatchAsteriskUsingRegex(key, executeDescriptor);
{ if (result != null)
return result; {
} return result;
}
//[#] match regex, i.e. foo.#
result = MatchPoundUsingRegex(key, executeDescriptor); //[#] match regex, i.e. foo.#
return result; result = MatchPoundUsingRegex(key, executeDescriptor);
} return result;
}
protected virtual IEnumerable<ConsumerExecutorDescriptor> FindConsumersFromInterfaceTypes(
IServiceProvider provider) protected virtual IEnumerable<ConsumerExecutorDescriptor> FindConsumersFromInterfaceTypes(
{ IServiceProvider provider)
var executorDescriptorList = new List<ConsumerExecutorDescriptor>(); {
var executorDescriptorList = new List<ConsumerExecutorDescriptor>();
var capSubscribeTypeInfo = typeof(ICapSubscribe).GetTypeInfo();
var capSubscribeTypeInfo = typeof(ICapSubscribe).GetTypeInfo();
foreach (var service in ServiceCollectionExtensions.ServiceCollection.Where(o => o.ImplementationType != null && o.ServiceType != null)) foreach (var service in ServiceCollectionExtensions.ServiceCollection.Where(o => o.ImplementationType != null && o.ServiceType != null))
{ {
var typeInfo = service.ImplementationType.GetTypeInfo(); var typeInfo = service.ImplementationType.GetTypeInfo();
if (!capSubscribeTypeInfo.IsAssignableFrom(typeInfo)) if (!capSubscribeTypeInfo.IsAssignableFrom(typeInfo))
{ {
continue; continue;
} }
var serviceTypeInfo = service.ServiceType.GetTypeInfo(); var serviceTypeInfo = service.ServiceType.GetTypeInfo();
executorDescriptorList.AddRange(GetTopicAttributesDescription(typeInfo, serviceTypeInfo)); executorDescriptorList.AddRange(GetTopicAttributesDescription(typeInfo, serviceTypeInfo));
} }
return executorDescriptorList; return executorDescriptorList;
} }
protected virtual IEnumerable<ConsumerExecutorDescriptor> FindConsumersFromControllerTypes() protected virtual IEnumerable<ConsumerExecutorDescriptor> FindConsumersFromControllerTypes()
{ {
var executorDescriptorList = new List<ConsumerExecutorDescriptor>(); var executorDescriptorList = new List<ConsumerExecutorDescriptor>();
var types = Assembly.GetEntryAssembly().ExportedTypes; var types = Assembly.GetEntryAssembly().ExportedTypes;
foreach (var type in types) foreach (var type in types)
{ {
var typeInfo = type.GetTypeInfo(); var typeInfo = type.GetTypeInfo();
if (Helper.IsController(typeInfo)) if (Helper.IsController(typeInfo))
{ {
executorDescriptorList.AddRange(GetTopicAttributesDescription(typeInfo)); executorDescriptorList.AddRange(GetTopicAttributesDescription(typeInfo));
} }
} }
return executorDescriptorList; return executorDescriptorList;
} }
protected IEnumerable<ConsumerExecutorDescriptor> GetTopicAttributesDescription(TypeInfo typeInfo, TypeInfo serviceTypeInfo = null) protected IEnumerable<ConsumerExecutorDescriptor> GetTopicAttributesDescription(TypeInfo typeInfo, TypeInfo serviceTypeInfo = null)
{ {
foreach (var method in typeInfo.DeclaredMethods) foreach (var method in typeInfo.DeclaredMethods)
{ {
var topicAttr = method.GetCustomAttributes<TopicAttribute>(true); var topicAttr = method.GetCustomAttributes<TopicAttribute>(true);
var topicAttributes = topicAttr as IList<TopicAttribute> ?? topicAttr.ToList(); var topicAttributes = topicAttr as IList<TopicAttribute> ?? topicAttr.ToList();
if (!topicAttributes.Any()) if (!topicAttributes.Any())
{ {
continue; continue;
} }
foreach (var attr in topicAttributes) foreach (var attr in topicAttributes)
{ {
if (attr.Group == null) if (attr.Group == null)
{ {
attr.Group = _capOptions.DefaultGroup + "." + _capOptions.Version; attr.Group = _capOptions.DefaultGroup + "." + _capOptions.Version;
} }
else else
{ {
attr.Group = attr.Group + "." + _capOptions.Version; attr.Group = attr.Group + "." + _capOptions.Version;
} }
yield return InitDescriptor(attr, method, typeInfo, serviceTypeInfo); yield return InitDescriptor(attr, method, typeInfo, serviceTypeInfo);
} }
} }
} }
private static ConsumerExecutorDescriptor InitDescriptor( private static ConsumerExecutorDescriptor InitDescriptor(
TopicAttribute attr, TopicAttribute attr,
MethodInfo methodInfo, MethodInfo methodInfo,
TypeInfo implType, TypeInfo implType,
TypeInfo serviceTypeInfo) TypeInfo serviceTypeInfo)
{ {
var descriptor = new ConsumerExecutorDescriptor var descriptor = new ConsumerExecutorDescriptor
{ {
Attribute = attr, Attribute = attr,
MethodInfo = methodInfo, MethodInfo = methodInfo,
ImplTypeInfo = implType, ImplTypeInfo = implType,
ServiceTypeInfo = serviceTypeInfo ServiceTypeInfo = serviceTypeInfo
}; };
return descriptor; return descriptor;
} }
private ConsumerExecutorDescriptor MatchUsingName(string key, IReadOnlyList<ConsumerExecutorDescriptor> executeDescriptor) private ConsumerExecutorDescriptor MatchUsingName(string key, IReadOnlyList<ConsumerExecutorDescriptor> executeDescriptor)
{ {
return executeDescriptor.FirstOrDefault(x => x.Attribute.Name == key); return executeDescriptor.FirstOrDefault(x => x.Attribute.Name == key);
} }
private ConsumerExecutorDescriptor MatchAsteriskUsingRegex(string key, IReadOnlyList<ConsumerExecutorDescriptor> executeDescriptor) private ConsumerExecutorDescriptor MatchAsteriskUsingRegex(string key, IReadOnlyList<ConsumerExecutorDescriptor> executeDescriptor)
{ {
var group = executeDescriptor.First().Attribute.Group; var group = executeDescriptor.First().Attribute.Group;
if (!_asteriskList.TryGetValue(group, out var tmpList)) if (!_asteriskList.TryGetValue(group, out var tmpList))
{ {
tmpList = executeDescriptor.Where(x => x.Attribute.Name.IndexOf('*') >= 0) tmpList = executeDescriptor.Where(x => x.Attribute.Name.IndexOf('*') >= 0)
.Select(x => new RegexExecuteDescriptor<ConsumerExecutorDescriptor> .Select(x => new RegexExecuteDescriptor<ConsumerExecutorDescriptor>
{ {
Name = ("^" + x.Attribute.Name + "$").Replace("*", "[0-9_a-zA-Z]+").Replace(".", "\\."), Name = ("^" + x.Attribute.Name + "$").Replace("*", "[0-9_a-zA-Z]+").Replace(".", "\\."),
Descriptor = x Descriptor = x
}).ToList(); }).ToList();
_asteriskList.TryAdd(group, tmpList); _asteriskList.TryAdd(group, tmpList);
} }
foreach (var red in tmpList) foreach (var red in tmpList)
{ {
if (Regex.IsMatch(key, red.Name, RegexOptions.Singleline)) if (Regex.IsMatch(key, red.Name, RegexOptions.Singleline))
{ {
return red.Descriptor; return red.Descriptor;
} }
} }
return null; return null;
} }
private ConsumerExecutorDescriptor MatchPoundUsingRegex(string key, IReadOnlyList<ConsumerExecutorDescriptor> executeDescriptor) private ConsumerExecutorDescriptor MatchPoundUsingRegex(string key, IReadOnlyList<ConsumerExecutorDescriptor> executeDescriptor)
{ {
var group = executeDescriptor.First().Attribute.Group; var group = executeDescriptor.First().Attribute.Group;
if (!_poundList.TryGetValue(group, out var tmpList)) if (!_poundList.TryGetValue(group, out var tmpList))
{ {
tmpList = executeDescriptor tmpList = executeDescriptor
.Where(x => x.Attribute.Name.IndexOf('#') >= 0) .Where(x => x.Attribute.Name.IndexOf('#') >= 0)
.Select(x => new RegexExecuteDescriptor<ConsumerExecutorDescriptor> .Select(x => new RegexExecuteDescriptor<ConsumerExecutorDescriptor>
{ {
Name = ("^" + x.Attribute.Name + "$").Replace("#", "[0-9_a-zA-Z\\.]+"), Name = ("^" + x.Attribute.Name + "$").Replace("#", "[0-9_a-zA-Z\\.]+"),
Descriptor = x Descriptor = x
}).ToList(); }).ToList();
_poundList.TryAdd(group, tmpList); _poundList.TryAdd(group, tmpList);
} }
foreach (var red in tmpList) foreach (var red in tmpList)
{ {
if (Regex.IsMatch(key, red.Name, RegexOptions.Singleline)) if (Regex.IsMatch(key, red.Name, RegexOptions.Singleline))
{ {
return red.Descriptor; return red.Descriptor;
} }
} }
return null; return null;
} }
private class RegexExecuteDescriptor<T> private class RegexExecuteDescriptor<T>
{ {
public string Name { get; set; } public string Name { get; set; }
public T Descriptor { get; set; } public T Descriptor { get; set; }
} }
} }
} }
\ No newline at end of file
...@@ -11,6 +11,7 @@ using DotNetCore.CAP.Models; ...@@ -11,6 +11,7 @@ using DotNetCore.CAP.Models;
using DotNetCore.CAP.Processor; using DotNetCore.CAP.Processor;
using DotNetCore.CAP.Processor.States; using DotNetCore.CAP.Processor.States;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace DotNetCore.CAP namespace DotNetCore.CAP
{ {
...@@ -21,7 +22,7 @@ namespace DotNetCore.CAP ...@@ -21,7 +22,7 @@ namespace DotNetCore.CAP
private readonly CapOptions _options; private readonly CapOptions _options;
private readonly IStateChanger _stateChanger; private readonly IStateChanger _stateChanger;
protected string ServersAddress { get; set; } protected abstract string ServersAddress { get; }
// diagnostics listener // diagnostics listener
// ReSharper disable once InconsistentNaming // ReSharper disable once InconsistentNaming
...@@ -30,11 +31,11 @@ namespace DotNetCore.CAP ...@@ -30,11 +31,11 @@ namespace DotNetCore.CAP
protected BasePublishMessageSender( protected BasePublishMessageSender(
ILogger logger, ILogger logger,
CapOptions options, IOptions<CapOptions> options,
IStorageConnection connection, IStorageConnection connection,
IStateChanger stateChanger) IStateChanger stateChanger)
{ {
_options = options; _options = options.Value;
_connection = connection; _connection = connection;
_stateChanger = stateChanger; _stateChanger = stateChanger;
_logger = logger; _logger = logger;
......
...@@ -11,6 +11,7 @@ using DotNetCore.CAP.Models; ...@@ -11,6 +11,7 @@ using DotNetCore.CAP.Models;
using DotNetCore.CAP.Processor; using DotNetCore.CAP.Processor;
using DotNetCore.CAP.Processor.States; using DotNetCore.CAP.Processor.States;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace DotNetCore.CAP namespace DotNetCore.CAP
{ {
...@@ -30,7 +31,7 @@ namespace DotNetCore.CAP ...@@ -30,7 +31,7 @@ namespace DotNetCore.CAP
public DefaultSubscriberExecutor( public DefaultSubscriberExecutor(
ILogger<DefaultSubscriberExecutor> logger, ILogger<DefaultSubscriberExecutor> logger,
CapOptions options, IOptions<CapOptions> options,
IConsumerInvokerFactory consumerInvokerFactory, IConsumerInvokerFactory consumerInvokerFactory,
ICallbackMessageSender callbackMessageSender, ICallbackMessageSender callbackMessageSender,
IStateChanger stateChanger, IStateChanger stateChanger,
...@@ -39,7 +40,7 @@ namespace DotNetCore.CAP ...@@ -39,7 +40,7 @@ namespace DotNetCore.CAP
{ {
_selector = selector; _selector = selector;
_callbackMessageSender = callbackMessageSender; _callbackMessageSender = callbackMessageSender;
_options = options; _options = options.Value;
_stateChanger = stateChanger; _stateChanger = stateChanger;
_connection = connection; _connection = connection;
_logger = logger; _logger = logger;
......
...@@ -94,7 +94,7 @@ namespace DotNetCore.CAP.Processor ...@@ -94,7 +94,7 @@ namespace DotNetCore.CAP.Processor
var returnedProcessors = new List<IProcessor> var returnedProcessors = new List<IProcessor>
{ {
_provider.GetRequiredService<TransportCheckProcessor>(), _provider.GetRequiredService<TransportCheckProcessor>(),
_provider.GetRequiredService<NeedRetryMessageProcessor>(), _provider.GetRequiredService<MessageNeedToRetryProcessor>(),
_provider.GetRequiredService<ICollectProcessor>() _provider.GetRequiredService<ICollectProcessor>()
}; };
......
...@@ -7,27 +7,28 @@ using System.Linq; ...@@ -7,27 +7,28 @@ using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace DotNetCore.CAP.Processor namespace DotNetCore.CAP.Processor
{ {
public class NeedRetryMessageProcessor : IProcessor public class MessageNeedToRetryProcessor : IProcessor
{ {
private readonly TimeSpan _delay = TimeSpan.FromSeconds(1); private readonly TimeSpan _delay = TimeSpan.FromSeconds(1);
private readonly ILogger<NeedRetryMessageProcessor> _logger; private readonly ILogger<MessageNeedToRetryProcessor> _logger;
private readonly IPublishMessageSender _publishMessageSender; private readonly IPublishMessageSender _publishMessageSender;
private readonly ISubscriberExecutor _subscriberExecutor; private readonly ISubscriberExecutor _subscriberExecutor;
private readonly TimeSpan _waitingInterval; private readonly TimeSpan _waitingInterval;
public NeedRetryMessageProcessor( public MessageNeedToRetryProcessor(
CapOptions options, IOptions<CapOptions> options,
ILogger<NeedRetryMessageProcessor> logger, ILogger<MessageNeedToRetryProcessor> logger,
ISubscriberExecutor subscriberExecutor, ISubscriberExecutor subscriberExecutor,
IPublishMessageSender publishMessageSender) IPublishMessageSender publishMessageSender)
{ {
_logger = logger; _logger = logger;
_subscriberExecutor = subscriberExecutor; _subscriberExecutor = subscriberExecutor;
_publishMessageSender = publishMessageSender; _publishMessageSender = publishMessageSender;
_waitingInterval = TimeSpan.FromSeconds(options.FailedRetryInterval); _waitingInterval = TimeSpan.FromSeconds(options.Value.FailedRetryInterval);
} }
public async Task ProcessAsync(ProcessingContext context) public async Task ProcessAsync(ProcessingContext context)
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
using System; using System;
namespace DotNetCore.CAP.Internal namespace DotNetCore.CAP
{ {
public class SubscriberNotFoundException : Exception public class SubscriberNotFoundException : Exception
{ {
......
using Castle.DynamicProxy;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using System;
using Xunit;
namespace DotNetCore.CAP.CastleDynamicProxyTest
{
public class ConsumerServiceSelectorTest
{
private IServiceProvider _provider;
public ConsumerServiceSelectorTest()
{
var services = new ServiceCollection();
services.AddSingleton(typeof(ICapSubscribe), f =>
{
var generator = new ProxyGenerator();
return generator.CreateClassProxy(typeof(TestSubscribeClass));
});
services.AddSingleton<ITestSubscribeClass, TestSubscribeClass>();
services.AddLogging();
services.TryAddSingleton<IConsumerServiceSelector, CastleCoreConsumerServiceSelector>();
services.AddCap(x => { });
_provider = services.BuildServiceProvider();
}
[Theory]
[InlineData("cap.castle.sub")]
public void CanFindCapSubscribeTopic(string topic)
{
var selector = _provider.GetRequiredService<IConsumerServiceSelector>();
var candidates = selector.SelectCandidates();
Assert.Equal(1, candidates.Count);
}
}
public interface ITestSubscribeClass
{
}
public class TestSubscribeClass : ITestSubscribeClass, ICapSubscribe
{
[CapSubscribe("cap.castle.sub")]
public void TestSubscribe(DateTime dateTime)
{
Console.WriteLine(dateTime);
}
}
}
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.2</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Castle.Core" Version="4.4.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.2.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.0.1" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\DotNetCore.CAP\DotNetCore.CAP.csproj" />
</ItemGroup>
</Project>
using Castle.Core;
using Castle.DynamicProxy;
using DotNetCore.CAP.Infrastructure;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Reflection;
namespace DotNetCore.CAP.CastleDynamicProxyTest
{
public class CastleCoreConsumerServiceSelector : DefaultConsumerServiceSelector
{
public CastleCoreConsumerServiceSelector(IServiceProvider serviceProvider, CapOptions capOptions)
: base(serviceProvider, capOptions)
{
}
protected override IEnumerable<ConsumerExecutorDescriptor> FindConsumersFromInterfaceTypes(IServiceProvider provider)
{
var executorDescriptorList = new List<ConsumerExecutorDescriptor>();
using (var scoped = provider.CreateScope())
{
var scopedProvider = scoped.ServiceProvider;
var consumerServices = scopedProvider.GetServices<ICapSubscribe>();
foreach (var service in consumerServices)
{
var serviceType = service.GetType();
// Castle dynamic proxy...
TypeInfo typeInfo = ProxyServices.IsDynamicProxy(serviceType) ? ProxyUtil.GetUnproxiedType(service).GetTypeInfo()
: serviceType.GetTypeInfo();
if (!typeof(ICapSubscribe).GetTypeInfo().IsAssignableFrom(typeInfo))
{
continue;
}
executorDescriptorList.AddRange(GetTopicAttributesDescription(typeInfo));
}
return executorDescriptorList;
}
}
}
}
using System; using System;
using System.Threading; using System.Threading;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using MongoDB.Driver; using MongoDB.Driver;
namespace DotNetCore.CAP.MongoDB.Test namespace DotNetCore.CAP.MongoDB.Test
...@@ -11,9 +12,9 @@ namespace DotNetCore.CAP.MongoDB.Test ...@@ -11,9 +12,9 @@ namespace DotNetCore.CAP.MongoDB.Test
protected IServiceProvider Provider { get; private set; } protected IServiceProvider Provider { get; private set; }
protected IMongoClient MongoClient => Provider.GetService<IMongoClient>(); protected IMongoClient MongoClient => Provider.GetService<IMongoClient>();
protected IMongoDatabase Database => MongoClient.GetDatabase(MongoDBOptions.DatabaseName); protected IMongoDatabase Database => MongoClient.GetDatabase(MongoDBOptions.Value.DatabaseName);
protected CapOptions CapOptions => Provider.GetService<CapOptions>(); protected CapOptions CapOptions => Provider.GetService<IOptions<CapOptions>>().Value;
protected MongoDBOptions MongoDBOptions => Provider.GetService<MongoDBOptions>(); protected IOptions<MongoDBOptions> MongoDBOptions => Provider.GetService<IOptions<MongoDBOptions>>();
protected DatabaseTestHost() protected DatabaseTestHost()
{ {
...@@ -37,9 +38,9 @@ namespace DotNetCore.CAP.MongoDB.Test ...@@ -37,9 +38,9 @@ namespace DotNetCore.CAP.MongoDB.Test
services.AddOptions(); services.AddOptions();
services.AddLogging(); services.AddLogging();
_connectionString = ConnectionUtil.ConnectionString; _connectionString = ConnectionUtil.ConnectionString;
services.AddOptions<CapOptions>();
services.AddSingleton(new MongoDBOptions() { DatabaseConnection = _connectionString }); services.Configure<MongoDBOptions>(x => x.DatabaseConnection = _connectionString);
services.AddSingleton(new CapOptions());
services.AddSingleton<IMongoClient>(x => new MongoClient(_connectionString)); services.AddSingleton<IMongoClient>(x => new MongoClient(_connectionString));
services.AddSingleton<MongoDBStorage>(); services.AddSingleton<MongoDBStorage>();
...@@ -49,7 +50,7 @@ namespace DotNetCore.CAP.MongoDB.Test ...@@ -49,7 +50,7 @@ namespace DotNetCore.CAP.MongoDB.Test
public void Dispose() public void Dispose()
{ {
MongoClient.DropDatabase(MongoDBOptions.DatabaseName); MongoClient.DropDatabase(MongoDBOptions.Value.DatabaseName);
} }
} }
} }
\ No newline at end of file
...@@ -17,7 +17,7 @@ namespace DotNetCore.CAP.MongoDB.Test ...@@ -17,7 +17,7 @@ namespace DotNetCore.CAP.MongoDB.Test
{ {
_api = new MongoDBMonitoringApi(MongoClient, MongoDBOptions); _api = new MongoDBMonitoringApi(MongoClient, MongoDBOptions);
var collection = Database.GetCollection<PublishedMessage>(MongoDBOptions.PublishedCollection); var collection = Database.GetCollection<PublishedMessage>(MongoDBOptions.Value.PublishedCollection);
collection.InsertMany(new[] collection.InsertMany(new[]
{ {
new PublishedMessage new PublishedMessage
......
...@@ -38,7 +38,7 @@ namespace DotNetCore.CAP.MongoDB.Test ...@@ -38,7 +38,7 @@ namespace DotNetCore.CAP.MongoDB.Test
public void ChangeReceivedState_Test() public void ChangeReceivedState_Test()
{ {
StoreReceivedMessageAsync_TestAsync(); StoreReceivedMessageAsync_TestAsync();
var collection = Database.GetCollection<ReceivedMessage>(MongoDBOptions.ReceivedCollection); var collection = Database.GetCollection<ReceivedMessage>(MongoDBOptions.Value.ReceivedCollection);
var msg = collection.Find(x => true).FirstOrDefault(); var msg = collection.Find(x => true).FirstOrDefault();
_connection.ChangeReceivedState(msg.Id, StatusName.Scheduled).Should().BeTrue(); _connection.ChangeReceivedState(msg.Id, StatusName.Scheduled).Should().BeTrue();
...@@ -64,7 +64,7 @@ namespace DotNetCore.CAP.MongoDB.Test ...@@ -64,7 +64,7 @@ namespace DotNetCore.CAP.MongoDB.Test
}; };
_connection.StoreReceivedMessage(msg); _connection.StoreReceivedMessage(msg);
var collection = Database.GetCollection<ReceivedMessage>(MongoDBOptions.ReceivedCollection); var collection = Database.GetCollection<ReceivedMessage>(MongoDBOptions.Value.ReceivedCollection);
var updateDef = Builders<ReceivedMessage> var updateDef = Builders<ReceivedMessage>
.Update.Set(x => x.Added, DateTime.Now.AddMinutes(-5)); .Update.Set(x => x.Added, DateTime.Now.AddMinutes(-5));
......
...@@ -11,11 +11,11 @@ namespace DotNetCore.CAP.MongoDB.Test ...@@ -11,11 +11,11 @@ namespace DotNetCore.CAP.MongoDB.Test
public void InitializeAsync_Test() public void InitializeAsync_Test()
{ {
var names = MongoClient.ListDatabaseNames()?.ToList(); var names = MongoClient.ListDatabaseNames()?.ToList();
names.Should().Contain(MongoDBOptions.DatabaseName); names.Should().Contain(MongoDBOptions.Value.DatabaseName);
var collections = Database.ListCollectionNames()?.ToList(); var collections = Database.ListCollectionNames()?.ToList();
collections.Should().Contain(MongoDBOptions.PublishedCollection); collections.Should().Contain(MongoDBOptions.Value.PublishedCollection);
collections.Should().Contain(MongoDBOptions.ReceivedCollection); collections.Should().Contain(MongoDBOptions.Value.ReceivedCollection);
} }
} }
} }
\ No newline at end of file
...@@ -3,6 +3,7 @@ using System.Threading.Tasks; ...@@ -3,6 +3,7 @@ using System.Threading.Tasks;
using Dapper; using Dapper;
using DotNetCore.CAP.Infrastructure; using DotNetCore.CAP.Infrastructure;
using DotNetCore.CAP.Models; using DotNetCore.CAP.Models;
using Microsoft.Extensions.Options;
using Xunit; using Xunit;
namespace DotNetCore.CAP.MySql.Test namespace DotNetCore.CAP.MySql.Test
...@@ -14,8 +15,8 @@ namespace DotNetCore.CAP.MySql.Test ...@@ -14,8 +15,8 @@ namespace DotNetCore.CAP.MySql.Test
public MySqlStorageConnectionTest() public MySqlStorageConnectionTest()
{ {
var options = GetService<MySqlOptions>(); var options = GetService<IOptions<MySqlOptions>>();
var capOptions = GetService<CapOptions>(); var capOptions = GetService<IOptions<CapOptions>>();
_storage = new MySqlStorageConnection(options, capOptions); _storage = new MySqlStorageConnection(options, capOptions);
} }
......
...@@ -28,8 +28,9 @@ namespace DotNetCore.CAP.MySql.Test ...@@ -28,8 +28,9 @@ namespace DotNetCore.CAP.MySql.Test
services.AddLogging(); services.AddLogging();
_connectionString = ConnectionUtil.GetConnectionString(); _connectionString = ConnectionUtil.GetConnectionString();
services.AddSingleton(new MySqlOptions { ConnectionString = _connectionString }); services.AddOptions<CapOptions>();
services.AddSingleton(new CapOptions()); services.Configure<MySqlOptions>(x => x.ConnectionString = _connectionString);
services.AddSingleton<MySqlStorage>(); services.AddSingleton<MySqlStorage>();
_services = services; _services = services;
......
...@@ -3,6 +3,7 @@ using System.Threading.Tasks; ...@@ -3,6 +3,7 @@ using System.Threading.Tasks;
using Dapper; using Dapper;
using DotNetCore.CAP.Infrastructure; using DotNetCore.CAP.Infrastructure;
using DotNetCore.CAP.Models; using DotNetCore.CAP.Models;
using Microsoft.Extensions.Options;
using Xunit; using Xunit;
namespace DotNetCore.CAP.PostgreSql.Test namespace DotNetCore.CAP.PostgreSql.Test
...@@ -14,9 +15,9 @@ namespace DotNetCore.CAP.PostgreSql.Test ...@@ -14,9 +15,9 @@ namespace DotNetCore.CAP.PostgreSql.Test
public PostgreSqlStorageConnectionTest() public PostgreSqlStorageConnectionTest()
{ {
var options = GetService<PostgreSqlOptions>(); var options = GetService<IOptions<PostgreSqlOptions>>();
var capOptions = GetService<CapOptions>(); var capOptions = GetService<IOptions<CapOptions>>();
_storage = new PostgreSqlStorageConnection(options,capOptions); _storage = new PostgreSqlStorageConnection(options, capOptions);
} }
[Fact] [Fact]
...@@ -74,13 +75,13 @@ namespace DotNetCore.CAP.PostgreSql.Test ...@@ -74,13 +75,13 @@ namespace DotNetCore.CAP.PostgreSql.Test
var insertedId = SnowflakeId.Default().NextId(); var insertedId = SnowflakeId.Default().NextId();
var receivedMessage = new CapReceivedMessage var receivedMessage = new CapReceivedMessage
{ {
Id= insertedId, Id = insertedId,
Name = "PostgreSqlStorageConnectionTest", Name = "PostgreSqlStorageConnectionTest",
Content = "", Content = "",
Group = "mygroup", Group = "mygroup",
StatusName = StatusName.Scheduled StatusName = StatusName.Scheduled
}; };
using (var connection = ConnectionUtil.CreateConnection()) using (var connection = ConnectionUtil.CreateConnection())
{ {
await connection.ExecuteAsync(sql, receivedMessage); await connection.ExecuteAsync(sql, receivedMessage);
......
...@@ -28,8 +28,9 @@ namespace DotNetCore.CAP.PostgreSql.Test ...@@ -28,8 +28,9 @@ namespace DotNetCore.CAP.PostgreSql.Test
services.AddLogging(); services.AddLogging();
_connectionString = ConnectionUtil.GetConnectionString(); _connectionString = ConnectionUtil.GetConnectionString();
services.AddSingleton(new PostgreSqlOptions { ConnectionString = _connectionString });
services.AddSingleton(new CapOptions()); services.AddOptions<CapOptions>();
services.Configure<PostgreSqlOptions>(x => x.ConnectionString = _connectionString);
services.AddSingleton<PostgreSqlStorage>(); services.AddSingleton<PostgreSqlStorage>();
_services = services; _services = services;
......
...@@ -4,6 +4,7 @@ using System.Data.SqlClient; ...@@ -4,6 +4,7 @@ using System.Data.SqlClient;
using Dapper; using Dapper;
using DotNetCore.CAP.SqlServer.Diagnostics; using DotNetCore.CAP.SqlServer.Diagnostics;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Moq; using Moq;
namespace DotNetCore.CAP.SqlServer.Test namespace DotNetCore.CAP.SqlServer.Test
...@@ -11,8 +12,8 @@ namespace DotNetCore.CAP.SqlServer.Test ...@@ -11,8 +12,8 @@ namespace DotNetCore.CAP.SqlServer.Test
public abstract class DatabaseTestHost : IDisposable public abstract class DatabaseTestHost : IDisposable
{ {
protected ILogger<SqlServerStorage> Logger; protected ILogger<SqlServerStorage> Logger;
protected CapOptions CapOptions; protected IOptions<CapOptions> CapOptions;
protected SqlServerOptions SqlSeverOptions; protected IOptions<SqlServerOptions> SqlSeverOptions;
protected DiagnosticProcessorObserver DiagnosticProcessorObserver; protected DiagnosticProcessorObserver DiagnosticProcessorObserver;
public bool SqlObjectInstalled; public bool SqlObjectInstalled;
...@@ -20,11 +21,14 @@ namespace DotNetCore.CAP.SqlServer.Test ...@@ -20,11 +21,14 @@ namespace DotNetCore.CAP.SqlServer.Test
protected DatabaseTestHost() protected DatabaseTestHost()
{ {
Logger = new Mock<ILogger<SqlServerStorage>>().Object; Logger = new Mock<ILogger<SqlServerStorage>>().Object;
CapOptions = new Mock<CapOptions>().Object;
SqlSeverOptions = new SqlServerOptions() var capOptions = new Mock<IOptions<CapOptions>>();
{ capOptions.Setup(x => x.Value).Returns(new CapOptions());
ConnectionString = ConnectionUtil.GetConnectionString() CapOptions = capOptions.Object;
};
var options = new Mock<IOptions<SqlServerOptions>>();
options.Setup(x => x.Value).Returns(new SqlServerOptions { ConnectionString = ConnectionUtil.GetConnectionString() });
SqlSeverOptions = options.Object;
DiagnosticProcessorObserver = new DiagnosticProcessorObserver(new Mock<IDispatcher>().Object); DiagnosticProcessorObserver = new DiagnosticProcessorObserver(new Mock<IDispatcher>().Object);
......
...@@ -14,7 +14,7 @@ namespace DotNetCore.CAP.SqlServer.Test ...@@ -14,7 +14,7 @@ namespace DotNetCore.CAP.SqlServer.Test
public SqlServerStorageConnectionTest() public SqlServerStorageConnectionTest()
{ {
_storage = new SqlServerStorageConnection(SqlSeverOptions, CapOptions); _storage = new SqlServerStorageConnection(SqlSeverOptions.Value, CapOptions.Value);
} }
[Fact] [Fact]
......
...@@ -4,6 +4,7 @@ using System.Threading.Tasks; ...@@ -4,6 +4,7 @@ using System.Threading.Tasks;
using DotNetCore.CAP.Abstractions; using DotNetCore.CAP.Abstractions;
using DotNetCore.CAP.Models; using DotNetCore.CAP.Models;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Xunit; using Xunit;
namespace DotNetCore.CAP.Test namespace DotNetCore.CAP.Test
...@@ -81,7 +82,7 @@ namespace DotNetCore.CAP.Test ...@@ -81,7 +82,7 @@ namespace DotNetCore.CAP.Test
var services = new ServiceCollection(); var services = new ServiceCollection();
services.AddCap(x => { }); services.AddCap(x => { });
var builder = services.BuildServiceProvider(); var builder = services.BuildServiceProvider();
var capOptions = builder.GetService<CapOptions>(); var capOptions = builder.GetService<IOptions<CapOptions>>().Value;
Assert.NotNull(capOptions); Assert.NotNull(capOptions);
} }
......
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using DotNetCore.CAP.Abstractions; using DotNetCore.CAP.Abstractions;
using DotNetCore.CAP.Internal;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Xunit; using Xunit;
......
...@@ -4,6 +4,7 @@ using System.Linq; ...@@ -4,6 +4,7 @@ using System.Linq;
using System.Reflection; using System.Reflection;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Xunit; using Xunit;
namespace DotNetCore.CAP.Test namespace DotNetCore.CAP.Test
...@@ -48,10 +49,10 @@ namespace DotNetCore.CAP.Test ...@@ -48,10 +49,10 @@ namespace DotNetCore.CAP.Test
{ {
private readonly CapOptions _capOptions; private readonly CapOptions _capOptions;
public MyConsumerServiceSelector(IServiceProvider serviceProvider, CapOptions capOptions) public MyConsumerServiceSelector(IServiceProvider serviceProvider)
: base(serviceProvider, capOptions) : base(serviceProvider)
{ {
_capOptions = capOptions; _capOptions = serviceProvider.GetService<IOptions<CapOptions>>().Value;
} }
protected override IEnumerable<ConsumerExecutorDescriptor> FindConsumersFromInterfaceTypes(IServiceProvider provider) protected override IEnumerable<ConsumerExecutorDescriptor> FindConsumersFromInterfaceTypes(IServiceProvider provider)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment