From 177e64c79788ba492e3ba1466030738a866c88cc Mon Sep 17 00:00:00 2001 From: Savorboard Date: Sat, 23 Dec 2017 09:30:08 +0800 Subject: [PATCH 001/223] add setting to generate xml summary document file --- src/DotNetCore.CAP/DotNetCore.CAP.csproj | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/DotNetCore.CAP/DotNetCore.CAP.csproj b/src/DotNetCore.CAP/DotNetCore.CAP.csproj index 5b22347..c3f3786 100644 --- a/src/DotNetCore.CAP/DotNetCore.CAP.csproj +++ b/src/DotNetCore.CAP/DotNetCore.CAP.csproj @@ -5,6 +5,10 @@ DotNetCore.CAP $(PackageTags); + + bin\Debug\netstandard2.0\DotNetCore.CAP.xml + 1701;1702;1705;CS1591 + From f1a25d9050c4274a7a1a60ad3497c6341520503a Mon Sep 17 00:00:00 2001 From: Savorboard Date: Sun, 24 Dec 2017 09:30:18 +0800 Subject: [PATCH 002/223] add setting to generate xml summary document file --- src/DotNetCore.CAP.Kafka/DotNetCore.CAP.Kafka.csproj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/DotNetCore.CAP.Kafka/DotNetCore.CAP.Kafka.csproj b/src/DotNetCore.CAP.Kafka/DotNetCore.CAP.Kafka.csproj index 6cc0872..2bc9d2f 100644 --- a/src/DotNetCore.CAP.Kafka/DotNetCore.CAP.Kafka.csproj +++ b/src/DotNetCore.CAP.Kafka/DotNetCore.CAP.Kafka.csproj @@ -10,7 +10,8 @@ NU1605;NU1701 - NU1701 + NU1701;CS1591 + bin\Debug\netstandard2.0\DotNetCore.CAP.Kafka.xml From ae14f62ff117469f70815e7681b2894c22579d8d Mon Sep 17 00:00:00 2001 From: Savorboard Date: Mon, 25 Dec 2017 09:30:26 +0800 Subject: [PATCH 003/223] add setting to generate xml summary document file --- src/DotNetCore.CAP.MySql/DotNetCore.CAP.MySql.csproj | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/DotNetCore.CAP.MySql/DotNetCore.CAP.MySql.csproj b/src/DotNetCore.CAP.MySql/DotNetCore.CAP.MySql.csproj index e6bd363..cb75f46 100644 --- a/src/DotNetCore.CAP.MySql/DotNetCore.CAP.MySql.csproj +++ b/src/DotNetCore.CAP.MySql/DotNetCore.CAP.MySql.csproj @@ -8,11 +8,16 @@ $(PackageTags);MySQL + + bin\Debug\netstandard2.0\DotNetCore.CAP.MySql.xml + 1701;1702;1705;CS1591 + + - + - + From bb7d8667a362df77f1b52f951926ab9ffdba631a Mon Sep 17 00:00:00 2001 From: Savorboard Date: Tue, 26 Dec 2017 09:30:35 +0800 Subject: [PATCH 004/223] add setting to generate xml summary document file --- .../DotNetCore.CAP.PostgreSql.csproj | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/DotNetCore.CAP.PostgreSql/DotNetCore.CAP.PostgreSql.csproj b/src/DotNetCore.CAP.PostgreSql/DotNetCore.CAP.PostgreSql.csproj index 1d4b553..b366894 100644 --- a/src/DotNetCore.CAP.PostgreSql/DotNetCore.CAP.PostgreSql.csproj +++ b/src/DotNetCore.CAP.PostgreSql/DotNetCore.CAP.PostgreSql.csproj @@ -8,8 +8,13 @@ $(PackageTags);PostgreSQL + + bin\Debug\netstandard2.0\DotNetCore.CAP.PostgreSql.xml + 1701;1702;1705;CS1591 + + - + From 55194bfcb7131f6de8a12cd6c957772d2aae6314 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Wed, 27 Dec 2017 09:30:42 +0800 Subject: [PATCH 005/223] add setting to generate xml summary document file --- src/DotNetCore.CAP.RabbitMQ/DotNetCore.CAP.RabbitMQ.csproj | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/DotNetCore.CAP.RabbitMQ/DotNetCore.CAP.RabbitMQ.csproj b/src/DotNetCore.CAP.RabbitMQ/DotNetCore.CAP.RabbitMQ.csproj index 55a0deb..419514f 100644 --- a/src/DotNetCore.CAP.RabbitMQ/DotNetCore.CAP.RabbitMQ.csproj +++ b/src/DotNetCore.CAP.RabbitMQ/DotNetCore.CAP.RabbitMQ.csproj @@ -7,6 +7,11 @@ DotNetCore.CAP.RabbitMQ $(PackageTags);RabbitMQ + + + bin\Debug\netstandard2.0\DotNetCore.CAP.RabbitMQ.xml + 1701;1702;1705;CS1591 + From d9494125ecf8cc74d4573a68ef0e2e69bffb26c2 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Thu, 28 Dec 2017 09:30:49 +0800 Subject: [PATCH 006/223] add setting to generate xml summary document file --- .../DotNetCore.CAP.SqlServer.csproj | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/DotNetCore.CAP.SqlServer/DotNetCore.CAP.SqlServer.csproj b/src/DotNetCore.CAP.SqlServer/DotNetCore.CAP.SqlServer.csproj index f3388f7..18ef872 100644 --- a/src/DotNetCore.CAP.SqlServer/DotNetCore.CAP.SqlServer.csproj +++ b/src/DotNetCore.CAP.SqlServer/DotNetCore.CAP.SqlServer.csproj @@ -8,11 +8,16 @@ $(PackageTags);SQL Server + + bin\Debug\netstandard2.0\DotNetCore.CAP.SqlServer.xml + 1701;1702;1705;CS1591 + + - + - + From 18154531e3d1f48cf8f5658bbe2a96c8cde337e2 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Fri, 22 Dec 2017 09:31:08 +0800 Subject: [PATCH 007/223] add setting to generate xml summary document file --- .../DotNetCore.CAP.MySql.Test.csproj | 6 +++--- .../DotNetCore.CAP.PostgreSql.Test.csproj | 2 +- .../DotNetCore.CAP.SqlServer.Test.csproj | 6 +++--- test/DotNetCore.CAP.Test/DotNetCore.CAP.Test.csproj | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/test/DotNetCore.CAP.MySql.Test/DotNetCore.CAP.MySql.Test.csproj b/test/DotNetCore.CAP.MySql.Test/DotNetCore.CAP.MySql.Test.csproj index 4a371c3..e7cd172 100644 --- a/test/DotNetCore.CAP.MySql.Test/DotNetCore.CAP.MySql.Test.csproj +++ b/test/DotNetCore.CAP.MySql.Test/DotNetCore.CAP.MySql.Test.csproj @@ -14,14 +14,14 @@ - + - + - + diff --git a/test/DotNetCore.CAP.PostgreSql.Test/DotNetCore.CAP.PostgreSql.Test.csproj b/test/DotNetCore.CAP.PostgreSql.Test/DotNetCore.CAP.PostgreSql.Test.csproj index 12113ef..2c34952 100644 --- a/test/DotNetCore.CAP.PostgreSql.Test/DotNetCore.CAP.PostgreSql.Test.csproj +++ b/test/DotNetCore.CAP.PostgreSql.Test/DotNetCore.CAP.PostgreSql.Test.csproj @@ -7,7 +7,7 @@ - + diff --git a/test/DotNetCore.CAP.SqlServer.Test/DotNetCore.CAP.SqlServer.Test.csproj b/test/DotNetCore.CAP.SqlServer.Test/DotNetCore.CAP.SqlServer.Test.csproj index 0a7849f..78c7cdd 100644 --- a/test/DotNetCore.CAP.SqlServer.Test/DotNetCore.CAP.SqlServer.Test.csproj +++ b/test/DotNetCore.CAP.SqlServer.Test/DotNetCore.CAP.SqlServer.Test.csproj @@ -11,14 +11,14 @@ - + - + - + diff --git a/test/DotNetCore.CAP.Test/DotNetCore.CAP.Test.csproj b/test/DotNetCore.CAP.Test/DotNetCore.CAP.Test.csproj index 821057e..28bf4b2 100644 --- a/test/DotNetCore.CAP.Test/DotNetCore.CAP.Test.csproj +++ b/test/DotNetCore.CAP.Test/DotNetCore.CAP.Test.csproj @@ -14,7 +14,7 @@ - + From 8e93c2a45e680ad836bbe85be74a1ab775af881e Mon Sep 17 00:00:00 2001 From: Savorboard Date: Thu, 28 Dec 2017 09:31:33 +0800 Subject: [PATCH 008/223] update version to 2.1.3 --- build/version.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/version.props b/build/version.props index 38a9b64..4ba57d7 100644 --- a/build/version.props +++ b/build/version.props @@ -2,7 +2,7 @@ 2 1 - 2 + 3 $(VersionMajor).$(VersionMinor).$(VersionPatch) From e55754272172e0849b5069b12f6021c51353c4db Mon Sep 17 00:00:00 2001 From: Savorboard Date: Fri, 16 Mar 2018 10:50:27 +0800 Subject: [PATCH 009/223] Release version 2.1.4 (#97) * Fixed the connection bug of getting message from table. #83 * update version to 2.1.4 * remove `TableNamePrefix` option from `MySqlOptions` to `EFOptions`. #84 * fixed entityframework rename table name prefix bug. #84 * fixed sql server scripts bug of create table scheme. #85 * fixed entityframework rename table name prefix bug. #84 * modify error message of logger write * Fixed bug of the FailedRetryCount does not increase when raised SubscriberNotFoundException. #90 * Fixed thread safety issue about KafkaOptions. #89 * upgrade nuget package --- build/version.props | 2 +- src/DotNetCore.CAP.Kafka/KafkaConsumerClient.cs | 16 +++++++++------- src/DotNetCore.CAP.MySql/CAP.EFOptions.cs | 7 +++++++ .../CAP.MySqlCapOptionsExtension.cs | 12 +++++++++++- src/DotNetCore.CAP.MySql/CAP.MySqlOptions.cs | 3 --- .../CAP.Options.Extensions.cs | 12 +++++++----- .../DotNetCore.CAP.MySql.csproj | 2 +- .../MySqlStorageConnection.cs | 2 ++ .../CAP.Options.Extensions.cs | 11 ++++++----- .../CAP.PostgreSqlCapOptionsExtension.cs | 9 +++++++++ .../DotNetCore.CAP.PostgreSql.csproj | 2 +- .../CAP.Options.Extensions.cs | 11 ++++++----- .../CAP.SqlServerCapOptionsExtension.cs | 7 ++++++- src/DotNetCore.CAP.SqlServer/SqlServerStorage.cs | 2 +- src/DotNetCore.CAP/DotNetCore.CAP.csproj | 4 ++-- src/DotNetCore.CAP/IQueueExecutor.Subscribe.cs | 4 +++- .../DotNetCore.CAP.MySql.Test.csproj | 6 +++--- .../DotNetCore.CAP.PostgreSql.Test.csproj | 4 ++-- .../DotNetCore.CAP.SqlServer.Test.csproj | 4 ++-- .../DotNetCore.CAP.Test.csproj | 4 ++-- 20 files changed, 81 insertions(+), 43 deletions(-) diff --git a/build/version.props b/build/version.props index 4ba57d7..fae43bc 100644 --- a/build/version.props +++ b/build/version.props @@ -2,7 +2,7 @@ 2 1 - 3 + 4 $(VersionMajor).$(VersionMinor).$(VersionPatch) diff --git a/src/DotNetCore.CAP.Kafka/KafkaConsumerClient.cs b/src/DotNetCore.CAP.Kafka/KafkaConsumerClient.cs index 2affaa6..241870e 100644 --- a/src/DotNetCore.CAP.Kafka/KafkaConsumerClient.cs +++ b/src/DotNetCore.CAP.Kafka/KafkaConsumerClient.cs @@ -66,16 +66,18 @@ namespace DotNetCore.CAP.Kafka private void InitKafkaClient() { - _kafkaOptions.MainConfig["group.id"] = _groupId; + lock (_kafkaOptions) + { + _kafkaOptions.MainConfig["group.id"] = _groupId; - var config = _kafkaOptions.AsKafkaConfig(); - _consumerClient = new Consumer(config, null, StringDeserializer); - _consumerClient.OnConsumeError += ConsumerClient_OnConsumeError; - _consumerClient.OnMessage += ConsumerClient_OnMessage; - _consumerClient.OnError += ConsumerClient_OnError; + var config = _kafkaOptions.AsKafkaConfig(); + _consumerClient = new Consumer(config, null, StringDeserializer); + _consumerClient.OnConsumeError += ConsumerClient_OnConsumeError; + _consumerClient.OnMessage += ConsumerClient_OnMessage; + _consumerClient.OnError += ConsumerClient_OnError; + } } - private void ConsumerClient_OnConsumeError(object sender, Message e) { var message = e.Deserialize(null, StringDeserializer); diff --git a/src/DotNetCore.CAP.MySql/CAP.EFOptions.cs b/src/DotNetCore.CAP.MySql/CAP.EFOptions.cs index 3cd77a3..390365b 100644 --- a/src/DotNetCore.CAP.MySql/CAP.EFOptions.cs +++ b/src/DotNetCore.CAP.MySql/CAP.EFOptions.cs @@ -5,6 +5,13 @@ namespace DotNetCore.CAP { public class EFOptions { + public const string DefaultSchema = "cap"; + + /// + /// Gets or sets the table name prefix to use when creating database objects. + /// + public string TableNamePrefix { get; set; } = DefaultSchema; + /// /// EF db context type. /// diff --git a/src/DotNetCore.CAP.MySql/CAP.MySqlCapOptionsExtension.cs b/src/DotNetCore.CAP.MySql/CAP.MySqlCapOptionsExtension.cs index 3d29fa1..a0bf29d 100644 --- a/src/DotNetCore.CAP.MySql/CAP.MySqlCapOptionsExtension.cs +++ b/src/DotNetCore.CAP.MySql/CAP.MySqlCapOptionsExtension.cs @@ -25,22 +25,32 @@ namespace DotNetCore.CAP services.AddScoped(); services.AddTransient(); + AddSingletionMySqlOptions(services); + } + + 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); + 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 diff --git a/src/DotNetCore.CAP.MySql/CAP.MySqlOptions.cs b/src/DotNetCore.CAP.MySql/CAP.MySqlOptions.cs index 5bec849..c75ae6a 100644 --- a/src/DotNetCore.CAP.MySql/CAP.MySqlOptions.cs +++ b/src/DotNetCore.CAP.MySql/CAP.MySqlOptions.cs @@ -1,5 +1,4 @@ // ReSharper disable once CheckNamespace - namespace DotNetCore.CAP { public class MySqlOptions : EFOptions @@ -8,7 +7,5 @@ namespace DotNetCore.CAP /// Gets or sets the database's connection string that will be used to store database entities. /// public string ConnectionString { get; set; } - - public string TableNamePrefix { get; set; } = "cap"; } } \ No newline at end of file diff --git a/src/DotNetCore.CAP.MySql/CAP.Options.Extensions.cs b/src/DotNetCore.CAP.MySql/CAP.Options.Extensions.cs index 5f66f6c..1949c82 100644 --- a/src/DotNetCore.CAP.MySql/CAP.Options.Extensions.cs +++ b/src/DotNetCore.CAP.MySql/CAP.Options.Extensions.cs @@ -16,6 +16,7 @@ namespace Microsoft.Extensions.DependencyInjection { if (configure == null) throw new ArgumentNullException(nameof(configure)); + options.RegisterExtension(new MySqlCapOptionsExtension(configure)); return options; @@ -24,7 +25,7 @@ namespace Microsoft.Extensions.DependencyInjection public static CapOptions UseEntityFramework(this CapOptions options) where TContext : DbContext { - return options.UseEntityFramework(opt => { opt.DbContextType = typeof(TContext); }); + return options.UseEntityFramework(opt => { }); } public static CapOptions UseEntityFramework(this CapOptions options, Action configure) @@ -32,10 +33,11 @@ namespace Microsoft.Extensions.DependencyInjection { if (configure == null) throw new ArgumentNullException(nameof(configure)); - var efOptions = new EFOptions {DbContextType = typeof(TContext)}; - configure(efOptions); - - options.RegisterExtension(new MySqlCapOptionsExtension(configure)); + options.RegisterExtension(new MySqlCapOptionsExtension(x => + { + configure(x); + x.DbContextType = typeof(TContext); + })); return options; } diff --git a/src/DotNetCore.CAP.MySql/DotNetCore.CAP.MySql.csproj b/src/DotNetCore.CAP.MySql/DotNetCore.CAP.MySql.csproj index d64ff32..515828b 100644 --- a/src/DotNetCore.CAP.MySql/DotNetCore.CAP.MySql.csproj +++ b/src/DotNetCore.CAP.MySql/DotNetCore.CAP.MySql.csproj @@ -17,7 +17,7 @@ - + diff --git a/src/DotNetCore.CAP.MySql/MySqlStorageConnection.cs b/src/DotNetCore.CAP.MySql/MySqlStorageConnection.cs index 1ed4e0e..7a59bde 100644 --- a/src/DotNetCore.CAP.MySql/MySqlStorageConnection.cs +++ b/src/DotNetCore.CAP.MySql/MySqlStorageConnection.cs @@ -57,6 +57,7 @@ SELECT * FROM `{_prefix}.published` WHERE Id=LAST_INSERT_ID();"; using (var connection = new MySqlConnection(Options.ConnectionString)) { + connection.Open(); connection.Execute("SELECT LAST_INSERT_ID(0)"); return await connection.QueryFirstOrDefaultAsync(sql); } @@ -103,6 +104,7 @@ SELECT * FROM `{_prefix}.received` WHERE Id=LAST_INSERT_ID();"; using (var connection = new MySqlConnection(Options.ConnectionString)) { + connection.Open(); connection.Execute("SELECT LAST_INSERT_ID(0)"); return await connection.QueryFirstOrDefaultAsync(sql); } diff --git a/src/DotNetCore.CAP.PostgreSql/CAP.Options.Extensions.cs b/src/DotNetCore.CAP.PostgreSql/CAP.Options.Extensions.cs index 9fde846..e001c05 100644 --- a/src/DotNetCore.CAP.PostgreSql/CAP.Options.Extensions.cs +++ b/src/DotNetCore.CAP.PostgreSql/CAP.Options.Extensions.cs @@ -24,7 +24,7 @@ namespace Microsoft.Extensions.DependencyInjection public static CapOptions UseEntityFramework(this CapOptions options) where TContext : DbContext { - return options.UseEntityFramework(opt => { opt.DbContextType = typeof(TContext); }); + return options.UseEntityFramework(opt => { }); } public static CapOptions UseEntityFramework(this CapOptions options, Action configure) @@ -32,10 +32,11 @@ namespace Microsoft.Extensions.DependencyInjection { if (configure == null) throw new ArgumentNullException(nameof(configure)); - var efOptions = new EFOptions {DbContextType = typeof(TContext)}; - configure(efOptions); - - options.RegisterExtension(new PostgreSqlCapOptionsExtension(configure)); + options.RegisterExtension(new PostgreSqlCapOptionsExtension(x => + { + configure(x); + x.DbContextType = typeof(TContext); + })); return options; } diff --git a/src/DotNetCore.CAP.PostgreSql/CAP.PostgreSqlCapOptionsExtension.cs b/src/DotNetCore.CAP.PostgreSql/CAP.PostgreSqlCapOptionsExtension.cs index e2d4a14..f1e94f8 100644 --- a/src/DotNetCore.CAP.PostgreSql/CAP.PostgreSqlCapOptionsExtension.cs +++ b/src/DotNetCore.CAP.PostgreSql/CAP.PostgreSqlCapOptionsExtension.cs @@ -25,10 +25,16 @@ namespace DotNetCore.CAP services.AddScoped(); services.AddTransient(); + AddSingletonPostgreSqlOptions(services); + } + + private void AddSingletonPostgreSqlOptions(IServiceCollection services) + { var postgreSqlOptions = new PostgreSqlOptions(); _configure(postgreSqlOptions); if (postgreSqlOptions.DbContextType != null) + { services.AddSingleton(x => { using (var scope = x.CreateScope()) @@ -39,8 +45,11 @@ namespace DotNetCore.CAP return postgreSqlOptions; } }); + } else + { services.AddSingleton(postgreSqlOptions); + } } } } \ No newline at end of file diff --git a/src/DotNetCore.CAP.PostgreSql/DotNetCore.CAP.PostgreSql.csproj b/src/DotNetCore.CAP.PostgreSql/DotNetCore.CAP.PostgreSql.csproj index 96dca04..702de21 100644 --- a/src/DotNetCore.CAP.PostgreSql/DotNetCore.CAP.PostgreSql.csproj +++ b/src/DotNetCore.CAP.PostgreSql/DotNetCore.CAP.PostgreSql.csproj @@ -17,7 +17,7 @@ - + diff --git a/src/DotNetCore.CAP.SqlServer/CAP.Options.Extensions.cs b/src/DotNetCore.CAP.SqlServer/CAP.Options.Extensions.cs index c977921..c882165 100644 --- a/src/DotNetCore.CAP.SqlServer/CAP.Options.Extensions.cs +++ b/src/DotNetCore.CAP.SqlServer/CAP.Options.Extensions.cs @@ -24,7 +24,7 @@ namespace Microsoft.Extensions.DependencyInjection public static CapOptions UseEntityFramework(this CapOptions options) where TContext : DbContext { - return options.UseEntityFramework(opt => { opt.DbContextType = typeof(TContext); }); + return options.UseEntityFramework(opt => { }); } public static CapOptions UseEntityFramework(this CapOptions options, Action configure) @@ -32,10 +32,11 @@ namespace Microsoft.Extensions.DependencyInjection { if (configure == null) throw new ArgumentNullException(nameof(configure)); - var efOptions = new EFOptions {DbContextType = typeof(TContext)}; - configure(efOptions); - - options.RegisterExtension(new SqlServerCapOptionsExtension(configure)); + options.RegisterExtension(new SqlServerCapOptionsExtension(x => + { + configure(x); + x.DbContextType = typeof(TContext); + })); return options; } diff --git a/src/DotNetCore.CAP.SqlServer/CAP.SqlServerCapOptionsExtension.cs b/src/DotNetCore.CAP.SqlServer/CAP.SqlServerCapOptionsExtension.cs index ba17ff0..55408bd 100644 --- a/src/DotNetCore.CAP.SqlServer/CAP.SqlServerCapOptionsExtension.cs +++ b/src/DotNetCore.CAP.SqlServer/CAP.SqlServerCapOptionsExtension.cs @@ -24,6 +24,7 @@ namespace DotNetCore.CAP services.AddScoped(); services.AddScoped(); services.AddTransient(); + AddSqlServerOptions(services); } @@ -34,18 +35,22 @@ namespace DotNetCore.CAP _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); + 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 diff --git a/src/DotNetCore.CAP.SqlServer/SqlServerStorage.cs b/src/DotNetCore.CAP.SqlServer/SqlServerStorage.cs index 57825b5..75d8398 100644 --- a/src/DotNetCore.CAP.SqlServer/SqlServerStorage.cs +++ b/src/DotNetCore.CAP.SqlServer/SqlServerStorage.cs @@ -54,7 +54,7 @@ namespace DotNetCore.CAP.SqlServer $@" IF NOT EXISTS (SELECT * FROM sys.schemas WHERE name = '{schema}') BEGIN - EXEC('CREATE SCHEMA {schema}') + EXEC('CREATE SCHEMA [{schema}]') END; IF OBJECT_ID(N'[{schema}].[Queue]',N'U') IS NULL diff --git a/src/DotNetCore.CAP/DotNetCore.CAP.csproj b/src/DotNetCore.CAP/DotNetCore.CAP.csproj index e4fb3ea..697d6db 100644 --- a/src/DotNetCore.CAP/DotNetCore.CAP.csproj +++ b/src/DotNetCore.CAP/DotNetCore.CAP.csproj @@ -48,12 +48,12 @@ - + - + diff --git a/src/DotNetCore.CAP/IQueueExecutor.Subscribe.cs b/src/DotNetCore.CAP/IQueueExecutor.Subscribe.cs index 7435b4b..d1ad6ea 100644 --- a/src/DotNetCore.CAP/IQueueExecutor.Subscribe.cs +++ b/src/DotNetCore.CAP/IQueueExecutor.Subscribe.cs @@ -36,7 +36,7 @@ namespace DotNetCore.CAP if (message == null) { - _logger.LogError($"Can not find mesage at cap received message table, message id:{fetched.MessageId} !!!"); + _logger.LogError($"Can not found the `message` at cap received message table, message id:{fetched.MessageId} !!!"); return OperateResult.Failed(); } @@ -68,6 +68,8 @@ namespace DotNetCore.CAP AddErrorReasonToContent(message, ex); + ++message.Retries; //issue: https://github.com/dotnetcore/CAP/issues/90 + await _stateChanger.ChangeStateAsync(message, new FailedState(), connection); fetched.RemoveFromQueue(); diff --git a/test/DotNetCore.CAP.MySql.Test/DotNetCore.CAP.MySql.Test.csproj b/test/DotNetCore.CAP.MySql.Test/DotNetCore.CAP.MySql.Test.csproj index 5f22d0a..b0dd92a 100644 --- a/test/DotNetCore.CAP.MySql.Test/DotNetCore.CAP.MySql.Test.csproj +++ b/test/DotNetCore.CAP.MySql.Test/DotNetCore.CAP.MySql.Test.csproj @@ -15,13 +15,13 @@ - - + + - + diff --git a/test/DotNetCore.CAP.PostgreSql.Test/DotNetCore.CAP.PostgreSql.Test.csproj b/test/DotNetCore.CAP.PostgreSql.Test/DotNetCore.CAP.PostgreSql.Test.csproj index 2c34952..be84f65 100644 --- a/test/DotNetCore.CAP.PostgreSql.Test/DotNetCore.CAP.PostgreSql.Test.csproj +++ b/test/DotNetCore.CAP.PostgreSql.Test/DotNetCore.CAP.PostgreSql.Test.csproj @@ -8,8 +8,8 @@ - - + + diff --git a/test/DotNetCore.CAP.SqlServer.Test/DotNetCore.CAP.SqlServer.Test.csproj b/test/DotNetCore.CAP.SqlServer.Test/DotNetCore.CAP.SqlServer.Test.csproj index 677adf2..035ed35 100644 --- a/test/DotNetCore.CAP.SqlServer.Test/DotNetCore.CAP.SqlServer.Test.csproj +++ b/test/DotNetCore.CAP.SqlServer.Test/DotNetCore.CAP.SqlServer.Test.csproj @@ -12,13 +12,13 @@ - + - + diff --git a/test/DotNetCore.CAP.Test/DotNetCore.CAP.Test.csproj b/test/DotNetCore.CAP.Test/DotNetCore.CAP.Test.csproj index 66c7673..96bfb13 100644 --- a/test/DotNetCore.CAP.Test/DotNetCore.CAP.Test.csproj +++ b/test/DotNetCore.CAP.Test/DotNetCore.CAP.Test.csproj @@ -8,13 +8,13 @@ - + - + From 77282fe1a67bb11da18ad872e8740ee253f54c42 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Tue, 27 Mar 2018 13:48:05 +0800 Subject: [PATCH 010/223] Remove database queue mode (#102) * Improve the implementation mechanism of queue mode. #96 * refactor the code . * add Copyright & License description into header text of code files. * refactor * set version to 2.2.0 * update unit tests. * add exception class to process the publish send and subscriber exectution exception * modify first retry time to three. * code refactor. * add retry mechanism * code refactor * refactor consumer execution * fix spell error * remove dashboard `processing ` content. * Modify the retry to retry the message only 4 minutes ago. * update samples * update ci configuration. --- CAP.sln | 16 +- ConfigureMSDTC.ps1 | 40 ----- appveyor.yml | 1 - build/version.props | 4 +- .../Sample.Kafka.SqlServer/AppDbContext.cs | 14 -- .../CmsContentSerializer.cs | 50 ------ .../Controllers/ValuesController.cs | 122 ++------------ samples/Sample.Kafka.SqlServer/Program.cs | 7 +- .../Properties/launchSettings.json | 29 ++++ ...erver.csproj => Sample.Kafka.MySql.csproj} | 7 +- samples/Sample.Kafka.SqlServer/Startup.cs | 18 +- .../Controllers/ValuesController.cs | 3 - .../Properties/launchSettings.json | 29 ++++ samples/Sample.RabbitMQ.MySql/Startup.cs | 6 +- .../AppDbContext.cs | 16 -- .../Controllers/ValuesController.cs | 59 ------- samples/Sample.RabbitMQ.PostgreSql/Program.cs | 25 --- .../Sample.RabbitMQ.PostgreSql.csproj | 18 -- samples/Sample.RabbitMQ.PostgreSql/Startup.cs | 42 ----- .../Sample.RabbitMQ.SqlServer/AppDbContext.cs | 17 -- .../Controllers/ValuesController.cs | 99 ----------- .../20170824130007_AddPersons.Designer.cs | 40 ----- .../Migrations/20170824130007_AddPersons.cs | 33 ---- .../Migrations/AppDbContextModelSnapshot.cs | 39 ----- samples/Sample.RabbitMQ.SqlServer/Program.cs | 24 --- .../Sample.RabbitMQ.SqlServer.csproj | 20 --- .../Services/ICmsService.cs | 13 -- .../Services/IOrderService.cs | 12 -- .../Services/Impl/CmsService.cs | 16 -- .../Services/Impl/OrderService.cs | 17 -- samples/Sample.RabbitMQ.SqlServer/Startup.cs | 49 ------ .../CAP.KafkaCapOptionsExtension.cs | 9 +- src/DotNetCore.CAP.Kafka/CAP.KafkaOptions.cs | 10 +- .../CAP.Options.Extensions.cs | 10 +- .../CAP.SubscribeAttribute.cs | 5 +- src/DotNetCore.CAP.Kafka/ConnectionPool.cs | 7 +- src/DotNetCore.CAP.Kafka/IConnectionPool.cs | 4 +- .../IPublishMessageSender.Kafka.cs | 65 +++++++ .../KafkaConsumerClient.cs | 10 +- .../KafkaConsumerClientFactory.cs | 5 +- .../PublishQueueExecutor.cs | 62 ------- src/DotNetCore.CAP.MySql/CAP.EFOptions.cs | 5 +- .../CAP.MySqlCapOptionsExtension.cs | 7 +- src/DotNetCore.CAP.MySql/CAP.MySqlOptions.cs | 4 +- .../CAP.Options.Extensions.cs | 15 +- src/DotNetCore.CAP.MySql/CapPublisher.cs | 38 +++-- src/DotNetCore.CAP.MySql/FetchedMessage.cs | 11 -- .../IAdditionalProcessor.Default.cs | 5 +- .../MySqlFetchedMessage.cs | 48 ------ .../MySqlMonitoringApi.cs | 51 +++--- src/DotNetCore.CAP.MySql/MySqlStorage.cs | 21 ++- .../MySqlStorageConnection.cs | 78 ++------- .../MySqlStorageTransaction.cs | 49 +++--- .../CAP.EFOptions.cs | 5 +- .../CAP.Options.Extensions.cs | 15 +- .../CAP.PostgreSqlCapOptionsExtension.cs | 7 +- .../CAP.PostgreSqlOptions.cs | 3 +- src/DotNetCore.CAP.PostgreSql/CapPublisher.cs | 36 ++-- .../FetchedMessage.cs | 11 -- .../IAdditionalProcessor.Default.cs | 5 +- .../PostgreSqlFetchedMessage.cs | 74 -------- .../PostgreSqlMonitoringApi.cs | 60 +++---- .../PostgreSqlStorage.cs | 20 ++- .../PostgreSqlStorageConnection.cs | 80 ++------- .../PostgreSqlStorageTransaction.cs | 49 ++++-- .../CAP.Options.Extensions.cs | 10 +- .../CAP.RabbiMQOptions.cs | 4 +- .../CAP.RabbitMQCapOptionsExtension.cs | 9 +- .../CAP.SubscribeAttribute.cs | 5 +- .../ConnectionChannelPool.cs | 10 +- .../IConnectionChannelPool.cs | 5 +- .../PublishQueueExecutor.cs | 38 +++-- .../RabbitMQConsumerClient.cs | 22 ++- .../RabbitMQConsumerClientFactory.cs | 5 +- src/DotNetCore.CAP.SqlServer/CAP.EFOptions.cs | 5 +- .../CAP.Options.Extensions.cs | 15 +- .../CAP.SqlServerCapOptionsExtension.cs | 7 +- .../CAP.SqlServerOptions.cs | 3 +- src/DotNetCore.CAP.SqlServer/CapPublisher.cs | 41 ++--- .../FetchedMessage.cs | 11 -- .../IAdditionalProcessor.Default.cs | 5 +- .../SqlServerFetchedMessage.cs | 74 -------- .../SqlServerMonitoringApi.cs | 52 +++--- .../SqlServerStorage.cs | 22 ++- .../SqlServerStorageConnection.cs | 84 ++------- .../SqlServerStorageTransaction.cs | 49 ++++-- .../Abstractions/CapPublisherBase.cs | 135 ++++++++++----- .../Abstractions/IContentSerializer.cs | 16 +- .../Abstractions/IModelBinderFactory.cs | 5 +- .../Abstractions/ISubscriberExecutor.cs | 17 -- .../Abstractions/ModelBinding/IModelBinder.cs | 5 +- .../ModelBinding/ModelBindingResult.cs | 11 +- .../Abstractions/TopicAttribute.cs | 5 +- .../CAP.AppBuilderExtensions.cs | 16 +- src/DotNetCore.CAP/CAP.Builder.cs | 5 +- src/DotNetCore.CAP/CAP.Options.cs | 39 ++--- .../CAP.ServiceCollectionExtensions.cs | 32 ++-- .../Dashboard/BatchCommandDispatcher.cs | 5 +- .../Dashboard/CAP.DashboardMiddleware.cs | 12 +- .../Dashboard/CAP.DashboardOptions.cs | 5 +- .../CAP.DashboardOptionsExtensions.cs | 10 +- .../Dashboard/CombinedResourceDispatcher.cs | 7 +- .../Dashboard/CommandDispatcher.cs | 9 +- .../Content/resx/Strings.Designer.cs | 9 - .../Dashboard/Content/resx/Strings.resx | 3 - .../Dashboard/Content/resx/Strings.zh.resx | 3 - .../Dashboard/DashboardContext.cs | 21 ++- .../Dashboard/DashboardMetric.cs | 3 + .../Dashboard/DashboardMetrics.cs | 31 +--- .../Dashboard/DashboardRequest.cs | 11 +- .../Dashboard/DashboardResponse.cs | 5 +- .../Dashboard/DashboardRoutes.cs | 5 +- .../Dashboard/EmbeddedResourceDispatcher.cs | 7 +- .../Dashboard/GatewayProxy/DownstreamUrl.cs | 5 +- .../GatewayProxy/GatewayProxyMiddleware.cs | 11 +- .../GatewayProxy/IRequestMapper.Default.cs | 13 +- .../Dashboard/GatewayProxy/IRequestMapper.cs | 5 +- .../Requester/HttpClientBuilder.cs | 5 +- .../Requester/HttpClientHttpRequester.cs | 8 +- .../GatewayProxy/Requester/IHttpClient.cs | 5 +- .../Requester/IHttpClientBuilder.cs | 5 +- .../Requester/IHttpClientCache.cs | 5 +- .../GatewayProxy/Requester/IHttpRequester.cs | 5 +- .../Requester/MemoryHttpClientCache.cs | 8 +- src/DotNetCore.CAP/Dashboard/HtmlHelper.cs | 78 ++++++++- .../IDashboardAuthorizationFilter.cs | 5 +- .../Dashboard/IDashboardDispatcher.cs | 5 +- .../Dashboard/IMonitoringApi.cs | 7 +- .../Dashboard/JsonDispatcher.cs | 7 +- src/DotNetCore.CAP/Dashboard/JsonStats.cs | 5 +- .../LocalRequestsOnlyAuthorizationFilter.cs | 13 +- src/DotNetCore.CAP/Dashboard/MenuItem.cs | 5 + .../Dashboard/MessageHistoryRenderer.cs | 32 +++- .../Dashboard/MessagesSidebarMenu.cs | 16 +- src/DotNetCore.CAP/Dashboard/Metric.cs | 5 +- .../Dashboard/Monitoring/MessageDto.cs | 5 +- .../Dashboard/Monitoring/MessageQueryDto.cs | 5 +- .../Dashboard/Monitoring/ServerDto.cs | 5 +- .../Dashboard/Monitoring/StatisticsDto.cs | 8 +- .../Dashboard/NavigationMenu.cs | 3 + .../Dashboard/NonEscapedString.cs | 5 +- src/DotNetCore.CAP/Dashboard/Pager.cs | 44 ++++- .../Dashboard/Pages/BlockMetric.cs | 5 +- .../Dashboard/Pages/Breadcrumbs.cs | 5 +- .../Dashboard/Pages/HomePage.cs | 5 +- .../Dashboard/Pages/InlineMetric.cs | 5 +- .../Dashboard/Pages/LayoutPage.cs | 5 +- .../Dashboard/Pages/LayoutPage.cshtml | 108 ++++++------ .../Dashboard/Pages/NodePage.cs | 7 +- .../Dashboard/Pages/PublishedPage.cs | 11 +- .../Dashboard/Pages/ReceivedPage.cs | 11 +- .../Dashboard/Pages/SidebarMenu.cs | 5 +- .../Dashboard/Pages/_Paginator.cs | 5 +- .../Dashboard/Pages/_Paginator.cshtml | 1 - .../Dashboard/Pages/_PerPageSelector.cs | 5 +- src/DotNetCore.CAP/Dashboard/RazorPage.cs | 20 ++- .../Dashboard/RazorPageDispatcher.cs | 5 +- .../Dashboard/RouteCollection.cs | 28 ++- .../Dashboard/RouteCollectionExtensions.cs | 90 ++++++++-- src/DotNetCore.CAP/Dashboard/UrlHelper.cs | 5 +- src/DotNetCore.CAP/DotNetCore.CAP.csproj | 5 + src/DotNetCore.CAP/IBootstrapper.Default.cs | 28 ++- src/DotNetCore.CAP/IBootstrapper.cs | 5 +- src/DotNetCore.CAP/ICallbackPublisher.cs | 7 +- src/DotNetCore.CAP/ICapOptionsExtension.cs | 7 +- src/DotNetCore.CAP/ICapPublisher.cs | 5 +- src/DotNetCore.CAP/ICapSubscribe.cs | 5 +- src/DotNetCore.CAP/IConsumerClient.cs | 5 +- src/DotNetCore.CAP/IConsumerClientFactory.cs | 5 +- .../IConsumerHandler.Default.cs | 81 ++++----- src/DotNetCore.CAP/IConsumerHandler.cs | 5 +- src/DotNetCore.CAP/IDispatcher.cs | 14 ++ src/DotNetCore.CAP/IFetchedMessage.cs | 3 + src/DotNetCore.CAP/IProcessingServer.cs | 5 +- src/DotNetCore.CAP/IPublishExecutor.cs | 7 +- .../IPublishMessageSender.Base.cs | 118 +++++++++++++ src/DotNetCore.CAP/IPublishMessageSender.cs | 13 ++ .../IQueueExecutor.Publish.Base.cs | 94 ----------- .../IQueueExecutor.Subscribe.cs | 133 --------------- src/DotNetCore.CAP/IQueueExecutor.cs | 9 - src/DotNetCore.CAP/IQueueExecutorFactory.cs | 9 - src/DotNetCore.CAP/IStorage.cs | 3 + src/DotNetCore.CAP/IStorageConnection.cs | 24 +-- src/DotNetCore.CAP/IStorageTransaction.cs | 9 +- .../ISubscribeExecutor.Default.cs | 159 ++++++++++++++++++ src/DotNetCore.CAP/ISubscriberExecutor.cs | 13 ++ src/DotNetCore.CAP/Infrastructure/Helper.cs | 20 ++- src/DotNetCore.CAP/Infrastructure/ObjectId.cs | 70 +++++++- .../Infrastructure/StatusName.cs | 7 +- .../Infrastructure/WaitHandleEx.cs | 5 +- src/DotNetCore.CAP/Internal/CapCache.cs | 39 ++++- .../Internal/ConsumerContext.cs | 5 +- .../Internal/ConsumerExecutedResult.cs | 7 +- .../Internal/ConsumerExecutorDescriptor.cs | 5 +- .../Internal/ConsumerInvokerFactory.cs | 11 +- .../Internal/HashCodeCombiner.cs | 9 +- .../ICallbackMessageSender.Default.cs | 17 +- .../Internal/ICallbackMessageSender.cs | 5 +- .../Internal/IConsumerInvoker.Default.cs | 23 ++- .../Internal/IConsumerInvoker.cs | 5 +- .../Internal/IConsumerInvokerFactory.cs | 5 +- .../IConsumerServiceSelector.Default.cs | 28 ++- .../Internal/IConsumerServiceSelector.cs | 5 +- .../Internal/IContentSerializer.Json.cs | 7 +- .../Internal/IMessagePacker.Default.cs | 7 +- .../Internal/IModelBinder.ComplexType.cs | 5 +- .../Internal/IModelBinder.SimpleType.cs | 23 ++- .../Internal/ISubscriberExecutor.Default.cs | 57 ------- .../Internal/MethodBindException.cs | 5 +- .../Internal/MethodMatcherCache.cs | 24 ++- .../Internal/ModelBinderFactory.cs | 20 ++- .../ObjectMethodExecutor/AwaitableInfo.cs | 4 +- .../CoercedAwaitableInfo.cs | 6 +- .../ObjectMethodExecutor.cs | 18 +- .../ObjectMethodExecutorAwaitable.cs | 4 +- .../ObjectMethodExecutorFSharpSupport.cs | 11 +- .../Internal/PublisherSentFailedException.cs | 14 ++ .../SubscriberExecutionFailedException.cs | 14 ++ .../Internal/SubscriberNotFoundException.cs | 5 +- src/DotNetCore.CAP/LoggerExtensions.cs | 125 +++++--------- src/DotNetCore.CAP/MessageContext.cs | 5 +- src/DotNetCore.CAP/Models/CapMessageDto.cs | 7 +- .../Models/CapPublishedMessage.cs | 5 +- src/DotNetCore.CAP/Models/CapQueue.cs | 5 +- .../Models/CapReceivedMessage.cs | 5 +- src/DotNetCore.CAP/Models/MessageType.cs | 5 +- src/DotNetCore.CAP/MqLogType.cs | 7 +- .../NodeDiscovery/CAP.DiscoveryOptions.cs | 3 +- .../CAP.DiscoveryOptionsExtensions.cs | 10 +- .../IDiscoveryProviderFactory.Default.cs | 7 +- .../IDiscoveryProviderFactory.cs | 5 +- .../INodeDiscoveryProvider.Consul.cs | 16 +- .../NodeDiscovery/INodeDiscoveryProvider.cs | 5 +- .../NodeDiscovery/IProcessingServer.Consul.cs | 5 +- src/DotNetCore.CAP/NodeDiscovery/Node.cs | 5 +- src/DotNetCore.CAP/OperateResult.cs | 11 +- .../Processor/IAdditionalProcessor.cs | 5 +- .../Processor/IDispatcher.Default.cs | 114 +++++++------ src/DotNetCore.CAP/Processor/IDispatcher.cs | 7 - .../Processor/IProcessingServer.Cap.cs | 58 +++---- .../Processor/IProcessor.InfiniteRetry.cs | 7 +- ...ssor.Failed.cs => IProcessor.NeedRetry.cs} | 63 +++---- .../Processor/IProcessor.PublishQueuer.cs | 67 -------- .../Processor/IProcessor.SubscribeQueuer.cs | 67 -------- src/DotNetCore.CAP/Processor/IProcessor.cs | 5 +- .../Processor/ProcessingContext.cs | 5 +- src/DotNetCore.CAP/Processor/RetryBehavior.cs | 16 +- .../Processor/States/IState.Enqueued.cs | 24 --- .../Processor/States/IState.Failed.cs | 5 +- .../Processor/States/IState.Processing.cs | 22 --- .../Processor/States/IState.Scheduled.cs | 5 +- .../Processor/States/IState.Succeeded.cs | 5 +- src/DotNetCore.CAP/Processor/States/IState.cs | 5 +- .../Processor/States/IStateChanger.Default.cs | 13 +- .../States/IStateChanger.Extensions.cs | 5 +- .../Processor/States/IStateChanger.cs | 5 +- src/DotNetCore.CAP/Properties/AssemblyInfo.cs | 5 +- src/DotNetCore.CAP/QueueExecutorFactory.cs | 26 --- .../DatabaseTestHost.cs | 4 +- .../MySqlStorageConnectionTest.cs | 41 ----- .../MySqlStorageTest.cs | 1 - .../DatabaseTestHost.cs | 3 +- .../PostgreSqlStorageConnectionTest.cs | 40 ----- .../PostgreSqlStorageTest.cs | 1 - .../DatabaseTestHost.cs | 1 - .../SqlServerStorageConnectionTest.cs | 42 +---- .../SqlServerStorageTest.cs | 1 - .../CallbackMessageSenderTest.cs | 2 +- .../Processor/DefaultDispatcherTest.cs | 142 ++++++++-------- .../Processor/StateChangerTest.cs | 4 +- .../QueueExecutorFactoryTest.cs | 76 ++++----- 271 files changed, 2671 insertions(+), 3044 deletions(-) delete mode 100644 ConfigureMSDTC.ps1 delete mode 100644 samples/Sample.Kafka.SqlServer/AppDbContext.cs delete mode 100644 samples/Sample.Kafka.SqlServer/CmsContentSerializer.cs create mode 100644 samples/Sample.Kafka.SqlServer/Properties/launchSettings.json rename samples/Sample.Kafka.SqlServer/{Sample.Kafka.SqlServer.csproj => Sample.Kafka.MySql.csproj} (74%) create mode 100644 samples/Sample.RabbitMQ.MySql/Properties/launchSettings.json delete mode 100644 samples/Sample.RabbitMQ.PostgreSql/AppDbContext.cs delete mode 100644 samples/Sample.RabbitMQ.PostgreSql/Controllers/ValuesController.cs delete mode 100644 samples/Sample.RabbitMQ.PostgreSql/Program.cs delete mode 100644 samples/Sample.RabbitMQ.PostgreSql/Sample.RabbitMQ.PostgreSql.csproj delete mode 100644 samples/Sample.RabbitMQ.PostgreSql/Startup.cs delete mode 100644 samples/Sample.RabbitMQ.SqlServer/AppDbContext.cs delete mode 100644 samples/Sample.RabbitMQ.SqlServer/Controllers/ValuesController.cs delete mode 100644 samples/Sample.RabbitMQ.SqlServer/Migrations/20170824130007_AddPersons.Designer.cs delete mode 100644 samples/Sample.RabbitMQ.SqlServer/Migrations/20170824130007_AddPersons.cs delete mode 100644 samples/Sample.RabbitMQ.SqlServer/Migrations/AppDbContextModelSnapshot.cs delete mode 100644 samples/Sample.RabbitMQ.SqlServer/Program.cs delete mode 100644 samples/Sample.RabbitMQ.SqlServer/Sample.RabbitMQ.SqlServer.csproj delete mode 100644 samples/Sample.RabbitMQ.SqlServer/Services/ICmsService.cs delete mode 100644 samples/Sample.RabbitMQ.SqlServer/Services/IOrderService.cs delete mode 100644 samples/Sample.RabbitMQ.SqlServer/Services/Impl/CmsService.cs delete mode 100644 samples/Sample.RabbitMQ.SqlServer/Services/Impl/OrderService.cs delete mode 100644 samples/Sample.RabbitMQ.SqlServer/Startup.cs create mode 100644 src/DotNetCore.CAP.Kafka/IPublishMessageSender.Kafka.cs delete mode 100644 src/DotNetCore.CAP.Kafka/PublishQueueExecutor.cs delete mode 100644 src/DotNetCore.CAP.MySql/FetchedMessage.cs delete mode 100644 src/DotNetCore.CAP.MySql/MySqlFetchedMessage.cs delete mode 100644 src/DotNetCore.CAP.PostgreSql/FetchedMessage.cs delete mode 100644 src/DotNetCore.CAP.PostgreSql/PostgreSqlFetchedMessage.cs delete mode 100644 src/DotNetCore.CAP.SqlServer/FetchedMessage.cs delete mode 100644 src/DotNetCore.CAP.SqlServer/SqlServerFetchedMessage.cs delete mode 100644 src/DotNetCore.CAP/Abstractions/ISubscriberExecutor.cs create mode 100644 src/DotNetCore.CAP/IDispatcher.cs create mode 100644 src/DotNetCore.CAP/IPublishMessageSender.Base.cs create mode 100644 src/DotNetCore.CAP/IPublishMessageSender.cs delete mode 100644 src/DotNetCore.CAP/IQueueExecutor.Publish.Base.cs delete mode 100644 src/DotNetCore.CAP/IQueueExecutor.Subscribe.cs delete mode 100644 src/DotNetCore.CAP/IQueueExecutor.cs delete mode 100644 src/DotNetCore.CAP/IQueueExecutorFactory.cs create mode 100644 src/DotNetCore.CAP/ISubscribeExecutor.Default.cs create mode 100644 src/DotNetCore.CAP/ISubscriberExecutor.cs delete mode 100644 src/DotNetCore.CAP/Internal/ISubscriberExecutor.Default.cs create mode 100644 src/DotNetCore.CAP/Internal/PublisherSentFailedException.cs create mode 100644 src/DotNetCore.CAP/Internal/SubscriberExecutionFailedException.cs delete mode 100644 src/DotNetCore.CAP/Processor/IDispatcher.cs rename src/DotNetCore.CAP/Processor/{IProcessor.Failed.cs => IProcessor.NeedRetry.cs} (71%) delete mode 100644 src/DotNetCore.CAP/Processor/IProcessor.PublishQueuer.cs delete mode 100644 src/DotNetCore.CAP/Processor/IProcessor.SubscribeQueuer.cs delete mode 100644 src/DotNetCore.CAP/Processor/States/IState.Enqueued.cs delete mode 100644 src/DotNetCore.CAP/Processor/States/IState.Processing.cs delete mode 100644 src/DotNetCore.CAP/QueueExecutorFactory.cs diff --git a/CAP.sln b/CAP.sln index cee4bf6..174a366 100644 --- a/CAP.sln +++ b/CAP.sln @@ -54,15 +54,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNetCore.CAP.MySql.Test", EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sample.RabbitMQ.MySql", "samples\Sample.RabbitMQ.MySql\Sample.RabbitMQ.MySql.csproj", "{9F3F9BFE-7B6A-4A7A-A6E6-8B517D611873}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sample.RabbitMQ.SqlServer", "samples\Sample.RabbitMQ.SqlServer\Sample.RabbitMQ.SqlServer.csproj", "{AF17B956-B79E-48B7-9B5B-EB15A386B112}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNetCore.CAP.PostgreSql", "src\DotNetCore.CAP.PostgreSql\DotNetCore.CAP.PostgreSql.csproj", "{82C403AB-ED68-4084-9A1D-11334F9F08F9}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sample.RabbitMQ.PostgreSql", "samples\Sample.RabbitMQ.PostgreSql\Sample.RabbitMQ.PostgreSql.csproj", "{A17E8E72-DFFC-4822-BB38-73D59A8B264E}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNetCore.CAP.PostgreSql.Test", "test\DotNetCore.CAP.PostgreSql.Test\DotNetCore.CAP.PostgreSql.Test.csproj", "{7CA3625D-1817-4695-881D-7E79A1E1DED2}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sample.Kafka.SqlServer", "samples\Sample.Kafka.SqlServer\Sample.Kafka.SqlServer.csproj", "{573B4D39-5489-48B3-9B6C-5234249CB980}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sample.Kafka.MySql", "samples\Sample.Kafka.SqlServer\Sample.Kafka.MySql.csproj", "{573B4D39-5489-48B3-9B6C-5234249CB980}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -105,18 +101,10 @@ Global {9F3F9BFE-7B6A-4A7A-A6E6-8B517D611873}.Debug|Any CPU.Build.0 = Debug|Any CPU {9F3F9BFE-7B6A-4A7A-A6E6-8B517D611873}.Release|Any CPU.ActiveCfg = Release|Any CPU {9F3F9BFE-7B6A-4A7A-A6E6-8B517D611873}.Release|Any CPU.Build.0 = Release|Any CPU - {AF17B956-B79E-48B7-9B5B-EB15A386B112}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {AF17B956-B79E-48B7-9B5B-EB15A386B112}.Debug|Any CPU.Build.0 = Debug|Any CPU - {AF17B956-B79E-48B7-9B5B-EB15A386B112}.Release|Any CPU.ActiveCfg = Release|Any CPU - {AF17B956-B79E-48B7-9B5B-EB15A386B112}.Release|Any CPU.Build.0 = Release|Any CPU {82C403AB-ED68-4084-9A1D-11334F9F08F9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {82C403AB-ED68-4084-9A1D-11334F9F08F9}.Debug|Any CPU.Build.0 = Debug|Any CPU {82C403AB-ED68-4084-9A1D-11334F9F08F9}.Release|Any CPU.ActiveCfg = Release|Any CPU {82C403AB-ED68-4084-9A1D-11334F9F08F9}.Release|Any CPU.Build.0 = Release|Any CPU - {A17E8E72-DFFC-4822-BB38-73D59A8B264E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A17E8E72-DFFC-4822-BB38-73D59A8B264E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A17E8E72-DFFC-4822-BB38-73D59A8B264E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A17E8E72-DFFC-4822-BB38-73D59A8B264E}.Release|Any CPU.Build.0 = Release|Any CPU {7CA3625D-1817-4695-881D-7E79A1E1DED2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {7CA3625D-1817-4695-881D-7E79A1E1DED2}.Debug|Any CPU.Build.0 = Debug|Any CPU {7CA3625D-1817-4695-881D-7E79A1E1DED2}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -139,9 +127,7 @@ Global {FA15685A-778A-4D2A-A2FE-27FAD2FFA65B} = {9B2AE124-6636-4DE9-83A3-70360DABD0C4} {80A84F62-1558-427B-BA74-B47AA8A665B5} = {C09CDAB0-6DD4-46E9-B7F3-3EF2A4741EA0} {9F3F9BFE-7B6A-4A7A-A6E6-8B517D611873} = {3A6B6931-A123-477A-9469-8B468B5385AF} - {AF17B956-B79E-48B7-9B5B-EB15A386B112} = {3A6B6931-A123-477A-9469-8B468B5385AF} {82C403AB-ED68-4084-9A1D-11334F9F08F9} = {9B2AE124-6636-4DE9-83A3-70360DABD0C4} - {A17E8E72-DFFC-4822-BB38-73D59A8B264E} = {3A6B6931-A123-477A-9469-8B468B5385AF} {7CA3625D-1817-4695-881D-7E79A1E1DED2} = {C09CDAB0-6DD4-46E9-B7F3-3EF2A4741EA0} {573B4D39-5489-48B3-9B6C-5234249CB980} = {3A6B6931-A123-477A-9469-8B468B5385AF} EndGlobalSection diff --git a/ConfigureMSDTC.ps1 b/ConfigureMSDTC.ps1 deleted file mode 100644 index d574b34..0000000 --- a/ConfigureMSDTC.ps1 +++ /dev/null @@ -1,40 +0,0 @@ -# ———————————— -# Enable MSDTC -# ———————————— - -Write-Host "Enabling MSDTC..." -ForegroundColor Yellow -$DTCSecurity = "Incoming" -$RegPath = "HKLM:\SOFTWARE\Microsoft\MSDTC\" - -#Set Security and MSDTC path - -$RegSecurityPath = "$RegPath\Security" - -Set-ItemProperty –path $RegSecurityPath –name "NetworkDtcAccess" –value 1 -Set-ItemProperty –path $RegSecurityPath –name "NetworkDtcAccessClients" –value 1 -Set-ItemProperty –path $RegSecurityPath –name "NetworkDtcAccessTransactions" –value 1 -Set-ItemProperty –path $RegSecurityPath –name "NetworkDtcAccessInbound" –value 1 -Set-ItemProperty –path $RegSecurityPath –name "NetworkDtcAccessOutbound" –value 1 -Set-ItemProperty –path $RegSecurityPath –name "LuTransactions" –value 1 - -if ($DTCSecurity –eq "None") -{ - Set-ItemProperty –path $RegPath –name "TurnOffRpcSecurity" –value 1 - Set-ItemProperty –path $RegPath –name "AllowOnlySecureRpcCalls" –value 0 - Set-ItemProperty –path $RegPath –name "FallbackToUnsecureRPCIfNecessary" –value 0 -} -elseif ($DTCSecurity –eq "Incoming") -{ - Set-ItemProperty –path $RegPath –name "TurnOffRpcSecurity" –value 0 - Set-ItemProperty –path $RegPath –name "AllowOnlySecureRpcCalls" –value 0 - Set-ItemProperty –path $RegPath –name "FallbackToUnsecureRPCIfNecessary" –value 1 -} -else -{ - Set-ItemProperty –path $RegPath –name "TurnOffRpcSecurity" –value 0 - Set-ItemProperty –path $RegPath –name "AllowOnlySecureRpcCalls" –value 1 - Set-ItemProperty –path $RegPath –name "FallbackToUnsecureRPCIfNecessary" –value 0 -} - -Restart-Service MSDTC -Write-Host "——MSDTC has been configured—–" –foregroundcolor green diff --git a/appveyor.yml b/appveyor.yml index 05802e7..2bdfaba 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -11,7 +11,6 @@ services: - mysql - postgresql build_script: -- ps: ./ConfigureMSDTC.ps1 - ps: ./build.ps1 test: off artifacts: diff --git a/build/version.props b/build/version.props index fae43bc..d705cb4 100644 --- a/build/version.props +++ b/build/version.props @@ -1,8 +1,8 @@ 2 - 1 - 4 + 2 + 0 $(VersionMajor).$(VersionMinor).$(VersionPatch) diff --git a/samples/Sample.Kafka.SqlServer/AppDbContext.cs b/samples/Sample.Kafka.SqlServer/AppDbContext.cs deleted file mode 100644 index 78d6492..0000000 --- a/samples/Sample.Kafka.SqlServer/AppDbContext.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Microsoft.EntityFrameworkCore; - -namespace Sample.Kafka.SqlServer -{ - public class AppDbContext : DbContext - { - - protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) - { - //optionsBuilder.UseSqlServer("Server=192.168.2.206;Initial Catalog=Sample.Kafka.SqlServer;User Id=cmswuliu;Password=h7xY81agBn*Veiu3;MultipleActiveResultSets=True"); - optionsBuilder.UseSqlServer("Server=192.168.2.206;Initial Catalog=TestCap;User Id=cmswuliu;Password=h7xY81agBn*Veiu3;MultipleActiveResultSets=True"); - } - } -} diff --git a/samples/Sample.Kafka.SqlServer/CmsContentSerializer.cs b/samples/Sample.Kafka.SqlServer/CmsContentSerializer.cs deleted file mode 100644 index dbbd736..0000000 --- a/samples/Sample.Kafka.SqlServer/CmsContentSerializer.cs +++ /dev/null @@ -1,50 +0,0 @@ -using System; -using DotNetCore.CAP.Abstractions; -using DotNetCore.CAP.Models; -using Newtonsoft.Json; - -namespace Sample.RabbitMQ.SqlServer -{ - public class MessageContent : CapMessage - { - [JsonProperty("id")] - public override string Id { get; set; } - - [JsonProperty("createdTime")] - public override DateTime Timestamp { get; set; } - - [JsonProperty("msgBody")] - public override string Content { get; set; } - - [JsonProperty("callbackTopicName")] - public override string CallbackName { get; set; } - } - - public class MyMessagePacker : IMessagePacker - { - private readonly IContentSerializer _serializer; - - public MyMessagePacker(IContentSerializer serializer) - { - _serializer = serializer; - } - - public string Pack(CapMessage obj) - { - var content = new MessageContent - { - Id = obj.Id, - Content = obj.Content, - CallbackName = obj.CallbackName, - Timestamp = obj.Timestamp - }; - return _serializer.Serialize(content); - } - - public CapMessage UnPack(string packingMessage) - { - return _serializer.DeSerialize(packingMessage); - } - } -} - diff --git a/samples/Sample.Kafka.SqlServer/Controllers/ValuesController.cs b/samples/Sample.Kafka.SqlServer/Controllers/ValuesController.cs index 77a28b3..afcec67 100644 --- a/samples/Sample.Kafka.SqlServer/Controllers/ValuesController.cs +++ b/samples/Sample.Kafka.SqlServer/Controllers/ValuesController.cs @@ -1,137 +1,43 @@ using System; -using System.Diagnostics; using System.Threading.Tasks; using DotNetCore.CAP; -using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; -using Newtonsoft.Json; +using MySql.Data.MySqlClient; -namespace Sample.Kafka.SqlServer.Controllers +namespace Sample.Kafka.MySql.Controllers { - public class Person - { - [JsonProperty("id")] - public string Id { get; set; } - - [JsonProperty("uname")] - public string Name { get; set; } - - public HAHA Haha { get; set; } - - public override string ToString() - { - return "Name:" + Name + ";Id:" + Id + "Haha:" + Haha?.ToString(); - } - } - - public class HAHA - { - [JsonProperty("id")] - public string Id { get; set; } - - [JsonProperty("uname")] - public string Name { get; set; } - public override string ToString() - { - return "Name:" + Name + ";Id:" + Id; - } - } - - [Route("api/[controller]")] public class ValuesController : Controller, ICapSubscribe { private readonly ICapPublisher _capBus; - private readonly AppDbContext _dbContext; - public ValuesController(ICapPublisher producer, AppDbContext dbContext) + public ValuesController(ICapPublisher producer) { _capBus = producer; - _dbContext = dbContext; } - [Route("~/publish")] - public IActionResult PublishMessage() + public async Task PublishMessage() { - var p = new Person + using (var connection = new MySqlConnection("Server=192.168.10.110;Database=testcap;UserId=root;Password=123123;")) { - Id = Guid.NewGuid().ToString(), - Name = "æ¨æ™“东", - Haha = new HAHA - { - Id = Guid.NewGuid().ToString(), - Name = "1-1æ¨æ™“东", - } - }; - - _capBus.Publish("wl.yxd.test", p, "wl.yxd.test.callback"); - - - //_capBus.Publish("wl.cj.test", p); - return Ok(); - } - - [CapSubscribe("wl.yxd.test.callback")] - public void KafkaTestCallback(Person p) - { - Console.WriteLine("回调内容:" + p); - } + connection.Open(); + var transaction = connection.BeginTransaction(); + //your business code here - [CapSubscribe("wl.cj.test")] - public string KafkaTestReceived(Person person) - { - Console.WriteLine(person); - Debug.WriteLine(person); - return "this is callback message"; - } - - [Route("~/publishWithTrans")] - public async Task PublishMessageWithTransaction() - { - using (var trans = await _dbContext.Database.BeginTransactionAsync()) - { - await _capBus.PublishAsync("sample.rabbitmq.mysql", ""); + await _capBus.PublishAsync("xxx.xxx.test2", 123456, transaction); - trans.Commit(); + transaction.Commit(); } - return Ok(); - } - - [CapSubscribe("sample.rabbitmq.mysql33333", Group = "Test.Group")] - public void KafkaTest22(Person person) - { - var aa = _dbContext.Database; - - _dbContext.Dispose(); - Console.WriteLine("[sample.kafka.sqlserver] message received " + person.ToString()); - Debug.WriteLine("[sample.kafka.sqlserver] message received " + person.ToString()); - } - - //[CapSubscribe("sample.rabbitmq.mysql22222")] - //public void KafkaTest22(DateTime time) - //{ - // Console.WriteLine("[sample.kafka.sqlserver] message received " + time.ToString()); - // Debug.WriteLine("[sample.kafka.sqlserver] message received " + time.ToString()); - //} - - [CapSubscribe("sample.rabbitmq.mysql22222")] - public async Task KafkaTest33(DateTime time) - { - Console.WriteLine("[sample.kafka.sqlserver] message received " + time.ToString()); - Debug.WriteLine("[sample.kafka.sqlserver] message received " + time.ToString()); - return await Task.FromResult(time); + return Ok("publish successful!"); } - [NonAction] - [CapSubscribe("sample.kafka.sqlserver3")] - [CapSubscribe("sample.kafka.sqlserver4")] - public void KafkaTest() + [CapSubscribe("xxx.xxx.test2")] + public void Test2(int value) { - Console.WriteLine("[sample.kafka.sqlserver] message received"); - Debug.WriteLine("[sample.kafka.sqlserver] message received"); + Console.WriteLine(value); } } } \ No newline at end of file diff --git a/samples/Sample.Kafka.SqlServer/Program.cs b/samples/Sample.Kafka.SqlServer/Program.cs index c8cd00a..7d62e55 100644 --- a/samples/Sample.Kafka.SqlServer/Program.cs +++ b/samples/Sample.Kafka.SqlServer/Program.cs @@ -1,10 +1,7 @@ -using System.IO; -using Microsoft.AspNetCore; -using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; -namespace Sample.Kafka.SqlServer +namespace Sample.Kafka.MySql { public class Program { diff --git a/samples/Sample.Kafka.SqlServer/Properties/launchSettings.json b/samples/Sample.Kafka.SqlServer/Properties/launchSettings.json new file mode 100644 index 0000000..60815cf --- /dev/null +++ b/samples/Sample.Kafka.SqlServer/Properties/launchSettings.json @@ -0,0 +1,29 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:57172/", + "sslPort": 0 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "cap", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "Sample.Kafka.SqlServer": { + "commandName": "Project", + "launchBrowser": true, + "launchUrl": "cap", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "http://localhost:57174/" + } + } +} \ No newline at end of file diff --git a/samples/Sample.Kafka.SqlServer/Sample.Kafka.SqlServer.csproj b/samples/Sample.Kafka.SqlServer/Sample.Kafka.MySql.csproj similarity index 74% rename from samples/Sample.Kafka.SqlServer/Sample.Kafka.SqlServer.csproj rename to samples/Sample.Kafka.SqlServer/Sample.Kafka.MySql.csproj index 1441491..2123eaa 100644 --- a/samples/Sample.Kafka.SqlServer/Sample.Kafka.SqlServer.csproj +++ b/samples/Sample.Kafka.SqlServer/Sample.Kafka.MySql.csproj @@ -2,20 +2,21 @@ netcoreapp2.0 - Sample.Kafka.SqlServer + Sample.Kafka.MySql NU1701 NU1701 - + + - + diff --git a/samples/Sample.Kafka.SqlServer/Startup.cs b/samples/Sample.Kafka.SqlServer/Startup.cs index bc15d25..cdea862 100644 --- a/samples/Sample.Kafka.SqlServer/Startup.cs +++ b/samples/Sample.Kafka.SqlServer/Startup.cs @@ -1,23 +1,18 @@ using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Sample.RabbitMQ.SqlServer; -namespace Sample.Kafka.SqlServer +namespace Sample.Kafka.MySql { public class Startup { public void ConfigureServices(IServiceCollection services) { - services.AddDbContext(); - services.AddCap(x => { - x.UseEntityFramework(); - x.UseKafka("192.168.2.215:9092"); + x.UseMySql("Server=192.168.10.110;Database=testcap;UserId=root;Password=123123;"); + x.UseKafka("192.168.10.110:9092"); x.UseDashboard(); + //x.UseDiscovery(d => //{ // d.DiscoveryServerHostName = "localhost"; @@ -26,11 +21,12 @@ namespace Sample.Kafka.SqlServer // d.CurrentNodePort = 5820; // d.NodeName = "CAP 2å·èŠ‚点"; //}); - }).AddMessagePacker(); + }); + services.AddMvc(); } - public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) + public void Configure(IApplicationBuilder app) { app.UseMvc(); diff --git a/samples/Sample.RabbitMQ.MySql/Controllers/ValuesController.cs b/samples/Sample.RabbitMQ.MySql/Controllers/ValuesController.cs index 0b394cb..7b30772 100644 --- a/samples/Sample.RabbitMQ.MySql/Controllers/ValuesController.cs +++ b/samples/Sample.RabbitMQ.MySql/Controllers/ValuesController.cs @@ -1,7 +1,4 @@ using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; using System.Threading.Tasks; using DotNetCore.CAP; using Microsoft.AspNetCore.Mvc; diff --git a/samples/Sample.RabbitMQ.MySql/Properties/launchSettings.json b/samples/Sample.RabbitMQ.MySql/Properties/launchSettings.json new file mode 100644 index 0000000..369b41c --- /dev/null +++ b/samples/Sample.RabbitMQ.MySql/Properties/launchSettings.json @@ -0,0 +1,29 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:57171/", + "sslPort": 0 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "cap", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "Sample.RabbitMQ.MySql": { + "commandName": "Project", + "launchBrowser": true, + "launchUrl": "cap", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "http://localhost:57173/" + } + } +} \ No newline at end of file diff --git a/samples/Sample.RabbitMQ.MySql/Startup.cs b/samples/Sample.RabbitMQ.MySql/Startup.cs index 12a2986..6525770 100644 --- a/samples/Sample.RabbitMQ.MySql/Startup.cs +++ b/samples/Sample.RabbitMQ.MySql/Startup.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; diff --git a/samples/Sample.RabbitMQ.PostgreSql/AppDbContext.cs b/samples/Sample.RabbitMQ.PostgreSql/AppDbContext.cs deleted file mode 100644 index d4bde28..0000000 --- a/samples/Sample.RabbitMQ.PostgreSql/AppDbContext.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.EntityFrameworkCore; - -namespace Sample.RabbitMQ.PostgreSql -{ - public class AppDbContext : DbContext - { - protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) - { - optionsBuilder.UseNpgsql("Server=localhost;Database=Sample.RabbitMQ.PostgreSql;UserId=postgres;Password=123123;"); - } - } -} diff --git a/samples/Sample.RabbitMQ.PostgreSql/Controllers/ValuesController.cs b/samples/Sample.RabbitMQ.PostgreSql/Controllers/ValuesController.cs deleted file mode 100644 index 10ea6d1..0000000 --- a/samples/Sample.RabbitMQ.PostgreSql/Controllers/ValuesController.cs +++ /dev/null @@ -1,59 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Threading.Tasks; -using DotNetCore.CAP; -using Microsoft.AspNetCore.Mvc; - -namespace Sample.RabbitMQ.PostgreSql.Controllers -{ - [Route("api/[controller]")] - public class ValuesController : Controller - { - private readonly AppDbContext _dbContext; - private readonly ICapPublisher _capBus; - - public ValuesController(AppDbContext dbContext, ICapPublisher capPublisher) - { - _dbContext = dbContext; - _capBus = capPublisher; - } - - [Route("~/publish")] - public IActionResult PublishMessage() - { - _capBus.Publish("sample.rabbitmq.mysql", DateTime.Now); - - return Ok(); - } - - - [Route("~/publish2")] - public IActionResult PublishMessage2() - { - _capBus.Publish("sample.kafka.sqlserver4", DateTime.Now); - - return Ok(); - } - - [Route("~/publishWithTrans")] - public async Task PublishMessageWithTransaction() - { - using (var trans = await _dbContext.Database.BeginTransactionAsync()) - { - await _capBus.PublishAsync("sample.kafka.sqlserver", ""); - - trans.Commit(); - } - return Ok(); - } - - [NonAction] - [CapSubscribe("sample.rabbitmq.mysql")] - public void ReceiveMessage() - { - Console.WriteLine("[sample.rabbitmq.mysql] message received"); - Debug.WriteLine("[sample.rabbitmq.mysql] message received"); - } - } -} diff --git a/samples/Sample.RabbitMQ.PostgreSql/Program.cs b/samples/Sample.RabbitMQ.PostgreSql/Program.cs deleted file mode 100644 index 3cb0755..0000000 --- a/samples/Sample.RabbitMQ.PostgreSql/Program.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Logging; - -namespace Sample.RabbitMQ.PostgreSql -{ - public class Program - { - public static void Main(string[] args) - { - BuildWebHost(args).Run(); - } - - public static IWebHost BuildWebHost(string[] args) => - WebHost.CreateDefaultBuilder(args) - .UseStartup() - .Build(); - } -} diff --git a/samples/Sample.RabbitMQ.PostgreSql/Sample.RabbitMQ.PostgreSql.csproj b/samples/Sample.RabbitMQ.PostgreSql/Sample.RabbitMQ.PostgreSql.csproj deleted file mode 100644 index f2fe0c5..0000000 --- a/samples/Sample.RabbitMQ.PostgreSql/Sample.RabbitMQ.PostgreSql.csproj +++ /dev/null @@ -1,18 +0,0 @@ - - - - netcoreapp2.0 - - - - - - - - - - - - - - diff --git a/samples/Sample.RabbitMQ.PostgreSql/Startup.cs b/samples/Sample.RabbitMQ.PostgreSql/Startup.cs deleted file mode 100644 index b0477c0..0000000 --- a/samples/Sample.RabbitMQ.PostgreSql/Startup.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.DependencyInjection; - -namespace Sample.RabbitMQ.PostgreSql -{ - public class Startup - { - // This method gets called by the runtime. Use this method to add services to the container. - public void ConfigureServices(IServiceCollection services) - { - services.AddDbContext(); - services.AddCap(x => - { - x.UseEntityFramework(); - x.UseRabbitMQ("localhost"); - x.UseDashboard(); - x.UseDiscovery(d => - { - d.DiscoveryServerHostName = "localhost"; - d.DiscoveryServerPort = 8500; - d.CurrentNodeHostName = "localhost"; - d.CurrentNodePort = 5800; - d.NodeName = "CAP 一å·èŠ‚点"; - }); - }); - services.AddMvc(); - } - - // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - public void Configure(IApplicationBuilder app, IHostingEnvironment env) - { - app.UseMvc(); - - app.UseCap(); - } - } -} diff --git a/samples/Sample.RabbitMQ.SqlServer/AppDbContext.cs b/samples/Sample.RabbitMQ.SqlServer/AppDbContext.cs deleted file mode 100644 index 1ba1fdb..0000000 --- a/samples/Sample.RabbitMQ.SqlServer/AppDbContext.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using Sample.RabbitMQ.SqlServer.Controllers; - -namespace Sample.RabbitMQ.SqlServer -{ - public class AppDbContext : DbContext - { - - public DbSet Persons { get; set; } - - protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) - { - //optionsBuilder.UseSqlServer("Server=192.168.2.206;Initial Catalog=TestCap;User Id=cmswuliu;Password=h7xY81agBn*Veiu3;MultipleActiveResultSets=True"); - optionsBuilder.UseSqlServer("Server=DESKTOP-M9R8T31;Initial Catalog=Sample.Kafka.SqlServer;User Id=sa;Password=P@ssw0rd;MultipleActiveResultSets=True"); - } - } -} diff --git a/samples/Sample.RabbitMQ.SqlServer/Controllers/ValuesController.cs b/samples/Sample.RabbitMQ.SqlServer/Controllers/ValuesController.cs deleted file mode 100644 index 6c0ccef..0000000 --- a/samples/Sample.RabbitMQ.SqlServer/Controllers/ValuesController.cs +++ /dev/null @@ -1,99 +0,0 @@ -using System; -using System.Diagnostics; -using System.Threading.Tasks; -using DotNetCore.CAP; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; - -namespace Sample.RabbitMQ.SqlServer.Controllers -{ - public class Person - { - public int Id { get; set; } - public string Name { get; set; } - public int Age { get; set; } - - public override string ToString() - { - return "Name:" + Name + ";Age:" + Age; - } - } - - - [Route("api/[controller]")] - public class ValuesController : Controller, ICapSubscribe - { - private readonly ICapPublisher _capBus; - private readonly AppDbContext _dbContext; - - public ValuesController(ICapPublisher producer, AppDbContext dbContext) - { - _capBus = producer; - _dbContext = dbContext; - } - - [Route("~/publish")] - public IActionResult PublishMessage() - { - - _capBus.Publish("sample.rabbitmq.sqlserver.order.check", DateTime.Now); - - //var person = new Person - //{ - // Name = "æ¨æ™“东", - // Age = 11, - // Id = 23 - //}; - //_capBus.Publish("sample.rabbitmq.mysql33333", person); - - return Ok(); - } - - [Route("~/publishWithTrans")] - public async Task PublishMessageWithTransaction() - { - using (var trans = await _dbContext.Database.BeginTransactionAsync()) - { - await _capBus.PublishAsync("sample.rabbitmq.mysql", ""); - - trans.Commit(); - } - return Ok(); - } - - [CapSubscribe("sample.rabbitmq.mysql33333",Group ="Test.Group")] - public void KafkaTest22(Person person) - { - var aa = _dbContext.Database; - - _dbContext.Dispose(); - - Console.WriteLine("[sample.kafka.sqlserver] message received " + person.ToString()); - Debug.WriteLine("[sample.kafka.sqlserver] message received " + person.ToString()); - } - - //[CapSubscribe("sample.rabbitmq.mysql22222")] - //public void KafkaTest22(DateTime time) - //{ - // Console.WriteLine("[sample.kafka.sqlserver] message received " + time.ToString()); - // Debug.WriteLine("[sample.kafka.sqlserver] message received " + time.ToString()); - //} - - [CapSubscribe("sample.rabbitmq.mysql22222")] - public async Task KafkaTest33(DateTime time) - { - Console.WriteLine("[sample.kafka.sqlserver] message received " + time.ToString()); - Debug.WriteLine("[sample.kafka.sqlserver] message received " + time.ToString()); - return await Task.FromResult(time); - } - - [NonAction] - [CapSubscribe("sample.kafka.sqlserver3")] - [CapSubscribe("sample.kafka.sqlserver4")] - public void KafkaTest() - { - Console.WriteLine("[sample.kafka.sqlserver] message received"); - Debug.WriteLine("[sample.kafka.sqlserver] message received"); - } - } -} \ No newline at end of file diff --git a/samples/Sample.RabbitMQ.SqlServer/Migrations/20170824130007_AddPersons.Designer.cs b/samples/Sample.RabbitMQ.SqlServer/Migrations/20170824130007_AddPersons.Designer.cs deleted file mode 100644 index 3d8122b..0000000 --- a/samples/Sample.RabbitMQ.SqlServer/Migrations/20170824130007_AddPersons.Designer.cs +++ /dev/null @@ -1,40 +0,0 @@ -// -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage; -using Microsoft.EntityFrameworkCore.Storage.Internal; -using Sample.RabbitMQ.SqlServer; -using System; - -namespace Sample.RabbitMQ.SqlServer.Migrations -{ - [DbContext(typeof(AppDbContext))] - [Migration("20170824130007_AddPersons")] - partial class AddPersons - { - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "2.0.0-rtm-26452") - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - - modelBuilder.Entity("Sample.RabbitMQ.SqlServer.Controllers.Person", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("Age"); - - b.Property("Name"); - - b.HasKey("Id"); - - b.ToTable("Persons"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/samples/Sample.RabbitMQ.SqlServer/Migrations/20170824130007_AddPersons.cs b/samples/Sample.RabbitMQ.SqlServer/Migrations/20170824130007_AddPersons.cs deleted file mode 100644 index 2fc048b..0000000 --- a/samples/Sample.RabbitMQ.SqlServer/Migrations/20170824130007_AddPersons.cs +++ /dev/null @@ -1,33 +0,0 @@ -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using System; -using System.Collections.Generic; - -namespace Sample.RabbitMQ.SqlServer.Migrations -{ - public partial class AddPersons : Migration - { - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateTable( - name: "Persons", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn), - Age = table.Column(type: "int", nullable: false), - Name = table.Column(type: "nvarchar(max)", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_Persons", x => x.Id); - }); - } - - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "Persons"); - } - } -} diff --git a/samples/Sample.RabbitMQ.SqlServer/Migrations/AppDbContextModelSnapshot.cs b/samples/Sample.RabbitMQ.SqlServer/Migrations/AppDbContextModelSnapshot.cs deleted file mode 100644 index 0a20ac3..0000000 --- a/samples/Sample.RabbitMQ.SqlServer/Migrations/AppDbContextModelSnapshot.cs +++ /dev/null @@ -1,39 +0,0 @@ -// -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage; -using Microsoft.EntityFrameworkCore.Storage.Internal; -using Sample.RabbitMQ.SqlServer; -using System; - -namespace Sample.RabbitMQ.SqlServer.Migrations -{ - [DbContext(typeof(AppDbContext))] - partial class AppDbContextModelSnapshot : ModelSnapshot - { - protected override void BuildModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "2.0.0-rtm-26452") - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - - modelBuilder.Entity("Sample.RabbitMQ.SqlServer.Controllers.Person", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("Age"); - - b.Property("Name"); - - b.HasKey("Id"); - - b.ToTable("Persons"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/samples/Sample.RabbitMQ.SqlServer/Program.cs b/samples/Sample.RabbitMQ.SqlServer/Program.cs deleted file mode 100644 index ebbd50e..0000000 --- a/samples/Sample.RabbitMQ.SqlServer/Program.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System.IO; -using Microsoft.AspNetCore; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; - -namespace Sample.RabbitMQ.SqlServer -{ - public class Program - { - - public static void Main(string[] args) - { - BuildWebHost(args).Run(); - } - - public static IWebHost BuildWebHost(string[] args) => - WebHost.CreateDefaultBuilder(args) - .UseUrls("http://*:5800") - .UseStartup() - .Build(); - - } -} \ No newline at end of file diff --git a/samples/Sample.RabbitMQ.SqlServer/Sample.RabbitMQ.SqlServer.csproj b/samples/Sample.RabbitMQ.SqlServer/Sample.RabbitMQ.SqlServer.csproj deleted file mode 100644 index a32da56..0000000 --- a/samples/Sample.RabbitMQ.SqlServer/Sample.RabbitMQ.SqlServer.csproj +++ /dev/null @@ -1,20 +0,0 @@ - - - - netcoreapp2.0 - Sample.RabbitMQ.SqlServer - - - - - - - - - - - - - - - diff --git a/samples/Sample.RabbitMQ.SqlServer/Services/ICmsService.cs b/samples/Sample.RabbitMQ.SqlServer/Services/ICmsService.cs deleted file mode 100644 index 5119cce..0000000 --- a/samples/Sample.RabbitMQ.SqlServer/Services/ICmsService.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using DotNetCore.CAP; - -namespace Sample.RabbitMQ.SqlServer.Services -{ - public interface ICmsService - { - void Add(); - } -} diff --git a/samples/Sample.RabbitMQ.SqlServer/Services/IOrderService.cs b/samples/Sample.RabbitMQ.SqlServer/Services/IOrderService.cs deleted file mode 100644 index 9f8e3ad..0000000 --- a/samples/Sample.RabbitMQ.SqlServer/Services/IOrderService.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace Sample.RabbitMQ.SqlServer.Services -{ - public interface IOrderService - { - void Check(); - } -} diff --git a/samples/Sample.RabbitMQ.SqlServer/Services/Impl/CmsService.cs b/samples/Sample.RabbitMQ.SqlServer/Services/Impl/CmsService.cs deleted file mode 100644 index b8c7800..0000000 --- a/samples/Sample.RabbitMQ.SqlServer/Services/Impl/CmsService.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using DotNetCore.CAP; - -namespace Sample.RabbitMQ.SqlServer.Services.Impl -{ - public class CmsService : ICmsService, ICapSubscribe - { - public void Add() - { - throw new NotImplementedException(); - } - } -} diff --git a/samples/Sample.RabbitMQ.SqlServer/Services/Impl/OrderService.cs b/samples/Sample.RabbitMQ.SqlServer/Services/Impl/OrderService.cs deleted file mode 100644 index 81d0eb7..0000000 --- a/samples/Sample.RabbitMQ.SqlServer/Services/Impl/OrderService.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using DotNetCore.CAP; - -namespace Sample.RabbitMQ.SqlServer.Services.Impl -{ - public class OrderService : IOrderService, ICapSubscribe - { - [CapSubscribe("sample.rabbitmq.sqlserver.order.check")] - public void Check() - { - Console.WriteLine("out"); - } - } -} diff --git a/samples/Sample.RabbitMQ.SqlServer/Startup.cs b/samples/Sample.RabbitMQ.SqlServer/Startup.cs deleted file mode 100644 index 4821b97..0000000 --- a/samples/Sample.RabbitMQ.SqlServer/Startup.cs +++ /dev/null @@ -1,49 +0,0 @@ -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Sample.RabbitMQ.SqlServer.Services; -using Sample.RabbitMQ.SqlServer.Services.Impl; - -namespace Sample.RabbitMQ.SqlServer -{ - public class Startup - { - public void ConfigureServices(IServiceCollection services) - { - services.AddDbContext(); - - services.AddScoped(); - services.AddTransient(); - - services.AddCap(x => - { - x.UseEntityFramework(); - x.UseRabbitMQ("localhost"); - x.UseDashboard(); - x.UseDiscovery(d => - { - d.DiscoveryServerHostName = "localhost"; - d.DiscoveryServerPort = 8500; - - d.CurrentNodeHostName = "192.168.1.11"; - d.CurrentNodePort = 5800; - d.NodeName = "CAP Node Windows"; - d.NodeId = 1; - }); - }); - - services.AddMvc(); - } - - public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) - { - loggerFactory.AddConsole(); - loggerFactory.AddDebug(); - - app.UseMvc(); - - app.UseCap(); - } - } -} \ No newline at end of file diff --git a/src/DotNetCore.CAP.Kafka/CAP.KafkaCapOptionsExtension.cs b/src/DotNetCore.CAP.Kafka/CAP.KafkaCapOptionsExtension.cs index b2e2129..04493f5 100644 --- a/src/DotNetCore.CAP.Kafka/CAP.KafkaCapOptionsExtension.cs +++ b/src/DotNetCore.CAP.Kafka/CAP.KafkaCapOptionsExtension.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using DotNetCore.CAP.Kafka; using Microsoft.Extensions.DependencyInjection; @@ -23,8 +26,8 @@ namespace DotNetCore.CAP services.AddSingleton(kafkaOptions); services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); services.AddSingleton(); } } diff --git a/src/DotNetCore.CAP.Kafka/CAP.KafkaOptions.cs b/src/DotNetCore.CAP.Kafka/CAP.KafkaOptions.cs index 1b1f687..629963b 100644 --- a/src/DotNetCore.CAP.Kafka/CAP.KafkaOptions.cs +++ b/src/DotNetCore.CAP.Kafka/CAP.KafkaOptions.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; @@ -45,16 +48,19 @@ namespace DotNetCore.CAP if (_kafkaConfig == null) { if (string.IsNullOrWhiteSpace(Servers)) + { throw new ArgumentNullException(nameof(Servers)); + } MainConfig["bootstrap.servers"] = Servers; MainConfig["queue.buffering.max.ms"] = "10"; MainConfig["socket.blocking.max.ms"] = "10"; MainConfig["enable.auto.commit"] = "false"; MainConfig["log.connection.close"] = "false"; - + _kafkaConfig = MainConfig.AsEnumerable(); } + return _kafkaConfig; } } diff --git a/src/DotNetCore.CAP.Kafka/CAP.Options.Extensions.cs b/src/DotNetCore.CAP.Kafka/CAP.Options.Extensions.cs index be980d6..e32278e 100644 --- a/src/DotNetCore.CAP.Kafka/CAP.Options.Extensions.cs +++ b/src/DotNetCore.CAP.Kafka/CAP.Options.Extensions.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using DotNetCore.CAP; // ReSharper disable once CheckNamespace @@ -24,7 +27,10 @@ namespace Microsoft.Extensions.DependencyInjection /// public static CapOptions UseKafka(this CapOptions options, Action configure) { - if (configure == null) throw new ArgumentNullException(nameof(configure)); + if (configure == null) + { + throw new ArgumentNullException(nameof(configure)); + } options.RegisterExtension(new KafkaCapOptionsExtension(configure)); diff --git a/src/DotNetCore.CAP.Kafka/CAP.SubscribeAttribute.cs b/src/DotNetCore.CAP.Kafka/CAP.SubscribeAttribute.cs index 161b073..ad4021e 100644 --- a/src/DotNetCore.CAP.Kafka/CAP.SubscribeAttribute.cs +++ b/src/DotNetCore.CAP.Kafka/CAP.SubscribeAttribute.cs @@ -1,4 +1,7 @@ -using DotNetCore.CAP.Abstractions; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using DotNetCore.CAP.Abstractions; // ReSharper disable once CheckNamespace namespace DotNetCore.CAP diff --git a/src/DotNetCore.CAP.Kafka/ConnectionPool.cs b/src/DotNetCore.CAP.Kafka/ConnectionPool.cs index 3746439..6879942 100644 --- a/src/DotNetCore.CAP.Kafka/ConnectionPool.cs +++ b/src/DotNetCore.CAP.Kafka/ConnectionPool.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using System.Collections.Concurrent; using System.Diagnostics; using System.Threading; @@ -35,7 +38,9 @@ namespace DotNetCore.CAP.Kafka _maxSize = 0; while (_pool.TryDequeue(out var context)) + { context.Dispose(); + } } private static Func CreateActivator(KafkaOptions options) diff --git a/src/DotNetCore.CAP.Kafka/IConnectionPool.cs b/src/DotNetCore.CAP.Kafka/IConnectionPool.cs index 36d5912..7fb01a5 100644 --- a/src/DotNetCore.CAP.Kafka/IConnectionPool.cs +++ b/src/DotNetCore.CAP.Kafka/IConnectionPool.cs @@ -1,4 +1,6 @@ - +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + using Confluent.Kafka; namespace DotNetCore.CAP.Kafka diff --git a/src/DotNetCore.CAP.Kafka/IPublishMessageSender.Kafka.cs b/src/DotNetCore.CAP.Kafka/IPublishMessageSender.Kafka.cs new file mode 100644 index 0000000..cda5755 --- /dev/null +++ b/src/DotNetCore.CAP.Kafka/IPublishMessageSender.Kafka.cs @@ -0,0 +1,65 @@ +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; +using System.Diagnostics; +using System.Text; +using System.Threading.Tasks; +using DotNetCore.CAP.Internal; +using DotNetCore.CAP.Processor.States; +using Microsoft.Extensions.Logging; + +namespace DotNetCore.CAP.Kafka +{ + internal class KafkaPublishMessageSender : BasePublishMessageSender + { + private readonly ConnectionPool _connectionPool; + private readonly ILogger _logger; + + public KafkaPublishMessageSender( + CapOptions options, IStateChanger stateChanger, IStorageConnection connection, + ConnectionPool connectionPool, ILogger logger) + : base(logger, options, connection, stateChanger) + { + _logger = logger; + _connectionPool = connectionPool; + } + + public override async Task PublishAsync(string keyName, string content) + { + var producer = _connectionPool.Rent(); + + try + { + var contentBytes = Encoding.UTF8.GetBytes(content); + var message = await producer.ProduceAsync(keyName, null, contentBytes); + + if (message.Error.HasError) + { + return OperateResult.Failed(new OperateError + { + Code = message.Error.Code.ToString(), + Description = message.Error.Reason + }); + } + + _logger.LogDebug($"kafka topic message [{keyName}] has been published."); + + return OperateResult.Success; + } + catch (Exception ex) + { + var wapperEx = new PublisherSentFailedException(ex.Message, ex); + return OperateResult.Failed(wapperEx); + } + finally + { + var returned = _connectionPool.Return(producer); + if (!returned) + { + producer.Dispose(); + } + } + } + } +} \ No newline at end of file diff --git a/src/DotNetCore.CAP.Kafka/KafkaConsumerClient.cs b/src/DotNetCore.CAP.Kafka/KafkaConsumerClient.cs index 241870e..ccce448 100644 --- a/src/DotNetCore.CAP.Kafka/KafkaConsumerClient.cs +++ b/src/DotNetCore.CAP.Kafka/KafkaConsumerClient.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using System.Collections.Generic; using System.Text; using System.Threading; @@ -29,10 +32,14 @@ namespace DotNetCore.CAP.Kafka public void Subscribe(IEnumerable topics) { if (topics == null) + { throw new ArgumentNullException(nameof(topics)); + } if (_consumerClient == null) + { InitKafkaClient(); + } _consumerClient.Subscribe(topics); } @@ -44,6 +51,7 @@ namespace DotNetCore.CAP.Kafka cancellationToken.ThrowIfCancellationRequested(); _consumerClient.Poll(timeout); } + // ReSharper disable once FunctionNeverReturns } diff --git a/src/DotNetCore.CAP.Kafka/KafkaConsumerClientFactory.cs b/src/DotNetCore.CAP.Kafka/KafkaConsumerClientFactory.cs index 8bda50a..54ddc5b 100644 --- a/src/DotNetCore.CAP.Kafka/KafkaConsumerClientFactory.cs +++ b/src/DotNetCore.CAP.Kafka/KafkaConsumerClientFactory.cs @@ -1,4 +1,7 @@ -namespace DotNetCore.CAP.Kafka +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +namespace DotNetCore.CAP.Kafka { internal sealed class KafkaConsumerClientFactory : IConsumerClientFactory { diff --git a/src/DotNetCore.CAP.Kafka/PublishQueueExecutor.cs b/src/DotNetCore.CAP.Kafka/PublishQueueExecutor.cs deleted file mode 100644 index 6bbda6b..0000000 --- a/src/DotNetCore.CAP.Kafka/PublishQueueExecutor.cs +++ /dev/null @@ -1,62 +0,0 @@ -using System; -using System.Text; -using System.Threading.Tasks; -using DotNetCore.CAP.Processor.States; -using Microsoft.Extensions.Logging; - -namespace DotNetCore.CAP.Kafka -{ - internal class PublishQueueExecutor : BasePublishQueueExecutor - { - private readonly ConnectionPool _connectionPool; - private readonly ILogger _logger; - - public PublishQueueExecutor( - CapOptions options, - IStateChanger stateChanger, - ConnectionPool connectionPool, - ILogger logger) - : base(options, stateChanger, logger) - { - _logger = logger; - _connectionPool = connectionPool; - } - - public override async Task PublishAsync(string keyName, string content) - { - var producer = _connectionPool.Rent(); - try - { - var contentBytes = Encoding.UTF8.GetBytes(content); - - var message = await producer.ProduceAsync(keyName, null, contentBytes); - - if (!message.Error.HasError) - { - _logger.LogDebug($"kafka topic message [{keyName}] has been published."); - - return OperateResult.Success; - } - return OperateResult.Failed(new OperateError - { - Code = message.Error.Code.ToString(), - Description = message.Error.Reason - }); - - } - catch (Exception ex) - { - _logger.LogError(ex, - $"An error occurred during sending the topic message to kafka. Topic:[{keyName}], Exception: {ex.Message}"); - - return OperateResult.Failed(ex); - } - finally - { - var returned = _connectionPool.Return(producer); - if (!returned) - producer.Dispose(); - } - } - } -} \ No newline at end of file diff --git a/src/DotNetCore.CAP.MySql/CAP.EFOptions.cs b/src/DotNetCore.CAP.MySql/CAP.EFOptions.cs index 390365b..c2b9983 100644 --- a/src/DotNetCore.CAP.MySql/CAP.EFOptions.cs +++ b/src/DotNetCore.CAP.MySql/CAP.EFOptions.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; // ReSharper disable once CheckNamespace namespace DotNetCore.CAP diff --git a/src/DotNetCore.CAP.MySql/CAP.MySqlCapOptionsExtension.cs b/src/DotNetCore.CAP.MySql/CAP.MySqlCapOptionsExtension.cs index a0bf29d..6e757ab 100644 --- a/src/DotNetCore.CAP.MySql/CAP.MySqlCapOptionsExtension.cs +++ b/src/DotNetCore.CAP.MySql/CAP.MySqlCapOptionsExtension.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using DotNetCore.CAP.MySql; using DotNetCore.CAP.Processor; using Microsoft.EntityFrameworkCore; @@ -41,7 +44,7 @@ namespace DotNetCore.CAP using (var scope = x.CreateScope()) { var provider = scope.ServiceProvider; - var dbContext = (DbContext)provider.GetService(mysqlOptions.DbContextType); + var dbContext = (DbContext) provider.GetService(mysqlOptions.DbContextType); mysqlOptions.ConnectionString = dbContext.Database.GetDbConnection().ConnectionString; return mysqlOptions; } diff --git a/src/DotNetCore.CAP.MySql/CAP.MySqlOptions.cs b/src/DotNetCore.CAP.MySql/CAP.MySqlOptions.cs index c75ae6a..bcac400 100644 --- a/src/DotNetCore.CAP.MySql/CAP.MySqlOptions.cs +++ b/src/DotNetCore.CAP.MySql/CAP.MySqlOptions.cs @@ -1,4 +1,6 @@ -// ReSharper disable once CheckNamespace +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + namespace DotNetCore.CAP { public class MySqlOptions : EFOptions diff --git a/src/DotNetCore.CAP.MySql/CAP.Options.Extensions.cs b/src/DotNetCore.CAP.MySql/CAP.Options.Extensions.cs index 1949c82..f59b75d 100644 --- a/src/DotNetCore.CAP.MySql/CAP.Options.Extensions.cs +++ b/src/DotNetCore.CAP.MySql/CAP.Options.Extensions.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using DotNetCore.CAP; using Microsoft.EntityFrameworkCore; @@ -14,7 +17,10 @@ namespace Microsoft.Extensions.DependencyInjection public static CapOptions UseMySql(this CapOptions options, Action configure) { - if (configure == null) throw new ArgumentNullException(nameof(configure)); + if (configure == null) + { + throw new ArgumentNullException(nameof(configure)); + } options.RegisterExtension(new MySqlCapOptionsExtension(configure)); @@ -31,7 +37,10 @@ namespace Microsoft.Extensions.DependencyInjection public static CapOptions UseEntityFramework(this CapOptions options, Action configure) where TContext : DbContext { - if (configure == null) throw new ArgumentNullException(nameof(configure)); + if (configure == null) + { + throw new ArgumentNullException(nameof(configure)); + } options.RegisterExtension(new MySqlCapOptionsExtension(x => { diff --git a/src/DotNetCore.CAP.MySql/CapPublisher.cs b/src/DotNetCore.CAP.MySql/CapPublisher.cs index 25f5127..662e257 100644 --- a/src/DotNetCore.CAP.MySql/CapPublisher.cs +++ b/src/DotNetCore.CAP.MySql/CapPublisher.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using System.Data; using System.Threading.Tasks; using Dapper; @@ -14,27 +17,31 @@ namespace DotNetCore.CAP.MySql public class CapPublisher : CapPublisherBase, ICallbackPublisher { private readonly DbContext _dbContext; - private readonly ILogger _logger; private readonly MySqlOptions _options; - public CapPublisher(IServiceProvider provider, - ILogger logger, + public CapPublisher(ILogger logger, IDispatcher dispatcher, IServiceProvider provider, MySqlOptions options) + : base(logger, dispatcher) { ServiceProvider = provider; _options = options; - _logger = logger; - if (_options.DbContextType == null) return; + if (_options.DbContextType == null) + { + return; + } + IsUsingEF = true; _dbContext = (DbContext) ServiceProvider.GetService(_options.DbContextType); } - public async Task PublishAsync(CapPublishedMessage message) + public async Task PublishCallbackAsync(CapPublishedMessage message) { using (var conn = new MySqlConnection(_options.ConnectionString)) { - await conn.ExecuteAsync(PrepareSql(), message); + var id = await conn.ExecuteScalarAsync(PrepareSql(), message); + message.Id = id; + Enqueue(message); } } @@ -51,23 +58,20 @@ namespace DotNetCore.CAP.MySql dbContextTransaction = _dbContext.Database.BeginTransaction(IsolationLevel.ReadCommitted); dbTrans = dbContextTransaction.GetDbTransaction(); } + DbTransaction = dbTrans; } - protected override void Execute(IDbConnection dbConnection, IDbTransaction dbTransaction, + protected override int Execute(IDbConnection dbConnection, IDbTransaction dbTransaction, CapPublishedMessage message) { - dbConnection.Execute(PrepareSql(), message, dbTransaction); - - _logger.LogInformation("Published Message has been persisted in the database. name:" + message); + return dbConnection.ExecuteScalar(PrepareSql(), message, dbTransaction); } - protected override async Task ExecuteAsync(IDbConnection dbConnection, IDbTransaction dbTransaction, + protected override async Task ExecuteAsync(IDbConnection dbConnection, IDbTransaction dbTransaction, CapPublishedMessage message) { - await dbConnection.ExecuteAsync(PrepareSql(), message, dbTransaction); - - _logger.LogInformation("Published Message has been persisted in the database. name:" + message); + return await dbConnection.ExecuteScalarAsync(PrepareSql(), message, dbTransaction); } #region private methods @@ -75,7 +79,7 @@ namespace DotNetCore.CAP.MySql private string PrepareSql() { return - $"INSERT INTO `{_options.TableNamePrefix}.published` (`Name`,`Content`,`Retries`,`Added`,`ExpiresAt`,`StatusName`)VALUES(@Name,@Content,@Retries,@Added,@ExpiresAt,@StatusName)"; + $"INSERT INTO `{_options.TableNamePrefix}.published` (`Name`,`Content`,`Retries`,`Added`,`ExpiresAt`,`StatusName`)VALUES(@Name,@Content,@Retries,@Added,@ExpiresAt,@StatusName);SELECT LAST_INSERT_ID()"; } #endregion private methods diff --git a/src/DotNetCore.CAP.MySql/FetchedMessage.cs b/src/DotNetCore.CAP.MySql/FetchedMessage.cs deleted file mode 100644 index a0f3b8e..0000000 --- a/src/DotNetCore.CAP.MySql/FetchedMessage.cs +++ /dev/null @@ -1,11 +0,0 @@ -using DotNetCore.CAP.Models; - -namespace DotNetCore.CAP.MySql -{ - internal class FetchedMessage - { - public int MessageId { get; set; } - - public MessageType MessageType { get; set; } - } -} \ No newline at end of file diff --git a/src/DotNetCore.CAP.MySql/IAdditionalProcessor.Default.cs b/src/DotNetCore.CAP.MySql/IAdditionalProcessor.Default.cs index a59d9e2..fd892cb 100644 --- a/src/DotNetCore.CAP.MySql/IAdditionalProcessor.Default.cs +++ b/src/DotNetCore.CAP.MySql/IAdditionalProcessor.Default.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using System.Threading.Tasks; using Dapper; using DotNetCore.CAP.Processor; diff --git a/src/DotNetCore.CAP.MySql/MySqlFetchedMessage.cs b/src/DotNetCore.CAP.MySql/MySqlFetchedMessage.cs deleted file mode 100644 index c4ab741..0000000 --- a/src/DotNetCore.CAP.MySql/MySqlFetchedMessage.cs +++ /dev/null @@ -1,48 +0,0 @@ -using Dapper; -using DotNetCore.CAP.Models; -using MySql.Data.MySqlClient; - -namespace DotNetCore.CAP.MySql -{ - public class MySqlFetchedMessage : IFetchedMessage - { - private readonly MySqlOptions _options; - private readonly string _processId; - - public MySqlFetchedMessage(int messageId, MessageType type, string processId, MySqlOptions options) - { - MessageId = messageId; - MessageType = type; - - _processId = processId; - _options = options; - } - - public int MessageId { get; } - - public MessageType MessageType { get; } - - public void RemoveFromQueue() - { - using (var connection = new MySqlConnection(_options.ConnectionString)) - { - connection.Execute($"DELETE FROM `{_options.TableNamePrefix}.queue` WHERE `ProcessId`=@ProcessId" - , new { ProcessId = _processId }); - } - } - - public void Requeue() - { - using (var connection = new MySqlConnection(_options.ConnectionString)) - { - connection.Execute($"UPDATE `{_options.TableNamePrefix}.queue` SET `ProcessId`=NULL WHERE `ProcessId`=@ProcessId" - , new { ProcessId = _processId }); - } - } - - public void Dispose() - { - // ignored - } - } -} \ No newline at end of file diff --git a/src/DotNetCore.CAP.MySql/MySqlMonitoringApi.cs b/src/DotNetCore.CAP.MySql/MySqlMonitoringApi.cs index 49fd270..6fd0888 100644 --- a/src/DotNetCore.CAP.MySql/MySqlMonitoringApi.cs +++ b/src/DotNetCore.CAP.MySql/MySqlMonitoringApi.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using System.Collections.Generic; using System.Data; using System.Linq; @@ -28,9 +31,7 @@ set transaction isolation level read committed; select count(Id) from `{0}.published` where StatusName = N'Succeeded'; select count(Id) from `{0}.received` where StatusName = N'Succeeded'; select count(Id) from `{0}.published` where StatusName = N'Failed'; -select count(Id) from `{0}.received` where StatusName = N'Failed'; -select count(Id) from `{0}.published` where StatusName in (N'Processing',N'Scheduled',N'Enqueued'); -select count(Id) from `{0}.received` where StatusName in (N'Processing',N'Scheduled',N'Enqueued');", _prefix); +select count(Id) from `{0}.received` where StatusName = N'Failed';", _prefix); var statistics = UseConnection(connection => { @@ -42,10 +43,8 @@ select count(Id) from `{0}.received` where StatusName in (N'Processing',N'Sched stats.PublishedFailed = multi.ReadSingle(); stats.ReceivedFailed = multi.ReadSingle(); - - stats.PublishedProcessing = multi.ReadSingle(); - stats.ReceivedProcessing = multi.ReadSingle(); } + return stats; }); return statistics; @@ -70,17 +69,24 @@ select count(Id) from `{0}.received` where StatusName in (N'Processing',N'Sched var tableName = queryDto.MessageType == MessageType.Publish ? "published" : "received"; var where = string.Empty; if (!string.IsNullOrEmpty(queryDto.StatusName)) - if (string.Equals(queryDto.StatusName, StatusName.Processing, - StringComparison.CurrentCultureIgnoreCase)) - where += " and StatusName in (N'Processing',N'Scheduled',N'Enqueued')"; - else - where += " and StatusName=@StatusName"; + { + where += " and StatusName=@StatusName"; + } + if (!string.IsNullOrEmpty(queryDto.Name)) + { where += " and Name=@Name"; + } + if (!string.IsNullOrEmpty(queryDto.Group)) + { where += " and Group=@Group"; + } + if (!string.IsNullOrEmpty(queryDto.Content)) + { where += " and Content like '%@Content%'"; + } var sqlQuery = $"select * from `{_prefix}.{tableName}` where 1=1 {where} order by Added desc limit @Limit offset @Offset"; @@ -101,11 +107,6 @@ select count(Id) from `{0}.received` where StatusName in (N'Processing',N'Sched return UseConnection(conn => GetNumberOfMessage(conn, "published", StatusName.Failed)); } - public int PublishedProcessingCount() - { - return UseConnection(conn => GetNumberOfMessage(conn, "published", StatusName.Processing)); - } - public int PublishedSucceededCount() { return UseConnection(conn => GetNumberOfMessage(conn, "published", StatusName.Succeeded)); @@ -116,11 +117,6 @@ select count(Id) from `{0}.received` where StatusName in (N'Processing',N'Sched return UseConnection(conn => GetNumberOfMessage(conn, "received", StatusName.Failed)); } - public int ReceivedProcessingCount() - { - return UseConnection(conn => GetNumberOfMessage(conn, "received", StatusName.Processing)); - } - public int ReceivedSucceededCount() { return UseConnection(conn => GetNumberOfMessage(conn, "received", StatusName.Succeeded)); @@ -128,9 +124,7 @@ select count(Id) from `{0}.received` where StatusName in (N'Processing',N'Sched private int GetNumberOfMessage(IDbConnection connection, string tableName, string statusName) { - var sqlQuery = statusName == StatusName.Processing - ? $"select count(Id) from `{_prefix}.{tableName}` where StatusName in (N'Processing',N'Scheduled',N'Enqueued')" - : $"select count(Id) from `{_prefix}.{tableName}` where StatusName = @state"; + var sqlQuery = $"select count(Id) from `{_prefix}.{tableName}` where StatusName = @state"; var count = connection.ExecuteScalar(sqlQuery, new {state = statusName}); return count; @@ -179,7 +173,12 @@ select aggr.* from ( .ToDictionary(x => (string) x.Key, x => (int) x.Count); foreach (var key in keyMaps.Keys) - if (!valuesMap.ContainsKey(key)) valuesMap.Add(key, 0); + { + if (!valuesMap.ContainsKey(key)) + { + valuesMap.Add(key, 0); + } + } var result = new Dictionary(); for (var i = 0; i < keyMaps.Count; i++) diff --git a/src/DotNetCore.CAP.MySql/MySqlStorage.cs b/src/DotNetCore.CAP.MySql/MySqlStorage.cs index 3755b0d..98fa0de 100644 --- a/src/DotNetCore.CAP.MySql/MySqlStorage.cs +++ b/src/DotNetCore.CAP.MySql/MySqlStorage.cs @@ -1,3 +1,6 @@ +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + using System; using System.Data; using System.Threading; @@ -11,10 +14,10 @@ namespace DotNetCore.CAP.MySql { public class MySqlStorage : IStorage { + private readonly CapOptions _capOptions; private readonly IDbConnection _existingConnection = null; private readonly ILogger _logger; private readonly MySqlOptions _options; - private readonly CapOptions _capOptions; public MySqlStorage(ILogger logger, MySqlOptions options, @@ -37,7 +40,11 @@ namespace DotNetCore.CAP.MySql public async Task InitializeAsync(CancellationToken cancellationToken) { - if (cancellationToken.IsCancellationRequested) return; + if (cancellationToken.IsCancellationRequested) + { + return; + } + var sql = CreateDbTablesScript(_options.TableNamePrefix); using (var connection = new MySqlConnection(_options.ConnectionString)) { @@ -51,11 +58,7 @@ namespace DotNetCore.CAP.MySql { var batchSql = $@" -CREATE TABLE IF NOT EXISTS `{prefix}.queue` ( - `MessageId` int(11) NOT NULL, - `MessageType` tinyint(4) NOT NULL, - `ProcessId` varchar(50) DEFAULT NULL -) ENGINE=InnoDB DEFAULT CHARSET=utf8; +DROP TABLE IF EXISTS `{prefix}.queue`; CREATE TABLE IF NOT EXISTS `{prefix}.received` ( `Id` int(127) NOT NULL AUTO_INCREMENT, @@ -102,7 +105,9 @@ CREATE TABLE IF NOT EXISTS `{prefix}.published` ( var connection = _existingConnection ?? new MySqlConnection(_options.ConnectionString); if (connection.State == ConnectionState.Closed) + { connection.Open(); + } return connection; } @@ -115,7 +120,9 @@ CREATE TABLE IF NOT EXISTS `{prefix}.published` ( internal void ReleaseConnection(IDbConnection connection) { if (connection != null && !IsExistingConnection(connection)) + { connection.Dispose(); + } } } } \ No newline at end of file diff --git a/src/DotNetCore.CAP.MySql/MySqlStorageConnection.cs b/src/DotNetCore.CAP.MySql/MySqlStorageConnection.cs index 7a59bde..691717d 100644 --- a/src/DotNetCore.CAP.MySql/MySqlStorageConnection.cs +++ b/src/DotNetCore.CAP.MySql/MySqlStorageConnection.cs @@ -1,3 +1,6 @@ +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + using System; using System.Collections.Generic; using System.Threading.Tasks; @@ -13,8 +16,6 @@ namespace DotNetCore.CAP.MySql private readonly CapOptions _capOptions; private readonly string _prefix; - private const string DateTimeMaxValue = "9999-12-31 23:59:59"; - public MySqlStorageConnection(MySqlOptions options, CapOptions capOptions) { _capOptions = capOptions; @@ -39,51 +40,32 @@ namespace DotNetCore.CAP.MySql } } - public Task FetchNextMessageAsync() + public async Task> GetPublishedMessagesOfNeedRetry() { - var processId = ObjectId.GenerateNewStringId(); - var sql = $@" -UPDATE `{_prefix}.queue` SET `ProcessId`=@ProcessId WHERE `ProcessId` IS NULL LIMIT 1; -SELECT `MessageId`,`MessageType` FROM `{_prefix}.queue` WHERE `ProcessId`=@ProcessId;"; - - return FetchNextMessageCoreAsync(sql, processId); - } - - public async Task GetNextPublishedMessageToBeEnqueuedAsync() - { - var sql = $@" -UPDATE `{_prefix}.published` SET Id=LAST_INSERT_ID(Id),ExpiresAt='{DateTimeMaxValue}' WHERE ExpiresAt IS NULL AND `StatusName` = '{StatusName.Scheduled}' LIMIT 1; -SELECT * FROM `{_prefix}.published` WHERE Id=LAST_INSERT_ID();"; + var fourMinsAgo = DateTime.Now.AddMinutes(-4); + var sql = + $"SELECT * FROM `{_prefix}.published` WHERE `Retries`<{_capOptions.FailedRetryCount} AND `Added`<'{fourMinsAgo}' AND (`StatusName` = '{StatusName.Failed}' OR `StatusName` = '{StatusName.Scheduled}') LIMIT 200;"; using (var connection = new MySqlConnection(Options.ConnectionString)) { - connection.Open(); - connection.Execute("SELECT LAST_INSERT_ID(0)"); - return await connection.QueryFirstOrDefaultAsync(sql); + return await connection.QueryAsync(sql); } } - public async Task> GetFailedPublishedMessages() + public async Task StoreReceivedMessageAsync(CapReceivedMessage message) { - var sql = $"SELECT * FROM `{_prefix}.published` WHERE `Retries`<{_capOptions.FailedRetryCount} AND `StatusName` = '{StatusName.Failed}' LIMIT 200;"; - - using (var connection = new MySqlConnection(Options.ConnectionString)) + if (message == null) { - return await connection.QueryAsync(sql); + throw new ArgumentNullException(nameof(message)); } - } - - public async Task StoreReceivedMessageAsync(CapReceivedMessage message) - { - if (message == null) throw new ArgumentNullException(nameof(message)); var sql = $@" INSERT INTO `{_prefix}.received`(`Name`,`Group`,`Content`,`Retries`,`Added`,`ExpiresAt`,`StatusName`) -VALUES(@Name,@Group,@Content,@Retries,@Added,@ExpiresAt,@StatusName);"; +VALUES(@Name,@Group,@Content,@Retries,@Added,@ExpiresAt,@StatusName);SELECT LAST_INSERT_ID();"; using (var connection = new MySqlConnection(Options.ConnectionString)) { - await connection.ExecuteAsync(sql, message); + return await connection.ExecuteScalarAsync(sql, message); } } @@ -96,23 +78,11 @@ VALUES(@Name,@Group,@Content,@Retries,@Added,@ExpiresAt,@StatusName);"; } } - public async Task GetNextReceivedMessageToBeEnqueuedAsync() + public async Task> GetReceivedMessagesOfNeedRetry() { - var sql = $@" -UPDATE `{_prefix}.received` SET Id=LAST_INSERT_ID(Id),ExpiresAt='{DateTimeMaxValue}' WHERE ExpiresAt IS NULL AND `StatusName` = '{StatusName.Scheduled}' LIMIT 1; -SELECT * FROM `{_prefix}.received` WHERE Id=LAST_INSERT_ID();"; - - using (var connection = new MySqlConnection(Options.ConnectionString)) - { - connection.Open(); - connection.Execute("SELECT LAST_INSERT_ID(0)"); - return await connection.QueryFirstOrDefaultAsync(sql); - } - } - - public async Task> GetFailedReceivedMessages() - { - var sql = $"SELECT * FROM `{_prefix}.received` WHERE `Retries`<{_capOptions.FailedRetryCount} AND `StatusName` = '{StatusName.Failed}' LIMIT 200;"; + var fourMinsAgo = DateTime.Now.AddMinutes(-4); + var sql = + $"SELECT * FROM `{_prefix}.received` WHERE `Retries`<{_capOptions.FailedRetryCount} AND `Added`<'{fourMinsAgo}' AND (`StatusName` = '{StatusName.Failed}' OR `StatusName` = '{StatusName.Scheduled}') LIMIT 200;"; using (var connection = new MySqlConnection(Options.ConnectionString)) { return await connection.QueryAsync(sql); @@ -141,20 +111,6 @@ SELECT * FROM `{_prefix}.received` WHERE Id=LAST_INSERT_ID();"; } } - private async Task FetchNextMessageCoreAsync(string sql, string processId) - { - FetchedMessage fetchedMessage; - using (var connection = new MySqlConnection(Options.ConnectionString)) - { - fetchedMessage = await connection.QuerySingleOrDefaultAsync(sql, new { ProcessId = processId }); - } - - if (fetchedMessage == null) - return null; - - return new MySqlFetchedMessage(fetchedMessage.MessageId, fetchedMessage.MessageType, processId, Options); - } - public void Dispose() { } diff --git a/src/DotNetCore.CAP.MySql/MySqlStorageTransaction.cs b/src/DotNetCore.CAP.MySql/MySqlStorageTransaction.cs index 0dbb424..7121b09 100644 --- a/src/DotNetCore.CAP.MySql/MySqlStorageTransaction.cs +++ b/src/DotNetCore.CAP.MySql/MySqlStorageTransaction.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using System.Data; using System.Threading.Tasks; using Dapper; @@ -11,7 +14,7 @@ namespace DotNetCore.CAP.MySql { private readonly IDbConnection _dbConnection; - private readonly IDbTransaction _dbTransaction; + //private readonly IDbTransaction _dbTransaction; private readonly string _prefix; public MySqlStorageTransaction(MySqlStorageConnection connection) @@ -20,55 +23,45 @@ namespace DotNetCore.CAP.MySql _prefix = options.TableNamePrefix; _dbConnection = new MySqlConnection(options.ConnectionString); - _dbConnection.Open(); - _dbTransaction = _dbConnection.BeginTransaction(IsolationLevel.ReadCommitted); + // _dbConnection.Open(); for performance + // _dbTransaction = _dbConnection.BeginTransaction(IsolationLevel.ReadCommitted); } public void UpdateMessage(CapPublishedMessage message) { - if (message == null) throw new ArgumentNullException(nameof(message)); + if (message == null) + { + throw new ArgumentNullException(nameof(message)); + } var sql = $"UPDATE `{_prefix}.published` SET `Retries` = @Retries,`Content`= @Content,`ExpiresAt` = @ExpiresAt,`StatusName`=@StatusName WHERE `Id`=@Id;"; - _dbConnection.Execute(sql, message, _dbTransaction); + _dbConnection.Execute(sql, message); } public void UpdateMessage(CapReceivedMessage message) { - if (message == null) throw new ArgumentNullException(nameof(message)); + if (message == null) + { + throw new ArgumentNullException(nameof(message)); + } var sql = $"UPDATE `{_prefix}.received` SET `Retries` = @Retries,`Content`= @Content,`ExpiresAt` = @ExpiresAt,`StatusName`=@StatusName WHERE `Id`=@Id;"; - _dbConnection.Execute(sql, message, _dbTransaction); - } - - public void EnqueueMessage(CapPublishedMessage message) - { - if (message == null) throw new ArgumentNullException(nameof(message)); - - var sql = $"INSERT INTO `{_prefix}.queue`(`MessageId`,`MessageType`) values(@MessageId,@MessageType);"; - _dbConnection.Execute(sql, new CapQueue {MessageId = message.Id, MessageType = MessageType.Publish}, - _dbTransaction); - } - - public void EnqueueMessage(CapReceivedMessage message) - { - if (message == null) throw new ArgumentNullException(nameof(message)); - - var sql = $"INSERT INTO `{_prefix}.queue`(`MessageId`,`MessageType`) values(@MessageId,@MessageType);"; - _dbConnection.Execute(sql, new CapQueue {MessageId = message.Id, MessageType = MessageType.Subscribe}, - _dbTransaction); + _dbConnection.Execute(sql, message); } public Task CommitAsync() { - _dbTransaction.Commit(); + _dbConnection.Close(); + _dbConnection.Dispose(); + //_dbTransaction.Commit(); return Task.CompletedTask; } public void Dispose() { - _dbTransaction.Dispose(); + //_dbTransaction.Dispose(); _dbConnection.Dispose(); } } diff --git a/src/DotNetCore.CAP.PostgreSql/CAP.EFOptions.cs b/src/DotNetCore.CAP.PostgreSql/CAP.EFOptions.cs index 294ed6a..883d297 100644 --- a/src/DotNetCore.CAP.PostgreSql/CAP.EFOptions.cs +++ b/src/DotNetCore.CAP.PostgreSql/CAP.EFOptions.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; // ReSharper disable once CheckNamespace namespace DotNetCore.CAP diff --git a/src/DotNetCore.CAP.PostgreSql/CAP.Options.Extensions.cs b/src/DotNetCore.CAP.PostgreSql/CAP.Options.Extensions.cs index e001c05..08ffb90 100644 --- a/src/DotNetCore.CAP.PostgreSql/CAP.Options.Extensions.cs +++ b/src/DotNetCore.CAP.PostgreSql/CAP.Options.Extensions.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using DotNetCore.CAP; using Microsoft.EntityFrameworkCore; @@ -14,7 +17,10 @@ namespace Microsoft.Extensions.DependencyInjection public static CapOptions UsePostgreSql(this CapOptions options, Action configure) { - if (configure == null) throw new ArgumentNullException(nameof(configure)); + if (configure == null) + { + throw new ArgumentNullException(nameof(configure)); + } options.RegisterExtension(new PostgreSqlCapOptionsExtension(configure)); @@ -30,7 +36,10 @@ namespace Microsoft.Extensions.DependencyInjection public static CapOptions UseEntityFramework(this CapOptions options, Action configure) where TContext : DbContext { - if (configure == null) throw new ArgumentNullException(nameof(configure)); + if (configure == null) + { + throw new ArgumentNullException(nameof(configure)); + } options.RegisterExtension(new PostgreSqlCapOptionsExtension(x => { diff --git a/src/DotNetCore.CAP.PostgreSql/CAP.PostgreSqlCapOptionsExtension.cs b/src/DotNetCore.CAP.PostgreSql/CAP.PostgreSqlCapOptionsExtension.cs index f1e94f8..62af675 100644 --- a/src/DotNetCore.CAP.PostgreSql/CAP.PostgreSqlCapOptionsExtension.cs +++ b/src/DotNetCore.CAP.PostgreSql/CAP.PostgreSqlCapOptionsExtension.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using DotNetCore.CAP.PostgreSql; using DotNetCore.CAP.Processor; using Microsoft.EntityFrameworkCore; @@ -40,7 +43,7 @@ namespace DotNetCore.CAP using (var scope = x.CreateScope()) { var provider = scope.ServiceProvider; - var dbContext = (DbContext)provider.GetService(postgreSqlOptions.DbContextType); + var dbContext = (DbContext) provider.GetService(postgreSqlOptions.DbContextType); postgreSqlOptions.ConnectionString = dbContext.Database.GetDbConnection().ConnectionString; return postgreSqlOptions; } diff --git a/src/DotNetCore.CAP.PostgreSql/CAP.PostgreSqlOptions.cs b/src/DotNetCore.CAP.PostgreSql/CAP.PostgreSqlOptions.cs index 7368b94..9f52154 100644 --- a/src/DotNetCore.CAP.PostgreSql/CAP.PostgreSqlOptions.cs +++ b/src/DotNetCore.CAP.PostgreSql/CAP.PostgreSqlOptions.cs @@ -1,4 +1,5 @@ -// ReSharper disable once CheckNamespace +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. namespace DotNetCore.CAP { diff --git a/src/DotNetCore.CAP.PostgreSql/CapPublisher.cs b/src/DotNetCore.CAP.PostgreSql/CapPublisher.cs index bbaa86e..e299dc4 100644 --- a/src/DotNetCore.CAP.PostgreSql/CapPublisher.cs +++ b/src/DotNetCore.CAP.PostgreSql/CapPublisher.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using System.Data; using System.Threading.Tasks; using Dapper; @@ -14,16 +17,14 @@ namespace DotNetCore.CAP.PostgreSql public class CapPublisher : CapPublisherBase, ICallbackPublisher { private readonly DbContext _dbContext; - private readonly ILogger _logger; private readonly PostgreSqlOptions _options; - public CapPublisher(IServiceProvider provider, - ILogger logger, - PostgreSqlOptions options) + public CapPublisher(ILogger logger, IDispatcher dispatcher, + IServiceProvider provider, PostgreSqlOptions options) + : base(logger, dispatcher) { ServiceProvider = provider; _options = options; - _logger = logger; if (_options.DbContextType != null) { @@ -31,12 +32,14 @@ namespace DotNetCore.CAP.PostgreSql _dbContext = (DbContext) ServiceProvider.GetService(_options.DbContextType); } } - - public async Task PublishAsync(CapPublishedMessage message) + + public async Task PublishCallbackAsync(CapPublishedMessage message) { using (var conn = new NpgsqlConnection(_options.ConnectionString)) { - await conn.ExecuteAsync(PrepareSql(), message); + var id = await conn.ExecuteScalarAsync(PrepareSql(), message); + message.Id = id; + Enqueue(message); } } @@ -53,23 +56,20 @@ namespace DotNetCore.CAP.PostgreSql dbContextTransaction = _dbContext.Database.BeginTransaction(IsolationLevel.ReadCommitted); dbTrans = dbContextTransaction.GetDbTransaction(); } + DbTransaction = dbTrans; } - protected override void Execute(IDbConnection dbConnection, IDbTransaction dbTransaction, + protected override int Execute(IDbConnection dbConnection, IDbTransaction dbTransaction, CapPublishedMessage message) { - dbConnection.Execute(PrepareSql(), message, dbTransaction); - - _logger.LogInformation("Published Message has been persisted in the database. name:" + message); + return dbConnection.ExecuteScalar(PrepareSql(), message, dbTransaction); } - protected override async Task ExecuteAsync(IDbConnection dbConnection, IDbTransaction dbTransaction, + protected override Task ExecuteAsync(IDbConnection dbConnection, IDbTransaction dbTransaction, CapPublishedMessage message) { - await dbConnection.ExecuteAsync(PrepareSql(), message, dbTransaction); - - _logger.LogInformation("Published Message has been persisted in the database. name:" + message); + return dbConnection.ExecuteScalarAsync(PrepareSql(), message, dbTransaction); } #region private methods @@ -77,7 +77,7 @@ namespace DotNetCore.CAP.PostgreSql private string PrepareSql() { return - $"INSERT INTO \"{_options.Schema}\".\"published\" (\"Name\",\"Content\",\"Retries\",\"Added\",\"ExpiresAt\",\"StatusName\")VALUES(@Name,@Content,@Retries,@Added,@ExpiresAt,@StatusName)"; + $"INSERT INTO \"{_options.Schema}\".\"published\" (\"Name\",\"Content\",\"Retries\",\"Added\",\"ExpiresAt\",\"StatusName\")VALUES(@Name,@Content,@Retries,@Added,@ExpiresAt,@StatusName) RETURNING \"Id\";"; } #endregion private methods diff --git a/src/DotNetCore.CAP.PostgreSql/FetchedMessage.cs b/src/DotNetCore.CAP.PostgreSql/FetchedMessage.cs deleted file mode 100644 index 4abd0f3..0000000 --- a/src/DotNetCore.CAP.PostgreSql/FetchedMessage.cs +++ /dev/null @@ -1,11 +0,0 @@ -using DotNetCore.CAP.Models; - -namespace DotNetCore.CAP.PostgreSql -{ - internal class FetchedMessage - { - public int MessageId { get; set; } - - public MessageType MessageType { get; set; } - } -} \ No newline at end of file diff --git a/src/DotNetCore.CAP.PostgreSql/IAdditionalProcessor.Default.cs b/src/DotNetCore.CAP.PostgreSql/IAdditionalProcessor.Default.cs index 256e14a..ef606eb 100644 --- a/src/DotNetCore.CAP.PostgreSql/IAdditionalProcessor.Default.cs +++ b/src/DotNetCore.CAP.PostgreSql/IAdditionalProcessor.Default.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using System.Threading.Tasks; using Dapper; using DotNetCore.CAP.Processor; diff --git a/src/DotNetCore.CAP.PostgreSql/PostgreSqlFetchedMessage.cs b/src/DotNetCore.CAP.PostgreSql/PostgreSqlFetchedMessage.cs deleted file mode 100644 index 943db78..0000000 --- a/src/DotNetCore.CAP.PostgreSql/PostgreSqlFetchedMessage.cs +++ /dev/null @@ -1,74 +0,0 @@ -using System; -using System.Data; -using System.Threading; -using Dapper; -using DotNetCore.CAP.Models; - -namespace DotNetCore.CAP.PostgreSql -{ - public class PostgreSqlFetchedMessage : IFetchedMessage - { - private static readonly TimeSpan KeepAliveInterval = TimeSpan.FromMinutes(1); - private readonly IDbConnection _connection; - private readonly object _lockObject = new object(); - private readonly Timer _timer; - private readonly IDbTransaction _transaction; - - public PostgreSqlFetchedMessage(int messageId, - MessageType type, - IDbConnection connection, - IDbTransaction transaction) - { - MessageId = messageId; - MessageType = type; - _connection = connection; - _transaction = transaction; - _timer = new Timer(ExecuteKeepAliveQuery, null, KeepAliveInterval, KeepAliveInterval); - } - - public int MessageId { get; } - - public MessageType MessageType { get; } - - public void RemoveFromQueue() - { - lock (_lockObject) - { - _transaction.Commit(); - } - } - - public void Requeue() - { - lock (_lockObject) - { - _transaction.Rollback(); - } - } - - public void Dispose() - { - lock (_lockObject) - { - _timer?.Dispose(); - _transaction.Dispose(); - _connection.Dispose(); - } - } - - private void ExecuteKeepAliveQuery(object obj) - { - lock (_lockObject) - { - try - { - _connection?.Execute("SELECT 1", _transaction); - } - catch - { - // ignored - } - } - } - } -} \ No newline at end of file diff --git a/src/DotNetCore.CAP.PostgreSql/PostgreSqlMonitoringApi.cs b/src/DotNetCore.CAP.PostgreSql/PostgreSqlMonitoringApi.cs index df6020a..8fcf9f8 100644 --- a/src/DotNetCore.CAP.PostgreSql/PostgreSqlMonitoringApi.cs +++ b/src/DotNetCore.CAP.PostgreSql/PostgreSqlMonitoringApi.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using System.Collections.Generic; using System.Data; using System.Linq; @@ -27,9 +30,7 @@ namespace DotNetCore.CAP.PostgreSql select count(""Id"") from ""{0}"".""published"" where ""StatusName"" = N'Succeeded'; select count(""Id"") from ""{0}"".""received"" where ""StatusName"" = N'Succeeded'; select count(""Id"") from ""{0}"".""published"" where ""StatusName"" = N'Failed'; -select count(""Id"") from ""{0}"".""received"" where ""StatusName"" = N'Failed'; -select count(""Id"") from ""{0}"".""published"" where ""StatusName"" in (N'Processing',N'Scheduled',N'Enqueued'); -select count(""Id"") from ""{0}"".""received"" where ""StatusName"" in (N'Processing',N'Scheduled',N'Enqueued');", +select count(""Id"") from ""{0}"".""received"" where ""StatusName"" = N'Failed';", _options.Schema); var statistics = UseConnection(connection => @@ -42,10 +43,8 @@ select count(""Id"") from ""{0}"".""received"" where ""StatusName"" in (N'Proce stats.PublishedFailed = multi.ReadSingle(); stats.ReceivedFailed = multi.ReadSingle(); - - stats.PublishedProcessing = multi.ReadSingle(); - stats.ReceivedProcessing = multi.ReadSingle(); } + return stats; }); return statistics; @@ -57,17 +56,24 @@ select count(""Id"") from ""{0}"".""received"" where ""StatusName"" in (N'Proce var where = string.Empty; if (!string.IsNullOrEmpty(queryDto.StatusName)) - if (string.Equals(queryDto.StatusName, StatusName.Processing, - StringComparison.CurrentCultureIgnoreCase)) - where += " and \"StatusName\" in (N'Processing',N'Scheduled',N'Enqueued')"; - else - where += " and Lower(\"StatusName\") = Lower(@StatusName)"; + { + where += " and Lower(\"StatusName\") = Lower(@StatusName)"; + } + if (!string.IsNullOrEmpty(queryDto.Name)) + { where += " and Lower(\"Name\") = Lower(@Name)"; + } + if (!string.IsNullOrEmpty(queryDto.Group)) + { where += " and Lower(\"Group\") = Lower(@Group)"; + } + if (!string.IsNullOrEmpty(queryDto.Content)) + { where += " and \"Content\" ILike '%@Content%'"; + } var sqlQuery = $"select * from \"{_options.Schema}\".\"{tableName}\" where 1=1 {where} order by \"Added\" desc offset @Offset limit @Limit"; @@ -88,11 +94,6 @@ select count(""Id"") from ""{0}"".""received"" where ""StatusName"" in (N'Proce return UseConnection(conn => GetNumberOfMessage(conn, "published", StatusName.Failed)); } - public int PublishedProcessingCount() - { - return UseConnection(conn => GetNumberOfMessage(conn, "published", StatusName.Processing)); - } - public int PublishedSucceededCount() { return UseConnection(conn => GetNumberOfMessage(conn, "published", StatusName.Succeeded)); @@ -103,11 +104,6 @@ select count(""Id"") from ""{0}"".""received"" where ""StatusName"" in (N'Proce return UseConnection(conn => GetNumberOfMessage(conn, "received", StatusName.Failed)); } - public int ReceivedProcessingCount() - { - return UseConnection(conn => GetNumberOfMessage(conn, "received", StatusName.Processing)); - } - public int ReceivedSucceededCount() { return UseConnection(conn => GetNumberOfMessage(conn, "received", StatusName.Succeeded)); @@ -129,11 +125,10 @@ select count(""Id"") from ""{0}"".""received"" where ""StatusName"" in (N'Proce private int GetNumberOfMessage(IDbConnection connection, string tableName, string statusName) { - var sqlQuery = statusName == StatusName.Processing - ? $"select count(\"Id\") from \"{_options.Schema}\".\"{tableName}\" where \"StatusName\" in (N'Processing',N'Scheduled',N'Enqueued')" - : $"select count(\"Id\") from \"{_options.Schema}\".\"{tableName}\" where Lower(\"StatusName\") = Lower(@state)"; + var sqlQuery = + $"select count(\"Id\") from \"{_options.Schema}\".\"{tableName}\" where Lower(\"StatusName\") = Lower(@state)"; - var count = connection.ExecuteScalar(sqlQuery, new { state = statusName }); + var count = connection.ExecuteScalar(sqlQuery, new {state = statusName}); return count; } @@ -175,12 +170,17 @@ with aggr as ( ) select ""Key"",""Count"" from aggr where ""Key""= Any(@keys);"; - var valuesMap = connection.Query(sqlQuery,new { keys = keyMaps.Keys.ToList(), statusName }) - .ToList() - .ToDictionary(x => (string)x.Key, x => (int)x.Count); + var valuesMap = connection.Query(sqlQuery, new {keys = keyMaps.Keys.ToList(), statusName}) + .ToList() + .ToDictionary(x => (string) x.Key, x => (int) x.Count); foreach (var key in keyMaps.Keys) - if (!valuesMap.ContainsKey(key)) valuesMap.Add(key, 0); + { + if (!valuesMap.ContainsKey(key)) + { + valuesMap.Add(key, 0); + } + } var result = new Dictionary(); for (var i = 0; i < keyMaps.Count; i++) diff --git a/src/DotNetCore.CAP.PostgreSql/PostgreSqlStorage.cs b/src/DotNetCore.CAP.PostgreSql/PostgreSqlStorage.cs index e1fd4f0..34c8038 100644 --- a/src/DotNetCore.CAP.PostgreSql/PostgreSqlStorage.cs +++ b/src/DotNetCore.CAP.PostgreSql/PostgreSqlStorage.cs @@ -1,3 +1,6 @@ +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + using System; using System.Data; using System.Threading; @@ -11,9 +14,9 @@ namespace DotNetCore.CAP.PostgreSql { public class PostgreSqlStorage : IStorage { + private readonly CapOptions _capOptions; private readonly IDbConnection _existingConnection = null; private readonly ILogger _logger; - private readonly CapOptions _capOptions; private readonly PostgreSqlOptions _options; public PostgreSqlStorage(ILogger logger, @@ -37,7 +40,10 @@ namespace DotNetCore.CAP.PostgreSql public async Task InitializeAsync(CancellationToken cancellationToken) { - if (cancellationToken.IsCancellationRequested) return; + if (cancellationToken.IsCancellationRequested) + { + return; + } var sql = CreateDbTablesScript(_options.Schema); @@ -45,6 +51,7 @@ namespace DotNetCore.CAP.PostgreSql { await connection.ExecuteAsync(sql); } + _logger.LogDebug("Ensuring all create database tables script are applied."); } @@ -68,7 +75,9 @@ namespace DotNetCore.CAP.PostgreSql var connection = _existingConnection ?? new NpgsqlConnection(_options.ConnectionString); if (connection.State == ConnectionState.Closed) + { connection.Open(); + } return connection; } @@ -81,7 +90,9 @@ namespace DotNetCore.CAP.PostgreSql internal void ReleaseConnection(IDbConnection connection) { if (connection != null && !IsExistingConnection(connection)) + { connection.Dispose(); + } } protected virtual string CreateDbTablesScript(string schema) @@ -89,10 +100,7 @@ namespace DotNetCore.CAP.PostgreSql var batchSql = $@" CREATE SCHEMA IF NOT EXISTS ""{schema}""; -CREATE TABLE IF NOT EXISTS ""{schema}"".""queue""( - ""MessageId"" int NOT NULL , - ""MessageType"" int NOT NULL -); +DROP TABLE IF EXISTS ""{schema}"".""queue""; CREATE TABLE IF NOT EXISTS ""{schema}"".""received""( ""Id"" SERIAL PRIMARY KEY NOT NULL, diff --git a/src/DotNetCore.CAP.PostgreSql/PostgreSqlStorageConnection.cs b/src/DotNetCore.CAP.PostgreSql/PostgreSqlStorageConnection.cs index 4874f8a..763407c 100644 --- a/src/DotNetCore.CAP.PostgreSql/PostgreSqlStorageConnection.cs +++ b/src/DotNetCore.CAP.PostgreSql/PostgreSqlStorageConnection.cs @@ -1,6 +1,8 @@ +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + using System; using System.Collections.Generic; -using System.Data; using System.Threading.Tasks; using Dapper; using DotNetCore.CAP.Infrastructure; @@ -36,44 +38,31 @@ namespace DotNetCore.CAP.PostgreSql } } - public Task FetchNextMessageAsync() - { - var sql = $@"DELETE FROM ""{Options.Schema}"".""queue"" WHERE ""MessageId"" = (SELECT ""MessageId"" FROM ""{Options.Schema}"".""queue"" FOR UPDATE SKIP LOCKED LIMIT 1) RETURNING *;"; - return FetchNextMessageCoreAsync(sql); - } - - public async Task GetNextPublishedMessageToBeEnqueuedAsync() + public async Task> GetPublishedMessagesOfNeedRetry() { + var fourMinsAgo = DateTime.Now.AddMinutes(-4); var sql = - $"SELECT * FROM \"{Options.Schema}\".\"published\" WHERE \"StatusName\" = '{StatusName.Scheduled}' FOR UPDATE SKIP LOCKED LIMIT 1;"; + $"SELECT * FROM \"{Options.Schema}\".\"published\" WHERE \"Retries\"<{_capOptions.FailedRetryCount} AND \"Added\"<'{fourMinsAgo}' AND (\"StatusName\"='{StatusName.Failed}' OR \"StatusName\"='{StatusName.Scheduled}') LIMIT 200;"; using (var connection = new NpgsqlConnection(Options.ConnectionString)) { - return await connection.QueryFirstOrDefaultAsync(sql); + return await connection.QueryAsync(sql); } } - public async Task> GetFailedPublishedMessages() + public async Task StoreReceivedMessageAsync(CapReceivedMessage message) { - var sql = - $"SELECT * FROM \"{Options.Schema}\".\"published\" WHERE \"Retries\"<{_capOptions.FailedRetryCount} AND \"StatusName\"='{StatusName.Failed}' LIMIT 200;"; - - using (var connection = new NpgsqlConnection(Options.ConnectionString)) + if (message == null) { - return await connection.QueryAsync(sql); + throw new ArgumentNullException(nameof(message)); } - } - - public async Task StoreReceivedMessageAsync(CapReceivedMessage message) - { - if (message == null) throw new ArgumentNullException(nameof(message)); var sql = - $"INSERT INTO \"{Options.Schema}\".\"received\"(\"Name\",\"Group\",\"Content\",\"Retries\",\"Added\",\"ExpiresAt\",\"StatusName\")VALUES(@Name,@Group,@Content,@Retries,@Added,@ExpiresAt,@StatusName);"; + $"INSERT INTO \"{Options.Schema}\".\"received\"(\"Name\",\"Group\",\"Content\",\"Retries\",\"Added\",\"ExpiresAt\",\"StatusName\")VALUES(@Name,@Group,@Content,@Retries,@Added,@ExpiresAt,@StatusName) RETURNING \"Id\";"; using (var connection = new NpgsqlConnection(Options.ConnectionString)) { - await connection.ExecuteAsync(sql, message); + return await connection.ExecuteScalarAsync(sql, message); } } @@ -86,20 +75,11 @@ namespace DotNetCore.CAP.PostgreSql } } - public async Task GetNextReceivedMessageToBeEnqueuedAsync() - { - var sql = - $"SELECT * FROM \"{Options.Schema}\".\"received\" WHERE \"StatusName\" = '{StatusName.Scheduled}' FOR UPDATE SKIP LOCKED LIMIT 1;"; - using (var connection = new NpgsqlConnection(Options.ConnectionString)) - { - return await connection.QueryFirstOrDefaultAsync(sql); - } - } - - public async Task> GetFailedReceivedMessages() + public async Task> GetReceivedMessagesOfNeedRetry() { + var fourMinsAgo = DateTime.Now.AddMinutes(-4); var sql = - $"SELECT * FROM \"{Options.Schema}\".\"received\" WHERE \"Retries\"<{_capOptions.FailedRetryCount} AND \"StatusName\"='{StatusName.Failed}' LIMIT 200;"; + $"SELECT * FROM \"{Options.Schema}\".\"received\" WHERE \"Retries\"<{_capOptions.FailedRetryCount} AND \"Added\"<'{fourMinsAgo}' AND (\"StatusName\"='{StatusName.Failed}' OR \"StatusName\"='{StatusName.Scheduled}') LIMIT 200;"; using (var connection = new NpgsqlConnection(Options.ConnectionString)) { return await connection.QueryAsync(sql); @@ -131,35 +111,5 @@ namespace DotNetCore.CAP.PostgreSql return connection.Execute(sql) > 0; } } - - private async Task FetchNextMessageCoreAsync(string sql, object args = null) - { - //here don't use `using` to dispose - var connection = new NpgsqlConnection(Options.ConnectionString); - await connection.OpenAsync(); - var transaction = connection.BeginTransaction(IsolationLevel.ReadCommitted); - FetchedMessage fetchedMessage; - try - { - fetchedMessage = await connection.QueryFirstOrDefaultAsync(sql, args, transaction); - } - catch (NpgsqlException) - { - transaction.Dispose(); - connection.Dispose(); - throw; - } - - if (fetchedMessage == null) - { - transaction.Rollback(); - transaction.Dispose(); - connection.Dispose(); - return null; - } - - return new PostgreSqlFetchedMessage(fetchedMessage.MessageId, fetchedMessage.MessageType, connection, - transaction); - } } } \ No newline at end of file diff --git a/src/DotNetCore.CAP.PostgreSql/PostgreSqlStorageTransaction.cs b/src/DotNetCore.CAP.PostgreSql/PostgreSqlStorageTransaction.cs index 3cb4931..d64c46a 100644 --- a/src/DotNetCore.CAP.PostgreSql/PostgreSqlStorageTransaction.cs +++ b/src/DotNetCore.CAP.PostgreSql/PostgreSqlStorageTransaction.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using System.Data; using System.Threading.Tasks; using Dapper; @@ -26,7 +29,10 @@ namespace DotNetCore.CAP.PostgreSql public void UpdateMessage(CapPublishedMessage message) { - if (message == null) throw new ArgumentNullException(nameof(message)); + if (message == null) + { + throw new ArgumentNullException(nameof(message)); + } var sql = $@"UPDATE ""{ @@ -37,7 +43,10 @@ namespace DotNetCore.CAP.PostgreSql public void UpdateMessage(CapReceivedMessage message) { - if (message == null) throw new ArgumentNullException(nameof(message)); + if (message == null) + { + throw new ArgumentNullException(nameof(message)); + } var sql = $@"UPDATE ""{ @@ -46,9 +55,24 @@ namespace DotNetCore.CAP.PostgreSql _dbConnection.Execute(sql, message, _dbTransaction); } + public Task CommitAsync() + { + _dbTransaction.Commit(); + return Task.CompletedTask; + } + + public void Dispose() + { + _dbTransaction.Dispose(); + _dbConnection.Dispose(); + } + public void EnqueueMessage(CapPublishedMessage message) { - if (message == null) throw new ArgumentNullException(nameof(message)); + if (message == null) + { + throw new ArgumentNullException(nameof(message)); + } var sql = $@"INSERT INTO ""{_schema}"".""queue"" values(@MessageId,@MessageType);"; _dbConnection.Execute(sql, new CapQueue {MessageId = message.Id, MessageType = MessageType.Publish}, @@ -57,23 +81,14 @@ namespace DotNetCore.CAP.PostgreSql public void EnqueueMessage(CapReceivedMessage message) { - if (message == null) throw new ArgumentNullException(nameof(message)); + if (message == null) + { + throw new ArgumentNullException(nameof(message)); + } var sql = $@"INSERT INTO ""{_schema}"".""queue"" values(@MessageId,@MessageType);"; _dbConnection.Execute(sql, new CapQueue {MessageId = message.Id, MessageType = MessageType.Subscribe}, _dbTransaction); } - - public Task CommitAsync() - { - _dbTransaction.Commit(); - return Task.CompletedTask; - } - - public void Dispose() - { - _dbTransaction.Dispose(); - _dbConnection.Dispose(); - } } } \ No newline at end of file diff --git a/src/DotNetCore.CAP.RabbitMQ/CAP.Options.Extensions.cs b/src/DotNetCore.CAP.RabbitMQ/CAP.Options.Extensions.cs index bdf0487..b8bd74f 100644 --- a/src/DotNetCore.CAP.RabbitMQ/CAP.Options.Extensions.cs +++ b/src/DotNetCore.CAP.RabbitMQ/CAP.Options.Extensions.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using DotNetCore.CAP; // ReSharper disable once CheckNamespace @@ -13,7 +16,10 @@ namespace Microsoft.Extensions.DependencyInjection public static CapOptions UseRabbitMQ(this CapOptions options, Action configure) { - if (configure == null) throw new ArgumentNullException(nameof(configure)); + if (configure == null) + { + throw new ArgumentNullException(nameof(configure)); + } options.RegisterExtension(new RabbitMQCapOptionsExtension(configure)); diff --git a/src/DotNetCore.CAP.RabbitMQ/CAP.RabbiMQOptions.cs b/src/DotNetCore.CAP.RabbitMQ/CAP.RabbiMQOptions.cs index d76ab91..c600a4c 100644 --- a/src/DotNetCore.CAP.RabbitMQ/CAP.RabbiMQOptions.cs +++ b/src/DotNetCore.CAP.RabbitMQ/CAP.RabbiMQOptions.cs @@ -1,5 +1,7 @@ -// ReSharper disable once CheckNamespace +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// ReSharper disable once CheckNamespace namespace DotNetCore.CAP { public class RabbitMQOptions diff --git a/src/DotNetCore.CAP.RabbitMQ/CAP.RabbitMQCapOptionsExtension.cs b/src/DotNetCore.CAP.RabbitMQ/CAP.RabbitMQCapOptionsExtension.cs index c2eb13e..a952313 100644 --- a/src/DotNetCore.CAP.RabbitMQ/CAP.RabbitMQCapOptionsExtension.cs +++ b/src/DotNetCore.CAP.RabbitMQ/CAP.RabbitMQCapOptionsExtension.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using DotNetCore.CAP.RabbitMQ; using Microsoft.Extensions.DependencyInjection; @@ -24,8 +27,8 @@ namespace DotNetCore.CAP services.AddSingleton(); services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); } } } \ No newline at end of file diff --git a/src/DotNetCore.CAP.RabbitMQ/CAP.SubscribeAttribute.cs b/src/DotNetCore.CAP.RabbitMQ/CAP.SubscribeAttribute.cs index 811a281..a09a8d6 100644 --- a/src/DotNetCore.CAP.RabbitMQ/CAP.SubscribeAttribute.cs +++ b/src/DotNetCore.CAP.RabbitMQ/CAP.SubscribeAttribute.cs @@ -1,4 +1,7 @@ -using DotNetCore.CAP.Abstractions; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using DotNetCore.CAP.Abstractions; // ReSharper disable once CheckNamespace namespace DotNetCore.CAP diff --git a/src/DotNetCore.CAP.RabbitMQ/ConnectionChannelPool.cs b/src/DotNetCore.CAP.RabbitMQ/ConnectionChannelPool.cs index 3ddedb1..4e028d1 100644 --- a/src/DotNetCore.CAP.RabbitMQ/ConnectionChannelPool.cs +++ b/src/DotNetCore.CAP.RabbitMQ/ConnectionChannelPool.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using System.Collections.Concurrent; using System.Diagnostics; using System.Threading; @@ -40,7 +43,10 @@ namespace DotNetCore.CAP.RabbitMQ public IConnection GetConnection() { if (_connection != null && _connection.IsOpen) + { return _connection; + } + _connection = _connectionActivator(); _connection.ConnectionShutdown += RabbitMQ_ConnectionShutdown; return _connection; @@ -51,7 +57,9 @@ namespace DotNetCore.CAP.RabbitMQ _maxSize = 0; while (_pool.TryDequeue(out var context)) + { context.Dispose(); + } } private static Func CreateConnection(RabbitMQOptions options) diff --git a/src/DotNetCore.CAP.RabbitMQ/IConnectionChannelPool.cs b/src/DotNetCore.CAP.RabbitMQ/IConnectionChannelPool.cs index c39985b..d7a6b08 100644 --- a/src/DotNetCore.CAP.RabbitMQ/IConnectionChannelPool.cs +++ b/src/DotNetCore.CAP.RabbitMQ/IConnectionChannelPool.cs @@ -1,4 +1,7 @@ -using RabbitMQ.Client; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using RabbitMQ.Client; namespace DotNetCore.CAP.RabbitMQ { diff --git a/src/DotNetCore.CAP.RabbitMQ/PublishQueueExecutor.cs b/src/DotNetCore.CAP.RabbitMQ/PublishQueueExecutor.cs index 3f0045e..9afedc7 100644 --- a/src/DotNetCore.CAP.RabbitMQ/PublishQueueExecutor.cs +++ b/src/DotNetCore.CAP.RabbitMQ/PublishQueueExecutor.cs @@ -1,21 +1,25 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using System.Text; using System.Threading.Tasks; +using DotNetCore.CAP.Internal; using DotNetCore.CAP.Processor.States; using Microsoft.Extensions.Logging; using RabbitMQ.Client; namespace DotNetCore.CAP.RabbitMQ { - internal sealed class PublishQueueExecutor : BasePublishQueueExecutor + internal sealed class RabbitMQPublishMessageSender : BasePublishMessageSender { private readonly IConnectionChannelPool _connectionChannelPool; private readonly ILogger _logger; private readonly RabbitMQOptions _rabbitMQOptions; - public PublishQueueExecutor(ILogger logger, CapOptions options, - RabbitMQOptions rabbitMQOptions, IConnectionChannelPool connectionChannelPool, IStateChanger stateChanger) - : base(options, stateChanger, logger) + public RabbitMQPublishMessageSender(ILogger logger, CapOptions options, RabbitMQOptions rabbitMQOptions, + IStorageConnection connection, IConnectionChannelPool connectionChannelPool, IStateChanger stateChanger) + : base(logger, options, connection, stateChanger) { _logger = logger; _connectionChannelPool = connectionChannelPool; @@ -30,10 +34,7 @@ namespace DotNetCore.CAP.RabbitMQ var body = Encoding.UTF8.GetBytes(content); channel.ExchangeDeclare(_rabbitMQOptions.TopicExchangeName, RabbitMQOptions.ExchangeType, true); - channel.BasicPublish(_rabbitMQOptions.TopicExchangeName, - keyName, - null, - body); + channel.BasicPublish(_rabbitMQOptions.TopicExchangeName, keyName, null, body); _logger.LogDebug($"RabbitMQ topic message [{keyName}] has been published."); @@ -41,21 +42,22 @@ namespace DotNetCore.CAP.RabbitMQ } catch (Exception ex) { - _logger.LogError( - $"RabbitMQ topic message [{keyName}] has been raised an exception of sending. the exception is: {ex.Message}"); - - return Task.FromResult(OperateResult.Failed(ex, - new OperateError - { - Code = ex.HResult.ToString(), - Description = ex.Message - })); + var wapperEx = new PublisherSentFailedException(ex.Message, ex); + var errors = new OperateError + { + Code = ex.HResult.ToString(), + Description = ex.Message + }; + + return Task.FromResult(OperateResult.Failed(wapperEx, errors)); } finally { var returned = _connectionChannelPool.Return(channel); if (!returned) + { channel.Dispose(); + } } } } diff --git a/src/DotNetCore.CAP.RabbitMQ/RabbitMQConsumerClient.cs b/src/DotNetCore.CAP.RabbitMQ/RabbitMQConsumerClient.cs index da483f0..acbff96 100644 --- a/src/DotNetCore.CAP.RabbitMQ/RabbitMQConsumerClient.cs +++ b/src/DotNetCore.CAP.RabbitMQ/RabbitMQConsumerClient.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using System.Collections.Generic; using System.Text; using System.Threading; @@ -13,9 +16,9 @@ namespace DotNetCore.CAP.RabbitMQ private readonly string _exchageName; private readonly string _queueName; private readonly RabbitMQOptions _rabbitMQOptions; + private IModel _channel; private IConnection _connection; - private IModel _channel; private ulong _deliveryTag; public RabbitMQConsumerClient(string queueName, @@ -36,10 +39,15 @@ namespace DotNetCore.CAP.RabbitMQ public void Subscribe(IEnumerable topics) { - if (topics == null) throw new ArgumentNullException(nameof(topics)); + if (topics == null) + { + throw new ArgumentNullException(nameof(topics)); + } foreach (var topic in topics) + { _channel.QueueBind(_queueName, _exchageName, topic); + } } public void Listening(TimeSpan timeout, CancellationToken cancellationToken) @@ -58,6 +66,7 @@ namespace DotNetCore.CAP.RabbitMQ cancellationToken.ThrowIfCancellationRequested(); cancellationToken.WaitHandle.WaitOne(timeout); } + // ReSharper disable once FunctionNeverReturns } @@ -88,8 +97,9 @@ namespace DotNetCore.CAP.RabbitMQ RabbitMQOptions.ExchangeType, true); - var arguments = new Dictionary { - { "x-message-ttl", _rabbitMQOptions.QueueMessageExpires } + var arguments = new Dictionary + { + {"x-message-ttl", _rabbitMQOptions.QueueMessageExpires} }; _channel.QueueDeclare(_queueName, true, false, false, arguments); } @@ -100,7 +110,7 @@ namespace DotNetCore.CAP.RabbitMQ { var args = new LogMessageEventArgs { - LogType = MqLogType.ConsumerCancelled, + LogType = MqLogType.ConsumerCancelled, Reason = e.ConsumerTag }; OnLog?.Invoke(sender, args); diff --git a/src/DotNetCore.CAP.RabbitMQ/RabbitMQConsumerClientFactory.cs b/src/DotNetCore.CAP.RabbitMQ/RabbitMQConsumerClientFactory.cs index 0a03c58..52c3dd1 100644 --- a/src/DotNetCore.CAP.RabbitMQ/RabbitMQConsumerClientFactory.cs +++ b/src/DotNetCore.CAP.RabbitMQ/RabbitMQConsumerClientFactory.cs @@ -1,4 +1,7 @@ -namespace DotNetCore.CAP.RabbitMQ +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +namespace DotNetCore.CAP.RabbitMQ { internal sealed class RabbitMQConsumerClientFactory : IConsumerClientFactory { diff --git a/src/DotNetCore.CAP.SqlServer/CAP.EFOptions.cs b/src/DotNetCore.CAP.SqlServer/CAP.EFOptions.cs index 0c62833..243b1c8 100644 --- a/src/DotNetCore.CAP.SqlServer/CAP.EFOptions.cs +++ b/src/DotNetCore.CAP.SqlServer/CAP.EFOptions.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; // ReSharper disable once CheckNamespace namespace DotNetCore.CAP diff --git a/src/DotNetCore.CAP.SqlServer/CAP.Options.Extensions.cs b/src/DotNetCore.CAP.SqlServer/CAP.Options.Extensions.cs index c882165..6aca9cc 100644 --- a/src/DotNetCore.CAP.SqlServer/CAP.Options.Extensions.cs +++ b/src/DotNetCore.CAP.SqlServer/CAP.Options.Extensions.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using DotNetCore.CAP; using Microsoft.EntityFrameworkCore; @@ -14,7 +17,10 @@ namespace Microsoft.Extensions.DependencyInjection public static CapOptions UseSqlServer(this CapOptions options, Action configure) { - if (configure == null) throw new ArgumentNullException(nameof(configure)); + if (configure == null) + { + throw new ArgumentNullException(nameof(configure)); + } options.RegisterExtension(new SqlServerCapOptionsExtension(configure)); @@ -30,7 +36,10 @@ namespace Microsoft.Extensions.DependencyInjection public static CapOptions UseEntityFramework(this CapOptions options, Action configure) where TContext : DbContext { - if (configure == null) throw new ArgumentNullException(nameof(configure)); + if (configure == null) + { + throw new ArgumentNullException(nameof(configure)); + } options.RegisterExtension(new SqlServerCapOptionsExtension(x => { diff --git a/src/DotNetCore.CAP.SqlServer/CAP.SqlServerCapOptionsExtension.cs b/src/DotNetCore.CAP.SqlServer/CAP.SqlServerCapOptionsExtension.cs index 55408bd..a176bb7 100644 --- a/src/DotNetCore.CAP.SqlServer/CAP.SqlServerCapOptionsExtension.cs +++ b/src/DotNetCore.CAP.SqlServer/CAP.SqlServerCapOptionsExtension.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using DotNetCore.CAP.Processor; using DotNetCore.CAP.SqlServer; using Microsoft.EntityFrameworkCore; @@ -41,7 +44,7 @@ namespace DotNetCore.CAP using (var scope = x.CreateScope()) { var provider = scope.ServiceProvider; - var dbContext = (DbContext)provider.GetService(sqlServerOptions.DbContextType); + var dbContext = (DbContext) provider.GetService(sqlServerOptions.DbContextType); sqlServerOptions.ConnectionString = dbContext.Database.GetDbConnection().ConnectionString; return sqlServerOptions; } diff --git a/src/DotNetCore.CAP.SqlServer/CAP.SqlServerOptions.cs b/src/DotNetCore.CAP.SqlServer/CAP.SqlServerOptions.cs index f2d44a4..b5c1e5f 100644 --- a/src/DotNetCore.CAP.SqlServer/CAP.SqlServerOptions.cs +++ b/src/DotNetCore.CAP.SqlServer/CAP.SqlServerOptions.cs @@ -1,4 +1,5 @@ -// ReSharper disable once CheckNamespace +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. namespace DotNetCore.CAP { diff --git a/src/DotNetCore.CAP.SqlServer/CapPublisher.cs b/src/DotNetCore.CAP.SqlServer/CapPublisher.cs index f362d12..b93ca68 100644 --- a/src/DotNetCore.CAP.SqlServer/CapPublisher.cs +++ b/src/DotNetCore.CAP.SqlServer/CapPublisher.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using System.Data; using System.Data.SqlClient; using System.Threading.Tasks; @@ -14,28 +17,31 @@ namespace DotNetCore.CAP.SqlServer public class CapPublisher : CapPublisherBase, ICallbackPublisher { private readonly DbContext _dbContext; - private readonly ILogger _logger; private readonly SqlServerOptions _options; - public CapPublisher(IServiceProvider provider, - ILogger logger, - SqlServerOptions options) + public CapPublisher(ILogger logger, IDispatcher dispatcher, + IServiceProvider provider, SqlServerOptions options) + : base(logger, dispatcher) { ServiceProvider = provider; - _logger = logger; _options = options; - if (_options.DbContextType == null) return; + if (_options.DbContextType == null) + { + return; + } IsUsingEF = true; - _dbContext = (DbContext)ServiceProvider.GetService(_options.DbContextType); + _dbContext = (DbContext) ServiceProvider.GetService(_options.DbContextType); } - public async Task PublishAsync(CapPublishedMessage message) + public async Task PublishCallbackAsync(CapPublishedMessage message) { using (var conn = new SqlConnection(_options.ConnectionString)) { - await conn.ExecuteAsync(PrepareSql(), message); + var id = await conn.ExecuteScalarAsync(PrepareSql(), message); + message.Id = id; + Enqueue(message); } } @@ -52,23 +58,20 @@ namespace DotNetCore.CAP.SqlServer dbContextTransaction = _dbContext.Database.BeginTransaction(IsolationLevel.ReadCommitted); dbTrans = dbContextTransaction.GetDbTransaction(); } + DbTransaction = dbTrans; } - protected override void Execute(IDbConnection dbConnection, IDbTransaction dbTransaction, + protected override int Execute(IDbConnection dbConnection, IDbTransaction dbTransaction, CapPublishedMessage message) { - dbConnection.Execute(PrepareSql(), message, dbTransaction); - - _logger.LogInformation("published message has been persisted to the database. name:" + message); + return dbConnection.ExecuteScalar(PrepareSql(), message, dbTransaction); } - protected override async Task ExecuteAsync(IDbConnection dbConnection, IDbTransaction dbTransaction, + protected override Task ExecuteAsync(IDbConnection dbConnection, IDbTransaction dbTransaction, CapPublishedMessage message) { - await dbConnection.ExecuteAsync(PrepareSql(), message, dbTransaction); - - _logger.LogInformation("published message has been persisted to the database. name:" + message); + return dbConnection.ExecuteScalarAsync(PrepareSql(), message, dbTransaction); } #region private methods @@ -76,7 +79,7 @@ namespace DotNetCore.CAP.SqlServer private string PrepareSql() { return - $"INSERT INTO {_options.Schema}.[Published] ([Name],[Content],[Retries],[Added],[ExpiresAt],[StatusName])VALUES(@Name,@Content,@Retries,@Added,@ExpiresAt,@StatusName)"; + $"INSERT INTO {_options.Schema}.[Published] ([Name],[Content],[Retries],[Added],[ExpiresAt],[StatusName])VALUES(@Name,@Content,@Retries,@Added,@ExpiresAt,@StatusName);SELECT SCOPE_IDENTITY();"; } #endregion private methods diff --git a/src/DotNetCore.CAP.SqlServer/FetchedMessage.cs b/src/DotNetCore.CAP.SqlServer/FetchedMessage.cs deleted file mode 100644 index 990b61e..0000000 --- a/src/DotNetCore.CAP.SqlServer/FetchedMessage.cs +++ /dev/null @@ -1,11 +0,0 @@ -using DotNetCore.CAP.Models; - -namespace DotNetCore.CAP.SqlServer -{ - internal class FetchedMessage - { - public int MessageId { get; set; } - - public MessageType MessageType { get; set; } - } -} \ No newline at end of file diff --git a/src/DotNetCore.CAP.SqlServer/IAdditionalProcessor.Default.cs b/src/DotNetCore.CAP.SqlServer/IAdditionalProcessor.Default.cs index e87f87a..8e344d6 100644 --- a/src/DotNetCore.CAP.SqlServer/IAdditionalProcessor.Default.cs +++ b/src/DotNetCore.CAP.SqlServer/IAdditionalProcessor.Default.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using System.Data.SqlClient; using System.Threading.Tasks; using Dapper; diff --git a/src/DotNetCore.CAP.SqlServer/SqlServerFetchedMessage.cs b/src/DotNetCore.CAP.SqlServer/SqlServerFetchedMessage.cs deleted file mode 100644 index e423258..0000000 --- a/src/DotNetCore.CAP.SqlServer/SqlServerFetchedMessage.cs +++ /dev/null @@ -1,74 +0,0 @@ -using System; -using System.Data; -using System.Threading; -using Dapper; -using DotNetCore.CAP.Models; - -namespace DotNetCore.CAP.SqlServer -{ - public class SqlServerFetchedMessage : IFetchedMessage - { - private static readonly TimeSpan KeepAliveInterval = TimeSpan.FromMinutes(1); - private readonly IDbConnection _connection; - private readonly object _lockObject = new object(); - private readonly Timer _timer; - private readonly IDbTransaction _transaction; - - public SqlServerFetchedMessage(int messageId, - MessageType type, - IDbConnection connection, - IDbTransaction transaction) - { - MessageId = messageId; - MessageType = type; - _connection = connection; - _transaction = transaction; - _timer = new Timer(ExecuteKeepAliveQuery, null, KeepAliveInterval, KeepAliveInterval); - } - - public int MessageId { get; } - - public MessageType MessageType { get; } - - public void RemoveFromQueue() - { - lock (_lockObject) - { - _transaction.Commit(); - } - } - - public void Requeue() - { - lock (_lockObject) - { - _transaction.Rollback(); - } - } - - public void Dispose() - { - lock (_lockObject) - { - _timer?.Dispose(); - _transaction.Dispose(); - _connection.Dispose(); - } - } - - private void ExecuteKeepAliveQuery(object obj) - { - lock (_lockObject) - { - try - { - _connection?.Execute("SELECT 1", _transaction); - } - catch - { - // ignored - } - } - } - } -} \ No newline at end of file diff --git a/src/DotNetCore.CAP.SqlServer/SqlServerMonitoringApi.cs b/src/DotNetCore.CAP.SqlServer/SqlServerMonitoringApi.cs index 1d0d1c0..732143e 100644 --- a/src/DotNetCore.CAP.SqlServer/SqlServerMonitoringApi.cs +++ b/src/DotNetCore.CAP.SqlServer/SqlServerMonitoringApi.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using System.Collections.Generic; using System.Data; using System.Linq; @@ -28,9 +31,7 @@ set transaction isolation level read committed; select count(Id) from [{0}].Published with (nolock) where StatusName = N'Succeeded'; select count(Id) from [{0}].Received with (nolock) where StatusName = N'Succeeded'; select count(Id) from [{0}].Published with (nolock) where StatusName = N'Failed'; -select count(Id) from [{0}].Received with (nolock) where StatusName = N'Failed'; -select count(Id) from [{0}].Published with (nolock) where StatusName in (N'Processing',N'Scheduled',N'Enqueued'); -select count(Id) from [{0}].Received with (nolock) where StatusName in (N'Processing',N'Scheduled',N'Enqueued');", +select count(Id) from [{0}].Received with (nolock) where StatusName = N'Failed';", _options.Schema); var statistics = UseConnection(connection => @@ -43,10 +44,8 @@ select count(Id) from [{0}].Received with (nolock) where StatusName in (N'Proces stats.PublishedFailed = multi.ReadSingle(); stats.ReceivedFailed = multi.ReadSingle(); - - stats.PublishedProcessing = multi.ReadSingle(); - stats.ReceivedProcessing = multi.ReadSingle(); } + return stats; }); return statistics; @@ -71,17 +70,24 @@ select count(Id) from [{0}].Received with (nolock) where StatusName in (N'Proces var tableName = queryDto.MessageType == MessageType.Publish ? "Published" : "Received"; var where = string.Empty; if (!string.IsNullOrEmpty(queryDto.StatusName)) - if (string.Equals(queryDto.StatusName, StatusName.Processing, - StringComparison.CurrentCultureIgnoreCase)) - where += " and statusname in (N'Processing',N'Scheduled',N'Enqueued')"; - else - where += " and statusname=@StatusName"; + { + where += " and statusname=@StatusName"; + } + if (!string.IsNullOrEmpty(queryDto.Name)) + { where += " and name=@Name"; + } + if (!string.IsNullOrEmpty(queryDto.Group)) + { where += " and group=@Group"; + } + if (!string.IsNullOrEmpty(queryDto.Content)) + { where += " and content like '%@Content%'"; + } var sqlQuery = $"select * from [{_options.Schema}].{tableName} where 1=1 {where} order by Added desc offset @Offset rows fetch next @Limit rows only"; @@ -102,11 +108,6 @@ select count(Id) from [{0}].Received with (nolock) where StatusName in (N'Proces return UseConnection(conn => GetNumberOfMessage(conn, "Published", StatusName.Failed)); } - public int PublishedProcessingCount() - { - return UseConnection(conn => GetNumberOfMessage(conn, "Published", StatusName.Processing)); - } - public int PublishedSucceededCount() { return UseConnection(conn => GetNumberOfMessage(conn, "Published", StatusName.Succeeded)); @@ -117,11 +118,6 @@ select count(Id) from [{0}].Received with (nolock) where StatusName in (N'Proces return UseConnection(conn => GetNumberOfMessage(conn, "Received", StatusName.Failed)); } - public int ReceivedProcessingCount() - { - return UseConnection(conn => GetNumberOfMessage(conn, "Received", StatusName.Processing)); - } - public int ReceivedSucceededCount() { return UseConnection(conn => GetNumberOfMessage(conn, "Received", StatusName.Succeeded)); @@ -129,9 +125,8 @@ select count(Id) from [{0}].Received with (nolock) where StatusName in (N'Proces private int GetNumberOfMessage(IDbConnection connection, string tableName, string statusName) { - var sqlQuery = statusName == StatusName.Processing - ? $"select count(Id) from [{_options.Schema}].{tableName} with (nolock) where StatusName in (N'Processing',N'Scheduled',N'Enqueued')" - : $"select count(Id) from [{_options.Schema}].{tableName} with (nolock) where StatusName = @state"; + var sqlQuery = + $"select count(Id) from [{_options.Schema}].{tableName} with (nolock) where StatusName = @state"; var count = connection.ExecuteScalar(sqlQuery, new {state = statusName}); return count; @@ -182,7 +177,12 @@ select [Key], [Count] from aggr with (nolock) where [Key] in @keys;"; .ToDictionary(x => (string) x.Key, x => (int) x.Count); foreach (var key in keyMaps.Keys) - if (!valuesMap.ContainsKey(key)) valuesMap.Add(key, 0); + { + if (!valuesMap.ContainsKey(key)) + { + valuesMap.Add(key, 0); + } + } var result = new Dictionary(); for (var i = 0; i < keyMaps.Count; i++) diff --git a/src/DotNetCore.CAP.SqlServer/SqlServerStorage.cs b/src/DotNetCore.CAP.SqlServer/SqlServerStorage.cs index 75d8398..4a0f164 100644 --- a/src/DotNetCore.CAP.SqlServer/SqlServerStorage.cs +++ b/src/DotNetCore.CAP.SqlServer/SqlServerStorage.cs @@ -1,3 +1,6 @@ +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + using System; using System.Data; using System.Data.SqlClient; @@ -11,9 +14,9 @@ namespace DotNetCore.CAP.SqlServer { public class SqlServerStorage : IStorage { + private readonly CapOptions _capOptions; private readonly IDbConnection _existingConnection = null; private readonly ILogger _logger; - private readonly CapOptions _capOptions; private readonly SqlServerOptions _options; public SqlServerStorage(ILogger logger, @@ -37,7 +40,10 @@ namespace DotNetCore.CAP.SqlServer public async Task InitializeAsync(CancellationToken cancellationToken) { - if (cancellationToken.IsCancellationRequested) return; + if (cancellationToken.IsCancellationRequested) + { + return; + } var sql = CreateDbTablesScript(_options.Schema); @@ -45,6 +51,7 @@ namespace DotNetCore.CAP.SqlServer { await connection.ExecuteAsync(sql); } + _logger.LogDebug("Ensuring all create database tables script are applied."); } @@ -57,12 +64,9 @@ BEGIN EXEC('CREATE SCHEMA [{schema}]') END; -IF OBJECT_ID(N'[{schema}].[Queue]',N'U') IS NULL +IF OBJECT_ID(N'[{schema}].[Queue]',N'U') IS NOT NULL BEGIN - CREATE TABLE [{schema}].[Queue]( - [MessageId] [int] NOT NULL, - [MessageType] [tinyint] NOT NULL - ) ON [PRIMARY] + DROP TABLE [{schema}].[Queue]; END; IF OBJECT_ID(N'[{schema}].[Received]',N'U') IS NULL @@ -122,7 +126,9 @@ END;"; var connection = _existingConnection ?? new SqlConnection(_options.ConnectionString); if (connection.State == ConnectionState.Closed) + { connection.Open(); + } return connection; } @@ -135,7 +141,9 @@ END;"; internal void ReleaseConnection(IDbConnection connection) { if (connection != null && !IsExistingConnection(connection)) + { connection.Dispose(); + } } } } \ No newline at end of file diff --git a/src/DotNetCore.CAP.SqlServer/SqlServerStorageConnection.cs b/src/DotNetCore.CAP.SqlServer/SqlServerStorageConnection.cs index 424c43d..9b931c1 100644 --- a/src/DotNetCore.CAP.SqlServer/SqlServerStorageConnection.cs +++ b/src/DotNetCore.CAP.SqlServer/SqlServerStorageConnection.cs @@ -1,6 +1,8 @@ +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + using System; using System.Collections.Generic; -using System.Data; using System.Data.SqlClient; using System.Threading.Tasks; using Dapper; @@ -36,49 +38,32 @@ namespace DotNetCore.CAP.SqlServer } } - public Task FetchNextMessageAsync() - { - var sql = $@" -DELETE TOP (1) -FROM [{Options.Schema}].[Queue] WITH (readpast, updlock, rowlock) -OUTPUT DELETED.MessageId,DELETED.[MessageType];"; - - return FetchNextMessageCoreAsync(sql); - } - - public async Task GetNextPublishedMessageToBeEnqueuedAsync() + public async Task> GetPublishedMessagesOfNeedRetry() { + var fourMinsAgo = DateTime.Now.AddMinutes(-4); var sql = - $"SELECT TOP (1) * FROM [{Options.Schema}].[Published] WITH (readpast) WHERE StatusName = '{StatusName.Scheduled}'"; + $"SELECT TOP (200) * FROM [{Options.Schema}].[Published] WITH (readpast) WHERE Retries<{_capOptions.FailedRetryCount} AND Added<'{fourMinsAgo}' AND (StatusName = '{StatusName.Failed}' OR StatusName = '{StatusName.Scheduled}')"; using (var connection = new SqlConnection(Options.ConnectionString)) { - return await connection.QueryFirstOrDefaultAsync(sql); + return await connection.QueryAsync(sql); } } - public async Task> GetFailedPublishedMessages() + public async Task StoreReceivedMessageAsync(CapReceivedMessage message) { - var sql = - $"SELECT TOP (200) * FROM [{Options.Schema}].[Published] WITH (readpast) WHERE Retries<{_capOptions.FailedRetryCount} AND StatusName = '{StatusName.Failed}'"; - - using (var connection = new SqlConnection(Options.ConnectionString)) + if (message == null) { - return await connection.QueryAsync(sql); + throw new ArgumentNullException(nameof(message)); } - } - - public async Task StoreReceivedMessageAsync(CapReceivedMessage message) - { - if (message == null) throw new ArgumentNullException(nameof(message)); var sql = $@" INSERT INTO [{Options.Schema}].[Received]([Name],[Group],[Content],[Retries],[Added],[ExpiresAt],[StatusName]) -VALUES(@Name,@Group,@Content,@Retries,@Added,@ExpiresAt,@StatusName);"; +VALUES(@Name,@Group,@Content,@Retries,@Added,@ExpiresAt,@StatusName);SELECT SCOPE_IDENTITY();"; using (var connection = new SqlConnection(Options.ConnectionString)) { - await connection.ExecuteAsync(sql, message); + return await connection.ExecuteScalarAsync(sql, message); } } @@ -91,20 +76,11 @@ VALUES(@Name,@Group,@Content,@Retries,@Added,@ExpiresAt,@StatusName);"; } } - public async Task GetNextReceivedMessageToBeEnqueuedAsync() - { - var sql = - $"SELECT TOP (1) * FROM [{Options.Schema}].[Received] WITH (readpast) WHERE StatusName = '{StatusName.Scheduled}'"; - using (var connection = new SqlConnection(Options.ConnectionString)) - { - return await connection.QueryFirstOrDefaultAsync(sql); - } - } - - public async Task> GetFailedReceivedMessages() + public async Task> GetReceivedMessagesOfNeedRetry() { + var fourMinsAgo = DateTime.Now.AddMinutes(-4); var sql = - $"SELECT TOP (200) * FROM [{Options.Schema}].[Received] WITH (readpast) WHERE Retries<{_capOptions.FailedRetryCount} AND StatusName = '{StatusName.Failed}'"; + $"SELECT TOP (200) * FROM [{Options.Schema}].[Received] WITH (readpast) WHERE Retries<{_capOptions.FailedRetryCount} AND Added<'{fourMinsAgo}' AND (StatusName = '{StatusName.Failed}' OR StatusName = '{StatusName.Scheduled}')"; using (var connection = new SqlConnection(Options.ConnectionString)) { return await connection.QueryAsync(sql); @@ -136,35 +112,5 @@ VALUES(@Name,@Group,@Content,@Retries,@Added,@ExpiresAt,@StatusName);"; public void Dispose() { } - - private async Task FetchNextMessageCoreAsync(string sql, object args = null) - { - //here don't use `using` to dispose - var connection = new SqlConnection(Options.ConnectionString); - await connection.OpenAsync(); - var transaction = connection.BeginTransaction(IsolationLevel.ReadCommitted); - FetchedMessage fetchedMessage; - try - { - fetchedMessage = await connection.QueryFirstOrDefaultAsync(sql, args, transaction); - } - catch (SqlException) - { - transaction.Dispose(); - connection.Dispose(); - throw; - } - - if (fetchedMessage == null) - { - transaction.Rollback(); - transaction.Dispose(); - connection.Dispose(); - return null; - } - - return new SqlServerFetchedMessage(fetchedMessage.MessageId, fetchedMessage.MessageType, connection, - transaction); - } } } \ No newline at end of file diff --git a/src/DotNetCore.CAP.SqlServer/SqlServerStorageTransaction.cs b/src/DotNetCore.CAP.SqlServer/SqlServerStorageTransaction.cs index 3cf8ae6..8889e4a 100644 --- a/src/DotNetCore.CAP.SqlServer/SqlServerStorageTransaction.cs +++ b/src/DotNetCore.CAP.SqlServer/SqlServerStorageTransaction.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using System.Data; using System.Data.SqlClient; using System.Threading.Tasks; @@ -26,7 +29,10 @@ namespace DotNetCore.CAP.SqlServer public void UpdateMessage(CapPublishedMessage message) { - if (message == null) throw new ArgumentNullException(nameof(message)); + if (message == null) + { + throw new ArgumentNullException(nameof(message)); + } var sql = $"UPDATE [{_schema}].[Published] SET [Retries] = @Retries,[Content] = @Content,[ExpiresAt] = @ExpiresAt,[StatusName]=@StatusName WHERE Id=@Id;"; @@ -35,16 +41,34 @@ namespace DotNetCore.CAP.SqlServer public void UpdateMessage(CapReceivedMessage message) { - if (message == null) throw new ArgumentNullException(nameof(message)); + if (message == null) + { + throw new ArgumentNullException(nameof(message)); + } var sql = $"UPDATE [{_schema}].[Received] SET [Retries] = @Retries,[Content] = @Content,[ExpiresAt] = @ExpiresAt,[StatusName]=@StatusName WHERE Id=@Id;"; _dbConnection.Execute(sql, message, _dbTransaction); } + public Task CommitAsync() + { + _dbTransaction.Commit(); + return Task.CompletedTask; + } + + public void Dispose() + { + _dbTransaction.Dispose(); + _dbConnection.Dispose(); + } + public void EnqueueMessage(CapPublishedMessage message) { - if (message == null) throw new ArgumentNullException(nameof(message)); + if (message == null) + { + throw new ArgumentNullException(nameof(message)); + } var sql = $"INSERT INTO [{_schema}].[Queue] values(@MessageId,@MessageType);"; _dbConnection.Execute(sql, new CapQueue {MessageId = message.Id, MessageType = MessageType.Publish}, @@ -53,23 +77,14 @@ namespace DotNetCore.CAP.SqlServer public void EnqueueMessage(CapReceivedMessage message) { - if (message == null) throw new ArgumentNullException(nameof(message)); + if (message == null) + { + throw new ArgumentNullException(nameof(message)); + } var sql = $"INSERT INTO [{_schema}].[Queue] values(@MessageId,@MessageType);"; _dbConnection.Execute(sql, new CapQueue {MessageId = message.Id, MessageType = MessageType.Subscribe}, _dbTransaction); } - - public Task CommitAsync() - { - _dbTransaction.Commit(); - return Task.CompletedTask; - } - - public void Dispose() - { - _dbTransaction.Dispose(); - _dbConnection.Dispose(); - } } } \ No newline at end of file diff --git a/src/DotNetCore.CAP/Abstractions/CapPublisherBase.cs b/src/DotNetCore.CAP/Abstractions/CapPublisherBase.cs index 37e9808..b7231dc 100644 --- a/src/DotNetCore.CAP/Abstractions/CapPublisherBase.cs +++ b/src/DotNetCore.CAP/Abstractions/CapPublisherBase.cs @@ -1,14 +1,26 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using System.Data; using System.Threading.Tasks; using DotNetCore.CAP.Infrastructure; using DotNetCore.CAP.Models; -using DotNetCore.CAP.Processor; +using Microsoft.Extensions.Logging; namespace DotNetCore.CAP.Abstractions { public abstract class CapPublisherBase : ICapPublisher, IDisposable { + private readonly IDispatcher _dispatcher; + private readonly ILogger _logger; + + protected CapPublisherBase(ILogger logger, IDispatcher dispatcher) + { + _logger = logger; + _dispatcher = dispatcher; + } + protected IDbConnection DbConnection { get; set; } protected IDbTransaction DbTransaction { get; set; } protected bool IsCapOpenedTrans { get; set; } @@ -21,9 +33,7 @@ namespace DotNetCore.CAP.Abstractions CheckIsUsingEF(name); PrepareConnectionForEF(); - var content = Serialize(contentObj, callbackName); - - PublishWithTrans(name, content); + PublishWithTrans(name, contentObj, callbackName); } public Task PublishAsync(string name, T contentObj, string callbackName = null) @@ -31,9 +41,7 @@ namespace DotNetCore.CAP.Abstractions CheckIsUsingEF(name); PrepareConnectionForEF(); - var content = Serialize(contentObj, callbackName); - - return PublishWithTransAsync(name, content); + return PublishWithTransAsync(name, contentObj, callbackName); } public void Publish(string name, T contentObj, IDbTransaction dbTransaction, string callbackName = null) @@ -41,9 +49,7 @@ namespace DotNetCore.CAP.Abstractions CheckIsAdoNet(name); PrepareConnectionForAdo(dbTransaction); - var content = Serialize(contentObj, callbackName); - - PublishWithTrans(name, content); + PublishWithTrans(name, contentObj, callbackName); } public Task PublishAsync(string name, T contentObj, IDbTransaction dbTransaction, string callbackName = null) @@ -51,28 +57,31 @@ namespace DotNetCore.CAP.Abstractions CheckIsAdoNet(name); PrepareConnectionForAdo(dbTransaction); - var content = Serialize(contentObj, callbackName); + return PublishWithTransAsync(name, contentObj, callbackName); + } - return PublishWithTransAsync(name, content); + protected void Enqueue(CapPublishedMessage message) + { + _dispatcher.EnqueueToPublish(message); } protected abstract void PrepareConnectionForEF(); - protected abstract void Execute(IDbConnection dbConnection, IDbTransaction dbTransaction, + protected abstract int Execute(IDbConnection dbConnection, IDbTransaction dbTransaction, CapPublishedMessage message); - protected abstract Task ExecuteAsync(IDbConnection dbConnection, IDbTransaction dbTransaction, + protected abstract Task ExecuteAsync(IDbConnection dbConnection, IDbTransaction dbTransaction, CapPublishedMessage message); protected virtual string Serialize(T obj, string callbackName = null) { - var packer = (IMessagePacker)ServiceProvider.GetService(typeof(IMessagePacker)); + var packer = (IMessagePacker) ServiceProvider.GetService(typeof(IMessagePacker)); string content; if (obj != null) { if (Helper.IsComplexType(obj.GetType())) { - var serializer = (IContentSerializer)ServiceProvider.GetService(typeof(IContentSerializer)); + var serializer = (IContentSerializer) ServiceProvider.GetService(typeof(IContentSerializer)); content = serializer.Serialize(obj); } else @@ -89,7 +98,6 @@ namespace DotNetCore.CAP.Abstractions { CallbackName = callbackName }; - return packer.Pack(message); } @@ -108,51 +116,99 @@ namespace DotNetCore.CAP.Abstractions private void CheckIsUsingEF(string name) { - if (name == null) throw new ArgumentNullException(nameof(name)); + if (name == null) + { + throw new ArgumentNullException(nameof(name)); + } + if (!IsUsingEF) + { throw new InvalidOperationException( "If you are using the EntityFramework, you need to configure the DbContextType first." + " otherwise you need to use overloaded method with IDbConnection and IDbTransaction."); + } } private void CheckIsAdoNet(string name) { - if (name == null) throw new ArgumentNullException(nameof(name)); + if (name == null) + { + throw new ArgumentNullException(nameof(name)); + } + if (IsUsingEF) + { throw new InvalidOperationException( "If you are using the EntityFramework, you do not need to use this overloaded."); + } } - private async Task PublishWithTransAsync(string name, string content) + private async Task PublishWithTransAsync(string name, T contentObj, string callbackName = null) { - var message = new CapPublishedMessage + try { - Name = name, - Content = content, - StatusName = StatusName.Scheduled - }; + var content = Serialize(contentObj, callbackName); - await ExecuteAsync(DbConnection, DbTransaction, message); + var message = new CapPublishedMessage + { + Name = name, + Content = content, + StatusName = StatusName.Scheduled + }; + + var id = await ExecuteAsync(DbConnection, DbTransaction, message); + + ClosedCap(); - ClosedCap(); + if (id > 0) + { + _logger.LogInformation($"message [{message}] has been persisted in the database."); + + message.Id = id; - PublishQueuer.PulseEvent.Set(); + Enqueue(message); + } + } + catch (Exception e) + { + _logger.LogError("An exception was occurred when publish message. exception message:" + e.Message, e); + Console.WriteLine(e); + throw; + } } - private void PublishWithTrans(string name, string content) + private void PublishWithTrans(string name, T contentObj, string callbackName = null) { - var message = new CapPublishedMessage + try { - Name = name, - Content = content, - StatusName = StatusName.Scheduled - }; + var content = Serialize(contentObj, callbackName); + + var message = new CapPublishedMessage + { + Name = name, + Content = content, + StatusName = StatusName.Scheduled + }; + + var id = Execute(DbConnection, DbTransaction, message); + + ClosedCap(); - Execute(DbConnection, DbTransaction, message); + if (id > 0) + { + _logger.LogInformation($"message [{message}] has been persisted in the database."); - ClosedCap(); + message.Id = id; - PublishQueuer.PulseEvent.Set(); + Enqueue(message); + } + } + catch (Exception e) + { + _logger.LogError("An exception was occurred when publish message. exception message:" + e.Message, e); + Console.WriteLine(e); + throw; + } } private void ClosedCap() @@ -162,8 +218,11 @@ namespace DotNetCore.CAP.Abstractions DbTransaction.Commit(); DbTransaction.Dispose(); } + if (IsCapOpenedConn) + { DbConnection.Dispose(); + } } public void Dispose() diff --git a/src/DotNetCore.CAP/Abstractions/IContentSerializer.cs b/src/DotNetCore.CAP/Abstractions/IContentSerializer.cs index 402843a..108f975 100644 --- a/src/DotNetCore.CAP/Abstractions/IContentSerializer.cs +++ b/src/DotNetCore.CAP/Abstractions/IContentSerializer.cs @@ -1,11 +1,17 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using DotNetCore.CAP.Models; namespace DotNetCore.CAP.Abstractions { /// /// Message content serializer. - /// By default, CAP will use Json as a serializer, and you can customize this interface to achieve serialization of other methods. + /// + /// By default, CAP will use Json as a serializer, and you can customize this interface to achieve serialization of + /// other methods. + /// /// public interface IContentSerializer { @@ -40,7 +46,7 @@ namespace DotNetCore.CAP.Abstractions /// /// /// We use the wrapper to provide some additional information for the message content,which is important for CAP。 - /// Typically, we may need to customize the field display name of the message, + /// Typically, we may need to customize the field display name of the message, /// which includes interacting with other message components, which can be adapted in this manner /// public interface IMessagePacker @@ -52,9 +58,9 @@ namespace DotNetCore.CAP.Abstractions string Pack(CapMessage obj); /// - /// Unpack a message strings to object. + /// Unpack a message strings to object. /// /// The string of packed message. CapMessage UnPack(string packingMessage); - } + } } \ No newline at end of file diff --git a/src/DotNetCore.CAP/Abstractions/IModelBinderFactory.cs b/src/DotNetCore.CAP/Abstractions/IModelBinderFactory.cs index df3423f..ce5de92 100644 --- a/src/DotNetCore.CAP/Abstractions/IModelBinderFactory.cs +++ b/src/DotNetCore.CAP/Abstractions/IModelBinderFactory.cs @@ -1,4 +1,7 @@ -using System.Reflection; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System.Reflection; using DotNetCore.CAP.Abstractions.ModelBinding; namespace DotNetCore.CAP.Abstractions diff --git a/src/DotNetCore.CAP/Abstractions/ISubscriberExecutor.cs b/src/DotNetCore.CAP/Abstractions/ISubscriberExecutor.cs deleted file mode 100644 index 18c9f5d..0000000 --- a/src/DotNetCore.CAP/Abstractions/ISubscriberExecutor.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Threading.Tasks; -using DotNetCore.CAP.Models; - -namespace DotNetCore.CAP.Abstractions -{ - /// - /// Consumer method executor. - /// - public interface ISubscriberExecutor - { - /// - /// Execute the consumer method. - /// - /// The received message. - Task ExecuteAsync(CapReceivedMessage receivedMessage); - } -} diff --git a/src/DotNetCore.CAP/Abstractions/ModelBinding/IModelBinder.cs b/src/DotNetCore.CAP/Abstractions/ModelBinding/IModelBinder.cs index ad276b7..6914c47 100644 --- a/src/DotNetCore.CAP/Abstractions/ModelBinding/IModelBinder.cs +++ b/src/DotNetCore.CAP/Abstractions/ModelBinding/IModelBinder.cs @@ -1,4 +1,7 @@ -using System.Threading.Tasks; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System.Threading.Tasks; namespace DotNetCore.CAP.Abstractions.ModelBinding { diff --git a/src/DotNetCore.CAP/Abstractions/ModelBinding/ModelBindingResult.cs b/src/DotNetCore.CAP/Abstractions/ModelBinding/ModelBindingResult.cs index 0a23d2b..62a9337 100644 --- a/src/DotNetCore.CAP/Abstractions/ModelBinding/ModelBindingResult.cs +++ b/src/DotNetCore.CAP/Abstractions/ModelBinding/ModelBindingResult.cs @@ -1,4 +1,7 @@ -using DotNetCore.CAP.Internal; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using DotNetCore.CAP.Internal; namespace DotNetCore.CAP.Abstractions.ModelBinding { @@ -42,7 +45,10 @@ namespace DotNetCore.CAP.Abstractions.ModelBinding public override string ToString() { if (IsSuccess) + { return $"Success '{Model}'"; + } + return "Failed"; } @@ -50,7 +56,10 @@ namespace DotNetCore.CAP.Abstractions.ModelBinding { var other = obj as ModelBindingResult?; if (other == null) + { return false; + } + return Equals(other.Value); } diff --git a/src/DotNetCore.CAP/Abstractions/TopicAttribute.cs b/src/DotNetCore.CAP/Abstractions/TopicAttribute.cs index 59dd897..6b83de4 100644 --- a/src/DotNetCore.CAP/Abstractions/TopicAttribute.cs +++ b/src/DotNetCore.CAP/Abstractions/TopicAttribute.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; namespace DotNetCore.CAP.Abstractions { diff --git a/src/DotNetCore.CAP/CAP.AppBuilderExtensions.cs b/src/DotNetCore.CAP/CAP.AppBuilderExtensions.cs index 51ccbff..2a8a586 100644 --- a/src/DotNetCore.CAP/CAP.AppBuilderExtensions.cs +++ b/src/DotNetCore.CAP/CAP.AppBuilderExtensions.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using DotNetCore.CAP; using DotNetCore.CAP.Dashboard.GatewayProxy; using Microsoft.Extensions.DependencyInjection; @@ -19,7 +22,9 @@ namespace Microsoft.AspNetCore.Builder public static IApplicationBuilder UseCap(this IApplicationBuilder app) { if (app == null) + { throw new ArgumentNullException(nameof(app)); + } CheckRequirement(app); @@ -31,7 +36,10 @@ namespace Microsoft.AspNetCore.Builder if (provider.GetService() != null) { if (provider.GetService() != null) + { app.UseMiddleware(); + } + app.UseMiddleware(); } @@ -42,18 +50,24 @@ namespace Microsoft.AspNetCore.Builder { var marker = app.ApplicationServices.GetService(); if (marker == null) + { throw new InvalidOperationException( "AddCap() must be called on the service collection. eg: services.AddCap(...)"); + } var messageQueueMarker = app.ApplicationServices.GetService(); if (messageQueueMarker == null) + { throw new InvalidOperationException( "You must be config used message queue provider at AddCap() options! eg: services.AddCap(options=>{ options.UseKafka(...) })"); + } var databaseMarker = app.ApplicationServices.GetService(); if (databaseMarker == null) + { throw new InvalidOperationException( "You must be config used database provider at AddCap() options! eg: services.AddCap(options=>{ options.UseSqlServer(...) })"); + } } } } \ No newline at end of file diff --git a/src/DotNetCore.CAP/CAP.Builder.cs b/src/DotNetCore.CAP/CAP.Builder.cs index e59270d..9722461 100644 --- a/src/DotNetCore.CAP/CAP.Builder.cs +++ b/src/DotNetCore.CAP/CAP.Builder.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using DotNetCore.CAP.Abstractions; using Microsoft.Extensions.DependencyInjection; diff --git a/src/DotNetCore.CAP/CAP.Options.cs b/src/DotNetCore.CAP/CAP.Options.cs index a7f6198..267a61f 100644 --- a/src/DotNetCore.CAP/CAP.Options.cs +++ b/src/DotNetCore.CAP/CAP.Options.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using System.Collections.Generic; using System.Reflection; using DotNetCore.CAP.Models; @@ -10,16 +13,6 @@ namespace DotNetCore.CAP /// public class CapOptions { - /// - /// Default value for polling delay timeout, in seconds. - /// - public const int DefaultPollingDelay = 15; - - /// - /// Default processor count to process messages of cap.queue. - /// - public const int DefaultQueueProcessorCount = 2; - /// /// Default succeeded message expiration time span, in seconds. /// @@ -28,18 +21,16 @@ namespace DotNetCore.CAP /// /// Failed message retry waiting interval. /// - public const int DefaultFailedMessageWaitingInterval = 600; + public const int DefaultFailedMessageWaitingInterval = 60; /// /// Failed message retry count. /// - public const int DefaultFailedRetryCount = 100; + public const int DefaultFailedRetryCount = 50; public CapOptions() { - PollingDelay = DefaultPollingDelay; - QueueProcessorCount = DefaultQueueProcessorCount; SucceedMessageExpiredAfter = DefaultSucceedMessageExpirationAfter; FailedRetryInterval = DefaultFailedMessageWaitingInterval; FailedRetryCount = DefaultFailedRetryCount; @@ -54,18 +45,6 @@ namespace DotNetCore.CAP /// public string DefaultGroup { get; set; } - /// - /// Producer job polling delay time. - /// Default is 15 sec. - /// - public int PollingDelay { get; set; } - - /// - /// Gets or sets the messages queue (Cap.Queue table) processor count. - /// Default is 2 processor. - /// - public int QueueProcessorCount { get; set; } - /// /// Sent or received succeed message after time span of due, then the message will be deleted at due time. /// Default is 24*3600 seconds. @@ -74,7 +53,7 @@ namespace DotNetCore.CAP /// /// Failed messages polling delay time. - /// Default is 600 seconds. + /// Default is 60 seconds. /// public int FailedRetryInterval { get; set; } @@ -85,7 +64,7 @@ namespace DotNetCore.CAP /// /// The number of message retries, the retry will stop when the threshold is reached. - /// Default is 100 times. + /// Default is 50 times. /// public int FailedRetryCount { get; set; } @@ -96,7 +75,9 @@ namespace DotNetCore.CAP public void RegisterExtension(ICapOptionsExtension extension) { if (extension == null) + { throw new ArgumentNullException(nameof(extension)); + } Extensions.Add(extension); } diff --git a/src/DotNetCore.CAP/CAP.ServiceCollectionExtensions.cs b/src/DotNetCore.CAP/CAP.ServiceCollectionExtensions.cs index cfb1fad..23b585b 100644 --- a/src/DotNetCore.CAP/CAP.ServiceCollectionExtensions.cs +++ b/src/DotNetCore.CAP/CAP.ServiceCollectionExtensions.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using System.Collections.Generic; using DotNetCore.CAP; using DotNetCore.CAP.Abstractions; @@ -25,7 +28,10 @@ namespace Microsoft.Extensions.DependencyInjection this IServiceCollection services, Action setupAction) { - if (setupAction == null) throw new ArgumentNullException(nameof(setupAction)); + if (setupAction == null) + { + throw new ArgumentNullException(nameof(setupAction)); + } services.TryAddSingleton(); services.Configure(setupAction); @@ -49,21 +55,21 @@ namespace Microsoft.Extensions.DependencyInjection services.AddSingleton(); //Queue's message processor - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); + services.AddTransient(); - //Executors - services.AddSingleton(); - services.AddSingleton(); - services.TryAddSingleton(); + //Sender and Executors + services.AddSingleton(); + // Warning: IPublishMessageSender need to inject at extension project. + services.AddSingleton(); //Options and extension service var options = new CapOptions(); setupAction(options); foreach (var serviceExtension in options.Extensions) + { serviceExtension.AddServices(services); + } + services.AddSingleton(options); return new CapBuilder(services); @@ -73,13 +79,19 @@ namespace Microsoft.Extensions.DependencyInjection { var consumerListenerServices = new List>(); foreach (var rejectedServices in services) + { if (rejectedServices.ImplementationType != null && typeof(ICapSubscribe).IsAssignableFrom(rejectedServices.ImplementationType)) + { consumerListenerServices.Add(new KeyValuePair(typeof(ICapSubscribe), rejectedServices.ImplementationType)); + } + } foreach (var service in consumerListenerServices) + { services.AddTransient(service.Key, service.Value); + } } } } \ No newline at end of file diff --git a/src/DotNetCore.CAP/Dashboard/BatchCommandDispatcher.cs b/src/DotNetCore.CAP/Dashboard/BatchCommandDispatcher.cs index ca707ee..d9df0bd 100644 --- a/src/DotNetCore.CAP/Dashboard/BatchCommandDispatcher.cs +++ b/src/DotNetCore.CAP/Dashboard/BatchCommandDispatcher.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using System.Net; using System.Threading.Tasks; diff --git a/src/DotNetCore.CAP/Dashboard/CAP.DashboardMiddleware.cs b/src/DotNetCore.CAP/Dashboard/CAP.DashboardMiddleware.cs index 9997280..70d85be 100644 --- a/src/DotNetCore.CAP/Dashboard/CAP.DashboardMiddleware.cs +++ b/src/DotNetCore.CAP/Dashboard/CAP.DashboardMiddleware.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using System.Linq; using System.Net; using System.Threading.Tasks; @@ -27,7 +30,10 @@ namespace DotNetCore.CAP public Task Invoke(HttpContext context) { if (!context.Request.Path.StartsWithSegments(_options.PathMatch, - out var matchedPath, out var remainingPath)) return _next(context); + out var matchedPath, out var remainingPath)) + { + return _next(context); + } // Update the path var path = context.Request.Path; @@ -41,7 +47,9 @@ namespace DotNetCore.CAP var findResult = _routes.FindDispatcher(context.Request.Path.Value); if (findResult == null) + { return _next.Invoke(context); + } if (_options.Authorization.Any(filter => !filter.Authorize(dashboardContext))) { diff --git a/src/DotNetCore.CAP/Dashboard/CAP.DashboardOptions.cs b/src/DotNetCore.CAP/Dashboard/CAP.DashboardOptions.cs index b144996..e04ab24 100644 --- a/src/DotNetCore.CAP/Dashboard/CAP.DashboardOptions.cs +++ b/src/DotNetCore.CAP/Dashboard/CAP.DashboardOptions.cs @@ -1,4 +1,7 @@ -using System.Collections.Generic; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System.Collections.Generic; using DotNetCore.CAP.Dashboard; // ReSharper disable once CheckNamespace diff --git a/src/DotNetCore.CAP/Dashboard/CAP.DashboardOptionsExtensions.cs b/src/DotNetCore.CAP/Dashboard/CAP.DashboardOptionsExtensions.cs index c2ef243..cfc1bf5 100644 --- a/src/DotNetCore.CAP/Dashboard/CAP.DashboardOptionsExtensions.cs +++ b/src/DotNetCore.CAP/Dashboard/CAP.DashboardOptionsExtensions.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using DotNetCore.CAP; using DotNetCore.CAP.Dashboard; using DotNetCore.CAP.Dashboard.GatewayProxy; @@ -40,7 +43,10 @@ namespace Microsoft.Extensions.DependencyInjection public static CapOptions UseDashboard(this CapOptions capOptions, Action options) { - if (options == null) throw new ArgumentNullException(nameof(options)); + if (options == null) + { + throw new ArgumentNullException(nameof(options)); + } capOptions.RegisterExtension(new DashboardOptionsExtension(options)); diff --git a/src/DotNetCore.CAP/Dashboard/CombinedResourceDispatcher.cs b/src/DotNetCore.CAP/Dashboard/CombinedResourceDispatcher.cs index 94c0d99..65875c4 100644 --- a/src/DotNetCore.CAP/Dashboard/CombinedResourceDispatcher.cs +++ b/src/DotNetCore.CAP/Dashboard/CombinedResourceDispatcher.cs @@ -1,4 +1,7 @@ -using System.Reflection; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System.Reflection; namespace DotNetCore.CAP.Dashboard { @@ -22,10 +25,12 @@ namespace DotNetCore.CAP.Dashboard protected override void WriteResponse(DashboardResponse response) { foreach (var resourceName in _resourceNames) + { WriteResource( response, _assembly, $"{_baseNamespace}.{resourceName}"); + } } } } \ No newline at end of file diff --git a/src/DotNetCore.CAP/Dashboard/CommandDispatcher.cs b/src/DotNetCore.CAP/Dashboard/CommandDispatcher.cs index 75ac2b4..2959673 100644 --- a/src/DotNetCore.CAP/Dashboard/CommandDispatcher.cs +++ b/src/DotNetCore.CAP/Dashboard/CommandDispatcher.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using System.Net; using System.Threading.Tasks; @@ -25,9 +28,13 @@ namespace DotNetCore.CAP.Dashboard } if (_command(context)) + { response.StatusCode = (int) HttpStatusCode.NoContent; + } else + { response.StatusCode = 422; + } return Task.FromResult(true); } diff --git a/src/DotNetCore.CAP/Dashboard/Content/resx/Strings.Designer.cs b/src/DotNetCore.CAP/Dashboard/Content/resx/Strings.Designer.cs index a9be4fa..c08a9ab 100644 --- a/src/DotNetCore.CAP/Dashboard/Content/resx/Strings.Designer.cs +++ b/src/DotNetCore.CAP/Dashboard/Content/resx/Strings.Designer.cs @@ -879,15 +879,6 @@ namespace DotNetCore.CAP.Dashboard.Resources { } } - /// - /// Looks up a localized string similar to Processing. - /// - public static string SidebarMenu_Processing { - get { - return ResourceManager.GetString("SidebarMenu_Processing", resourceCulture); - } - } - /// /// Looks up a localized string similar to Succeeded. /// diff --git a/src/DotNetCore.CAP/Dashboard/Content/resx/Strings.resx b/src/DotNetCore.CAP/Dashboard/Content/resx/Strings.resx index b8aa259..57ed89d 100644 --- a/src/DotNetCore.CAP/Dashboard/Content/resx/Strings.resx +++ b/src/DotNetCore.CAP/Dashboard/Content/resx/Strings.resx @@ -240,9 +240,6 @@ Failed - - Processing - Succeeded diff --git a/src/DotNetCore.CAP/Dashboard/Content/resx/Strings.zh.resx b/src/DotNetCore.CAP/Dashboard/Content/resx/Strings.zh.resx index a75494d..90ed01e 100644 --- a/src/DotNetCore.CAP/Dashboard/Content/resx/Strings.zh.resx +++ b/src/DotNetCore.CAP/Dashboard/Content/resx/Strings.zh.resx @@ -234,9 +234,6 @@ 失败 - - 执行中 - å®Œæˆ diff --git a/src/DotNetCore.CAP/Dashboard/DashboardContext.cs b/src/DotNetCore.CAP/Dashboard/DashboardContext.cs index de6dd43..c97a308 100644 --- a/src/DotNetCore.CAP/Dashboard/DashboardContext.cs +++ b/src/DotNetCore.CAP/Dashboard/DashboardContext.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using System.Text.RegularExpressions; using Microsoft.AspNetCore.Http; @@ -8,8 +11,15 @@ namespace DotNetCore.CAP.Dashboard { protected DashboardContext(IStorage storage, DashboardOptions options) { - if (storage == null) throw new ArgumentNullException(nameof(storage)); - if (options == null) throw new ArgumentNullException(nameof(options)); + if (storage == null) + { + throw new ArgumentNullException(nameof(storage)); + } + + if (options == null) + { + throw new ArgumentNullException(nameof(options)); + } Storage = storage; Options = options; @@ -36,7 +46,10 @@ namespace DotNetCore.CAP.Dashboard HttpContext httpContext) : base(storage, options) { - if (httpContext == null) throw new ArgumentNullException(nameof(httpContext)); + if (httpContext == null) + { + throw new ArgumentNullException(nameof(httpContext)); + } HttpContext = httpContext; Request = new CapDashboardRequest(httpContext); diff --git a/src/DotNetCore.CAP/Dashboard/DashboardMetric.cs b/src/DotNetCore.CAP/Dashboard/DashboardMetric.cs index 8bda1b1..6d0209b 100644 --- a/src/DotNetCore.CAP/Dashboard/DashboardMetric.cs +++ b/src/DotNetCore.CAP/Dashboard/DashboardMetric.cs @@ -1,3 +1,6 @@ +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + using System; namespace DotNetCore.CAP.Dashboard diff --git a/src/DotNetCore.CAP/Dashboard/DashboardMetrics.cs b/src/DotNetCore.CAP/Dashboard/DashboardMetrics.cs index c6c4ba9..2fcd370 100644 --- a/src/DotNetCore.CAP/Dashboard/DashboardMetrics.cs +++ b/src/DotNetCore.CAP/Dashboard/DashboardMetrics.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using System.Collections.Generic; using System.Linq; using DotNetCore.CAP.Dashboard.Resources; @@ -64,24 +67,6 @@ namespace DotNetCore.CAP.Dashboard } : null); - //---------------------------------------------------- - - public static readonly DashboardMetric PublishedProcessingCount = new DashboardMetric( - "published_processing:count", - "Metrics_ProcessingJobs", - page => new Metric(page.Statistics.PublishedProcessing.ToString("N0")) - { - Style = page.Statistics.PublishedProcessing > 0 ? MetricStyle.Warning : MetricStyle.Default - }); - - public static readonly DashboardMetric ReceivedProcessingCount = new DashboardMetric( - "received_processing:count", - "Metrics_ProcessingJobs", - page => new Metric(page.Statistics.ReceivedProcessing.ToString("N0")) - { - Style = page.Statistics.ReceivedProcessing > 0 ? MetricStyle.Warning : MetricStyle.Default - }); - //---------------------------------------------------- public static readonly DashboardMetric PublishedSucceededCount = new DashboardMetric( "published_succeeded:count", @@ -129,9 +114,6 @@ namespace DotNetCore.CAP.Dashboard AddMetric(PublishedFailedCountOrNull); AddMetric(ReceivedFailedCountOrNull); - AddMetric(PublishedProcessingCount); - AddMetric(ReceivedProcessingCount); - AddMetric(PublishedSucceededCount); AddMetric(ReceivedSucceededCount); @@ -141,7 +123,10 @@ namespace DotNetCore.CAP.Dashboard public static void AddMetric(DashboardMetric metric) { - if (metric == null) throw new ArgumentNullException(nameof(metric)); + if (metric == null) + { + throw new ArgumentNullException(nameof(metric)); + } lock (Metrics) { diff --git a/src/DotNetCore.CAP/Dashboard/DashboardRequest.cs b/src/DotNetCore.CAP/Dashboard/DashboardRequest.cs index 2499206..5fdb054 100644 --- a/src/DotNetCore.CAP/Dashboard/DashboardRequest.cs +++ b/src/DotNetCore.CAP/Dashboard/DashboardRequest.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; @@ -25,7 +28,11 @@ namespace DotNetCore.CAP.Dashboard public CapDashboardRequest(HttpContext context) { - if (context == null) throw new ArgumentNullException(nameof(context)); + if (context == null) + { + throw new ArgumentNullException(nameof(context)); + } + _context = context; } diff --git a/src/DotNetCore.CAP/Dashboard/DashboardResponse.cs b/src/DotNetCore.CAP/Dashboard/DashboardResponse.cs index d435f2a..ceb0d52 100644 --- a/src/DotNetCore.CAP/Dashboard/DashboardResponse.cs +++ b/src/DotNetCore.CAP/Dashboard/DashboardResponse.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using System.Globalization; using System.IO; using System.Threading.Tasks; diff --git a/src/DotNetCore.CAP/Dashboard/DashboardRoutes.cs b/src/DotNetCore.CAP/Dashboard/DashboardRoutes.cs index 30e2689..312da57 100644 --- a/src/DotNetCore.CAP/Dashboard/DashboardRoutes.cs +++ b/src/DotNetCore.CAP/Dashboard/DashboardRoutes.cs @@ -1,4 +1,7 @@ -using System.Reflection; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System.Reflection; using DotNetCore.CAP.Dashboard.Pages; using DotNetCore.CAP.Infrastructure; diff --git a/src/DotNetCore.CAP/Dashboard/EmbeddedResourceDispatcher.cs b/src/DotNetCore.CAP/Dashboard/EmbeddedResourceDispatcher.cs index ce19de3..c48257c 100644 --- a/src/DotNetCore.CAP/Dashboard/EmbeddedResourceDispatcher.cs +++ b/src/DotNetCore.CAP/Dashboard/EmbeddedResourceDispatcher.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using System.Reflection; using System.Threading.Tasks; @@ -47,8 +50,10 @@ namespace DotNetCore.CAP.Dashboard using (var inputStream = assembly.GetManifestResourceStream(resourceName)) { if (inputStream == null) + { throw new ArgumentException( $@"Resource with name {resourceName} not found in assembly {assembly}."); + } inputStream.CopyTo(response.Body); } diff --git a/src/DotNetCore.CAP/Dashboard/GatewayProxy/DownstreamUrl.cs b/src/DotNetCore.CAP/Dashboard/GatewayProxy/DownstreamUrl.cs index b4ba407..61ca4b1 100644 --- a/src/DotNetCore.CAP/Dashboard/GatewayProxy/DownstreamUrl.cs +++ b/src/DotNetCore.CAP/Dashboard/GatewayProxy/DownstreamUrl.cs @@ -1,4 +1,7 @@ -namespace DotNetCore.CAP.Dashboard.GatewayProxy +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +namespace DotNetCore.CAP.Dashboard.GatewayProxy { public class DownstreamUrl { diff --git a/src/DotNetCore.CAP/Dashboard/GatewayProxy/GatewayProxyMiddleware.cs b/src/DotNetCore.CAP/Dashboard/GatewayProxy/GatewayProxyMiddleware.cs index b166acd..596b32b 100644 --- a/src/DotNetCore.CAP/Dashboard/GatewayProxy/GatewayProxyMiddleware.cs +++ b/src/DotNetCore.CAP/Dashboard/GatewayProxy/GatewayProxyMiddleware.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -94,7 +97,9 @@ namespace DotNetCore.CAP.Dashboard.GatewayProxy public async Task SetResponseOnHttpContext(HttpContext context, HttpResponseMessage response) { foreach (var httpResponseHeader in response.Content.Headers) + { AddHeaderIfDoesntExist(context, httpResponseHeader); + } var content = await response.Content.ReadAsByteArrayAsync(); @@ -113,7 +118,9 @@ namespace DotNetCore.CAP.Dashboard.GatewayProxy using (Stream stream = new MemoryStream(content)) { if (response.StatusCode != HttpStatusCode.NotModified) + { await stream.CopyToAsync(context.Response.Body); + } } } @@ -134,8 +141,10 @@ namespace DotNetCore.CAP.Dashboard.GatewayProxy KeyValuePair> httpResponseHeader) { if (!context.Response.Headers.ContainsKey(httpResponseHeader.Key)) + { context.Response.Headers.Add(httpResponseHeader.Key, new StringValues(httpResponseHeader.Value.ToArray())); + } } } } \ No newline at end of file diff --git a/src/DotNetCore.CAP/Dashboard/GatewayProxy/IRequestMapper.Default.cs b/src/DotNetCore.CAP/Dashboard/GatewayProxy/IRequestMapper.Default.cs index 90d80a9..4548ce5 100644 --- a/src/DotNetCore.CAP/Dashboard/GatewayProxy/IRequestMapper.Default.cs +++ b/src/DotNetCore.CAP/Dashboard/GatewayProxy/IRequestMapper.Default.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -45,7 +48,9 @@ namespace DotNetCore.CAP.Dashboard.GatewayProxy FragmentString fragment = new FragmentString()) { if (scheme == null) + { throw new ArgumentNullException(nameof(scheme)); + } var combinedPath = pathBase.HasValue || path.HasValue ? (pathBase + path).ToString() : "/"; @@ -75,7 +80,9 @@ namespace DotNetCore.CAP.Dashboard.GatewayProxy private async Task MapContent(HttpRequest request) { if (request.Body == null) + { return null; + } var content = new ByteArrayContent(await ToByteArray(request.Body)); @@ -97,8 +104,12 @@ namespace DotNetCore.CAP.Dashboard.GatewayProxy private void MapHeaders(HttpRequest request, HttpRequestMessage requestMessage) { foreach (var header in request.Headers) + { if (IsSupportedHeader(header)) + { requestMessage.Headers.TryAddWithoutValidation(header.Key, header.Value.ToArray()); + } + } } private async Task ToByteArray(Stream stream) diff --git a/src/DotNetCore.CAP/Dashboard/GatewayProxy/IRequestMapper.cs b/src/DotNetCore.CAP/Dashboard/GatewayProxy/IRequestMapper.cs index ee1f994..e4f3d52 100644 --- a/src/DotNetCore.CAP/Dashboard/GatewayProxy/IRequestMapper.cs +++ b/src/DotNetCore.CAP/Dashboard/GatewayProxy/IRequestMapper.cs @@ -1,4 +1,7 @@ -using System.Net.Http; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System.Net.Http; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; diff --git a/src/DotNetCore.CAP/Dashboard/GatewayProxy/Requester/HttpClientBuilder.cs b/src/DotNetCore.CAP/Dashboard/GatewayProxy/Requester/HttpClientBuilder.cs index df913b6..843f9df 100644 --- a/src/DotNetCore.CAP/Dashboard/GatewayProxy/Requester/HttpClientBuilder.cs +++ b/src/DotNetCore.CAP/Dashboard/GatewayProxy/Requester/HttpClientBuilder.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using System.Collections.Generic; using System.Linq; using System.Net.Http; diff --git a/src/DotNetCore.CAP/Dashboard/GatewayProxy/Requester/HttpClientHttpRequester.cs b/src/DotNetCore.CAP/Dashboard/GatewayProxy/Requester/HttpClientHttpRequester.cs index 807e452..a608690 100644 --- a/src/DotNetCore.CAP/Dashboard/GatewayProxy/Requester/HttpClientHttpRequester.cs +++ b/src/DotNetCore.CAP/Dashboard/GatewayProxy/Requester/HttpClientHttpRequester.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using System.Net.Http; using System.Threading.Tasks; using Microsoft.Extensions.Logging; @@ -44,7 +47,10 @@ namespace DotNetCore.CAP.Dashboard.GatewayProxy.Requester var httpClient = _cacheHandlers.Get(cacheKey); if (httpClient == null) + { httpClient = builder.Create(); + } + return httpClient; } diff --git a/src/DotNetCore.CAP/Dashboard/GatewayProxy/Requester/IHttpClient.cs b/src/DotNetCore.CAP/Dashboard/GatewayProxy/Requester/IHttpClient.cs index 8a8d12f..6fb0bb8 100644 --- a/src/DotNetCore.CAP/Dashboard/GatewayProxy/Requester/IHttpClient.cs +++ b/src/DotNetCore.CAP/Dashboard/GatewayProxy/Requester/IHttpClient.cs @@ -1,4 +1,7 @@ -using System.Net.Http; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System.Net.Http; using System.Threading.Tasks; namespace DotNetCore.CAP.Dashboard.GatewayProxy.Requester diff --git a/src/DotNetCore.CAP/Dashboard/GatewayProxy/Requester/IHttpClientBuilder.cs b/src/DotNetCore.CAP/Dashboard/GatewayProxy/Requester/IHttpClientBuilder.cs index 3c24f6e..ed7f46e 100644 --- a/src/DotNetCore.CAP/Dashboard/GatewayProxy/Requester/IHttpClientBuilder.cs +++ b/src/DotNetCore.CAP/Dashboard/GatewayProxy/Requester/IHttpClientBuilder.cs @@ -1,4 +1,7 @@ -using System.Net.Http; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System.Net.Http; namespace DotNetCore.CAP.Dashboard.GatewayProxy.Requester { diff --git a/src/DotNetCore.CAP/Dashboard/GatewayProxy/Requester/IHttpClientCache.cs b/src/DotNetCore.CAP/Dashboard/GatewayProxy/Requester/IHttpClientCache.cs index d1b5ce3..088a64d 100644 --- a/src/DotNetCore.CAP/Dashboard/GatewayProxy/Requester/IHttpClientCache.cs +++ b/src/DotNetCore.CAP/Dashboard/GatewayProxy/Requester/IHttpClientCache.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; namespace DotNetCore.CAP.Dashboard.GatewayProxy.Requester { diff --git a/src/DotNetCore.CAP/Dashboard/GatewayProxy/Requester/IHttpRequester.cs b/src/DotNetCore.CAP/Dashboard/GatewayProxy/Requester/IHttpRequester.cs index 3b87f07..b1c7fec 100644 --- a/src/DotNetCore.CAP/Dashboard/GatewayProxy/Requester/IHttpRequester.cs +++ b/src/DotNetCore.CAP/Dashboard/GatewayProxy/Requester/IHttpRequester.cs @@ -1,4 +1,7 @@ -using System.Net.Http; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System.Net.Http; using System.Threading.Tasks; namespace DotNetCore.CAP.Dashboard.GatewayProxy.Requester diff --git a/src/DotNetCore.CAP/Dashboard/GatewayProxy/Requester/MemoryHttpClientCache.cs b/src/DotNetCore.CAP/Dashboard/GatewayProxy/Requester/MemoryHttpClientCache.cs index 20498ec..21aa033 100644 --- a/src/DotNetCore.CAP/Dashboard/GatewayProxy/Requester/MemoryHttpClientCache.cs +++ b/src/DotNetCore.CAP/Dashboard/GatewayProxy/Requester/MemoryHttpClientCache.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using System.Collections.Concurrent; namespace DotNetCore.CAP.Dashboard.GatewayProxy.Requester @@ -31,7 +34,10 @@ namespace DotNetCore.CAP.Dashboard.GatewayProxy.Requester { IHttpClient client = null; if (_httpClientsCache.TryGetValue(id, out var connectionQueue)) + { connectionQueue.TryDequeue(out client); + } + return client; } diff --git a/src/DotNetCore.CAP/Dashboard/HtmlHelper.cs b/src/DotNetCore.CAP/Dashboard/HtmlHelper.cs index 75d09c1..a685fa7 100644 --- a/src/DotNetCore.CAP/Dashboard/HtmlHelper.cs +++ b/src/DotNetCore.CAP/Dashboard/HtmlHelper.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using System.Collections.Generic; using System.Linq; using System.Net; @@ -24,44 +27,71 @@ namespace DotNetCore.CAP.Dashboard public NonEscapedString Breadcrumbs(string title, IDictionary items) { - if (items == null) throw new ArgumentNullException(nameof(items)); + if (items == null) + { + throw new ArgumentNullException(nameof(items)); + } + return RenderPartial(new Breadcrumbs(title, items)); } public NonEscapedString MessagesSidebar(MessageType type) { if (type == MessageType.Publish) + { return SidebarMenu(MessagesSidebarMenu.PublishedItems); + } + return SidebarMenu(MessagesSidebarMenu.ReceivedItems); } public NonEscapedString SidebarMenu(IEnumerable> items) { - if (items == null) throw new ArgumentNullException(nameof(items)); + if (items == null) + { + throw new ArgumentNullException(nameof(items)); + } + return RenderPartial(new SidebarMenu(items)); } public NonEscapedString BlockMetric(DashboardMetric metric) { - if (metric == null) throw new ArgumentNullException(nameof(metric)); + if (metric == null) + { + throw new ArgumentNullException(nameof(metric)); + } + return RenderPartial(new BlockMetric(metric)); } public NonEscapedString InlineMetric(DashboardMetric metric) { - if (metric == null) throw new ArgumentNullException(nameof(metric)); + if (metric == null) + { + throw new ArgumentNullException(nameof(metric)); + } + return RenderPartial(new InlineMetric(metric)); } public NonEscapedString Paginator(Pager pager) { - if (pager == null) throw new ArgumentNullException(nameof(pager)); + if (pager == null) + { + throw new ArgumentNullException(nameof(pager)); + } + return RenderPartial(new Paginator(pager)); } public NonEscapedString PerPageSelector(Pager pager) { - if (pager == null) throw new ArgumentNullException(nameof(pager)); + if (pager == null) + { + throw new ArgumentNullException(nameof(pager)); + } + return RenderPartial(new PerPageSelector(pager)); } @@ -79,7 +109,9 @@ namespace DotNetCore.CAP.Dashboard public NonEscapedString StateLabel(string stateName) { if (string.IsNullOrWhiteSpace(stateName)) + { return Raw($"{Strings.Common_NoState}"); + } return Raw( $"{stateName}"); @@ -102,40 +134,59 @@ namespace DotNetCore.CAP.Dashboard public string ToHumanDuration(TimeSpan? duration, bool displaySign = true) { - if (duration == null) return null; + if (duration == null) + { + return null; + } var builder = new StringBuilder(); if (displaySign) + { builder.Append(duration.Value.TotalMilliseconds < 0 ? "-" : "+"); + } duration = duration.Value.Duration(); if (duration.Value.Days > 0) + { builder.Append($"{duration.Value.Days}d "); + } if (duration.Value.Hours > 0) + { builder.Append($"{duration.Value.Hours}h "); + } if (duration.Value.Minutes > 0) + { builder.Append($"{duration.Value.Minutes}m "); + } if (duration.Value.TotalHours < 1) + { if (duration.Value.Seconds > 0) { builder.Append(duration.Value.Seconds); if (duration.Value.Milliseconds > 0) + { builder.Append($".{duration.Value.Milliseconds.ToString().PadLeft(3, '0')}"); + } builder.Append("s "); } else { if (duration.Value.Milliseconds > 0) + { builder.Append($"{duration.Value.Milliseconds}ms "); + } } + } if (builder.Length <= 1) + { builder.Append(" <1ms "); + } builder.Remove(builder.Length - 1, 1); @@ -237,14 +288,25 @@ namespace DotNetCore.CAP.Dashboard private string WrapType(Type type) { if (type == null) + { return string.Empty; + } if (type.Name == "Void") + { return WrapKeyword(type.Name.ToLower()); + } + if (Helper.IsComplexType(type)) + { return WrapType(type.Name); + } + if (type.IsPrimitive || type == typeof(string) || type == typeof(decimal)) + { return WrapKeyword(type.Name.ToLower()); + } + return WrapType(type.Name); } diff --git a/src/DotNetCore.CAP/Dashboard/IDashboardAuthorizationFilter.cs b/src/DotNetCore.CAP/Dashboard/IDashboardAuthorizationFilter.cs index f3833a8..0e984ba 100644 --- a/src/DotNetCore.CAP/Dashboard/IDashboardAuthorizationFilter.cs +++ b/src/DotNetCore.CAP/Dashboard/IDashboardAuthorizationFilter.cs @@ -1,4 +1,7 @@ -namespace DotNetCore.CAP.Dashboard +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +namespace DotNetCore.CAP.Dashboard { public interface IDashboardAuthorizationFilter { diff --git a/src/DotNetCore.CAP/Dashboard/IDashboardDispatcher.cs b/src/DotNetCore.CAP/Dashboard/IDashboardDispatcher.cs index ce9cf3e..9204b6e 100644 --- a/src/DotNetCore.CAP/Dashboard/IDashboardDispatcher.cs +++ b/src/DotNetCore.CAP/Dashboard/IDashboardDispatcher.cs @@ -1,4 +1,7 @@ -using System.Threading.Tasks; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System.Threading.Tasks; namespace DotNetCore.CAP.Dashboard { diff --git a/src/DotNetCore.CAP/Dashboard/IMonitoringApi.cs b/src/DotNetCore.CAP/Dashboard/IMonitoringApi.cs index 4e9f60b..e5ecd71 100644 --- a/src/DotNetCore.CAP/Dashboard/IMonitoringApi.cs +++ b/src/DotNetCore.CAP/Dashboard/IMonitoringApi.cs @@ -1,3 +1,6 @@ +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + using System; using System.Collections.Generic; using DotNetCore.CAP.Dashboard.Monitoring; @@ -13,14 +16,10 @@ namespace DotNetCore.CAP.Dashboard int PublishedFailedCount(); - int PublishedProcessingCount(); - int PublishedSucceededCount(); int ReceivedFailedCount(); - int ReceivedProcessingCount(); - int ReceivedSucceededCount(); IDictionary HourlySucceededJobs(MessageType type); diff --git a/src/DotNetCore.CAP/Dashboard/JsonDispatcher.cs b/src/DotNetCore.CAP/Dashboard/JsonDispatcher.cs index c50109f..89eadea 100644 --- a/src/DotNetCore.CAP/Dashboard/JsonDispatcher.cs +++ b/src/DotNetCore.CAP/Dashboard/JsonDispatcher.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using System.Threading.Tasks; using Newtonsoft.Json; using Newtonsoft.Json.Converters; @@ -37,7 +40,9 @@ namespace DotNetCore.CAP.Dashboard } if (_jsonCommand != null) + { serialized = _jsonCommand(context); + } context.Response.ContentType = "application/json"; await context.Response.WriteAsync(serialized ?? string.Empty); diff --git a/src/DotNetCore.CAP/Dashboard/JsonStats.cs b/src/DotNetCore.CAP/Dashboard/JsonStats.cs index fbc9f2c..4b3db7b 100644 --- a/src/DotNetCore.CAP/Dashboard/JsonStats.cs +++ b/src/DotNetCore.CAP/Dashboard/JsonStats.cs @@ -1,4 +1,7 @@ -using System.Collections.Generic; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Newtonsoft.Json; diff --git a/src/DotNetCore.CAP/Dashboard/LocalRequestsOnlyAuthorizationFilter.cs b/src/DotNetCore.CAP/Dashboard/LocalRequestsOnlyAuthorizationFilter.cs index 145cdff..42746d7 100644 --- a/src/DotNetCore.CAP/Dashboard/LocalRequestsOnlyAuthorizationFilter.cs +++ b/src/DotNetCore.CAP/Dashboard/LocalRequestsOnlyAuthorizationFilter.cs @@ -1,4 +1,7 @@ -using DotNetCore.CAP.Infrastructure; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using DotNetCore.CAP.Infrastructure; namespace DotNetCore.CAP.Dashboard { @@ -8,19 +11,27 @@ namespace DotNetCore.CAP.Dashboard { // if unknown, assume not local if (string.IsNullOrEmpty(context.Request.RemoteIpAddress)) + { return false; + } // check if localhost if (context.Request.RemoteIpAddress == "127.0.0.1" || context.Request.RemoteIpAddress == "::1") + { return true; + } // compare with local address if (context.Request.RemoteIpAddress == context.Request.LocalIpAddress) + { return true; + } // check if private ip if (Helper.IsInnerIP(context.Request.RemoteIpAddress)) + { return true; + } return false; } diff --git a/src/DotNetCore.CAP/Dashboard/MenuItem.cs b/src/DotNetCore.CAP/Dashboard/MenuItem.cs index 3f0ea1c..d6aac72 100644 --- a/src/DotNetCore.CAP/Dashboard/MenuItem.cs +++ b/src/DotNetCore.CAP/Dashboard/MenuItem.cs @@ -1,3 +1,6 @@ +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + using System.Collections.Generic; using System.Linq; @@ -23,7 +26,9 @@ namespace DotNetCore.CAP.Dashboard var metrics = new List {Metric}; if (Metrics != null) + { metrics.AddRange(Metrics); + } return metrics.Where(x => x != null).ToList(); } diff --git a/src/DotNetCore.CAP/Dashboard/MessageHistoryRenderer.cs b/src/DotNetCore.CAP/Dashboard/MessageHistoryRenderer.cs index 990aee7..2df3b98 100644 --- a/src/DotNetCore.CAP/Dashboard/MessageHistoryRenderer.cs +++ b/src/DotNetCore.CAP/Dashboard/MessageHistoryRenderer.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Net; @@ -23,18 +26,13 @@ namespace DotNetCore.CAP.Dashboard { Register(StatusName.Succeeded, SucceededRenderer); Register(StatusName.Failed, FailedRenderer); - Register(StatusName.Processing, ProcessingRenderer); - BackgroundStateColors.Add(StatusName.Enqueued, "#F5F5F5"); BackgroundStateColors.Add(StatusName.Succeeded, "#EDF7ED"); BackgroundStateColors.Add(StatusName.Failed, "#FAEBEA"); - BackgroundStateColors.Add(StatusName.Processing, "#FCEFDC"); BackgroundStateColors.Add(StatusName.Scheduled, "#E0F3F8"); - ForegroundStateColors.Add(StatusName.Enqueued, "#999"); ForegroundStateColors.Add(StatusName.Succeeded, "#5cb85c"); ForegroundStateColors.Add(StatusName.Failed, "#d9534f"); - ForegroundStateColors.Add(StatusName.Processing, "#f0ad4e"); ForegroundStateColors.Add(StatusName.Scheduled, "#5bc0de"); } @@ -46,7 +44,9 @@ namespace DotNetCore.CAP.Dashboard public static string GetBackgroundStateColor(string stateName) { if (stateName == null || !BackgroundStateColors.ContainsKey(stateName)) + { return "inherit"; + } return BackgroundStateColors[stateName]; } @@ -59,7 +59,9 @@ namespace DotNetCore.CAP.Dashboard public static string GetForegroundStateColor(string stateName) { if (stateName == null || !ForegroundStateColors.ContainsKey(stateName)) + { return "inherit"; + } return ForegroundStateColors[stateName]; } @@ -68,9 +70,13 @@ namespace DotNetCore.CAP.Dashboard Func, NonEscapedString> renderer) { if (!Renderers.ContainsKey(state)) + { Renderers.Add(state, renderer); + } else + { Renderers[state] = renderer; + } } public static bool Exists(string state) @@ -96,7 +102,10 @@ namespace DotNetCore.CAP.Dashboard public static NonEscapedString DefaultRenderer(HtmlHelper helper, IDictionary stateData) { - if (stateData == null || stateData.Count == 0) return null; + if (stateData == null || stateData.Count == 0) + { + return null; + } var builder = new StringBuilder(); builder.Append("
"); @@ -146,7 +155,10 @@ namespace DotNetCore.CAP.Dashboard builder.Append("
"); - if (!itemsAdded) return null; + if (!itemsAdded) + { + return null; + } return new NonEscapedString(builder.ToString()); } @@ -166,9 +178,13 @@ namespace DotNetCore.CAP.Dashboard string serverId = null; if (stateData.ContainsKey("ServerId")) + { serverId = stateData["ServerId"]; + } else if (stateData.ContainsKey("ServerName")) + { serverId = stateData["ServerName"]; + } if (serverId != null) { diff --git a/src/DotNetCore.CAP/Dashboard/MessagesSidebarMenu.cs b/src/DotNetCore.CAP/Dashboard/MessagesSidebarMenu.cs index 0d68f5d..35a4dfd 100644 --- a/src/DotNetCore.CAP/Dashboard/MessagesSidebarMenu.cs +++ b/src/DotNetCore.CAP/Dashboard/MessagesSidebarMenu.cs @@ -1,3 +1,6 @@ +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + using System; using System.Collections.Generic; using DotNetCore.CAP.Dashboard.Resources; @@ -20,13 +23,6 @@ namespace DotNetCore.CAP.Dashboard Metric = DashboardMetrics.PublishedSucceededCount }); - PublishedItems.Add(page => - new MenuItem(Strings.SidebarMenu_Processing, page.Url.To("/published/processing")) - { - Active = page.RequestPath.StartsWith("/published/processing"), - Metric = DashboardMetrics.PublishedProcessingCount - }); - PublishedItems.Add(page => new MenuItem(Strings.SidebarMenu_Failed, page.Url.To("/published/failed")) { Active = page.RequestPath.StartsWith("/published/failed"), @@ -41,12 +37,6 @@ namespace DotNetCore.CAP.Dashboard Metric = DashboardMetrics.ReceivedSucceededCount }); - ReceivedItems.Add(page => new MenuItem(Strings.SidebarMenu_Processing, page.Url.To("/received/processing")) - { - Active = page.RequestPath.StartsWith("/received/processing"), - Metric = DashboardMetrics.ReceivedProcessingCount - }); - ReceivedItems.Add(page => new MenuItem(Strings.SidebarMenu_Failed, page.Url.To("/received/failed")) { Active = page.RequestPath.StartsWith("/received/failed"), diff --git a/src/DotNetCore.CAP/Dashboard/Metric.cs b/src/DotNetCore.CAP/Dashboard/Metric.cs index fafa537..661511e 100644 --- a/src/DotNetCore.CAP/Dashboard/Metric.cs +++ b/src/DotNetCore.CAP/Dashboard/Metric.cs @@ -1,4 +1,7 @@ -namespace DotNetCore.CAP.Dashboard +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +namespace DotNetCore.CAP.Dashboard { public class Metric { diff --git a/src/DotNetCore.CAP/Dashboard/Monitoring/MessageDto.cs b/src/DotNetCore.CAP/Dashboard/Monitoring/MessageDto.cs index 5c2b1f0..1a256ba 100644 --- a/src/DotNetCore.CAP/Dashboard/Monitoring/MessageDto.cs +++ b/src/DotNetCore.CAP/Dashboard/Monitoring/MessageDto.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; namespace DotNetCore.CAP.Dashboard.Monitoring { diff --git a/src/DotNetCore.CAP/Dashboard/Monitoring/MessageQueryDto.cs b/src/DotNetCore.CAP/Dashboard/Monitoring/MessageQueryDto.cs index 98f3e97..02da29b 100644 --- a/src/DotNetCore.CAP/Dashboard/Monitoring/MessageQueryDto.cs +++ b/src/DotNetCore.CAP/Dashboard/Monitoring/MessageQueryDto.cs @@ -1,4 +1,7 @@ -using DotNetCore.CAP.Models; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using DotNetCore.CAP.Models; namespace DotNetCore.CAP.Dashboard.Monitoring { diff --git a/src/DotNetCore.CAP/Dashboard/Monitoring/ServerDto.cs b/src/DotNetCore.CAP/Dashboard/Monitoring/ServerDto.cs index 8564205..f332b5e 100644 --- a/src/DotNetCore.CAP/Dashboard/Monitoring/ServerDto.cs +++ b/src/DotNetCore.CAP/Dashboard/Monitoring/ServerDto.cs @@ -1,4 +1,7 @@ -namespace DotNetCore.CAP.Dashboard.Monitoring +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +namespace DotNetCore.CAP.Dashboard.Monitoring { public class SubscriberDto { diff --git a/src/DotNetCore.CAP/Dashboard/Monitoring/StatisticsDto.cs b/src/DotNetCore.CAP/Dashboard/Monitoring/StatisticsDto.cs index 38a8355..7c7a529 100644 --- a/src/DotNetCore.CAP/Dashboard/Monitoring/StatisticsDto.cs +++ b/src/DotNetCore.CAP/Dashboard/Monitoring/StatisticsDto.cs @@ -1,4 +1,7 @@ -namespace DotNetCore.CAP.Dashboard.Monitoring +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +namespace DotNetCore.CAP.Dashboard.Monitoring { public class StatisticsDto { @@ -9,8 +12,5 @@ public int PublishedFailed { get; set; } public int ReceivedFailed { get; set; } - - public int PublishedProcessing { get; set; } - public int ReceivedProcessing { get; set; } } } \ No newline at end of file diff --git a/src/DotNetCore.CAP/Dashboard/NavigationMenu.cs b/src/DotNetCore.CAP/Dashboard/NavigationMenu.cs index a0f94e7..4d8babd 100644 --- a/src/DotNetCore.CAP/Dashboard/NavigationMenu.cs +++ b/src/DotNetCore.CAP/Dashboard/NavigationMenu.cs @@ -1,3 +1,6 @@ +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + using System; using System.Collections.Generic; using DotNetCore.CAP.Dashboard.Resources; diff --git a/src/DotNetCore.CAP/Dashboard/NonEscapedString.cs b/src/DotNetCore.CAP/Dashboard/NonEscapedString.cs index 60b60b9..dcc235c 100644 --- a/src/DotNetCore.CAP/Dashboard/NonEscapedString.cs +++ b/src/DotNetCore.CAP/Dashboard/NonEscapedString.cs @@ -1,4 +1,7 @@ -namespace DotNetCore.CAP.Dashboard +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +namespace DotNetCore.CAP.Dashboard { public class NonEscapedString { diff --git a/src/DotNetCore.CAP/Dashboard/Pager.cs b/src/DotNetCore.CAP/Dashboard/Pager.cs index 8962ff0..55b7267 100644 --- a/src/DotNetCore.CAP/Dashboard/Pager.cs +++ b/src/DotNetCore.CAP/Dashboard/Pager.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using System.Collections.Generic; namespace DotNetCore.CAP.Dashboard @@ -35,14 +38,21 @@ namespace DotNetCore.CAP.Dashboard public string PageUrl(int page) { - if (page < 1 || page > TotalPageCount) return "#"; + if (page < 1 || page > TotalPageCount) + { + return "#"; + } return BasePageUrl + "?from=" + (page - 1) * RecordsPerPage + "&count=" + RecordsPerPage; } public string RecordsPerPageUrl(int perPage) { - if (perPage <= 0) return "#"; + if (perPage <= 0) + { + return "#"; + } + return BasePageUrl + "?from=0&count=" + perPage; } @@ -51,23 +61,35 @@ namespace DotNetCore.CAP.Dashboard // start page index _startPageIndex = CurrentPage - PageItemsCount / 2; if (_startPageIndex + PageItemsCount > TotalPageCount) + { _startPageIndex = TotalPageCount + 1 - PageItemsCount; + } + if (_startPageIndex < 1) + { _startPageIndex = 1; + } // end page index _endPageIndex = _startPageIndex + PageItemsCount - 1; if (_endPageIndex > TotalPageCount) + { _endPageIndex = TotalPageCount; + } var pagerItems = new List(); - if (TotalPageCount == 0) return pagerItems; + if (TotalPageCount == 0) + { + return pagerItems; + } AddPrevious(pagerItems); // first page if (_startPageIndex > 1) + { pagerItems.Add(new Item(1, false, ItemType.Page)); + } // more page before numeric page buttons AddMoreBefore(pagerItems); @@ -80,7 +102,9 @@ namespace DotNetCore.CAP.Dashboard // last page if (_endPageIndex < TotalPageCount) + { pagerItems.Add(new Item(TotalPageCount, false, ItemType.Page)); + } // Next page AddNext(pagerItems); @@ -99,7 +123,11 @@ namespace DotNetCore.CAP.Dashboard if (_startPageIndex > 2) { var index = _startPageIndex - 1; - if (index < 1) index = 1; + if (index < 1) + { + index = 1; + } + var item = new Item(index, false, ItemType.MorePage); results.Add(item); } @@ -110,7 +138,11 @@ namespace DotNetCore.CAP.Dashboard if (_endPageIndex < TotalPageCount - 1) { var index = _startPageIndex + PageItemsCount; - if (index > TotalPageCount) index = TotalPageCount; + if (index > TotalPageCount) + { + index = TotalPageCount; + } + var item = new Item(index, false, ItemType.MorePage); results.Add(item); } diff --git a/src/DotNetCore.CAP/Dashboard/Pages/BlockMetric.cs b/src/DotNetCore.CAP/Dashboard/Pages/BlockMetric.cs index 4687d2d..faa0c84 100644 --- a/src/DotNetCore.CAP/Dashboard/Pages/BlockMetric.cs +++ b/src/DotNetCore.CAP/Dashboard/Pages/BlockMetric.cs @@ -1,4 +1,7 @@ -namespace DotNetCore.CAP.Dashboard.Pages +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +namespace DotNetCore.CAP.Dashboard.Pages { internal partial class BlockMetric { diff --git a/src/DotNetCore.CAP/Dashboard/Pages/Breadcrumbs.cs b/src/DotNetCore.CAP/Dashboard/Pages/Breadcrumbs.cs index 20c2339..2295890 100644 --- a/src/DotNetCore.CAP/Dashboard/Pages/Breadcrumbs.cs +++ b/src/DotNetCore.CAP/Dashboard/Pages/Breadcrumbs.cs @@ -1,4 +1,7 @@ -using System.Collections.Generic; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System.Collections.Generic; namespace DotNetCore.CAP.Dashboard.Pages { diff --git a/src/DotNetCore.CAP/Dashboard/Pages/HomePage.cs b/src/DotNetCore.CAP/Dashboard/Pages/HomePage.cs index 2e1b0ce..8773831 100644 --- a/src/DotNetCore.CAP/Dashboard/Pages/HomePage.cs +++ b/src/DotNetCore.CAP/Dashboard/Pages/HomePage.cs @@ -1,4 +1,7 @@ -using System.Collections.Generic; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System.Collections.Generic; namespace DotNetCore.CAP.Dashboard.Pages { diff --git a/src/DotNetCore.CAP/Dashboard/Pages/InlineMetric.cs b/src/DotNetCore.CAP/Dashboard/Pages/InlineMetric.cs index f24c19a..26b9ba1 100644 --- a/src/DotNetCore.CAP/Dashboard/Pages/InlineMetric.cs +++ b/src/DotNetCore.CAP/Dashboard/Pages/InlineMetric.cs @@ -1,4 +1,7 @@ -namespace DotNetCore.CAP.Dashboard.Pages +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +namespace DotNetCore.CAP.Dashboard.Pages { internal partial class InlineMetric { diff --git a/src/DotNetCore.CAP/Dashboard/Pages/LayoutPage.cs b/src/DotNetCore.CAP/Dashboard/Pages/LayoutPage.cs index 820402b..720e552 100644 --- a/src/DotNetCore.CAP/Dashboard/Pages/LayoutPage.cs +++ b/src/DotNetCore.CAP/Dashboard/Pages/LayoutPage.cs @@ -1,4 +1,7 @@ -namespace DotNetCore.CAP.Dashboard.Pages +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +namespace DotNetCore.CAP.Dashboard.Pages { partial class LayoutPage { diff --git a/src/DotNetCore.CAP/Dashboard/Pages/LayoutPage.cshtml b/src/DotNetCore.CAP/Dashboard/Pages/LayoutPage.cshtml index 28ef474..e73ee90 100644 --- a/src/DotNetCore.CAP/Dashboard/Pages/LayoutPage.cshtml +++ b/src/DotNetCore.CAP/Dashboard/Pages/LayoutPage.cshtml @@ -16,68 +16,68 @@ - -
+ +
- - - - -
- @RenderBody() -
-
- - -
+ +
+ @RenderBody()
+
+ + + +
+
- + \ No newline at end of file diff --git a/src/DotNetCore.CAP/Dashboard/Pages/NodePage.cs b/src/DotNetCore.CAP/Dashboard/Pages/NodePage.cs index 2728dc4..9f677b8 100644 --- a/src/DotNetCore.CAP/Dashboard/Pages/NodePage.cs +++ b/src/DotNetCore.CAP/Dashboard/Pages/NodePage.cs @@ -1,4 +1,7 @@ -using System.Collections.Generic; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System.Collections.Generic; using DotNetCore.CAP.NodeDiscovery; using Microsoft.Extensions.DependencyInjection; @@ -31,8 +34,10 @@ namespace DotNetCore.CAP.Dashboard.Pages { return new List(); } + _nodes = _discoveryProvider.GetNodes().GetAwaiter().GetResult(); } + return _nodes; } } diff --git a/src/DotNetCore.CAP/Dashboard/Pages/PublishedPage.cs b/src/DotNetCore.CAP/Dashboard/Pages/PublishedPage.cs index 469ef0a..2ccece2 100644 --- a/src/DotNetCore.CAP/Dashboard/Pages/PublishedPage.cs +++ b/src/DotNetCore.CAP/Dashboard/Pages/PublishedPage.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; namespace DotNetCore.CAP.Dashboard.Pages { @@ -15,10 +18,10 @@ namespace DotNetCore.CAP.Dashboard.Pages { if (string.Equals(StatusName, Infrastructure.StatusName.Succeeded, StringComparison.CurrentCultureIgnoreCase)) + { return api.PublishedSucceededCount(); - if (string.Equals(StatusName, Infrastructure.StatusName.Processing, - StringComparison.CurrentCultureIgnoreCase)) - return api.PublishedProcessingCount(); + } + return api.PublishedFailedCount(); } } diff --git a/src/DotNetCore.CAP/Dashboard/Pages/ReceivedPage.cs b/src/DotNetCore.CAP/Dashboard/Pages/ReceivedPage.cs index ddf2caa..9090876 100644 --- a/src/DotNetCore.CAP/Dashboard/Pages/ReceivedPage.cs +++ b/src/DotNetCore.CAP/Dashboard/Pages/ReceivedPage.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; namespace DotNetCore.CAP.Dashboard.Pages { @@ -15,10 +18,10 @@ namespace DotNetCore.CAP.Dashboard.Pages { if (string.Equals(StatusName, Infrastructure.StatusName.Succeeded, StringComparison.CurrentCultureIgnoreCase)) + { return api.ReceivedSucceededCount(); - if (string.Equals(StatusName, Infrastructure.StatusName.Processing, - StringComparison.CurrentCultureIgnoreCase)) - return api.ReceivedProcessingCount(); + } + return api.ReceivedFailedCount(); } } diff --git a/src/DotNetCore.CAP/Dashboard/Pages/SidebarMenu.cs b/src/DotNetCore.CAP/Dashboard/Pages/SidebarMenu.cs index 8f79ae3..a2fcd41 100644 --- a/src/DotNetCore.CAP/Dashboard/Pages/SidebarMenu.cs +++ b/src/DotNetCore.CAP/Dashboard/Pages/SidebarMenu.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using System.Collections.Generic; namespace DotNetCore.CAP.Dashboard.Pages diff --git a/src/DotNetCore.CAP/Dashboard/Pages/_Paginator.cs b/src/DotNetCore.CAP/Dashboard/Pages/_Paginator.cs index e20da01..0794cf7 100644 --- a/src/DotNetCore.CAP/Dashboard/Pages/_Paginator.cs +++ b/src/DotNetCore.CAP/Dashboard/Pages/_Paginator.cs @@ -1,4 +1,7 @@ -namespace DotNetCore.CAP.Dashboard.Pages +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +namespace DotNetCore.CAP.Dashboard.Pages { internal partial class Paginator { diff --git a/src/DotNetCore.CAP/Dashboard/Pages/_Paginator.cshtml b/src/DotNetCore.CAP/Dashboard/Pages/_Paginator.cshtml index 4c2b640..b377a57 100644 --- a/src/DotNetCore.CAP/Dashboard/Pages/_Paginator.cshtml +++ b/src/DotNetCore.CAP/Dashboard/Pages/_Paginator.cshtml @@ -1,5 +1,4 @@ @* Generator: Template TypeVisibility: Internal GeneratePrettyNames: True TrimLeadingUnderscores : true *@ - @using DotNetCore.CAP.Dashboard @using DotNetCore.CAP.Dashboard.Resources @inherits DotNetCore.CAP.Dashboard.RazorPage diff --git a/src/DotNetCore.CAP/Dashboard/Pages/_PerPageSelector.cs b/src/DotNetCore.CAP/Dashboard/Pages/_PerPageSelector.cs index cbe86fb..6f88462 100644 --- a/src/DotNetCore.CAP/Dashboard/Pages/_PerPageSelector.cs +++ b/src/DotNetCore.CAP/Dashboard/Pages/_PerPageSelector.cs @@ -1,4 +1,7 @@ -namespace DotNetCore.CAP.Dashboard.Pages +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +namespace DotNetCore.CAP.Dashboard.Pages { internal partial class PerPageSelector { diff --git a/src/DotNetCore.CAP/Dashboard/RazorPage.cs b/src/DotNetCore.CAP/Dashboard/RazorPage.cs index 93b3425..616441d 100644 --- a/src/DotNetCore.CAP/Dashboard/RazorPage.cs +++ b/src/DotNetCore.CAP/Dashboard/RazorPage.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using System.Diagnostics; using System.Net; using System.Text; @@ -36,7 +39,11 @@ namespace DotNetCore.CAP.Dashboard { get { - if (_statisticsLazy == null) throw new InvalidOperationException("Page is not initialized."); + if (_statisticsLazy == null) + { + throw new InvalidOperationException("Page is not initialized."); + } + return _statisticsLazy.Value; } } @@ -104,6 +111,7 @@ namespace DotNetCore.CAP.Dashboard { return $"{discoveryOptions.NodeName}({discoveryOptions.NodeId})"; } + return null; } @@ -111,7 +119,7 @@ namespace DotNetCore.CAP.Dashboard { if (CapCache.Global.TryGet("cap.nodes.count", out var count)) { - dto.Servers = (int)count; + dto.Servers = (int) count; } else { @@ -128,7 +136,10 @@ namespace DotNetCore.CAP.Dashboard protected void WriteLiteral(string textToAppend) { if (string.IsNullOrEmpty(textToAppend)) + { return; + } + _content.Append(textToAppend); } @@ -136,7 +147,10 @@ namespace DotNetCore.CAP.Dashboard protected virtual void Write(object value) { if (value == null) + { return; + } + var html = value as NonEscapedString; WriteLiteral(html?.ToString() ?? Encode(value.ToString())); } diff --git a/src/DotNetCore.CAP/Dashboard/RazorPageDispatcher.cs b/src/DotNetCore.CAP/Dashboard/RazorPageDispatcher.cs index ade0988..f707a8d 100644 --- a/src/DotNetCore.CAP/Dashboard/RazorPageDispatcher.cs +++ b/src/DotNetCore.CAP/Dashboard/RazorPageDispatcher.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using System.Text.RegularExpressions; using System.Threading.Tasks; diff --git a/src/DotNetCore.CAP/Dashboard/RouteCollection.cs b/src/DotNetCore.CAP/Dashboard/RouteCollection.cs index 3831ad6..b093e9f 100644 --- a/src/DotNetCore.CAP/Dashboard/RouteCollection.cs +++ b/src/DotNetCore.CAP/Dashboard/RouteCollection.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using System.Collections.Generic; using System.Text.RegularExpressions; @@ -11,24 +14,39 @@ namespace DotNetCore.CAP.Dashboard public void Add(string pathTemplate, IDashboardDispatcher dispatcher) { - if (pathTemplate == null) throw new ArgumentNullException(nameof(pathTemplate)); - if (dispatcher == null) throw new ArgumentNullException(nameof(dispatcher)); + if (pathTemplate == null) + { + throw new ArgumentNullException(nameof(pathTemplate)); + } + + if (dispatcher == null) + { + throw new ArgumentNullException(nameof(dispatcher)); + } _dispatchers.Add(new Tuple(pathTemplate, dispatcher)); } public Tuple FindDispatcher(string path) { - if (path.Length == 0) path = "/"; + if (path.Length == 0) + { + path = "/"; + } foreach (var dispatcher in _dispatchers) { var pattern = dispatcher.Item1; if (!pattern.StartsWith("^", StringComparison.OrdinalIgnoreCase)) + { pattern = "^" + pattern; + } + if (!pattern.EndsWith("$", StringComparison.OrdinalIgnoreCase)) + { pattern += "$"; + } var match = Regex.Match( path, @@ -36,7 +54,9 @@ namespace DotNetCore.CAP.Dashboard RegexOptions.CultureInvariant | RegexOptions.IgnoreCase | RegexOptions.Singleline); if (match.Success) + { return new Tuple(dispatcher.Item2, match); + } } return null; diff --git a/src/DotNetCore.CAP/Dashboard/RouteCollectionExtensions.cs b/src/DotNetCore.CAP/Dashboard/RouteCollectionExtensions.cs index 393335c..dec40eb 100644 --- a/src/DotNetCore.CAP/Dashboard/RouteCollectionExtensions.cs +++ b/src/DotNetCore.CAP/Dashboard/RouteCollectionExtensions.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using System.Text.RegularExpressions; namespace DotNetCore.CAP.Dashboard @@ -10,9 +13,20 @@ namespace DotNetCore.CAP.Dashboard string pathTemplate, Func pageFunc) { - if (routes == null) throw new ArgumentNullException(nameof(routes)); - if (pathTemplate == null) throw new ArgumentNullException(nameof(pathTemplate)); - if (pageFunc == null) throw new ArgumentNullException(nameof(pageFunc)); + if (routes == null) + { + throw new ArgumentNullException(nameof(routes)); + } + + if (pathTemplate == null) + { + throw new ArgumentNullException(nameof(pathTemplate)); + } + + if (pageFunc == null) + { + throw new ArgumentNullException(nameof(pageFunc)); + } routes.Add(pathTemplate, new RazorPageDispatcher(pageFunc)); } @@ -22,9 +36,20 @@ namespace DotNetCore.CAP.Dashboard string pathTemplate, Func command) { - if (routes == null) throw new ArgumentNullException(nameof(routes)); - if (pathTemplate == null) throw new ArgumentNullException(nameof(pathTemplate)); - if (command == null) throw new ArgumentNullException(nameof(command)); + if (routes == null) + { + throw new ArgumentNullException(nameof(routes)); + } + + if (pathTemplate == null) + { + throw new ArgumentNullException(nameof(pathTemplate)); + } + + if (command == null) + { + throw new ArgumentNullException(nameof(command)); + } routes.Add(pathTemplate, new CommandDispatcher(command)); } @@ -34,9 +59,20 @@ namespace DotNetCore.CAP.Dashboard string pathTemplate, Func func) { - if (routes == null) throw new ArgumentNullException(nameof(routes)); - if (pathTemplate == null) throw new ArgumentNullException(nameof(pathTemplate)); - if (func == null) throw new ArgumentNullException(nameof(func)); + if (routes == null) + { + throw new ArgumentNullException(nameof(routes)); + } + + if (pathTemplate == null) + { + throw new ArgumentNullException(nameof(pathTemplate)); + } + + if (func == null) + { + throw new ArgumentNullException(nameof(func)); + } routes.Add(pathTemplate, new JsonDispatcher(func)); } @@ -46,9 +82,20 @@ namespace DotNetCore.CAP.Dashboard string pathTemplate, Func jsonfunc) { - if (routes == null) throw new ArgumentNullException(nameof(routes)); - if (pathTemplate == null) throw new ArgumentNullException(nameof(pathTemplate)); - if (jsonfunc == null) throw new ArgumentNullException(nameof(jsonfunc)); + if (routes == null) + { + throw new ArgumentNullException(nameof(routes)); + } + + if (pathTemplate == null) + { + throw new ArgumentNullException(nameof(pathTemplate)); + } + + if (jsonfunc == null) + { + throw new ArgumentNullException(nameof(jsonfunc)); + } routes.Add(pathTemplate, new JsonDispatcher(jsonfunc)); } @@ -58,9 +105,20 @@ namespace DotNetCore.CAP.Dashboard string pathTemplate, Action command) { - if (routes == null) throw new ArgumentNullException(nameof(routes)); - if (pathTemplate == null) throw new ArgumentNullException(nameof(pathTemplate)); - if (command == null) throw new ArgumentNullException(nameof(command)); + if (routes == null) + { + throw new ArgumentNullException(nameof(routes)); + } + + if (pathTemplate == null) + { + throw new ArgumentNullException(nameof(pathTemplate)); + } + + if (command == null) + { + throw new ArgumentNullException(nameof(command)); + } routes.Add(pathTemplate, new BatchCommandDispatcher(command)); } diff --git a/src/DotNetCore.CAP/Dashboard/UrlHelper.cs b/src/DotNetCore.CAP/Dashboard/UrlHelper.cs index 9eb89d1..b3acbfa 100644 --- a/src/DotNetCore.CAP/Dashboard/UrlHelper.cs +++ b/src/DotNetCore.CAP/Dashboard/UrlHelper.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using System.Net; namespace DotNetCore.CAP.Dashboard diff --git a/src/DotNetCore.CAP/DotNetCore.CAP.csproj b/src/DotNetCore.CAP/DotNetCore.CAP.csproj index 697d6db..816da2f 100644 --- a/src/DotNetCore.CAP/DotNetCore.CAP.csproj +++ b/src/DotNetCore.CAP/DotNetCore.CAP.csproj @@ -142,4 +142,9 @@ Strings.Designer.cs + + + RazorGenerator + + \ No newline at end of file diff --git a/src/DotNetCore.CAP/IBootstrapper.Default.cs b/src/DotNetCore.CAP/IBootstrapper.Default.cs index 64f298c..d89986a 100644 --- a/src/DotNetCore.CAP/IBootstrapper.Default.cs +++ b/src/DotNetCore.CAP/IBootstrapper.Default.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; @@ -19,10 +22,6 @@ namespace DotNetCore.CAP private readonly ILogger _logger; private Task _bootstrappingTask; - private IStorage Storage { get; } - - private IEnumerable Processors { get; } - public DefaultBootstrapper( ILogger logger, IStorage storage, @@ -49,6 +48,10 @@ namespace DotNetCore.CAP }); } + private IStorage Storage { get; } + + private IEnumerable Processors { get; } + public Task BootstrapAsync() { return _bootstrappingTask = BootstrapTaskAsync(); @@ -58,15 +61,23 @@ namespace DotNetCore.CAP { await Storage.InitializeAsync(_cts.Token); - if (_cts.IsCancellationRequested) return; + if (_cts.IsCancellationRequested) + { + return; + } _appLifetime.ApplicationStopping.Register(() => { foreach (var item in Processors) + { item.Dispose(); + } }); - if (_cts.IsCancellationRequested) return; + if (_cts.IsCancellationRequested) + { + return; + } await BootstrapCoreAsync(); @@ -77,6 +88,7 @@ namespace DotNetCore.CAP protected virtual Task BootstrapCoreAsync() { foreach (var item in Processors) + { try { item.Start(); @@ -85,6 +97,8 @@ namespace DotNetCore.CAP { _logger.ProcessorsStartedError(ex); } + } + return Task.CompletedTask; } } diff --git a/src/DotNetCore.CAP/IBootstrapper.cs b/src/DotNetCore.CAP/IBootstrapper.cs index aacd1dc..9da1627 100644 --- a/src/DotNetCore.CAP/IBootstrapper.cs +++ b/src/DotNetCore.CAP/IBootstrapper.cs @@ -1,4 +1,7 @@ -using System.Threading.Tasks; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System.Threading.Tasks; namespace DotNetCore.CAP { diff --git a/src/DotNetCore.CAP/ICallbackPublisher.cs b/src/DotNetCore.CAP/ICallbackPublisher.cs index 37170b9..0046e94 100644 --- a/src/DotNetCore.CAP/ICallbackPublisher.cs +++ b/src/DotNetCore.CAP/ICallbackPublisher.cs @@ -1,4 +1,7 @@ -using System.Threading.Tasks; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System.Threading.Tasks; using DotNetCore.CAP.Models; namespace DotNetCore.CAP @@ -11,6 +14,6 @@ namespace DotNetCore.CAP /// /// Publish a callback message /// - Task PublishAsync(CapPublishedMessage obj); + Task PublishCallbackAsync(CapPublishedMessage obj); } } \ No newline at end of file diff --git a/src/DotNetCore.CAP/ICapOptionsExtension.cs b/src/DotNetCore.CAP/ICapOptionsExtension.cs index cc467b8..f6c2fc4 100644 --- a/src/DotNetCore.CAP/ICapOptionsExtension.cs +++ b/src/DotNetCore.CAP/ICapOptionsExtension.cs @@ -1,4 +1,7 @@ -using Microsoft.Extensions.DependencyInjection; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using Microsoft.Extensions.DependencyInjection; namespace DotNetCore.CAP { @@ -10,7 +13,7 @@ namespace DotNetCore.CAP /// /// Registered child service. /// - /// add service to the + /// add service to the void AddServices(IServiceCollection services); } } \ No newline at end of file diff --git a/src/DotNetCore.CAP/ICapPublisher.cs b/src/DotNetCore.CAP/ICapPublisher.cs index 9d0fd2f..8ba45e1 100644 --- a/src/DotNetCore.CAP/ICapPublisher.cs +++ b/src/DotNetCore.CAP/ICapPublisher.cs @@ -1,4 +1,7 @@ -using System.Data; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System.Data; using System.Threading.Tasks; namespace DotNetCore.CAP diff --git a/src/DotNetCore.CAP/ICapSubscribe.cs b/src/DotNetCore.CAP/ICapSubscribe.cs index b358609..9b536ad 100644 --- a/src/DotNetCore.CAP/ICapSubscribe.cs +++ b/src/DotNetCore.CAP/ICapSubscribe.cs @@ -1,4 +1,7 @@ -namespace DotNetCore.CAP +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +namespace DotNetCore.CAP { /// /// An empty interface, which is used to mark the current class have a CAP methods. diff --git a/src/DotNetCore.CAP/IConsumerClient.cs b/src/DotNetCore.CAP/IConsumerClient.cs index 4dd3143..8b46e71 100644 --- a/src/DotNetCore.CAP/IConsumerClient.cs +++ b/src/DotNetCore.CAP/IConsumerClient.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using System.Collections.Generic; using System.Threading; diff --git a/src/DotNetCore.CAP/IConsumerClientFactory.cs b/src/DotNetCore.CAP/IConsumerClientFactory.cs index 94b9cae..a5ce33f 100644 --- a/src/DotNetCore.CAP/IConsumerClientFactory.cs +++ b/src/DotNetCore.CAP/IConsumerClientFactory.cs @@ -1,4 +1,7 @@ -namespace DotNetCore.CAP +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +namespace DotNetCore.CAP { /// /// Consumer client factory to create consumer client instance. diff --git a/src/DotNetCore.CAP/IConsumerHandler.Default.cs b/src/DotNetCore.CAP/IConsumerHandler.Default.cs index 645a121..86baecc 100644 --- a/src/DotNetCore.CAP/IConsumerHandler.Default.cs +++ b/src/DotNetCore.CAP/IConsumerHandler.Default.cs @@ -1,41 +1,41 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using System.Linq; using System.Threading; using System.Threading.Tasks; using DotNetCore.CAP.Infrastructure; using DotNetCore.CAP.Internal; using DotNetCore.CAP.Models; -using DotNetCore.CAP.Processor; -using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; namespace DotNetCore.CAP { internal class ConsumerHandler : IConsumerHandler { + private readonly IStorageConnection _connection; private readonly IConsumerClientFactory _consumerClientFactory; - private readonly CancellationTokenSource _cts; + private readonly IDispatcher _dispatcher; private readonly ILogger _logger; - private readonly TimeSpan _pollingDelay = TimeSpan.FromSeconds(1); private readonly MethodMatcherCache _selector; - private readonly IServiceProvider _serviceProvider; private Task _compositeTask; - private bool _disposed; - public ConsumerHandler( - IServiceProvider serviceProvider, - IConsumerClientFactory consumerClientFactory, + public ConsumerHandler(IConsumerClientFactory consumerClientFactory, + IDispatcher dispatcher, + IStorageConnection connection, ILogger logger, MethodMatcherCache selector) { _selector = selector; _logger = logger; - _serviceProvider = serviceProvider; _consumerClientFactory = consumerClientFactory; + _dispatcher = dispatcher; + _connection = connection; _cts = new CancellationTokenSource(); } @@ -44,25 +44,30 @@ namespace DotNetCore.CAP var groupingMatches = _selector.GetCandidatesMethodsOfGroupNameGrouped(); foreach (var matchGroup in groupingMatches) + { Task.Factory.StartNew(() => - { - using (var client = _consumerClientFactory.Create(matchGroup.Key)) - { - RegisterMessageProcessor(client); + { + using (var client = _consumerClientFactory.Create(matchGroup.Key)) + { + RegisterMessageProcessor(client); - client.Subscribe(matchGroup.Value.Select(x => x.Attribute.Name)); + client.Subscribe(matchGroup.Value.Select(x => x.Attribute.Name)); - client.Listening(_pollingDelay, _cts.Token); - } - }, _cts.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default); + client.Listening(_pollingDelay, _cts.Token); + } + }, _cts.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default); + } - _compositeTask = Task.CompletedTask; + _compositeTask = Task.CompletedTask; } public void Dispose() { if (_disposed) + { return; + } + _disposed = true; _cts.Cancel(); try @@ -73,35 +78,35 @@ namespace DotNetCore.CAP { var innerEx = ex.InnerExceptions[0]; if (!(innerEx is OperationCanceledException)) + { _logger.ExpectedOperationCanceledException(innerEx); + } } } public void Pulse() { - SubscribeQueuer.PulseEvent.Set(); + //ignore } private void RegisterMessageProcessor(IConsumerClient client) { client.OnMessageReceived += (sender, message) => { - _logger.EnqueuingReceivedMessage(message.Name, message.Content); + try + { + var storedMessage = StoreMessage(message); + + client.Commit(); - using (var scope = _serviceProvider.CreateScope()) + _dispatcher.EnqueueToExecute(storedMessage); + } + catch (Exception e) { - try - { - StoreMessage(scope, message); - client.Commit(); - } - catch (Exception e) - { - _logger.LogError(e, "An exception occurred when storage received message. Message:'{0}'.", message); - client.Reject(); - } + _logger.LogError(e, "An exception occurred when storage received message. Message:'{0}'.", + message); + client.Reject(); } - Pulse(); }; client.OnLog += WriteLog; @@ -134,15 +139,15 @@ namespace DotNetCore.CAP } } - private static void StoreMessage(IServiceScope serviceScope, MessageContext messageContext) + private CapReceivedMessage StoreMessage(MessageContext messageContext) { - var provider = serviceScope.ServiceProvider; - var messageStore = provider.GetRequiredService(); var receivedMessage = new CapReceivedMessage(messageContext) { StatusName = StatusName.Scheduled }; - messageStore.StoreReceivedMessageAsync(receivedMessage).GetAwaiter().GetResult(); + var id = _connection.StoreReceivedMessageAsync(receivedMessage).GetAwaiter().GetResult(); + receivedMessage.Id = id; + return receivedMessage; } } } \ No newline at end of file diff --git a/src/DotNetCore.CAP/IConsumerHandler.cs b/src/DotNetCore.CAP/IConsumerHandler.cs index f9e7c6c..86588e2 100644 --- a/src/DotNetCore.CAP/IConsumerHandler.cs +++ b/src/DotNetCore.CAP/IConsumerHandler.cs @@ -1,4 +1,7 @@ -namespace DotNetCore.CAP +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +namespace DotNetCore.CAP { /// /// Handler received message of subscribed. diff --git a/src/DotNetCore.CAP/IDispatcher.cs b/src/DotNetCore.CAP/IDispatcher.cs new file mode 100644 index 0000000..e8553ea --- /dev/null +++ b/src/DotNetCore.CAP/IDispatcher.cs @@ -0,0 +1,14 @@ +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using DotNetCore.CAP.Models; + +namespace DotNetCore.CAP +{ + public interface IDispatcher + { + void EnqueueToPublish(CapPublishedMessage message); + + void EnqueueToExecute(CapReceivedMessage message); + } +} \ No newline at end of file diff --git a/src/DotNetCore.CAP/IFetchedMessage.cs b/src/DotNetCore.CAP/IFetchedMessage.cs index ca7acee..34a78ca 100644 --- a/src/DotNetCore.CAP/IFetchedMessage.cs +++ b/src/DotNetCore.CAP/IFetchedMessage.cs @@ -1,3 +1,6 @@ +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + using System; using DotNetCore.CAP.Models; diff --git a/src/DotNetCore.CAP/IProcessingServer.cs b/src/DotNetCore.CAP/IProcessingServer.cs index 5369800..b7baa9a 100644 --- a/src/DotNetCore.CAP/IProcessingServer.cs +++ b/src/DotNetCore.CAP/IProcessingServer.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; namespace DotNetCore.CAP { diff --git a/src/DotNetCore.CAP/IPublishExecutor.cs b/src/DotNetCore.CAP/IPublishExecutor.cs index 08245de..3997273 100644 --- a/src/DotNetCore.CAP/IPublishExecutor.cs +++ b/src/DotNetCore.CAP/IPublishExecutor.cs @@ -1,4 +1,7 @@ -using System.Threading.Tasks; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System.Threading.Tasks; namespace DotNetCore.CAP { @@ -15,4 +18,4 @@ namespace DotNetCore.CAP /// Task PublishAsync(string keyName, string content); } -} +} \ No newline at end of file diff --git a/src/DotNetCore.CAP/IPublishMessageSender.Base.cs b/src/DotNetCore.CAP/IPublishMessageSender.Base.cs new file mode 100644 index 0000000..d57fc8c --- /dev/null +++ b/src/DotNetCore.CAP/IPublishMessageSender.Base.cs @@ -0,0 +1,118 @@ +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; +using System.Diagnostics; +using System.Threading.Tasks; +using DotNetCore.CAP.Infrastructure; +using DotNetCore.CAP.Internal; +using DotNetCore.CAP.Models; +using DotNetCore.CAP.Processor; +using DotNetCore.CAP.Processor.States; +using Microsoft.Extensions.Logging; + +namespace DotNetCore.CAP +{ + public abstract class BasePublishMessageSender : IPublishMessageSender, IPublishExecutor + { + private readonly IStorageConnection _connection; + private readonly ILogger _logger; + private readonly CapOptions _options; + private readonly IStateChanger _stateChanger; + + protected BasePublishMessageSender( + ILogger logger, + CapOptions options, + IStorageConnection connection, + IStateChanger stateChanger) + { + _options = options; + _connection = connection; + _stateChanger = stateChanger; + _logger = logger; + } + + public abstract Task PublishAsync(string keyName, string content); + + public async Task SendAsync(CapPublishedMessage message) + { + var sp = Stopwatch.StartNew(); + + var result = await PublishAsync(message.Name, message.Content); + + sp.Stop(); + + if (result.Succeeded) + { + await SetSuccessfulState(message); + _logger.MessageHasBeenSent(sp.Elapsed.TotalSeconds); + + return OperateResult.Success; + } + + _logger.MessagePublishException(message.Id, result.Exception); + + await SetFailedState(message, result.Exception, out bool stillRetry); + + if (stillRetry) + { + _logger.SenderRetrying(3); + + await SendAsync(message); + } + return OperateResult.Failed(result.Exception); + } + + private static bool UpdateMessageForRetryAsync(CapPublishedMessage message) + { + var retryBehavior = RetryBehavior.DefaultRetry; + + var retries = ++message.Retries; + if (retries >= retryBehavior.RetryCount) + { + return false; + } + + var due = message.Added.AddSeconds(retryBehavior.RetryIn(retries)); + message.ExpiresAt = due; + + return true; + } + + private Task SetSuccessfulState(CapPublishedMessage message) + { + var succeededState = new SucceededState(_options.SucceedMessageExpiredAfter); + + return _stateChanger.ChangeStateAsync(message, succeededState, _connection); + } + + private Task SetFailedState(CapPublishedMessage message, Exception ex, out bool stillRetry) + { + IState newState = new FailedState(); + + if (ex is PublisherSentFailedException) + { + stillRetry = false; + message.Retries = _options.FailedRetryCount; // not retry if PublisherSentFailedException + } + else + { + stillRetry = UpdateMessageForRetryAsync(message); + if (stillRetry) + { + _logger.ConsumerExecutionFailedWillRetry(ex); + return Task.CompletedTask; + } + } + + AddErrorReasonToContent(message, ex); + + return _stateChanger.ChangeStateAsync(message, newState, _connection); + } + + private static void AddErrorReasonToContent(CapPublishedMessage message, Exception exception) + { + message.Content = Helper.AddExceptionProperty(message.Content, exception); + } + } +} \ No newline at end of file diff --git a/src/DotNetCore.CAP/IPublishMessageSender.cs b/src/DotNetCore.CAP/IPublishMessageSender.cs new file mode 100644 index 0000000..c99afd8 --- /dev/null +++ b/src/DotNetCore.CAP/IPublishMessageSender.cs @@ -0,0 +1,13 @@ +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System.Threading.Tasks; +using DotNetCore.CAP.Models; + +namespace DotNetCore.CAP +{ + public interface IPublishMessageSender + { + Task SendAsync(CapPublishedMessage message); + } +} \ No newline at end of file diff --git a/src/DotNetCore.CAP/IQueueExecutor.Publish.Base.cs b/src/DotNetCore.CAP/IQueueExecutor.Publish.Base.cs deleted file mode 100644 index 194e595..0000000 --- a/src/DotNetCore.CAP/IQueueExecutor.Publish.Base.cs +++ /dev/null @@ -1,94 +0,0 @@ -using System; -using System.Diagnostics; -using System.Threading.Tasks; -using DotNetCore.CAP.Infrastructure; -using DotNetCore.CAP.Models; -using DotNetCore.CAP.Processor; -using DotNetCore.CAP.Processor.States; -using Microsoft.Extensions.Logging; - -namespace DotNetCore.CAP -{ - public abstract class BasePublishQueueExecutor : IQueueExecutor, IPublishExecutor - { - private readonly ILogger _logger; - private readonly CapOptions _options; - private readonly IStateChanger _stateChanger; - - protected BasePublishQueueExecutor( - CapOptions options, - IStateChanger stateChanger, - ILogger logger) - { - _options = options; - _stateChanger = stateChanger; - _logger = logger; - } - - public async Task ExecuteAsync(IStorageConnection connection, IFetchedMessage fetched) - { - var message = await connection.GetPublishedMessageAsync(fetched.MessageId); - try - { - var sp = Stopwatch.StartNew(); - await _stateChanger.ChangeStateAsync(message, new ProcessingState(), connection); - - if (message.Retries > 0) - _logger.JobRetrying(message.Retries); - var result = await PublishAsync(message.Name, message.Content); - sp.Stop(); - - IState newState; - if (!result.Succeeded) - { - var shouldRetry = UpdateMessageForRetryAsync(message); - if (shouldRetry) - { - newState = new ScheduledState(); - _logger.JobFailedWillRetry(result.Exception); - } - else - { - newState = new FailedState(); - _logger.JobFailed(result.Exception); - } - message.Content = Helper.AddExceptionProperty(message.Content, result.Exception); - } - else - { - newState = new SucceededState(_options.SucceedMessageExpiredAfter); - } - await _stateChanger.ChangeStateAsync(message, newState, connection); - - fetched.RemoveFromQueue(); - - if (result.Succeeded) - _logger.JobExecuted(sp.Elapsed.TotalSeconds); - - return OperateResult.Success; - } - catch (Exception ex) - { - fetched.Requeue(); - _logger.ExceptionOccuredWhileExecuting(message?.Name, ex); - return OperateResult.Failed(ex); - } - } - - public abstract Task PublishAsync(string keyName, string content); - - private static bool UpdateMessageForRetryAsync(CapPublishedMessage message) - { - var retryBehavior = RetryBehavior.DefaultRetry; - - var retries = ++message.Retries; - if (retries >= retryBehavior.RetryCount) - return false; - - var due = message.Added.AddSeconds(retryBehavior.RetryIn(retries)); - message.ExpiresAt = due; - - return true; - } - } -} \ No newline at end of file diff --git a/src/DotNetCore.CAP/IQueueExecutor.Subscribe.cs b/src/DotNetCore.CAP/IQueueExecutor.Subscribe.cs deleted file mode 100644 index d1ad6ea..0000000 --- a/src/DotNetCore.CAP/IQueueExecutor.Subscribe.cs +++ /dev/null @@ -1,133 +0,0 @@ -using System; -using System.Diagnostics; -using System.Threading.Tasks; -using DotNetCore.CAP.Abstractions; -using DotNetCore.CAP.Infrastructure; -using DotNetCore.CAP.Internal; -using DotNetCore.CAP.Models; -using DotNetCore.CAP.Processor; -using DotNetCore.CAP.Processor.States; -using Microsoft.Extensions.Logging; - -namespace DotNetCore.CAP -{ - public class SubscribeQueueExecutor : IQueueExecutor - { - private readonly ILogger _logger; - private readonly CapOptions _options; - private readonly IStateChanger _stateChanger; - private readonly ISubscriberExecutor _subscriberExecutor; - - public SubscribeQueueExecutor( - CapOptions options, - IStateChanger stateChanger, - ISubscriberExecutor subscriberExecutor, - ILogger logger) - { - _options = options; - _subscriberExecutor = subscriberExecutor; - _stateChanger = stateChanger; - _logger = logger; - } - - public async Task ExecuteAsync(IStorageConnection connection, IFetchedMessage fetched) - { - var message = await connection.GetReceivedMessageAsync(fetched.MessageId); - - if (message == null) - { - _logger.LogError($"Can not found the `message` at cap received message table, message id:{fetched.MessageId} !!!"); - return OperateResult.Failed(); - } - - try - { - var sp = Stopwatch.StartNew(); - await _stateChanger.ChangeStateAsync(message, new ProcessingState(), connection); - - if (message.Retries > 0) - _logger.JobRetrying(message.Retries); - - var result = await _subscriberExecutor.ExecuteAsync(message); - sp.Stop(); - - var state = GetNewState(result, message); - - await _stateChanger.ChangeStateAsync(message, state, connection); - - fetched.RemoveFromQueue(); - - if (result.Succeeded) - _logger.JobExecuted(sp.Elapsed.TotalSeconds); - - return OperateResult.Success; - } - catch (SubscriberNotFoundException ex) - { - _logger.LogError(ex.Message); - - AddErrorReasonToContent(message, ex); - - ++message.Retries; //issue: https://github.com/dotnetcore/CAP/issues/90 - - await _stateChanger.ChangeStateAsync(message, new FailedState(), connection); - - fetched.RemoveFromQueue(); - - return OperateResult.Failed(ex); - } - catch (Exception ex) - { - _logger.ExceptionOccuredWhileExecuting(message.Name, ex); - - fetched.Requeue(); - - return OperateResult.Failed(ex); - } - } - - private IState GetNewState(OperateResult result, CapReceivedMessage message) - { - IState newState; - if (!result.Succeeded) - { - var shouldRetry = UpdateMessageForRetry(message); - if (shouldRetry) - { - newState = new ScheduledState(); - _logger.JobFailedWillRetry(result.Exception); - } - else - { - newState = new FailedState(); - _logger.JobFailed(result.Exception); - } - AddErrorReasonToContent(message, result.Exception); - } - else - { - newState = new SucceededState(_options.SucceedMessageExpiredAfter); - } - return newState; - } - - private static bool UpdateMessageForRetry(CapReceivedMessage message) - { - var retryBehavior = RetryBehavior.DefaultRetry; - - var retries = ++message.Retries; - if (retries >= retryBehavior.RetryCount) - return false; - - var due = message.Added.AddSeconds(retryBehavior.RetryIn(retries)); - message.ExpiresAt = due; - - return true; - } - - private static void AddErrorReasonToContent(CapReceivedMessage message, Exception exception) - { - message.Content = Helper.AddExceptionProperty(message.Content, exception); - } - } -} \ No newline at end of file diff --git a/src/DotNetCore.CAP/IQueueExecutor.cs b/src/DotNetCore.CAP/IQueueExecutor.cs deleted file mode 100644 index ad4a6b1..0000000 --- a/src/DotNetCore.CAP/IQueueExecutor.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.Threading.Tasks; - -namespace DotNetCore.CAP -{ - public interface IQueueExecutor - { - Task ExecuteAsync(IStorageConnection connection, IFetchedMessage message); - } -} \ No newline at end of file diff --git a/src/DotNetCore.CAP/IQueueExecutorFactory.cs b/src/DotNetCore.CAP/IQueueExecutorFactory.cs deleted file mode 100644 index 5b46ac6..0000000 --- a/src/DotNetCore.CAP/IQueueExecutorFactory.cs +++ /dev/null @@ -1,9 +0,0 @@ -using DotNetCore.CAP.Models; - -namespace DotNetCore.CAP -{ - public interface IQueueExecutorFactory - { - IQueueExecutor GetInstance(MessageType messageType); - } -} \ No newline at end of file diff --git a/src/DotNetCore.CAP/IStorage.cs b/src/DotNetCore.CAP/IStorage.cs index 1f857ac..a31b351 100644 --- a/src/DotNetCore.CAP/IStorage.cs +++ b/src/DotNetCore.CAP/IStorage.cs @@ -1,3 +1,6 @@ +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + using System.Threading; using System.Threading.Tasks; using DotNetCore.CAP.Dashboard; diff --git a/src/DotNetCore.CAP/IStorageConnection.cs b/src/DotNetCore.CAP/IStorageConnection.cs index a4f7b0b..18d5ff8 100644 --- a/src/DotNetCore.CAP/IStorageConnection.cs +++ b/src/DotNetCore.CAP/IStorageConnection.cs @@ -1,3 +1,6 @@ +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + using System; using System.Collections.Generic; using System.Threading.Tasks; @@ -18,20 +21,10 @@ namespace DotNetCore.CAP /// The message's id. Task GetPublishedMessageAsync(int id); - /// - /// Fetches the next message to be executed. - /// - Task FetchNextMessageAsync(); - - /// - /// Returns the next message to be enqueued. - /// - Task GetNextPublishedMessageToBeEnqueuedAsync(); - /// /// Returns executed failed messages. /// - Task> GetFailedPublishedMessages(); + Task> GetPublishedMessagesOfNeedRetry(); // Received messages @@ -39,7 +32,7 @@ namespace DotNetCore.CAP /// Stores the message. /// /// The message to store. - Task StoreReceivedMessageAsync(CapReceivedMessage message); + Task StoreReceivedMessageAsync(CapReceivedMessage message); /// /// Returns the message with the given id. @@ -47,15 +40,10 @@ namespace DotNetCore.CAP /// The message's id. Task GetReceivedMessageAsync(int id); - /// - /// Returns the next message to be enqueued. - /// - Task GetNextReceivedMessageToBeEnqueuedAsync(); - /// /// Returns executed failed message. /// - Task> GetFailedReceivedMessages(); + Task> GetReceivedMessagesOfNeedRetry(); /// /// Creates and returns an . diff --git a/src/DotNetCore.CAP/IStorageTransaction.cs b/src/DotNetCore.CAP/IStorageTransaction.cs index 05fea1b..7ee5185 100644 --- a/src/DotNetCore.CAP/IStorageTransaction.cs +++ b/src/DotNetCore.CAP/IStorageTransaction.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using System.Threading.Tasks; using DotNetCore.CAP.Models; @@ -14,10 +17,6 @@ namespace DotNetCore.CAP void UpdateMessage(CapReceivedMessage message); - void EnqueueMessage(CapPublishedMessage message); - - void EnqueueMessage(CapReceivedMessage message); - Task CommitAsync(); } } \ No newline at end of file diff --git a/src/DotNetCore.CAP/ISubscribeExecutor.Default.cs b/src/DotNetCore.CAP/ISubscribeExecutor.Default.cs new file mode 100644 index 0000000..0ea2b13 --- /dev/null +++ b/src/DotNetCore.CAP/ISubscribeExecutor.Default.cs @@ -0,0 +1,159 @@ +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; +using System.Diagnostics; +using System.Threading.Tasks; +using DotNetCore.CAP.Infrastructure; +using DotNetCore.CAP.Internal; +using DotNetCore.CAP.Models; +using DotNetCore.CAP.Processor; +using DotNetCore.CAP.Processor.States; +using Microsoft.Extensions.Logging; + +namespace DotNetCore.CAP +{ + internal class DefaultSubscriberExecutor : ISubscriberExecutor + { + private readonly ICallbackMessageSender _callbackMessageSender; + private readonly IStorageConnection _connection; + private readonly ILogger _logger; + private readonly CapOptions _options; + + private readonly MethodMatcherCache _selector; + private readonly IStateChanger _stateChanger; + + public DefaultSubscriberExecutor( + ILogger logger, + CapOptions options, + IConsumerInvokerFactory consumerInvokerFactory, + ICallbackMessageSender callbackMessageSender, + IStateChanger stateChanger, + IStorageConnection connection, + MethodMatcherCache selector) + { + _selector = selector; + _callbackMessageSender = callbackMessageSender; + _options = options; + _stateChanger = stateChanger; + _connection = connection; + _logger = logger; + + Invoker = consumerInvokerFactory.CreateInvoker(); + } + + private IConsumerInvoker Invoker { get; } + + public async Task ExecuteAsync(CapReceivedMessage message) + { + if (message == null) + { + throw new ArgumentNullException(nameof(message)); + } + + try + { + var sp = Stopwatch.StartNew(); + + await InvokeConsumerMethodAsync(message); + + sp.Stop(); + + await SetSuccessfulState(message); + + _logger.ConsumerExecuted(sp.Elapsed.TotalSeconds); + + return OperateResult.Success; + } + catch (Exception ex) + { + _logger.ExceptionOccuredWhileExecuting(message.Name, ex); + + await SetFailedState(message, ex, out bool stillRetry); + if (stillRetry) + { + await ExecuteAsync(message); + } + + return OperateResult.Failed(ex); + } + } + + private Task SetSuccessfulState(CapReceivedMessage message) + { + var succeededState = new SucceededState(_options.SucceedMessageExpiredAfter); + + return _stateChanger.ChangeStateAsync(message, succeededState, _connection); + } + + private Task SetFailedState(CapReceivedMessage message, Exception ex, out bool stillRetry) + { + IState newState = new FailedState(); + + if (ex is SubscriberNotFoundException) + { + stillRetry = false; + message.Retries = _options.FailedRetryCount; // not retry if SubscriberNotFoundException + } + else + { + stillRetry = UpdateMessageForRetry(message); + if (stillRetry) + { + _logger.ConsumerExecutionFailedWillRetry(ex); + return Task.CompletedTask; + } + } + + AddErrorReasonToContent(message, ex); + + return _stateChanger.ChangeStateAsync(message, newState, _connection); + } + + private static bool UpdateMessageForRetry(CapReceivedMessage message) + { + var retryBehavior = RetryBehavior.DefaultRetry; + + var retries = ++message.Retries; + if (retries >= retryBehavior.RetryCount) + { + return false; + } + + var due = message.Added.AddSeconds(retryBehavior.RetryIn(retries)); + message.ExpiresAt = due; + + return true; + } + + private static void AddErrorReasonToContent(CapReceivedMessage message, Exception exception) + { + message.Content = Helper.AddExceptionProperty(message.Content, exception); + } + + private async Task InvokeConsumerMethodAsync(CapReceivedMessage receivedMessage) + { + if (!_selector.TryGetTopicExector(receivedMessage.Name, receivedMessage.Group, + out var executor)) + { + var error = $"message can not be found subscriber, Message:{receivedMessage},\r\n see: https://github.com/dotnetcore/CAP/issues/63"; + throw new SubscriberNotFoundException(error); + } + try + { + var consumerContext = new ConsumerContext(executor, receivedMessage.ToMessageContext()); + + var ret = await Invoker.InvokeAsync(consumerContext); + + if (!string.IsNullOrEmpty(ret.CallbackName)) + { + await _callbackMessageSender.SendAsync(ret.MessageId, ret.CallbackName, ret.Result); + } + } + catch (Exception ex) + { + throw new SubscriberExecutionFailedException(ex.Message, ex); + } + } + } +} \ No newline at end of file diff --git a/src/DotNetCore.CAP/ISubscriberExecutor.cs b/src/DotNetCore.CAP/ISubscriberExecutor.cs new file mode 100644 index 0000000..a099b60 --- /dev/null +++ b/src/DotNetCore.CAP/ISubscriberExecutor.cs @@ -0,0 +1,13 @@ +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System.Threading.Tasks; +using DotNetCore.CAP.Models; + +namespace DotNetCore.CAP +{ + public interface ISubscriberExecutor + { + Task ExecuteAsync(CapReceivedMessage message); + } +} \ No newline at end of file diff --git a/src/DotNetCore.CAP/Infrastructure/Helper.cs b/src/DotNetCore.CAP/Infrastructure/Helper.cs index b04592e..92ae6fb 100644 --- a/src/DotNetCore.CAP/Infrastructure/Helper.cs +++ b/src/DotNetCore.CAP/Infrastructure/Helper.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using System.ComponentModel; using System.Reflection; using Newtonsoft.Json; @@ -32,7 +35,10 @@ namespace DotNetCore.CAP.Infrastructure public static object FromJson(string value, Type type) { - if (type == null) throw new ArgumentNullException(nameof(type)); + if (type == null) + { + throw new ArgumentNullException(nameof(type)); + } return value != null ? JsonConvert.DeserializeObject(value, type, _serializerSettings) @@ -54,13 +60,19 @@ namespace DotNetCore.CAP.Infrastructure public static bool IsController(TypeInfo typeInfo) { if (!typeInfo.IsClass) + { return false; + } if (typeInfo.IsAbstract) + { return false; + } if (!typeInfo.IsPublic) + { return false; + } return !typeInfo.ContainsGenericParameters && typeInfo.Name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase); @@ -149,9 +161,13 @@ namespace DotNetCore.CAP.Infrastructure var jObj = JObject.Parse(json); if (jObj.TryGetValue(propertyName, out var _)) + { jObj[propertyName] = propertyValue; + } else + { jObj.Add(new JProperty(propertyName, propertyValue)); + } return jObj.ToString(Formatting.None); } diff --git a/src/DotNetCore.CAP/Infrastructure/ObjectId.cs b/src/DotNetCore.CAP/Infrastructure/ObjectId.cs index 4a51166..4b93ce6 100644 --- a/src/DotNetCore.CAP/Infrastructure/ObjectId.cs +++ b/src/DotNetCore.CAP/Infrastructure/ObjectId.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using System.Diagnostics; using System.Linq; using System.Runtime.CompilerServices; @@ -57,7 +60,10 @@ namespace DotNetCore.CAP.Infrastructure public ObjectId(byte[] bytes) { if (bytes == null) + { throw new ArgumentNullException("bytes"); + } + Unpack(bytes, out _timestamp, out _machine, out _pid, out _increment); } @@ -83,11 +89,16 @@ namespace DotNetCore.CAP.Infrastructure public ObjectId(int timestamp, int machine, short pid, int increment) { if ((machine & 0xff000000) != 0) + { throw new ArgumentOutOfRangeException("machine", "The machine value must be between 0 and 16777215 (it must fit in 3 bytes)."); + } + if ((increment & 0xff000000) != 0) + { throw new ArgumentOutOfRangeException("increment", "The increment value must be between 0 and 16777215 (it must fit in 3 bytes)."); + } _timestamp = timestamp; _machine = machine; @@ -102,7 +113,10 @@ namespace DotNetCore.CAP.Infrastructure public ObjectId(string value) { if (value == null) + { throw new ArgumentNullException("value"); + } + Unpack(ParseHexString(value), out _timestamp, out _machine, out _pid, out _increment); } @@ -256,11 +270,16 @@ namespace DotNetCore.CAP.Infrastructure public static byte[] Pack(int timestamp, int machine, short pid, int increment) { if ((machine & 0xff000000) != 0) + { throw new ArgumentOutOfRangeException("machine", "The machine value must be between 0 and 16777215 (it must fit in 3 bytes)."); + } + if ((increment & 0xff000000) != 0) + { throw new ArgumentOutOfRangeException("increment", "The increment value must be between 0 and 16777215 (it must fit in 3 bytes)."); + } var bytes = new byte[12]; bytes[0] = (byte) (timestamp >> 24); @@ -286,9 +305,15 @@ namespace DotNetCore.CAP.Infrastructure public static ObjectId Parse(string s) { if (s == null) + { throw new ArgumentNullException("s"); + } + if (s.Length != 24) + { throw new ArgumentOutOfRangeException("s", "ObjectId string value must be 24 characters."); + } + return new ObjectId(ParseHexString(s)); } @@ -303,9 +328,15 @@ namespace DotNetCore.CAP.Infrastructure public static void Unpack(byte[] bytes, out int timestamp, out int machine, out short pid, out int increment) { if (bytes == null) + { throw new ArgumentNullException("bytes"); + } + if (bytes.Length != 12) + { throw new ArgumentOutOfRangeException("bytes", "Byte array must be 12 bytes long."); + } + timestamp = (bytes[0] << 24) + (bytes[1] << 16) + (bytes[2] << 8) + bytes[3]; machine = (bytes[4] << 16) + (bytes[5] << 8) + bytes[6]; pid = (short) ((bytes[7] << 8) + bytes[8]); @@ -349,11 +380,23 @@ namespace DotNetCore.CAP.Infrastructure public int CompareTo(ObjectId other) { var r = _timestamp.CompareTo(other._timestamp); - if (r != 0) return r; + if (r != 0) + { + return r; + } + r = _machine.CompareTo(other._machine); - if (r != 0) return r; + if (r != 0) + { + return r; + } + r = _pid.CompareTo(other._pid); - if (r != 0) return r; + if (r != 0) + { + return r; + } + return _increment.CompareTo(other._increment); } @@ -379,7 +422,10 @@ namespace DotNetCore.CAP.Infrastructure public override bool Equals(object obj) { if (obj is ObjectId) + { return Equals((ObjectId) obj); + } + return false; } @@ -423,15 +469,21 @@ namespace DotNetCore.CAP.Infrastructure public static byte[] ParseHexString(string s) { if (s == null) + { throw new ArgumentNullException("s"); + } if (s.Length % 2 == 1) + { throw new Exception("The binary key cannot have an odd number of digits"); + } var arr = new byte[s.Length >> 1]; for (var i = 0; i < s.Length >> 1; ++i) + { arr[i] = (byte) ((GetHexVal(s[i << 1]) << 4) + GetHexVal(s[(i << 1) + 1])); + } return arr; } @@ -444,7 +496,10 @@ namespace DotNetCore.CAP.Infrastructure public static string ToHexString(byte[] bytes) { if (bytes == null) + { throw new ArgumentNullException("bytes"); + } + var result = new char[bytes.Length * 2]; for (var i = 0; i < bytes.Length; i++) { @@ -452,6 +507,7 @@ namespace DotNetCore.CAP.Infrastructure result[2 * i] = (char) val; result[2 * i + 1] = (char) (val >> 16); } + return new string(result); } @@ -474,9 +530,15 @@ namespace DotNetCore.CAP.Infrastructure public static DateTime ToUniversalTime(DateTime dateTime) { if (dateTime == DateTime.MinValue) + { return DateTime.SpecifyKind(DateTime.MinValue, DateTimeKind.Utc); + } + if (dateTime == DateTime.MaxValue) + { return DateTime.SpecifyKind(DateTime.MaxValue, DateTimeKind.Utc); + } + return dateTime.ToUniversalTime(); } diff --git a/src/DotNetCore.CAP/Infrastructure/StatusName.cs b/src/DotNetCore.CAP/Infrastructure/StatusName.cs index 62ee0fd..65d9427 100644 --- a/src/DotNetCore.CAP/Infrastructure/StatusName.cs +++ b/src/DotNetCore.CAP/Infrastructure/StatusName.cs @@ -1,4 +1,7 @@ -namespace DotNetCore.CAP.Infrastructure +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +namespace DotNetCore.CAP.Infrastructure { /// /// The message status name. @@ -6,8 +9,6 @@ public struct StatusName { public const string Scheduled = nameof(Scheduled); - public const string Enqueued = nameof(Enqueued); - public const string Processing = nameof(Processing); public const string Succeeded = nameof(Succeeded); public const string Failed = nameof(Failed); } diff --git a/src/DotNetCore.CAP/Infrastructure/WaitHandleEx.cs b/src/DotNetCore.CAP/Infrastructure/WaitHandleEx.cs index 5a277d2..14ed410 100644 --- a/src/DotNetCore.CAP/Infrastructure/WaitHandleEx.cs +++ b/src/DotNetCore.CAP/Infrastructure/WaitHandleEx.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using System.Threading; using System.Threading.Tasks; diff --git a/src/DotNetCore.CAP/Internal/CapCache.cs b/src/DotNetCore.CAP/Internal/CapCache.cs index d9826db..243f935 100644 --- a/src/DotNetCore.CAP/Internal/CapCache.cs +++ b/src/DotNetCore.CAP/Internal/CapCache.cs @@ -1,3 +1,6 @@ +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + using System; using System.Collections.Generic; using System.Linq; @@ -56,6 +59,7 @@ namespace DotNetCore.CAP.Internal Clear(); _locker.Dispose(); } + // Dispose unmanaged resources } } @@ -71,7 +75,9 @@ namespace DotNetCore.CAP.Internal try { foreach (var t in _timers.Values) + { t.Dispose(); + } } catch { @@ -98,9 +104,11 @@ namespace DotNetCore.CAP.Internal if (_timers.TryGetValue(key, out timer)) { if (restartTimerIfExists) + { timer.Change( cacheTimeout ?? Timeout.InfiniteTimeSpan, Timeout.InfiniteTimeSpan); + } } else { @@ -139,7 +147,10 @@ namespace DotNetCore.CAP.Internal /// public void AddOrUpdate(K key, T cacheObject, TimeSpan? cacheTimeout, bool restartTimerIfExists = false) { - if (disposed) return; + if (disposed) + { + return; + } _locker.EnterWriteLock(); try @@ -147,9 +158,13 @@ namespace DotNetCore.CAP.Internal CheckTimer(key, cacheTimeout, restartTimerIfExists); if (!_cache.ContainsKey(key)) + { _cache.Add(key, cacheObject); + } else + { _cache[key] = cacheObject; + } } finally { @@ -182,7 +197,10 @@ namespace DotNetCore.CAP.Internal /// The object from the cache or default(T), if not found. public T Get(K key) { - if (disposed) return default(T); + if (disposed) + { + return default(T); + } _locker.EnterReadLock(); try @@ -227,7 +245,10 @@ namespace DotNetCore.CAP.Internal /// The key pattern to remove. The Predicate has to return true to get key removed. public void Remove(Predicate keyPattern) { - if (disposed) return; + if (disposed) + { + return; + } _locker.EnterWriteLock(); try @@ -245,6 +266,7 @@ namespace DotNetCore.CAP.Internal catch { } + _timers.Remove(workKey); _cache.Remove(workKey); } @@ -262,7 +284,10 @@ namespace DotNetCore.CAP.Internal /// The cache-key to remove. public void Remove(K key) { - if (disposed) return; + if (disposed) + { + return; + } _locker.EnterWriteLock(); try @@ -276,6 +301,7 @@ namespace DotNetCore.CAP.Internal catch { } + _timers.Remove(key); _cache.Remove(key); } @@ -293,7 +319,10 @@ namespace DotNetCore.CAP.Internal /// True if the key exists in the cache, otherwise False. public bool Exists(K key) { - if (disposed) return false; + if (disposed) + { + return false; + } _locker.EnterReadLock(); try diff --git a/src/DotNetCore.CAP/Internal/ConsumerContext.cs b/src/DotNetCore.CAP/Internal/ConsumerContext.cs index cafd33f..5610d7a 100644 --- a/src/DotNetCore.CAP/Internal/ConsumerContext.cs +++ b/src/DotNetCore.CAP/Internal/ConsumerContext.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; namespace DotNetCore.CAP.Internal { diff --git a/src/DotNetCore.CAP/Internal/ConsumerExecutedResult.cs b/src/DotNetCore.CAP/Internal/ConsumerExecutedResult.cs index 093fbb3..d63bfd6 100644 --- a/src/DotNetCore.CAP/Internal/ConsumerExecutedResult.cs +++ b/src/DotNetCore.CAP/Internal/ConsumerExecutedResult.cs @@ -1,6 +1,5 @@ -using System; -using System.Collections.Generic; -using System.Text; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. namespace DotNetCore.CAP.Internal { @@ -19,4 +18,4 @@ namespace DotNetCore.CAP.Internal public string CallbackName { get; set; } } -} +} \ No newline at end of file diff --git a/src/DotNetCore.CAP/Internal/ConsumerExecutorDescriptor.cs b/src/DotNetCore.CAP/Internal/ConsumerExecutorDescriptor.cs index b69f394..07dca25 100644 --- a/src/DotNetCore.CAP/Internal/ConsumerExecutorDescriptor.cs +++ b/src/DotNetCore.CAP/Internal/ConsumerExecutorDescriptor.cs @@ -1,4 +1,7 @@ -using System.Reflection; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System.Reflection; using DotNetCore.CAP.Abstractions; namespace DotNetCore.CAP.Internal diff --git a/src/DotNetCore.CAP/Internal/ConsumerInvokerFactory.cs b/src/DotNetCore.CAP/Internal/ConsumerInvokerFactory.cs index c6eb9e0..90633dc 100644 --- a/src/DotNetCore.CAP/Internal/ConsumerInvokerFactory.cs +++ b/src/DotNetCore.CAP/Internal/ConsumerInvokerFactory.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using DotNetCore.CAP.Abstractions; using Microsoft.Extensions.Logging; @@ -6,7 +9,7 @@ namespace DotNetCore.CAP.Internal { internal class ConsumerInvokerFactory : IConsumerInvokerFactory { - private readonly ILogger _logger; + private readonly ILoggerFactory _loggerFactory; private readonly IMessagePacker _messagePacker; private readonly IModelBinderFactory _modelBinderFactory; private readonly IServiceProvider _serviceProvider; @@ -17,7 +20,7 @@ namespace DotNetCore.CAP.Internal IModelBinderFactory modelBinderFactory, IServiceProvider serviceProvider) { - _logger = loggerFactory.CreateLogger(); + _loggerFactory = loggerFactory; _messagePacker = messagePacker; _modelBinderFactory = modelBinderFactory; _serviceProvider = serviceProvider; @@ -25,7 +28,7 @@ namespace DotNetCore.CAP.Internal public IConsumerInvoker CreateInvoker() { - return new DefaultConsumerInvoker(_logger, _serviceProvider, _messagePacker, _modelBinderFactory); + return new DefaultConsumerInvoker(_loggerFactory, _serviceProvider, _messagePacker, _modelBinderFactory); } } } \ No newline at end of file diff --git a/src/DotNetCore.CAP/Internal/HashCodeCombiner.cs b/src/DotNetCore.CAP/Internal/HashCodeCombiner.cs index 842d8ff..43f000e 100644 --- a/src/DotNetCore.CAP/Internal/HashCodeCombiner.cs +++ b/src/DotNetCore.CAP/Internal/HashCodeCombiner.cs @@ -1,4 +1,7 @@ -using System.Collections; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; @@ -10,7 +13,8 @@ namespace DotNetCore.CAP.Internal public int CombinedHash { - [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return _combinedHash64.GetHashCode(); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { return _combinedHash64.GetHashCode(); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -34,6 +38,7 @@ namespace DotNetCore.CAP.Internal Add(o); count++; } + Add(count); } } diff --git a/src/DotNetCore.CAP/Internal/ICallbackMessageSender.Default.cs b/src/DotNetCore.CAP/Internal/ICallbackMessageSender.Default.cs index 1a4f446..1dc7fc8 100644 --- a/src/DotNetCore.CAP/Internal/ICallbackMessageSender.Default.cs +++ b/src/DotNetCore.CAP/Internal/ICallbackMessageSender.Default.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using System.Threading.Tasks; using DotNetCore.CAP.Abstractions; using DotNetCore.CAP.Infrastructure; @@ -10,10 +13,10 @@ namespace DotNetCore.CAP.Internal { internal class CallbackMessageSender : ICallbackMessageSender { - private readonly ILogger _logger; - private readonly IServiceProvider _serviceProvider; private readonly IContentSerializer _contentSerializer; + private readonly ILogger _logger; private readonly IMessagePacker _messagePacker; + private readonly IServiceProvider _serviceProvider; public CallbackMessageSender( ILogger logger, @@ -31,9 +34,13 @@ namespace DotNetCore.CAP.Internal { string body; if (bodyObj != null && Helper.IsComplexType(bodyObj.GetType())) + { body = _contentSerializer.Serialize(bodyObj); + } else + { body = bodyObj?.ToString(); + } _logger.LogDebug($"Callback message will publishing, name:{topicName},content:{body}"); @@ -56,8 +63,8 @@ namespace DotNetCore.CAP.Internal { var provider = scope.ServiceProvider; var callbackPublisher = provider.GetService(); - await callbackPublisher.PublishAsync(publishedMessage); + await callbackPublisher.PublishCallbackAsync(publishedMessage); } } } -} +} \ No newline at end of file diff --git a/src/DotNetCore.CAP/Internal/ICallbackMessageSender.cs b/src/DotNetCore.CAP/Internal/ICallbackMessageSender.cs index 2f7d3e6..b66cf9b 100644 --- a/src/DotNetCore.CAP/Internal/ICallbackMessageSender.cs +++ b/src/DotNetCore.CAP/Internal/ICallbackMessageSender.cs @@ -1,4 +1,7 @@ -using System.Threading.Tasks; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System.Threading.Tasks; namespace DotNetCore.CAP.Internal { diff --git a/src/DotNetCore.CAP/Internal/IConsumerInvoker.Default.cs b/src/DotNetCore.CAP/Internal/IConsumerInvoker.Default.cs index 7303f80..7fc4d0b 100644 --- a/src/DotNetCore.CAP/Internal/IConsumerInvoker.Default.cs +++ b/src/DotNetCore.CAP/Internal/IConsumerInvoker.Default.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using System.Threading.Tasks; using DotNetCore.CAP.Abstractions; using Microsoft.Extensions.DependencyInjection; @@ -10,11 +13,11 @@ namespace DotNetCore.CAP.Internal internal class DefaultConsumerInvoker : IConsumerInvoker { private readonly ILogger _logger; + private readonly IMessagePacker _messagePacker; private readonly IModelBinderFactory _modelBinderFactory; private readonly IServiceProvider _serviceProvider; - private readonly IMessagePacker _messagePacker; - public DefaultConsumerInvoker(ILogger logger, + public DefaultConsumerInvoker(ILoggerFactory loggerFactory, IServiceProvider serviceProvider, IMessagePacker messagePacker, IModelBinderFactory modelBinderFactory) @@ -22,7 +25,7 @@ namespace DotNetCore.CAP.Internal _modelBinderFactory = modelBinderFactory; _serviceProvider = serviceProvider; _messagePacker = messagePacker; - _logger = logger; + _logger = loggerFactory.CreateLogger(); } public async Task InvokeAsync(ConsumerContext context) @@ -44,9 +47,14 @@ namespace DotNetCore.CAP.Internal object resultObj; if (executor.MethodParameters.Length > 0) + { resultObj = await ExecuteWithParameterAsync(executor, obj, message.Content); + } else + { resultObj = await ExecuteAsync(executor, obj); + } + return new ConsumerExecutedResult(resultObj, message.Id, message.CallbackName); } } @@ -54,7 +62,10 @@ namespace DotNetCore.CAP.Internal private async Task ExecuteAsync(ObjectMethodExecutor executor, object @class) { if (executor.IsMethodAsync) + { return await executor.ExecuteAsync(@class); + } + return executor.Execute(@class); } @@ -69,9 +80,13 @@ namespace DotNetCore.CAP.Internal if (bindResult.IsSuccess) { if (executor.IsMethodAsync) + { return await executor.ExecuteAsync(@class, bindResult.Model); + } + return executor.Execute(@class, bindResult.Model); } + throw new MethodBindException( $"Parameters:{firstParameter.Name} bind failed! ParameterString is: {parameterString} "); } diff --git a/src/DotNetCore.CAP/Internal/IConsumerInvoker.cs b/src/DotNetCore.CAP/Internal/IConsumerInvoker.cs index 13c7964..892c2e3 100644 --- a/src/DotNetCore.CAP/Internal/IConsumerInvoker.cs +++ b/src/DotNetCore.CAP/Internal/IConsumerInvoker.cs @@ -1,4 +1,7 @@ -using System.Threading.Tasks; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System.Threading.Tasks; namespace DotNetCore.CAP.Internal { diff --git a/src/DotNetCore.CAP/Internal/IConsumerInvokerFactory.cs b/src/DotNetCore.CAP/Internal/IConsumerInvokerFactory.cs index 44d38d5..9fd7e74 100644 --- a/src/DotNetCore.CAP/Internal/IConsumerInvokerFactory.cs +++ b/src/DotNetCore.CAP/Internal/IConsumerInvokerFactory.cs @@ -1,4 +1,7 @@ -namespace DotNetCore.CAP.Internal +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +namespace DotNetCore.CAP.Internal { internal interface IConsumerInvokerFactory { diff --git a/src/DotNetCore.CAP/Internal/IConsumerServiceSelector.Default.cs b/src/DotNetCore.CAP/Internal/IConsumerServiceSelector.Default.cs index e20d306..7bf92f3 100644 --- a/src/DotNetCore.CAP/Internal/IConsumerServiceSelector.Default.cs +++ b/src/DotNetCore.CAP/Internal/IConsumerServiceSelector.Default.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using System.Collections.Generic; using System.Linq; using System.Reflection; @@ -10,7 +13,7 @@ namespace DotNetCore.CAP.Internal { /// /// - /// A default implementation. + /// A default implementation. /// internal class DefaultConsumerServiceSelector : IConsumerServiceSelector { @@ -18,7 +21,7 @@ namespace DotNetCore.CAP.Internal private readonly IServiceProvider _serviceProvider; /// - /// Creates a new . + /// Creates a new . /// public DefaultConsumerServiceSelector(IServiceProvider serviceProvider, CapOptions capOptions) { @@ -27,9 +30,9 @@ namespace DotNetCore.CAP.Internal } /// - /// Selects the best candidate from for - /// the - /// current message associated. + /// Selects the best candidate from for + /// the + /// current message associated. /// public ConsumerExecutorDescriptor SelectBestCandidate(string key, IReadOnlyList executeDescriptor) @@ -61,10 +64,13 @@ namespace DotNetCore.CAP.Internal { var typeInfo = service.GetType().GetTypeInfo(); if (!typeof(ICapSubscribe).GetTypeInfo().IsAssignableFrom(typeInfo)) + { continue; + } executorDescriptorList.AddRange(GetTopicAttributesDescription(typeInfo)); } + return executorDescriptorList; } } @@ -78,7 +84,9 @@ namespace DotNetCore.CAP.Internal { var typeInfo = type.GetTypeInfo(); if (Helper.IsController(typeInfo)) + { executorDescriptorList.AddRange(GetTopicAttributesDescription(typeInfo)); + } } return executorDescriptorList; @@ -91,12 +99,18 @@ namespace DotNetCore.CAP.Internal var topicAttr = method.GetCustomAttributes(true); var topicAttributes = topicAttr as IList ?? topicAttr.ToList(); - if (!topicAttributes.Any()) continue; + if (!topicAttributes.Any()) + { + continue; + } foreach (var attr in topicAttributes) { if (attr.Group == null) + { attr.Group = _capOptions.DefaultGroup; + } + yield return InitDescriptor(attr, method, typeInfo); } } diff --git a/src/DotNetCore.CAP/Internal/IConsumerServiceSelector.cs b/src/DotNetCore.CAP/Internal/IConsumerServiceSelector.cs index 3944d79..3c1dc94 100644 --- a/src/DotNetCore.CAP/Internal/IConsumerServiceSelector.cs +++ b/src/DotNetCore.CAP/Internal/IConsumerServiceSelector.cs @@ -1,4 +1,7 @@ -using System.Collections.Generic; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System.Collections.Generic; namespace DotNetCore.CAP.Internal { diff --git a/src/DotNetCore.CAP/Internal/IContentSerializer.Json.cs b/src/DotNetCore.CAP/Internal/IContentSerializer.Json.cs index 21b656d..408b82c 100644 --- a/src/DotNetCore.CAP/Internal/IContentSerializer.Json.cs +++ b/src/DotNetCore.CAP/Internal/IContentSerializer.Json.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using DotNetCore.CAP.Abstractions; using DotNetCore.CAP.Infrastructure; @@ -6,7 +9,7 @@ namespace DotNetCore.CAP.Internal { internal class JsonContentSerializer : IContentSerializer { - public T DeSerialize(string messageObjStr) + public T DeSerialize(string messageObjStr) { return Helper.FromJson(messageObjStr); } diff --git a/src/DotNetCore.CAP/Internal/IMessagePacker.Default.cs b/src/DotNetCore.CAP/Internal/IMessagePacker.Default.cs index ea333fa..6de5ac8 100644 --- a/src/DotNetCore.CAP/Internal/IMessagePacker.Default.cs +++ b/src/DotNetCore.CAP/Internal/IMessagePacker.Default.cs @@ -1,4 +1,7 @@ -using DotNetCore.CAP.Abstractions; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using DotNetCore.CAP.Abstractions; using DotNetCore.CAP.Models; namespace DotNetCore.CAP.Internal @@ -22,4 +25,4 @@ namespace DotNetCore.CAP.Internal return _serializer.DeSerialize(packingMessage); } } -} +} \ No newline at end of file diff --git a/src/DotNetCore.CAP/Internal/IModelBinder.ComplexType.cs b/src/DotNetCore.CAP/Internal/IModelBinder.ComplexType.cs index c2bfc19..79cf4c3 100644 --- a/src/DotNetCore.CAP/Internal/IModelBinder.ComplexType.cs +++ b/src/DotNetCore.CAP/Internal/IModelBinder.ComplexType.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using System.Reflection; using System.Threading.Tasks; using DotNetCore.CAP.Abstractions; diff --git a/src/DotNetCore.CAP/Internal/IModelBinder.SimpleType.cs b/src/DotNetCore.CAP/Internal/IModelBinder.SimpleType.cs index c6f6c80..3761891 100644 --- a/src/DotNetCore.CAP/Internal/IModelBinder.SimpleType.cs +++ b/src/DotNetCore.CAP/Internal/IModelBinder.SimpleType.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using System.ComponentModel; using System.Globalization; using System.Reflection; @@ -22,7 +25,9 @@ namespace DotNetCore.CAP.Internal public Task BindModelAsync(string content) { if (content == null) + { throw new ArgumentNullException(nameof(content)); + } var parameterType = _parameterInfo.ParameterType; @@ -30,27 +35,43 @@ namespace DotNetCore.CAP.Internal { object model; if (parameterType == typeof(string)) + { if (string.IsNullOrWhiteSpace(content)) + { model = null; + } else + { model = content; + } + } else if (string.IsNullOrWhiteSpace(content)) + { model = null; + } else + { model = _typeConverter.ConvertFrom( null, CultureInfo.CurrentCulture, content); + } if (model == null && !IsReferenceOrNullableType(parameterType)) + { return Task.FromResult(ModelBindingResult.Failed()); + } + return Task.FromResult(ModelBindingResult.Success(model)); } catch (Exception exception) { var isFormatException = exception is FormatException; if (!isFormatException && exception.InnerException != null) + { exception = ExceptionDispatchInfo.Capture(exception.InnerException).SourceException; + } + throw; } } diff --git a/src/DotNetCore.CAP/Internal/ISubscriberExecutor.Default.cs b/src/DotNetCore.CAP/Internal/ISubscriberExecutor.Default.cs deleted file mode 100644 index ca13a7f..0000000 --- a/src/DotNetCore.CAP/Internal/ISubscriberExecutor.Default.cs +++ /dev/null @@ -1,57 +0,0 @@ -using System; -using System.Threading.Tasks; -using DotNetCore.CAP.Abstractions; -using DotNetCore.CAP.Models; -using Microsoft.Extensions.Logging; - -namespace DotNetCore.CAP.Internal -{ - internal class DefaultSubscriberExecutor : ISubscriberExecutor - { - private readonly ICallbackMessageSender _callbackMessageSender; - private readonly ILogger _logger; - private readonly MethodMatcherCache _selector; - - private IConsumerInvoker Invoker { get; } - - public DefaultSubscriberExecutor(MethodMatcherCache selector, - IConsumerInvokerFactory consumerInvokerFactory, - ICallbackMessageSender callbackMessageSender, - ILogger logger) - { - _selector = selector; - _callbackMessageSender = callbackMessageSender; - _logger = logger; - - Invoker = consumerInvokerFactory.CreateInvoker(); - } - - public async Task ExecuteAsync(CapReceivedMessage receivedMessage) - { - if (!_selector.TryGetTopicExector(receivedMessage.Name, receivedMessage.Group, - out var executor)) - { - var error = "message can not be found subscriber. Message:" + receivedMessage; - error += "\r\n see: https://github.com/dotnetcore/CAP/issues/63"; - throw new SubscriberNotFoundException(error); - } - - var consumerContext = new ConsumerContext(executor, receivedMessage.ToMessageContext()); - try - { - var ret = await Invoker.InvokeAsync(consumerContext); - if (!string.IsNullOrEmpty(ret.CallbackName)) - await _callbackMessageSender.SendAsync(ret.MessageId, ret.CallbackName, ret.Result); - - return OperateResult.Success; - } - catch (Exception ex) - { - _logger.ConsumerMethodExecutingFailed($"Group:{receivedMessage.Group}, Topic:{receivedMessage.Name}", - ex); - - return OperateResult.Failed(ex); - } - } - } -} \ No newline at end of file diff --git a/src/DotNetCore.CAP/Internal/MethodBindException.cs b/src/DotNetCore.CAP/Internal/MethodBindException.cs index 63eb454..1d6c0bc 100644 --- a/src/DotNetCore.CAP/Internal/MethodBindException.cs +++ b/src/DotNetCore.CAP/Internal/MethodBindException.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; namespace DotNetCore.CAP.Internal { diff --git a/src/DotNetCore.CAP/Internal/MethodMatcherCache.cs b/src/DotNetCore.CAP/Internal/MethodMatcherCache.cs index 831a082..d6b40ba 100644 --- a/src/DotNetCore.CAP/Internal/MethodMatcherCache.cs +++ b/src/DotNetCore.CAP/Internal/MethodMatcherCache.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; @@ -24,14 +27,19 @@ namespace DotNetCore.CAP.Internal /// public ConcurrentDictionary> GetCandidatesMethodsOfGroupNameGrouped() { - if (Entries.Count != 0) return Entries; + if (Entries.Count != 0) + { + return Entries; + } var executorCollection = _selector.SelectCandidates(); var groupedCandidates = executorCollection.GroupBy(x => x.Attribute.Group); foreach (var item in groupedCandidates) + { Entries.TryAdd(item.Key, item.ToList()); + } return Entries; } @@ -44,7 +52,9 @@ namespace DotNetCore.CAP.Internal public IDictionary> GetTopicExector(string topicName) { if (Entries == null) + { throw new ArgumentNullException(nameof(Entries)); + } var dic = new Dictionary>(); foreach (var item in Entries) @@ -52,11 +62,13 @@ namespace DotNetCore.CAP.Internal var topicCandidates = item.Value.Where(x => x.Attribute.Name == topicName); dic.Add(item.Key, topicCandidates.ToList()); } + return dic; } /// - /// Attempts to get the topic exector associated with the specified topic name and group name from the . + /// Attempts to get the topic exector associated with the specified topic name and group name from the + /// . /// /// The topic name of the value to get. /// The group name of the value to get. @@ -66,7 +78,9 @@ namespace DotNetCore.CAP.Internal out ConsumerExecutorDescriptor matchTopic) { if (Entries == null) + { throw new ArgumentNullException(nameof(Entries)); + } matchTopic = null; @@ -75,6 +89,7 @@ namespace DotNetCore.CAP.Internal matchTopic = groupMatchTopics.FirstOrDefault(x => x.Attribute.Name == topicName); return matchTopic != null; } + return false; } @@ -89,7 +104,9 @@ namespace DotNetCore.CAP.Internal } if (Entries == null) + { throw new ArgumentNullException(nameof(Entries)); + } _allTopics = new List(); @@ -97,6 +114,7 @@ namespace DotNetCore.CAP.Internal { _allTopics.AddRange(descriptors.Select(x => x.Attribute.Name)); } + return _allTopics; } } diff --git a/src/DotNetCore.CAP/Internal/ModelBinderFactory.cs b/src/DotNetCore.CAP/Internal/ModelBinderFactory.cs index a7ad0de..d8f0291 100644 --- a/src/DotNetCore.CAP/Internal/ModelBinderFactory.cs +++ b/src/DotNetCore.CAP/Internal/ModelBinderFactory.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using System.Collections.Concurrent; using System.Reflection; using System.Runtime.CompilerServices; @@ -13,8 +16,8 @@ namespace DotNetCore.CAP.Internal /// internal class ModelBinderFactory : IModelBinderFactory { - private readonly IContentSerializer _serializer; private readonly ConcurrentDictionary _cache; + private readonly IContentSerializer _serializer; public ModelBinderFactory(IContentSerializer contentSerializer) { @@ -25,13 +28,17 @@ namespace DotNetCore.CAP.Internal public IModelBinder CreateBinder(ParameterInfo parameter) { if (parameter == null) + { throw new ArgumentNullException(nameof(parameter)); + } object token = parameter; var binder = CreateBinderCoreCached(parameter, token); if (binder == null) + { throw new InvalidOperationException("Format Could Not Create IModelBinder"); + } return binder; } @@ -39,11 +46,18 @@ namespace DotNetCore.CAP.Internal private IModelBinder CreateBinderCoreCached(ParameterInfo parameterInfo, object token) { if (TryGetCachedBinder(parameterInfo, token, out var binder)) + { return binder; + } + if (!Helper.IsComplexType(parameterInfo.ParameterType)) + { binder = new SimpleTypeModelBinder(parameterInfo); + } else + { binder = new ComplexTypeModelBinder(parameterInfo, _serializer); + } AddToCache(parameterInfo, token, binder); @@ -53,7 +67,9 @@ namespace DotNetCore.CAP.Internal private void AddToCache(ParameterInfo info, object cacheToken, IModelBinder binder) { if (cacheToken == null) + { return; + } _cache.TryAdd(new Key(info, cacheToken), binder); } diff --git a/src/DotNetCore.CAP/Internal/ObjectMethodExecutor/AwaitableInfo.cs b/src/DotNetCore.CAP/Internal/ObjectMethodExecutor/AwaitableInfo.cs index 7046303..5c494e9 100644 --- a/src/DotNetCore.CAP/Internal/ObjectMethodExecutor/AwaitableInfo.cs +++ b/src/DotNetCore.CAP/Internal/ObjectMethodExecutor/AwaitableInfo.cs @@ -1,5 +1,5 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. using System; using System.Linq; diff --git a/src/DotNetCore.CAP/Internal/ObjectMethodExecutor/CoercedAwaitableInfo.cs b/src/DotNetCore.CAP/Internal/ObjectMethodExecutor/CoercedAwaitableInfo.cs index 386e297..168e2e9 100644 --- a/src/DotNetCore.CAP/Internal/ObjectMethodExecutor/CoercedAwaitableInfo.cs +++ b/src/DotNetCore.CAP/Internal/ObjectMethodExecutor/CoercedAwaitableInfo.cs @@ -1,5 +1,5 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. using System; using System.Linq.Expressions; @@ -41,11 +41,13 @@ namespace Microsoft.Extensions.Internal if (ObjectMethodExecutorFSharpSupport.TryBuildCoercerFromFSharpAsyncToAwaitable(type, out var coercerExpression, out var coercerResultType)) + { if (AwaitableInfo.IsTypeAwaitable(coercerResultType, out var coercedAwaitableInfo)) { info = new CoercedAwaitableInfo(coercerExpression, coercerResultType, coercedAwaitableInfo); return true; } + } info = default(CoercedAwaitableInfo); return false; diff --git a/src/DotNetCore.CAP/Internal/ObjectMethodExecutor/ObjectMethodExecutor.cs b/src/DotNetCore.CAP/Internal/ObjectMethodExecutor/ObjectMethodExecutor.cs index 9f33dfa..cf25fc9 100644 --- a/src/DotNetCore.CAP/Internal/ObjectMethodExecutor/ObjectMethodExecutor.cs +++ b/src/DotNetCore.CAP/Internal/ObjectMethodExecutor/ObjectMethodExecutor.cs @@ -1,5 +1,5 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. using System; using System.Collections.Generic; @@ -30,7 +30,9 @@ namespace Microsoft.Extensions.Internal private ObjectMethodExecutor(MethodInfo methodInfo, TypeInfo targetTypeInfo, object[] parameterDefaultValues) { if (methodInfo == null) + { throw new ArgumentNullException(nameof(methodInfo)); + } MethodInfo = methodInfo; MethodParameters = methodInfo.GetParameters(); @@ -48,7 +50,9 @@ namespace Microsoft.Extensions.Internal _executor = GetExecutor(methodInfo, targetTypeInfo); if (IsMethodAsync) + { _executorAsync = GetExecutorAsync(methodInfo, targetTypeInfo, coercedAwaitableInfo); + } _parameterDefaultValues = parameterDefaultValues; } @@ -75,7 +79,9 @@ namespace Microsoft.Extensions.Internal object[] parameterDefaultValues) { if (parameterDefaultValues == null) + { throw new ArgumentNullException(nameof(parameterDefaultValues)); + } return new ObjectMethodExecutor(methodInfo, targetTypeInfo, parameterDefaultValues); } @@ -127,11 +133,15 @@ namespace Microsoft.Extensions.Internal public object GetDefaultValueForParameter(int index) { if (_parameterDefaultValues == null) + { throw new InvalidOperationException( $"Cannot call {nameof(GetDefaultValueForParameter)}, because no parameter default values were supplied."); + } if (index < 0 || index > MethodParameters.Length - 1) + { throw new ArgumentOutOfRangeException(nameof(index)); + } return _parameterDefaultValues[index]; } @@ -241,6 +251,7 @@ namespace Microsoft.Extensions.Internal var getResultParam = Expression.Parameter(typeof(object), "awaiter"); Func getResultFunc; if (awaitableInfo.ResultType == typeof(void)) + { getResultFunc = Expression.Lambda>( Expression.Block( Expression.Call( @@ -249,7 +260,9 @@ namespace Microsoft.Extensions.Internal Expression.Constant(null) ), getResultParam).Compile(); + } else + { getResultFunc = Expression.Lambda>( Expression.Convert( Expression.Call( @@ -257,6 +270,7 @@ namespace Microsoft.Extensions.Internal awaitableInfo.AwaiterGetResultMethod), typeof(object)), getResultParam).Compile(); + } // var onCompletedFunc = (object awaiter, Action continuation) => { // ((CustomAwaiterType)awaiter).OnCompleted(continuation); diff --git a/src/DotNetCore.CAP/Internal/ObjectMethodExecutor/ObjectMethodExecutorAwaitable.cs b/src/DotNetCore.CAP/Internal/ObjectMethodExecutor/ObjectMethodExecutorAwaitable.cs index ed2fee8..e5becb1 100644 --- a/src/DotNetCore.CAP/Internal/ObjectMethodExecutor/ObjectMethodExecutorAwaitable.cs +++ b/src/DotNetCore.CAP/Internal/ObjectMethodExecutor/ObjectMethodExecutorAwaitable.cs @@ -1,5 +1,5 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. using System; using System.Runtime.CompilerServices; diff --git a/src/DotNetCore.CAP/Internal/ObjectMethodExecutor/ObjectMethodExecutorFSharpSupport.cs b/src/DotNetCore.CAP/Internal/ObjectMethodExecutor/ObjectMethodExecutorFSharpSupport.cs index fb2be0f..09a8fda 100644 --- a/src/DotNetCore.CAP/Internal/ObjectMethodExecutor/ObjectMethodExecutorFSharpSupport.cs +++ b/src/DotNetCore.CAP/Internal/ObjectMethodExecutor/ObjectMethodExecutorFSharpSupport.cs @@ -1,5 +1,5 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. using System; using System.Linq; @@ -73,12 +73,17 @@ namespace Microsoft.Extensions.Internal { var typeFullName = possibleFSharpAsyncGenericType?.FullName; if (!string.Equals(typeFullName, "Microsoft.FSharp.Control.FSharpAsync`1", StringComparison.Ordinal)) + { return false; + } lock (_fsharpValuesCacheLock) { if (_fsharpCoreAssembly != null) + { return possibleFSharpAsyncGenericType.Assembly == _fsharpCoreAssembly; + } + return TryPopulateFSharpValueCaches(possibleFSharpAsyncGenericType); } } @@ -90,7 +95,9 @@ namespace Microsoft.Extensions.Internal var fsharpAsyncType = assembly.GetType("Microsoft.FSharp.Control.FSharpAsync"); if (fsharpOptionType == null || fsharpAsyncType == null) + { return false; + } // Get a reference to FSharpOption.None var fsharpOptionOfTaskCreationOptionsType = fsharpOptionType diff --git a/src/DotNetCore.CAP/Internal/PublisherSentFailedException.cs b/src/DotNetCore.CAP/Internal/PublisherSentFailedException.cs new file mode 100644 index 0000000..b0f0a2c --- /dev/null +++ b/src/DotNetCore.CAP/Internal/PublisherSentFailedException.cs @@ -0,0 +1,14 @@ +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; + +namespace DotNetCore.CAP.Internal +{ + public class PublisherSentFailedException : Exception + { + public PublisherSentFailedException(string message, Exception ex) : base(message, ex) + { + } + } +} \ No newline at end of file diff --git a/src/DotNetCore.CAP/Internal/SubscriberExecutionFailedException.cs b/src/DotNetCore.CAP/Internal/SubscriberExecutionFailedException.cs new file mode 100644 index 0000000..bc35f9d --- /dev/null +++ b/src/DotNetCore.CAP/Internal/SubscriberExecutionFailedException.cs @@ -0,0 +1,14 @@ +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; + +namespace DotNetCore.CAP.Internal +{ + internal class SubscriberExecutionFailedException : Exception + { + public SubscriberExecutionFailedException(string message, Exception ex) : base(message, ex) + { + } + } +} \ No newline at end of file diff --git a/src/DotNetCore.CAP/Internal/SubscriberNotFoundException.cs b/src/DotNetCore.CAP/Internal/SubscriberNotFoundException.cs index 6bdb4d3..c27ba29 100644 --- a/src/DotNetCore.CAP/Internal/SubscriberNotFoundException.cs +++ b/src/DotNetCore.CAP/Internal/SubscriberNotFoundException.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; namespace DotNetCore.CAP.Internal { diff --git a/src/DotNetCore.CAP/LoggerExtensions.cs b/src/DotNetCore.CAP/LoggerExtensions.cs index 99cccba..031be9d 100644 --- a/src/DotNetCore.CAP/LoggerExtensions.cs +++ b/src/DotNetCore.CAP/LoggerExtensions.cs @@ -1,35 +1,33 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; +using System.Diagnostics.CodeAnalysis; using Microsoft.Extensions.Logging; namespace DotNetCore.CAP { + [SuppressMessage("ReSharper", "InconsistentNaming")] internal static class LoggerExtensions { - private static readonly Action _serverStarting; + private static readonly Action _serverStarting; private static readonly Action _processorsStartingError; private static readonly Action _serverShuttingDown; private static readonly Action _expectedOperationCanceledException; - - private static readonly Action _enqueueingSentMessage; - private static readonly Action _enqueueingReceivdeMessage; - private static readonly Action _executingConsumerMethod; - private static readonly Action _receivedMessageRetryExecuting; private static readonly Action _modelBinderFormattingException; - - private static readonly Action _jobFailed; - private static readonly Action _jobFailedWillRetry; - private static readonly Action _jobExecuted; - private static readonly Action _jobRetrying; - private static readonly Action _exceptionOccuredWhileExecutingJob; - - private static readonly Action _messageQueueError; + private static readonly Action _consumerFailedWillRetry; + private static readonly Action _consumerExecuted; + private static readonly Action _senderRetrying; + private static readonly Action _exceptionOccuredWhileExecuting; + private static readonly Action _messageHasBeenSent; + private static readonly Action _messagePublishException; static LoggerExtensions() { - _serverStarting = LoggerMessage.Define( + _serverStarting = LoggerMessage.Define( LogLevel.Debug, 1, - "Starting the processing server. Detected {MachineProcessorCount} machine processor(s). Initiating {ProcessorCount} job processor(s)."); + "Starting the processing server."); _processorsStartingError = LoggerMessage.Define( LogLevel.Error, @@ -46,22 +44,12 @@ namespace DotNetCore.CAP 3, "Expected an OperationCanceledException, but found '{ExceptionMessage}'."); - _enqueueingSentMessage = LoggerMessage.Define( - LogLevel.Debug, - 2, - "Enqueuing a topic to the sent message store. NameKey: '{NameKey}' Content: '{Content}'."); - - _enqueueingReceivdeMessage = LoggerMessage.Define( - LogLevel.Debug, - 2, - "Enqueuing a topic to the received message store. NameKey: '{NameKey}. Content: '{Content}'."); - - _executingConsumerMethod = LoggerMessage.Define( + LoggerMessage.Define( LogLevel.Error, 5, "Consumer method '{methodName}' failed to execute."); - _receivedMessageRetryExecuting = LoggerMessage.Define( + LoggerMessage.Define( LogLevel.Error, 5, "Received message topic method '{topicName}' failed to execute."); @@ -72,81 +60,65 @@ namespace DotNetCore.CAP "When call subscribe method, a parameter format conversion exception occurs. MethodName:'{MethodName}' ParameterName:'{ParameterName}' Content:'{Content}'." ); - _jobRetrying = LoggerMessage.Define( + _senderRetrying = LoggerMessage.Define( LogLevel.Debug, 3, - "Retrying a job: {Retries}..."); + "Retrying send a message: {Retries}..."); - _jobExecuted = LoggerMessage.Define( + _consumerExecuted = LoggerMessage.Define( LogLevel.Debug, 4, - "Job executed. Took: {Seconds} secs."); + "Consumer executed. Took: {Seconds} secs."); - _jobFailed = LoggerMessage.Define( - LogLevel.Warning, - 1, - "Job failed to execute."); - - _jobFailedWillRetry = LoggerMessage.Define( + _consumerFailedWillRetry = LoggerMessage.Define( LogLevel.Warning, 2, - "Job failed to execute. Will retry."); + "Consumer failed to execute. Will retry."); - _exceptionOccuredWhileExecutingJob = LoggerMessage.Define( + _exceptionOccuredWhileExecuting = LoggerMessage.Define( LogLevel.Error, 6, - "An exception occured while trying to execute a message: '{MessageId}'. " + - "Requeuing for another retry."); + "An exception occured while trying to store a message: '{MessageId}'. "); - _messageQueueError = LoggerMessage.Define( - LogLevel.Error, - 7, - "The MessageQueue Client fires an internal error:'{error}'."); - } - - public static void JobFailed(this ILogger logger, Exception ex) - { - _jobFailed(logger, ex); - } - - public static void JobFailedWillRetry(this ILogger logger, Exception ex) - { - _jobFailedWillRetry(logger, ex); - } + _messageHasBeenSent = LoggerMessage.Define( + LogLevel.Debug, + 4, + "Message published. Took: {Seconds} secs."); - public static void JobRetrying(this ILogger logger, int retries) - { - _jobRetrying(logger, retries, null); + _messagePublishException = LoggerMessage.Define( + LogLevel.Error, + 6, + "An exception occured while publishing a message: '{MessageId}'. "); } - public static void JobExecuted(this ILogger logger, double seconds) + public static void ConsumerExecutionFailedWillRetry(this ILogger logger, Exception ex) { - _jobExecuted(logger, seconds, null); + _consumerFailedWillRetry(logger, ex); } - public static void ConsumerMethodExecutingFailed(this ILogger logger, string methodName, Exception ex) + public static void SenderRetrying(this ILogger logger, int retries) { - _executingConsumerMethod(logger, methodName, ex); + _senderRetrying(logger, retries, null); } - public static void ReceivedMessageRetryExecutingFailed(this ILogger logger, string topicName, Exception ex) + public static void MessageHasBeenSent(this ILogger logger, double seconds) { - _receivedMessageRetryExecuting(logger, topicName, ex); + _messageHasBeenSent(logger, seconds, null); } - public static void EnqueuingReceivedMessage(this ILogger logger, string nameKey, string content) + public static void MessagePublishException(this ILogger logger, int messageId, Exception ex) { - _enqueueingReceivdeMessage(logger, nameKey, content, null); + _messagePublishException(logger, messageId, ex); } - public static void EnqueuingSentMessage(this ILogger logger, string nameKey, string content) + public static void ConsumerExecuted(this ILogger logger, double seconds) { - _enqueueingSentMessage(logger, nameKey, content, null); + _consumerExecuted(logger, seconds, null); } - public static void ServerStarting(this ILogger logger, int machineProcessorCount, int processorCount) + public static void ServerStarting(this ILogger logger) { - _serverStarting(logger, machineProcessorCount, processorCount, null); + _serverStarting(logger, null); } public static void ProcessorsStartedError(this ILogger logger, Exception ex) @@ -166,7 +138,7 @@ namespace DotNetCore.CAP public static void ExceptionOccuredWhileExecuting(this ILogger logger, string messageId, Exception ex) { - _exceptionOccuredWhileExecutingJob(logger, messageId, ex); + _exceptionOccuredWhileExecuting(logger, messageId, ex); } public static void ModelBinderFormattingException(this ILogger logger, string methodName, string parameterName, @@ -174,10 +146,5 @@ namespace DotNetCore.CAP { _modelBinderFormattingException(logger, methodName, parameterName, content, ex); } - - public static void MessageQueueError(this ILogger logger, string error) - { - _messageQueueError(logger, error, null); - } } } \ No newline at end of file diff --git a/src/DotNetCore.CAP/MessageContext.cs b/src/DotNetCore.CAP/MessageContext.cs index 632d8ea..b42d7ca 100644 --- a/src/DotNetCore.CAP/MessageContext.cs +++ b/src/DotNetCore.CAP/MessageContext.cs @@ -1,4 +1,7 @@ -namespace DotNetCore.CAP +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +namespace DotNetCore.CAP { /// /// Message context diff --git a/src/DotNetCore.CAP/Models/CapMessageDto.cs b/src/DotNetCore.CAP/Models/CapMessageDto.cs index 0b3c699..c79fb97 100644 --- a/src/DotNetCore.CAP/Models/CapMessageDto.cs +++ b/src/DotNetCore.CAP/Models/CapMessageDto.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using DotNetCore.CAP.Infrastructure; namespace DotNetCore.CAP.Models @@ -14,7 +17,7 @@ namespace DotNetCore.CAP.Models public virtual string CallbackName { get; set; } } - public sealed class CapMessageDto: CapMessage + public sealed class CapMessageDto : CapMessage { public CapMessageDto() { diff --git a/src/DotNetCore.CAP/Models/CapPublishedMessage.cs b/src/DotNetCore.CAP/Models/CapPublishedMessage.cs index cc7efa6..41c554d 100644 --- a/src/DotNetCore.CAP/Models/CapPublishedMessage.cs +++ b/src/DotNetCore.CAP/Models/CapPublishedMessage.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; namespace DotNetCore.CAP.Models { diff --git a/src/DotNetCore.CAP/Models/CapQueue.cs b/src/DotNetCore.CAP/Models/CapQueue.cs index ccdd812..9a5b9f9 100644 --- a/src/DotNetCore.CAP/Models/CapQueue.cs +++ b/src/DotNetCore.CAP/Models/CapQueue.cs @@ -1,4 +1,7 @@ -namespace DotNetCore.CAP.Models +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +namespace DotNetCore.CAP.Models { public class CapQueue { diff --git a/src/DotNetCore.CAP/Models/CapReceivedMessage.cs b/src/DotNetCore.CAP/Models/CapReceivedMessage.cs index eae744d..790aca2 100644 --- a/src/DotNetCore.CAP/Models/CapReceivedMessage.cs +++ b/src/DotNetCore.CAP/Models/CapReceivedMessage.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; namespace DotNetCore.CAP.Models { diff --git a/src/DotNetCore.CAP/Models/MessageType.cs b/src/DotNetCore.CAP/Models/MessageType.cs index 5097962..141040e 100644 --- a/src/DotNetCore.CAP/Models/MessageType.cs +++ b/src/DotNetCore.CAP/Models/MessageType.cs @@ -1,4 +1,7 @@ -namespace DotNetCore.CAP.Models +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +namespace DotNetCore.CAP.Models { public enum MessageType { diff --git a/src/DotNetCore.CAP/MqLogType.cs b/src/DotNetCore.CAP/MqLogType.cs index 1a392cb..3d84a48 100644 --- a/src/DotNetCore.CAP/MqLogType.cs +++ b/src/DotNetCore.CAP/MqLogType.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; namespace DotNetCore.CAP { @@ -21,4 +24,4 @@ namespace DotNetCore.CAP public MqLogType LogType { get; set; } } -} +} \ No newline at end of file diff --git a/src/DotNetCore.CAP/NodeDiscovery/CAP.DiscoveryOptions.cs b/src/DotNetCore.CAP/NodeDiscovery/CAP.DiscoveryOptions.cs index 09e617d..008f568 100644 --- a/src/DotNetCore.CAP/NodeDiscovery/CAP.DiscoveryOptions.cs +++ b/src/DotNetCore.CAP/NodeDiscovery/CAP.DiscoveryOptions.cs @@ -1,4 +1,5 @@ -// ReSharper disable once CheckNamespace +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. namespace DotNetCore.CAP { diff --git a/src/DotNetCore.CAP/NodeDiscovery/CAP.DiscoveryOptionsExtensions.cs b/src/DotNetCore.CAP/NodeDiscovery/CAP.DiscoveryOptionsExtensions.cs index cc0decc..8617520 100644 --- a/src/DotNetCore.CAP/NodeDiscovery/CAP.DiscoveryOptionsExtensions.cs +++ b/src/DotNetCore.CAP/NodeDiscovery/CAP.DiscoveryOptionsExtensions.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using DotNetCore.CAP; using DotNetCore.CAP.NodeDiscovery; using Microsoft.Extensions.DependencyInjection; @@ -44,7 +47,10 @@ namespace Microsoft.Extensions.DependencyInjection public static CapOptions UseDiscovery(this CapOptions capOptions, Action options) { - if (options == null) throw new ArgumentNullException(nameof(options)); + if (options == null) + { + throw new ArgumentNullException(nameof(options)); + } capOptions.RegisterExtension(new DiscoveryOptionsExtension(options)); diff --git a/src/DotNetCore.CAP/NodeDiscovery/IDiscoveryProviderFactory.Default.cs b/src/DotNetCore.CAP/NodeDiscovery/IDiscoveryProviderFactory.Default.cs index e7639a6..5627282 100644 --- a/src/DotNetCore.CAP/NodeDiscovery/IDiscoveryProviderFactory.Default.cs +++ b/src/DotNetCore.CAP/NodeDiscovery/IDiscoveryProviderFactory.Default.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using Microsoft.Extensions.Logging; namespace DotNetCore.CAP.NodeDiscovery @@ -15,7 +18,9 @@ namespace DotNetCore.CAP.NodeDiscovery public INodeDiscoveryProvider Create(DiscoveryOptions options) { if (options == null) + { throw new ArgumentNullException(nameof(options)); + } return new ConsulNodeDiscoveryProvider(_loggerFactory, options); } diff --git a/src/DotNetCore.CAP/NodeDiscovery/IDiscoveryProviderFactory.cs b/src/DotNetCore.CAP/NodeDiscovery/IDiscoveryProviderFactory.cs index df3da3d..22dcee9 100644 --- a/src/DotNetCore.CAP/NodeDiscovery/IDiscoveryProviderFactory.cs +++ b/src/DotNetCore.CAP/NodeDiscovery/IDiscoveryProviderFactory.cs @@ -1,4 +1,7 @@ -namespace DotNetCore.CAP.NodeDiscovery +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +namespace DotNetCore.CAP.NodeDiscovery { internal interface IDiscoveryProviderFactory { diff --git a/src/DotNetCore.CAP/NodeDiscovery/INodeDiscoveryProvider.Consul.cs b/src/DotNetCore.CAP/NodeDiscovery/INodeDiscoveryProvider.Consul.cs index 6f5cc59..1aca30b 100644 --- a/src/DotNetCore.CAP/NodeDiscovery/INodeDiscoveryProvider.Consul.cs +++ b/src/DotNetCore.CAP/NodeDiscovery/INodeDiscoveryProvider.Consul.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; @@ -36,8 +39,7 @@ namespace DotNetCore.CAP.NodeDiscovery foreach (var service in services.Response) { var serviceInfo = await _consul.Catalog.Service(service.Key); - var node = serviceInfo.Response. - SkipWhile(x => !x.ServiceTags.Contains("CAP")) + var node = serviceInfo.Response.SkipWhile(x => !x.ServiceTags.Contains("CAP")) .Select(info => new Node { Id = info.ServiceID, @@ -58,7 +60,8 @@ namespace DotNetCore.CAP.NodeDiscovery { CapCache.Global.AddOrUpdate("cap.nodes.count", 0, TimeSpan.FromSeconds(20)); - _logger.LogError($"Get consul nodes raised an exception. Exception:{ex.Message},{ex.InnerException.Message}"); + _logger.LogError( + $"Get consul nodes raised an exception. Exception:{ex.Message},{ex.InnerException.Message}"); return null; } } @@ -73,7 +76,7 @@ namespace DotNetCore.CAP.NodeDiscovery Name = _options.NodeName, Address = _options.CurrentNodeHostName, Port = _options.CurrentNodePort, - Tags = new[] { "CAP", "Client", "Dashboard" }, + Tags = new[] {"CAP", "Client", "Dashboard"}, Check = new AgentServiceCheck { DeregisterCriticalServiceAfter = TimeSpan.FromSeconds(30), @@ -86,7 +89,8 @@ namespace DotNetCore.CAP.NodeDiscovery } catch (Exception ex) { - _logger.LogError($"Get consul nodes raised an exception. Exception:{ex.Message},{ex.InnerException.Message}"); + _logger.LogError( + $"Get consul nodes raised an exception. Exception:{ex.Message},{ex.InnerException.Message}"); return null; } } diff --git a/src/DotNetCore.CAP/NodeDiscovery/INodeDiscoveryProvider.cs b/src/DotNetCore.CAP/NodeDiscovery/INodeDiscoveryProvider.cs index e2b07ca..960fbc4 100644 --- a/src/DotNetCore.CAP/NodeDiscovery/INodeDiscoveryProvider.cs +++ b/src/DotNetCore.CAP/NodeDiscovery/INodeDiscoveryProvider.cs @@ -1,4 +1,7 @@ -using System.Collections.Generic; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System.Collections.Generic; using System.Threading.Tasks; namespace DotNetCore.CAP.NodeDiscovery diff --git a/src/DotNetCore.CAP/NodeDiscovery/IProcessingServer.Consul.cs b/src/DotNetCore.CAP/NodeDiscovery/IProcessingServer.Consul.cs index 04541df..af3b065 100644 --- a/src/DotNetCore.CAP/NodeDiscovery/IProcessingServer.Consul.cs +++ b/src/DotNetCore.CAP/NodeDiscovery/IProcessingServer.Consul.cs @@ -1,4 +1,7 @@ -namespace DotNetCore.CAP.NodeDiscovery +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +namespace DotNetCore.CAP.NodeDiscovery { internal class ConsulProcessingNodeServer : IProcessingServer { diff --git a/src/DotNetCore.CAP/NodeDiscovery/Node.cs b/src/DotNetCore.CAP/NodeDiscovery/Node.cs index 44d7bf2..ea077f7 100644 --- a/src/DotNetCore.CAP/NodeDiscovery/Node.cs +++ b/src/DotNetCore.CAP/NodeDiscovery/Node.cs @@ -1,4 +1,7 @@ -namespace DotNetCore.CAP.NodeDiscovery +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +namespace DotNetCore.CAP.NodeDiscovery { public class Node { diff --git a/src/DotNetCore.CAP/OperateResult.cs b/src/DotNetCore.CAP/OperateResult.cs index 726f388..0be4570 100644 --- a/src/DotNetCore.CAP/OperateResult.cs +++ b/src/DotNetCore.CAP/OperateResult.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using System.Collections.Generic; using System.Linq; @@ -47,7 +50,10 @@ namespace DotNetCore.CAP { var result = new OperateResult {Succeeded = false}; if (errors != null) + { result._errors.AddRange(errors); + } + return result; } @@ -59,7 +65,10 @@ namespace DotNetCore.CAP Exception = ex }; if (errors != null) + { result._errors.AddRange(errors); + } + return result; } diff --git a/src/DotNetCore.CAP/Processor/IAdditionalProcessor.cs b/src/DotNetCore.CAP/Processor/IAdditionalProcessor.cs index b678614..51fc1c3 100644 --- a/src/DotNetCore.CAP/Processor/IAdditionalProcessor.cs +++ b/src/DotNetCore.CAP/Processor/IAdditionalProcessor.cs @@ -1,4 +1,7 @@ -namespace DotNetCore.CAP.Processor +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +namespace DotNetCore.CAP.Processor { public interface IAdditionalProcessor : IProcessor { diff --git a/src/DotNetCore.CAP/Processor/IDispatcher.Default.cs b/src/DotNetCore.CAP/Processor/IDispatcher.Default.cs index 8f76de1..95e9cdb 100644 --- a/src/DotNetCore.CAP/Processor/IDispatcher.Default.cs +++ b/src/DotNetCore.CAP/Processor/IDispatcher.Default.cs @@ -1,81 +1,101 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; +using System.Collections.Concurrent; using System.Threading; using System.Threading.Tasks; -using DotNetCore.CAP.Infrastructure; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; +using DotNetCore.CAP.Models; +using Microsoft.Extensions.Logging; namespace DotNetCore.CAP.Processor { - public class DefaultDispatcher : IDispatcher + public class Dispatcher : IDispatcher, IDisposable { - internal static readonly AutoResetEvent PulseEvent = new AutoResetEvent(true); + private readonly CancellationTokenSource _cts = new CancellationTokenSource(); + private readonly ISubscriberExecutor _executor; + private readonly ILogger _logger; + + private readonly BlockingCollection _publishedMessageQueue = + new BlockingCollection(new ConcurrentQueue()); - private readonly TimeSpan _pollingDelay; - private readonly IQueueExecutorFactory _queueExecutorFactory; + private readonly BlockingCollection _receivedMessageQueue = + new BlockingCollection(new ConcurrentQueue()); - public DefaultDispatcher(IQueueExecutorFactory queueExecutorFactory, - IOptions capOptions) + private readonly IPublishMessageSender _sender; + + public Dispatcher(ILogger logger, + IPublishMessageSender sender, + ISubscriberExecutor executor) { - _queueExecutorFactory = queueExecutorFactory; - _pollingDelay = TimeSpan.FromSeconds(capOptions.Value.PollingDelay); - } + _logger = logger; + _sender = sender; + _executor = executor; - public bool Waiting { get; private set; } + Task.Factory.StartNew(Sending); + Task.Factory.StartNew(Processing); + } - public Task ProcessAsync(ProcessingContext context) + public void EnqueueToPublish(CapPublishedMessage message) { - if (context == null) - throw new ArgumentNullException(nameof(context)); + _publishedMessageQueue.Add(message); + } - context.ThrowIfStopping(); + public void EnqueueToExecute(CapReceivedMessage message) + { + _receivedMessageQueue.Add(message); + } - return ProcessCoreAsync(context); + public void Dispose() + { + _cts.Cancel(); } - public async Task ProcessCoreAsync(ProcessingContext context) + private void Sending() { try { - var worked = await Step(context); - - context.ThrowIfStopping(); - - Waiting = true; - - if (!worked) + while (!_publishedMessageQueue.IsCompleted) { - var token = GetTokenToWaitOn(context); - await WaitHandleEx.WaitAnyAsync(PulseEvent, token.WaitHandle, _pollingDelay); + if (_publishedMessageQueue.TryTake(out var message, 100, _cts.Token)) + { + try + { + _sender.SendAsync(message); + } + catch (Exception ex) + { + _logger.ExceptionOccuredWhileExecuting(message.Name, ex); + } + } } } - finally + catch (OperationCanceledException) { - Waiting = false; + // expected } } - protected virtual CancellationToken GetTokenToWaitOn(ProcessingContext context) - { - return context.CancellationToken; - } - - private async Task Step(ProcessingContext context) + private void Processing() { - IFetchedMessage fetched; - using (var scopedContext = context.CreateScope()) + try { - var provider = scopedContext.Provider; - var connection = provider.GetRequiredService(); - - if ((fetched = await connection.FetchNextMessageAsync()) != null) - using (fetched) + foreach (var message in _receivedMessageQueue.GetConsumingEnumerable(_cts.Token)) + { + try + { + _executor.ExecuteAsync(message); + } + catch (Exception ex) { - var queueExecutor = _queueExecutorFactory.GetInstance(fetched.MessageType); - await queueExecutor.ExecuteAsync(connection, fetched); + _logger.ExceptionOccuredWhileExecuting(message.Name, ex); } + } + } + catch (OperationCanceledException) + { + // expected } - return fetched != null; } } } \ No newline at end of file diff --git a/src/DotNetCore.CAP/Processor/IDispatcher.cs b/src/DotNetCore.CAP/Processor/IDispatcher.cs deleted file mode 100644 index f612d02..0000000 --- a/src/DotNetCore.CAP/Processor/IDispatcher.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace DotNetCore.CAP.Processor -{ - public interface IDispatcher : IProcessor - { - bool Waiting { get; } - } -} \ No newline at end of file diff --git a/src/DotNetCore.CAP/Processor/IProcessingServer.Cap.cs b/src/DotNetCore.CAP/Processor/IProcessingServer.Cap.cs index 2118021..b9d8802 100644 --- a/src/DotNetCore.CAP/Processor/IProcessingServer.Cap.cs +++ b/src/DotNetCore.CAP/Processor/IProcessingServer.Cap.cs @@ -1,11 +1,13 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; namespace DotNetCore.CAP.Processor { @@ -14,39 +16,30 @@ namespace DotNetCore.CAP.Processor private readonly CancellationTokenSource _cts; private readonly ILogger _logger; private readonly ILoggerFactory _loggerFactory; - private readonly IList _messageDispatchers; - private readonly CapOptions _options; private readonly IServiceProvider _provider; + private Task _compositeTask; private ProcessingContext _context; private bool _disposed; - private IProcessor[] _processors; - public CapProcessingServer( ILogger logger, ILoggerFactory loggerFactory, - IServiceProvider provider, - IOptions options) + IServiceProvider provider) { _logger = logger; _loggerFactory = loggerFactory; _provider = provider; - _options = options.Value; _cts = new CancellationTokenSource(); - _messageDispatchers = new List(); } public void Start() { - var processorCount = _options.QueueProcessorCount; - _processors = GetProcessors(processorCount); - - _logger.ServerStarting(processorCount, _processors.Length); + _logger.ServerStarting(); _context = new ProcessingContext(_provider, _cts.Token); - var processorTasks = _processors + var processorTasks = GetProcessors() .Select(InfiniteRetry) .Select(p => p.ProcessAsync(_context)); _compositeTask = Task.WhenAll(processorTasks); @@ -54,18 +47,16 @@ namespace DotNetCore.CAP.Processor public void Pulse() { - if (!AllProcessorsWaiting()) - return; - - _logger.LogTrace("Pulsing the Queuer."); - - PublishQueuer.PulseEvent.Set(); + _logger.LogTrace("Pulsing the processor."); } public void Dispose() { if (_disposed) + { return; + } + _disposed = true; _logger.ServerShuttingDown(); @@ -78,35 +69,24 @@ namespace DotNetCore.CAP.Processor { var innerEx = ex.InnerExceptions[0]; if (!(innerEx is OperationCanceledException)) + { _logger.ExpectedOperationCanceledException(innerEx); + } } } - private bool AllProcessorsWaiting() - { - return _messageDispatchers.All(processor => processor.Waiting); - } - private IProcessor InfiniteRetry(IProcessor inner) { return new InfiniteRetryProcessor(inner, _loggerFactory); } - private IProcessor[] GetProcessors(int processorCount) + private IProcessor[] GetProcessors() { - var returnedProcessors = new List(); - for (var i = 0; i < processorCount; i++) + var returnedProcessors = new List { - var messageProcessors = _provider.GetRequiredService(); - _messageDispatchers.Add(messageProcessors); - } - returnedProcessors.AddRange(_messageDispatchers); - - returnedProcessors.Add(_provider.GetRequiredService()); - returnedProcessors.Add(_provider.GetRequiredService()); - returnedProcessors.Add(_provider.GetRequiredService()); - - returnedProcessors.Add(_provider.GetRequiredService()); + _provider.GetRequiredService(), + _provider.GetRequiredService() + }; return returnedProcessors.ToArray(); } diff --git a/src/DotNetCore.CAP/Processor/IProcessor.InfiniteRetry.cs b/src/DotNetCore.CAP/Processor/IProcessor.InfiniteRetry.cs index ed0508e..b7b5d43 100644 --- a/src/DotNetCore.CAP/Processor/IProcessor.InfiniteRetry.cs +++ b/src/DotNetCore.CAP/Processor/IProcessor.InfiniteRetry.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using System.Threading.Tasks; using Microsoft.Extensions.Logging; @@ -20,6 +23,7 @@ namespace DotNetCore.CAP.Processor public async Task ProcessAsync(ProcessingContext context) { while (!context.IsStopping) + { try { await _inner.ProcessAsync(context); @@ -32,6 +36,7 @@ namespace DotNetCore.CAP.Processor { _logger.LogWarning(1, ex, "Processor '{ProcessorName}' failed. Retrying...", _inner.ToString()); } + } } public override string ToString() diff --git a/src/DotNetCore.CAP/Processor/IProcessor.Failed.cs b/src/DotNetCore.CAP/Processor/IProcessor.NeedRetry.cs similarity index 71% rename from src/DotNetCore.CAP/Processor/IProcessor.Failed.cs rename to src/DotNetCore.CAP/Processor/IProcessor.NeedRetry.cs index 6c158e4..b1109a3 100644 --- a/src/DotNetCore.CAP/Processor/IProcessor.Failed.cs +++ b/src/DotNetCore.CAP/Processor/IProcessor.NeedRetry.cs @@ -1,6 +1,8 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using System.Threading.Tasks; -using DotNetCore.CAP.Abstractions; using DotNetCore.CAP.Infrastructure; using DotNetCore.CAP.Models; using DotNetCore.CAP.Processor.States; @@ -10,28 +12,25 @@ using Microsoft.Extensions.Options; namespace DotNetCore.CAP.Processor { - public class FailedProcessor : IProcessor + public class NeedRetryMessageProcessor : IProcessor { private readonly TimeSpan _delay = TimeSpan.FromSeconds(1); private readonly ILogger _logger; private readonly CapOptions _options; - private readonly IServiceProvider _provider; + private readonly IPublishExecutor _publishExecutor; private readonly IStateChanger _stateChanger; private readonly ISubscriberExecutor _subscriberExecutor; - private readonly IPublishExecutor _publishExecutor; private readonly TimeSpan _waitingInterval; - public FailedProcessor( + public NeedRetryMessageProcessor( IOptions options, - ILogger logger, - IServiceProvider provider, + ILogger logger, IStateChanger stateChanger, ISubscriberExecutor subscriberExecutor, IPublishExecutor publishExecutor) { _options = options.Value; _logger = logger; - _provider = provider; _stateChanger = stateChanger; _subscriberExecutor = subscriberExecutor; _publishExecutor = publishExecutor; @@ -41,32 +40,33 @@ namespace DotNetCore.CAP.Processor public async Task ProcessAsync(ProcessingContext context) { if (context == null) + { throw new ArgumentNullException(nameof(context)); + } - using (var scope = _provider.CreateScope()) - { - var provider = scope.ServiceProvider; - var connection = provider.GetRequiredService(); + var connection = context.Provider.GetRequiredService(); - await Task.WhenAll( - ProcessPublishedAsync(connection, context), - ProcessReceivedAsync(connection, context)); + await Task.WhenAll( + ProcessPublishedAsync(connection, context), + ProcessReceivedAsync(connection, context)); - await context.WaitAsync(_waitingInterval); - } + await context.WaitAsync(_waitingInterval); } private async Task ProcessPublishedAsync(IStorageConnection connection, ProcessingContext context) { - var messages = await connection.GetFailedPublishedMessages(); + var messages = await connection.GetPublishedMessagesOfNeedRetry(); var hasException = false; foreach (var message in messages) { if (message.Retries > _options.FailedRetryCount) + { continue; + } if (!hasException) + { try { _options.FailedCallback?.Invoke(MessageType.Publish, message.Name, message.Content); @@ -76,6 +76,7 @@ namespace DotNetCore.CAP.Processor hasException = true; _logger.LogWarning("Failed call-back method raised an exception:" + ex.Message); } + } using (var transaction = connection.CreateTransaction()) { @@ -88,9 +89,9 @@ namespace DotNetCore.CAP.Processor catch (Exception e) { message.Content = Helper.AddExceptionProperty(message.Content, e); - message.Retries++; transaction.UpdateMessage(message); } + await transaction.CommitAsync(); } @@ -102,15 +103,18 @@ namespace DotNetCore.CAP.Processor private async Task ProcessReceivedAsync(IStorageConnection connection, ProcessingContext context) { - var messages = await connection.GetFailedReceivedMessages(); + var messages = await connection.GetReceivedMessagesOfNeedRetry(); var hasException = false; foreach (var message in messages) { if (message.Retries > _options.FailedRetryCount) + { continue; + } if (!hasException) + { try { _options.FailedCallback?.Invoke(MessageType.Subscribe, message.Name, message.Content); @@ -120,23 +124,10 @@ namespace DotNetCore.CAP.Processor hasException = true; _logger.LogWarning("Failed call-back method raised an exception:" + ex.Message); } - - using (var transaction = connection.CreateTransaction()) - { - var ret = await _subscriberExecutor.ExecuteAsync(message); - if (ret.Succeeded) - { - _stateChanger.ChangeState(message, new SucceededState(), transaction); - } - else - { - message.Retries++; - message.Content = Helper.AddExceptionProperty(message.Content, ret.Exception); - transaction.UpdateMessage(message); - } - await transaction.CommitAsync(); } + await _subscriberExecutor.ExecuteAsync(message); + context.ThrowIfStopping(); await context.WaitAsync(_delay); diff --git a/src/DotNetCore.CAP/Processor/IProcessor.PublishQueuer.cs b/src/DotNetCore.CAP/Processor/IProcessor.PublishQueuer.cs deleted file mode 100644 index 90d1b5f..0000000 --- a/src/DotNetCore.CAP/Processor/IProcessor.PublishQueuer.cs +++ /dev/null @@ -1,67 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using DotNetCore.CAP.Infrastructure; -using DotNetCore.CAP.Models; -using DotNetCore.CAP.Processor.States; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; - -namespace DotNetCore.CAP.Processor -{ - public class PublishQueuer : IProcessor - { - public static readonly AutoResetEvent PulseEvent = new AutoResetEvent(true); - private readonly ILogger _logger; - private readonly TimeSpan _pollingDelay; - private readonly IServiceProvider _provider; - private readonly IStateChanger _stateChanger; - - public PublishQueuer( - ILogger logger, - IOptions options, - IStateChanger stateChanger, - IServiceProvider provider) - { - _logger = logger; - _stateChanger = stateChanger; - _provider = provider; - - var capOptions = options.Value; - _pollingDelay = TimeSpan.FromSeconds(capOptions.PollingDelay); - } - - public async Task ProcessAsync(ProcessingContext context) - { - _logger.LogDebug("Publish Queuer start calling."); - using (var scope = _provider.CreateScope()) - { - CapPublishedMessage sentMessage; - var provider = scope.ServiceProvider; - var connection = provider.GetRequiredService(); - - while ( - !context.IsStopping && - (sentMessage = await connection.GetNextPublishedMessageToBeEnqueuedAsync()) != null) - - { - var state = new EnqueuedState(); - - using (var transaction = connection.CreateTransaction()) - { - _stateChanger.ChangeState(sentMessage, state, transaction); - await transaction.CommitAsync(); - } - } - } - - context.ThrowIfStopping(); - - DefaultDispatcher.PulseEvent.Set(); - - await WaitHandleEx.WaitAnyAsync(PulseEvent, - context.CancellationToken.WaitHandle, _pollingDelay); - } - } -} \ No newline at end of file diff --git a/src/DotNetCore.CAP/Processor/IProcessor.SubscribeQueuer.cs b/src/DotNetCore.CAP/Processor/IProcessor.SubscribeQueuer.cs deleted file mode 100644 index 29913d5..0000000 --- a/src/DotNetCore.CAP/Processor/IProcessor.SubscribeQueuer.cs +++ /dev/null @@ -1,67 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using DotNetCore.CAP.Infrastructure; -using DotNetCore.CAP.Models; -using DotNetCore.CAP.Processor.States; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; - -namespace DotNetCore.CAP.Processor -{ - public class SubscribeQueuer : IProcessor - { - internal static readonly AutoResetEvent PulseEvent = new AutoResetEvent(true); - private readonly ILogger _logger; - private readonly TimeSpan _pollingDelay; - private readonly IServiceProvider _provider; - private readonly IStateChanger _stateChanger; - - public SubscribeQueuer( - ILogger logger, - IOptions options, - IStateChanger stateChanger, - IServiceProvider provider) - { - _logger = logger; - _stateChanger = stateChanger; - _provider = provider; - - var capOptions = options.Value; - _pollingDelay = TimeSpan.FromSeconds(capOptions.PollingDelay); - } - - public async Task ProcessAsync(ProcessingContext context) - { - _logger.LogDebug("SubscribeQueuer start calling."); - using (var scope = _provider.CreateScope()) - { - CapReceivedMessage message; - var provider = scope.ServiceProvider; - var connection = provider.GetRequiredService(); - - while ( - !context.IsStopping && - (message = await connection.GetNextReceivedMessageToBeEnqueuedAsync()) != null) - - { - var state = new EnqueuedState(); - - using (var transaction = connection.CreateTransaction()) - { - _stateChanger.ChangeState(message, state, transaction); - await transaction.CommitAsync(); - } - } - } - - context.ThrowIfStopping(); - - DefaultDispatcher.PulseEvent.Set(); - - await WaitHandleEx.WaitAnyAsync(PulseEvent, - context.CancellationToken.WaitHandle, _pollingDelay); - } - } -} \ No newline at end of file diff --git a/src/DotNetCore.CAP/Processor/IProcessor.cs b/src/DotNetCore.CAP/Processor/IProcessor.cs index 20d484f..3e83c6e 100644 --- a/src/DotNetCore.CAP/Processor/IProcessor.cs +++ b/src/DotNetCore.CAP/Processor/IProcessor.cs @@ -1,4 +1,7 @@ -using System.Threading.Tasks; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System.Threading.Tasks; namespace DotNetCore.CAP.Processor { diff --git a/src/DotNetCore.CAP/Processor/ProcessingContext.cs b/src/DotNetCore.CAP/Processor/ProcessingContext.cs index 5129532..6a280b2 100644 --- a/src/DotNetCore.CAP/Processor/ProcessingContext.cs +++ b/src/DotNetCore.CAP/Processor/ProcessingContext.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; diff --git a/src/DotNetCore.CAP/Processor/RetryBehavior.cs b/src/DotNetCore.CAP/Processor/RetryBehavior.cs index d1767c0..05115fc 100644 --- a/src/DotNetCore.CAP/Processor/RetryBehavior.cs +++ b/src/DotNetCore.CAP/Processor/RetryBehavior.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; namespace DotNetCore.CAP.Processor { @@ -17,9 +20,9 @@ namespace DotNetCore.CAP.Processor static RetryBehavior() { - DefaultRetryCount = 15; + DefaultRetryCount = 3; DefaultRetryInThunk = retries => - (int) Math.Round(Math.Pow(retries - 1, 4) + 15 + _random.Next(30) * retries); + (int) Math.Round(Math.Pow(retries - 1, 4) + 3 + _random.Next(30) * retries); DefaultRetry = new RetryBehavior(true); NoRetry = new RetryBehavior(false); @@ -39,7 +42,12 @@ namespace DotNetCore.CAP.Processor public RetryBehavior(bool retry, int retryCount, Func retryInThunk) { if (retry) - if (retryCount < 0) throw new ArgumentOutOfRangeException(nameof(retryCount), "Can't be negative."); + { + if (retryCount < 0) + { + throw new ArgumentOutOfRangeException(nameof(retryCount), "Can't be negative."); + } + } Retry = retry; RetryCount = retryCount; diff --git a/src/DotNetCore.CAP/Processor/States/IState.Enqueued.cs b/src/DotNetCore.CAP/Processor/States/IState.Enqueued.cs deleted file mode 100644 index ca53699..0000000 --- a/src/DotNetCore.CAP/Processor/States/IState.Enqueued.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using DotNetCore.CAP.Models; - -namespace DotNetCore.CAP.Processor.States -{ - public class EnqueuedState : IState - { - public const string StateName = "Enqueued"; - - public TimeSpan? ExpiresAfter => null; - - public string Name => StateName; - - public void Apply(CapPublishedMessage message, IStorageTransaction transaction) - { - transaction.EnqueueMessage(message); - } - - public void Apply(CapReceivedMessage message, IStorageTransaction transaction) - { - transaction.EnqueueMessage(message); - } - } -} \ No newline at end of file diff --git a/src/DotNetCore.CAP/Processor/States/IState.Failed.cs b/src/DotNetCore.CAP/Processor/States/IState.Failed.cs index 49fda9b..4fb34b6 100644 --- a/src/DotNetCore.CAP/Processor/States/IState.Failed.cs +++ b/src/DotNetCore.CAP/Processor/States/IState.Failed.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using DotNetCore.CAP.Models; namespace DotNetCore.CAP.Processor.States diff --git a/src/DotNetCore.CAP/Processor/States/IState.Processing.cs b/src/DotNetCore.CAP/Processor/States/IState.Processing.cs deleted file mode 100644 index 9827e76..0000000 --- a/src/DotNetCore.CAP/Processor/States/IState.Processing.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; -using DotNetCore.CAP.Models; - -namespace DotNetCore.CAP.Processor.States -{ - public class ProcessingState : IState - { - public const string StateName = "Processing"; - - public TimeSpan? ExpiresAfter => null; - - public string Name => StateName; - - public void Apply(CapPublishedMessage message, IStorageTransaction transaction) - { - } - - public void Apply(CapReceivedMessage message, IStorageTransaction transaction) - { - } - } -} \ No newline at end of file diff --git a/src/DotNetCore.CAP/Processor/States/IState.Scheduled.cs b/src/DotNetCore.CAP/Processor/States/IState.Scheduled.cs index 49f0c95..b5cd805 100644 --- a/src/DotNetCore.CAP/Processor/States/IState.Scheduled.cs +++ b/src/DotNetCore.CAP/Processor/States/IState.Scheduled.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using DotNetCore.CAP.Models; namespace DotNetCore.CAP.Processor.States diff --git a/src/DotNetCore.CAP/Processor/States/IState.Succeeded.cs b/src/DotNetCore.CAP/Processor/States/IState.Succeeded.cs index fdb41e5..3c0d16d 100644 --- a/src/DotNetCore.CAP/Processor/States/IState.Succeeded.cs +++ b/src/DotNetCore.CAP/Processor/States/IState.Succeeded.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using DotNetCore.CAP.Models; namespace DotNetCore.CAP.Processor.States diff --git a/src/DotNetCore.CAP/Processor/States/IState.cs b/src/DotNetCore.CAP/Processor/States/IState.cs index c43fc74..f1b8417 100644 --- a/src/DotNetCore.CAP/Processor/States/IState.cs +++ b/src/DotNetCore.CAP/Processor/States/IState.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using DotNetCore.CAP.Models; namespace DotNetCore.CAP.Processor.States diff --git a/src/DotNetCore.CAP/Processor/States/IStateChanger.Default.cs b/src/DotNetCore.CAP/Processor/States/IStateChanger.Default.cs index 93af696..436dcb6 100644 --- a/src/DotNetCore.CAP/Processor/States/IStateChanger.Default.cs +++ b/src/DotNetCore.CAP/Processor/States/IStateChanger.Default.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using DotNetCore.CAP.Models; namespace DotNetCore.CAP.Processor.States @@ -9,9 +12,13 @@ namespace DotNetCore.CAP.Processor.States { var now = DateTime.Now; if (state.ExpiresAfter != null) + { message.ExpiresAt = now.Add(state.ExpiresAfter.Value); + } else + { message.ExpiresAt = null; + } message.StatusName = state.Name; state.Apply(message, transaction); @@ -22,9 +29,13 @@ namespace DotNetCore.CAP.Processor.States { var now = DateTime.Now; if (state.ExpiresAfter != null) + { message.ExpiresAt = now.Add(state.ExpiresAfter.Value); + } else + { message.ExpiresAt = null; + } message.StatusName = state.Name; state.Apply(message, transaction); diff --git a/src/DotNetCore.CAP/Processor/States/IStateChanger.Extensions.cs b/src/DotNetCore.CAP/Processor/States/IStateChanger.Extensions.cs index 6bd1d12..b0e1e75 100644 --- a/src/DotNetCore.CAP/Processor/States/IStateChanger.Extensions.cs +++ b/src/DotNetCore.CAP/Processor/States/IStateChanger.Extensions.cs @@ -1,4 +1,7 @@ -using System.Threading.Tasks; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System.Threading.Tasks; using DotNetCore.CAP.Models; namespace DotNetCore.CAP.Processor.States diff --git a/src/DotNetCore.CAP/Processor/States/IStateChanger.cs b/src/DotNetCore.CAP/Processor/States/IStateChanger.cs index 949ea31..c15e23a 100644 --- a/src/DotNetCore.CAP/Processor/States/IStateChanger.cs +++ b/src/DotNetCore.CAP/Processor/States/IStateChanger.cs @@ -1,4 +1,7 @@ -using DotNetCore.CAP.Models; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using DotNetCore.CAP.Models; namespace DotNetCore.CAP.Processor.States { diff --git a/src/DotNetCore.CAP/Properties/AssemblyInfo.cs b/src/DotNetCore.CAP/Properties/AssemblyInfo.cs index 0e62b37..3008bc6 100644 --- a/src/DotNetCore.CAP/Properties/AssemblyInfo.cs +++ b/src/DotNetCore.CAP/Properties/AssemblyInfo.cs @@ -1,3 +1,6 @@ -using System.Runtime.CompilerServices; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System.Runtime.CompilerServices; [assembly: InternalsVisibleTo("DotNetCore.CAP.Test")] \ No newline at end of file diff --git a/src/DotNetCore.CAP/QueueExecutorFactory.cs b/src/DotNetCore.CAP/QueueExecutorFactory.cs deleted file mode 100644 index 4e6882f..0000000 --- a/src/DotNetCore.CAP/QueueExecutorFactory.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using System.Linq; -using DotNetCore.CAP.Models; -using Microsoft.Extensions.DependencyInjection; - -namespace DotNetCore.CAP -{ - public class QueueExecutorFactory : IQueueExecutorFactory - { - private readonly IServiceProvider _serviceProvider; - - public QueueExecutorFactory(IServiceProvider serviceProvider) - { - _serviceProvider = serviceProvider; - } - - public IQueueExecutor GetInstance(MessageType messageType) - { - var queueExecutors = _serviceProvider.GetServices(); - - return messageType == MessageType.Publish - ? queueExecutors.FirstOrDefault(x => x is BasePublishQueueExecutor) - : queueExecutors.FirstOrDefault(x => !(x is BasePublishQueueExecutor)); - } - } -} \ No newline at end of file diff --git a/test/DotNetCore.CAP.MySql.Test/DatabaseTestHost.cs b/test/DotNetCore.CAP.MySql.Test/DatabaseTestHost.cs index 1fac1b6..66ffa33 100644 --- a/test/DotNetCore.CAP.MySql.Test/DatabaseTestHost.cs +++ b/test/DotNetCore.CAP.MySql.Test/DatabaseTestHost.cs @@ -1,6 +1,5 @@ using System.Threading; using Dapper; -using Microsoft.EntityFrameworkCore; namespace DotNetCore.CAP.MySql.Test { @@ -59,8 +58,7 @@ CREATE DATABASE `{databaseName}`;"); { connection.Execute($@" TRUNCATE TABLE `cap.published`; -TRUNCATE TABLE `cap.received`; -TRUNCATE TABLE `cap.queue`;"); +TRUNCATE TABLE `cap.received`;"); } } } diff --git a/test/DotNetCore.CAP.MySql.Test/MySqlStorageConnectionTest.cs b/test/DotNetCore.CAP.MySql.Test/MySqlStorageConnectionTest.cs index 50bc489..aefa831 100644 --- a/test/DotNetCore.CAP.MySql.Test/MySqlStorageConnectionTest.cs +++ b/test/DotNetCore.CAP.MySql.Test/MySqlStorageConnectionTest.cs @@ -40,27 +40,6 @@ namespace DotNetCore.CAP.MySql.Test Assert.Equal(StatusName.Scheduled, message.StatusName); } - [Fact] - public async Task FetchNextMessageAsync_Test() - { - var sql = "INSERT INTO `Cap.Queue`(`MessageId`,`MessageType`) VALUES(@MessageId,@MessageType);"; - var queue = new CapQueue - { - MessageId = 3333, - MessageType = MessageType.Publish - }; - using (var connection = ConnectionUtil.CreateConnection()) - { - connection.Execute(sql, queue); - } - using (var fetchedMessage = await _storage.FetchNextMessageAsync()) - { - Assert.NotNull(fetchedMessage); - Assert.Equal(MessageType.Publish, fetchedMessage.MessageType); - Assert.Equal(3333, fetchedMessage.MessageId); - } - } - [Fact] public async Task StoreReceivedMessageAsync_Test() { @@ -110,25 +89,5 @@ namespace DotNetCore.CAP.MySql.Test Assert.Equal("MySqlStorageConnectionTest", message.Name); Assert.Equal("mygroup", message.Group); } - - [Fact] - public async Task GetNextReceviedMessageToBeEnqueuedAsync_Test() - { - var receivedMessage = new CapReceivedMessage - { - Name = "MySqlStorageConnectionTest", - Content = "", - Group = "mygroup", - StatusName = StatusName.Scheduled - }; - await _storage.StoreReceivedMessageAsync(receivedMessage); - - var message = await _storage.GetNextReceivedMessageToBeEnqueuedAsync(); - - Assert.NotNull(message); - Assert.Equal(StatusName.Scheduled, message.StatusName); - Assert.Equal("MySqlStorageConnectionTest", message.Name); - Assert.Equal("mygroup", message.Group); - } } } \ No newline at end of file diff --git a/test/DotNetCore.CAP.MySql.Test/MySqlStorageTest.cs b/test/DotNetCore.CAP.MySql.Test/MySqlStorageTest.cs index 715cb5c..49337f5 100644 --- a/test/DotNetCore.CAP.MySql.Test/MySqlStorageTest.cs +++ b/test/DotNetCore.CAP.MySql.Test/MySqlStorageTest.cs @@ -30,7 +30,6 @@ namespace DotNetCore.CAP.MySql.Test [Theory] [InlineData("cap.published")] - [InlineData("cap.queue")] [InlineData("cap.received")] public void DatabaseTable_IsExists(string tableName) { diff --git a/test/DotNetCore.CAP.PostgreSql.Test/DatabaseTestHost.cs b/test/DotNetCore.CAP.PostgreSql.Test/DatabaseTestHost.cs index 66ae8e1..ec59a74 100644 --- a/test/DotNetCore.CAP.PostgreSql.Test/DatabaseTestHost.cs +++ b/test/DotNetCore.CAP.PostgreSql.Test/DatabaseTestHost.cs @@ -59,8 +59,7 @@ CREATE DATABASE ""{databaseName}"";"); { connection.Execute($@" TRUNCATE TABLE ""cap"".""published""; -TRUNCATE TABLE ""cap"".""received""; -TRUNCATE TABLE ""cap"".""queue"";"); +TRUNCATE TABLE ""cap"".""received"";"); } } } diff --git a/test/DotNetCore.CAP.PostgreSql.Test/PostgreSqlStorageConnectionTest.cs b/test/DotNetCore.CAP.PostgreSql.Test/PostgreSqlStorageConnectionTest.cs index df9dd64..b1cb25c 100644 --- a/test/DotNetCore.CAP.PostgreSql.Test/PostgreSqlStorageConnectionTest.cs +++ b/test/DotNetCore.CAP.PostgreSql.Test/PostgreSqlStorageConnectionTest.cs @@ -40,26 +40,6 @@ namespace DotNetCore.CAP.PostgreSql.Test Assert.Equal(StatusName.Scheduled, message.StatusName); } - [Fact] - public async Task FetchNextMessageAsync_Test() - { - var sql = @"INSERT INTO ""cap"".""queue""(""MessageId"",""MessageType"") VALUES(@MessageId,@MessageType);"; - var queue = new CapQueue - { - MessageId = 3333, - MessageType = MessageType.Publish - }; - using (var connection = ConnectionUtil.CreateConnection()) - { - connection.Execute(sql, queue); - } - var fetchedMessage = await _storage.FetchNextMessageAsync(); - fetchedMessage.Dispose(); - Assert.NotNull(fetchedMessage); - Assert.Equal(MessageType.Publish, fetchedMessage.MessageType); - Assert.Equal(3333, fetchedMessage.MessageId); - } - [Fact] public async Task StoreReceivedMessageAsync_Test() { @@ -109,25 +89,5 @@ namespace DotNetCore.CAP.PostgreSql.Test Assert.Equal("PostgreSqlStorageConnectionTest", message.Name); Assert.Equal("mygroup", message.Group); } - - [Fact] - public async Task GetNextReceviedMessageToBeEnqueuedAsync_Test() - { - var receivedMessage = new CapReceivedMessage - { - Name = "PostgreSqlStorageConnectionTest", - Content = "", - Group = "mygroup", - StatusName = StatusName.Scheduled - }; - await _storage.StoreReceivedMessageAsync(receivedMessage); - - var message = await _storage.GetNextReceivedMessageToBeEnqueuedAsync(); - - Assert.NotNull(message); - Assert.Equal(StatusName.Scheduled, message.StatusName); - Assert.Equal("PostgreSqlStorageConnectionTest", message.Name); - Assert.Equal("mygroup", message.Group); - } } } \ No newline at end of file diff --git a/test/DotNetCore.CAP.PostgreSql.Test/PostgreSqlStorageTest.cs b/test/DotNetCore.CAP.PostgreSql.Test/PostgreSqlStorageTest.cs index c4f5748..ec0bc2b 100644 --- a/test/DotNetCore.CAP.PostgreSql.Test/PostgreSqlStorageTest.cs +++ b/test/DotNetCore.CAP.PostgreSql.Test/PostgreSqlStorageTest.cs @@ -32,7 +32,6 @@ namespace DotNetCore.CAP.PostgreSql.Test [Theory] [InlineData("cap.published")] - [InlineData("cap.queue")] [InlineData("cap.received")] public void DatabaseTable_IsExists(string tableName) { diff --git a/test/DotNetCore.CAP.SqlServer.Test/DatabaseTestHost.cs b/test/DotNetCore.CAP.SqlServer.Test/DatabaseTestHost.cs index 5bf5a7d..96828f6 100644 --- a/test/DotNetCore.CAP.SqlServer.Test/DatabaseTestHost.cs +++ b/test/DotNetCore.CAP.SqlServer.Test/DatabaseTestHost.cs @@ -2,7 +2,6 @@ using System.Data; using System.Data.SqlClient; using System.Threading; using Dapper; -using Microsoft.EntityFrameworkCore; namespace DotNetCore.CAP.SqlServer.Test { diff --git a/test/DotNetCore.CAP.SqlServer.Test/SqlServerStorageConnectionTest.cs b/test/DotNetCore.CAP.SqlServer.Test/SqlServerStorageConnectionTest.cs index 84eba4e..3408ee3 100644 --- a/test/DotNetCore.CAP.SqlServer.Test/SqlServerStorageConnectionTest.cs +++ b/test/DotNetCore.CAP.SqlServer.Test/SqlServerStorageConnectionTest.cs @@ -39,27 +39,7 @@ namespace DotNetCore.CAP.SqlServer.Test Assert.Equal("SqlServerStorageConnectionTest", message.Name); Assert.Equal(StatusName.Scheduled, message.StatusName); } - - [Fact] - public async Task FetchNextMessageAsync_Test() - { - var sql = "INSERT INTO [Cap].[Queue]([MessageId],[MessageType]) VALUES(@MessageId,@MessageType);"; - var queue = new CapQueue - { - MessageId = 3333, - MessageType = MessageType.Publish - }; - using (var connection = ConnectionUtil.CreateConnection()) - { - connection.Execute(sql, queue); - } - var fetchedMessage = await _storage.FetchNextMessageAsync(); - fetchedMessage.Dispose(); - Assert.NotNull(fetchedMessage); - Assert.Equal(MessageType.Publish, fetchedMessage.MessageType); - Assert.Equal(3333, fetchedMessage.MessageId); - } - + [Fact] public async Task StoreReceivedMessageAsync_Test() { @@ -109,25 +89,5 @@ namespace DotNetCore.CAP.SqlServer.Test Assert.Equal("SqlServerStorageConnectionTest", message.Name); Assert.Equal("mygroup", message.Group); } - - [Fact] - public async Task GetNextReceviedMessageToBeEnqueuedAsync_Test() - { - var receivedMessage = new CapReceivedMessage - { - Name = "SqlServerStorageConnectionTest", - Content = "", - Group = "mygroup", - StatusName = StatusName.Scheduled - }; - await _storage.StoreReceivedMessageAsync(receivedMessage); - - var message = await _storage.GetNextReceivedMessageToBeEnqueuedAsync(); - - Assert.NotNull(message); - Assert.Equal(StatusName.Scheduled, message.StatusName); - Assert.Equal("SqlServerStorageConnectionTest", message.Name); - Assert.Equal("mygroup", message.Group); - } } } \ No newline at end of file diff --git a/test/DotNetCore.CAP.SqlServer.Test/SqlServerStorageTest.cs b/test/DotNetCore.CAP.SqlServer.Test/SqlServerStorageTest.cs index af5fc41..427ed30 100644 --- a/test/DotNetCore.CAP.SqlServer.Test/SqlServerStorageTest.cs +++ b/test/DotNetCore.CAP.SqlServer.Test/SqlServerStorageTest.cs @@ -25,7 +25,6 @@ SELECT 'False'"; [Theory] [InlineData("[Cap].[Published]")] - [InlineData("[Cap].[Queue]")] [InlineData("[Cap].[Received]")] public void DatabaseTable_IsExists(string tableName) { diff --git a/test/DotNetCore.CAP.Test/CallbackMessageSenderTest.cs b/test/DotNetCore.CAP.Test/CallbackMessageSenderTest.cs index a29772d..6aa9a44 100644 --- a/test/DotNetCore.CAP.Test/CallbackMessageSenderTest.cs +++ b/test/DotNetCore.CAP.Test/CallbackMessageSenderTest.cs @@ -36,7 +36,7 @@ namespace DotNetCore.CAP.Test { // Arrange _mockCallbackPublisher - .Setup(x => x.PublishAsync(It.IsAny())) + .Setup(x => x.PublishCallbackAsync(It.IsAny())) .Returns(Task.CompletedTask).Verifiable(); _mockContentSerializer diff --git a/test/DotNetCore.CAP.Test/Processor/DefaultDispatcherTest.cs b/test/DotNetCore.CAP.Test/Processor/DefaultDispatcherTest.cs index 8ccdbf0..1f3c77e 100644 --- a/test/DotNetCore.CAP.Test/Processor/DefaultDispatcherTest.cs +++ b/test/DotNetCore.CAP.Test/Processor/DefaultDispatcherTest.cs @@ -1,89 +1,83 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using DotNetCore.CAP.Models; -using DotNetCore.CAP.Processor; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; -using Moq; -using Xunit; +//using System; +//using System.Threading; +//using System.Threading.Tasks; +//using DotNetCore.CAP.Models; +//using DotNetCore.CAP.Processor; +//using Microsoft.Extensions.DependencyInjection; +//using Microsoft.Extensions.Options; +//using Moq; +//using Xunit; -namespace DotNetCore.CAP.Test -{ - public class DefaultDispatcherTest - { - private CancellationTokenSource _cancellationTokenSource; - private ProcessingContext _context; - private IServiceProvider _provider; - private Mock _mockStorageConnection; - private Mock _mockQueueExecutorFactory; - private Mock _mockQueueExecutor; +//namespace DotNetCore.CAP.Test +//{ +// public class DefaultDispatcherTest +// { +// private CancellationTokenSource _cancellationTokenSource; +// private ProcessingContext _context; +// private IServiceProvider _provider; +// private Mock _mockStorageConnection; - public DefaultDispatcherTest() - { - _mockStorageConnection = new Mock(); - _mockQueueExecutorFactory = new Mock(); - _mockQueueExecutor = new Mock(); - _mockQueueExecutorFactory.Setup(x => x.GetInstance(MessageType.Publish)).Returns(_mockQueueExecutor.Object); - _cancellationTokenSource = new CancellationTokenSource(); +// public DefaultDispatcherTest() +// { +// _mockStorageConnection = new Mock(); + +// _cancellationTokenSource = new CancellationTokenSource(); - var services = new ServiceCollection(); - services.AddTransient(); - services.AddLogging(); - services.Configure>(x => { }); - services.AddOptions(); - services.AddSingleton(_mockStorageConnection.Object); - services.AddSingleton(_mockQueueExecutorFactory.Object); - _provider = services.BuildServiceProvider(); +// var services = new ServiceCollection(); +// services.AddLogging(); +// services.Configure>(x => { }); +// services.AddOptions(); +// services.AddSingleton(_mockStorageConnection.Object); +// _provider = services.BuildServiceProvider(); - _context = new ProcessingContext(_provider, _cancellationTokenSource.Token); - } +// _context = new ProcessingContext(_provider, _cancellationTokenSource.Token); +// } - [Fact] - public void MockTest() - { - Assert.NotNull(_provider.GetServices()); - } +// [Fact] +// public void MockTest() +// { +// Assert.NotNull(_provider.GetServices()); +// } - [Fact] - public async void ProcessAsync_CancellationTokenCancelled_ThrowsImmediately() - { - // Arrange - _cancellationTokenSource.Cancel(); - var fixture = Create(); +// [Fact] +// public async void ProcessAsync_CancellationTokenCancelled_ThrowsImmediately() +// { +// // Arrange +// _cancellationTokenSource.Cancel(); +// var fixture = Create(); - // Act - await Assert.ThrowsAsync(() => fixture.ProcessAsync(_context)); - } +// // Act +// await Assert.ThrowsAsync(() => fixture.ProcessAsync(_context)); +// } - [Fact] - public async Task ProcessAsync() - { - // Arrange - var job = new CapPublishedMessage - { - }; +// [Fact] +// public async Task ProcessAsync() +// { +// // Arrange +// var job = new CapPublishedMessage +// { +// }; - var mockFetchedJob = Mock.Get(Mock.Of(fj => fj.MessageId == 42 && fj.MessageType == MessageType.Publish)); +// var mockFetchedJob = Mock.Get(Mock.Of(fj => fj.MessageId == 42 && fj.MessageType == MessageType.Publish)); - _mockStorageConnection - .Setup(m => m.FetchNextMessageAsync()) - .ReturnsAsync(mockFetchedJob.Object).Verifiable(); +// _mockStorageConnection +// .Setup(m => m.FetchNextMessageAsync()) +// .ReturnsAsync(mockFetchedJob.Object).Verifiable(); - _mockQueueExecutor - .Setup(x => x.ExecuteAsync(_mockStorageConnection.Object, mockFetchedJob.Object)) - .Returns(Task.FromResult(OperateResult.Success)); +// _mockQueueExecutor +// .Setup(x => x.ExecuteAsync(_mockStorageConnection.Object, mockFetchedJob.Object)) +// .Returns(Task.FromResult(OperateResult.Success)); - var fixture = Create(); +// var fixture = Create(); - // Act - await fixture.ProcessAsync(_context); +// // Act +// await fixture.ProcessAsync(_context); - // Assert - _mockStorageConnection.VerifyAll(); - } +// // Assert +// _mockStorageConnection.VerifyAll(); +// } - private DefaultDispatcher Create() - => _provider.GetService(); - } -} \ No newline at end of file +// private DefaultDispatcher Create() +// => _provider.GetService(); +// } +//} \ No newline at end of file diff --git a/test/DotNetCore.CAP.Test/Processor/StateChangerTest.cs b/test/DotNetCore.CAP.Test/Processor/StateChangerTest.cs index 9ff5208..5a72ffa 100644 --- a/test/DotNetCore.CAP.Test/Processor/StateChangerTest.cs +++ b/test/DotNetCore.CAP.Test/Processor/StateChangerTest.cs @@ -16,7 +16,7 @@ namespace DotNetCore.CAP.Test var fixture = Create(); var message = new CapPublishedMessage { - StatusName = StatusName.Enqueued + StatusName = StatusName.Scheduled }; var state = Mock.Of(s => s.Name == "s" && s.ExpiresAfter == null); var mockTransaction = new Mock(); @@ -39,7 +39,7 @@ namespace DotNetCore.CAP.Test var fixture = Create(); var message = new CapPublishedMessage { - StatusName = StatusName.Enqueued + StatusName = StatusName.Scheduled }; var state = Mock.Of(s => s.Name == "s" && s.ExpiresAfter == TimeSpan.FromHours(1)); var mockTransaction = new Mock(); diff --git a/test/DotNetCore.CAP.Test/QueueExecutorFactoryTest.cs b/test/DotNetCore.CAP.Test/QueueExecutorFactoryTest.cs index 03302f0..107d074 100644 --- a/test/DotNetCore.CAP.Test/QueueExecutorFactoryTest.cs +++ b/test/DotNetCore.CAP.Test/QueueExecutorFactoryTest.cs @@ -1,46 +1,46 @@ -using System; -using DotNetCore.CAP.Internal; -using Microsoft.Extensions.DependencyInjection; -using Xunit; -using Moq; +//using System; +//using DotNetCore.CAP.Internal; +//using Microsoft.Extensions.DependencyInjection; +//using Xunit; +//using Moq; -namespace DotNetCore.CAP.Test -{ - public class QueueExecutorFactoryTest - { - private IServiceProvider _provider; +//namespace DotNetCore.CAP.Test +//{ +// public class QueueExecutorFactoryTest +// { +// private IServiceProvider _provider; - public QueueExecutorFactoryTest() - { - var services = new ServiceCollection(); - services.AddLogging(); - services.AddOptions(); +// public QueueExecutorFactoryTest() +// { +// var services = new ServiceCollection(); +// services.AddLogging(); +// services.AddOptions(); - services.AddCap(x => { }); - _provider = services.BuildServiceProvider(); - } +// services.AddCap(x => { }); +// _provider = services.BuildServiceProvider(); +// } - [Fact] - public void CanCreateInstance() - { - var queueExecutorFactory = _provider.GetService(); - Assert.NotNull(queueExecutorFactory); +// [Fact] +// public void CanCreateInstance() +// { +// var queueExecutorFactory = _provider.GetService(); +// Assert.NotNull(queueExecutorFactory); - var publishExecutor = queueExecutorFactory.GetInstance(Models.MessageType.Publish); - Assert.Null(publishExecutor); +// var publishExecutor = queueExecutorFactory.GetInstance(Models.MessageType.Publish); +// Assert.Null(publishExecutor); - var disPatchExector = queueExecutorFactory.GetInstance(Models.MessageType.Subscribe); - Assert.NotNull(disPatchExector); - } +// var disPatchExector = queueExecutorFactory.GetInstance(Models.MessageType.Subscribe); +// Assert.NotNull(disPatchExector); +// } - [Fact] - public void CanGetSubscribeExector() - { - var queueExecutorFactory = _provider.GetService(); - Assert.NotNull(queueExecutorFactory); +// [Fact] +// public void CanGetSubscribeExector() +// { +// var queueExecutorFactory = _provider.GetService(); +// Assert.NotNull(queueExecutorFactory); - var publishExecutor = queueExecutorFactory.GetInstance(Models.MessageType.Publish); - Assert.Null(publishExecutor); - } - } -} \ No newline at end of file +// var publishExecutor = queueExecutorFactory.GetInstance(Models.MessageType.Publish); +// Assert.Null(publishExecutor); +// } +// } +//} \ No newline at end of file From e6bad00b4d3fcedd1643e6d2c7c59f50b3979068 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Sat, 31 Mar 2018 19:02:39 +0800 Subject: [PATCH 011/223] upgrade dependent nuget packages. --- .../Sample.Kafka.MySql.csproj | 2 +- .../Sample.RabbitMQ.MySql.csproj | 2 +- .../DotNetCore.CAP.Kafka.csproj | 2 +- .../DotNetCore.CAP.MySql.csproj | 6 +++--- .../DotNetCore.CAP.PostgreSql.csproj | 4 ++-- .../DotNetCore.CAP.SqlServer.csproj | 6 +++--- src/DotNetCore.CAP/DotNetCore.CAP.csproj | 10 +++++----- .../DotNetCore.CAP.MySql.Test.csproj | 18 +++++++++--------- .../DotNetCore.CAP.PostgreSql.Test.csproj | 2 +- .../DotNetCore.CAP.SqlServer.Test.csproj | 18 +++++++++--------- .../DotNetCore.CAP.Test.csproj | 6 +++--- 11 files changed, 38 insertions(+), 38 deletions(-) diff --git a/samples/Sample.Kafka.SqlServer/Sample.Kafka.MySql.csproj b/samples/Sample.Kafka.SqlServer/Sample.Kafka.MySql.csproj index 2123eaa..1c5a152 100644 --- a/samples/Sample.Kafka.SqlServer/Sample.Kafka.MySql.csproj +++ b/samples/Sample.Kafka.SqlServer/Sample.Kafka.MySql.csproj @@ -9,7 +9,7 @@ - + diff --git a/samples/Sample.RabbitMQ.MySql/Sample.RabbitMQ.MySql.csproj b/samples/Sample.RabbitMQ.MySql/Sample.RabbitMQ.MySql.csproj index 053e875..d3a3987 100644 --- a/samples/Sample.RabbitMQ.MySql/Sample.RabbitMQ.MySql.csproj +++ b/samples/Sample.RabbitMQ.MySql/Sample.RabbitMQ.MySql.csproj @@ -10,7 +10,7 @@ - + diff --git a/src/DotNetCore.CAP.Kafka/DotNetCore.CAP.Kafka.csproj b/src/DotNetCore.CAP.Kafka/DotNetCore.CAP.Kafka.csproj index 3161a38..72e580e 100644 --- a/src/DotNetCore.CAP.Kafka/DotNetCore.CAP.Kafka.csproj +++ b/src/DotNetCore.CAP.Kafka/DotNetCore.CAP.Kafka.csproj @@ -15,7 +15,7 @@ - + diff --git a/src/DotNetCore.CAP.MySql/DotNetCore.CAP.MySql.csproj b/src/DotNetCore.CAP.MySql/DotNetCore.CAP.MySql.csproj index 515828b..38482c6 100644 --- a/src/DotNetCore.CAP.MySql/DotNetCore.CAP.MySql.csproj +++ b/src/DotNetCore.CAP.MySql/DotNetCore.CAP.MySql.csproj @@ -15,9 +15,9 @@ - - - + + + diff --git a/src/DotNetCore.CAP.PostgreSql/DotNetCore.CAP.PostgreSql.csproj b/src/DotNetCore.CAP.PostgreSql/DotNetCore.CAP.PostgreSql.csproj index 702de21..81b1b5c 100644 --- a/src/DotNetCore.CAP.PostgreSql/DotNetCore.CAP.PostgreSql.csproj +++ b/src/DotNetCore.CAP.PostgreSql/DotNetCore.CAP.PostgreSql.csproj @@ -15,8 +15,8 @@ - - + + diff --git a/src/DotNetCore.CAP.SqlServer/DotNetCore.CAP.SqlServer.csproj b/src/DotNetCore.CAP.SqlServer/DotNetCore.CAP.SqlServer.csproj index 7436359..7d03a24 100644 --- a/src/DotNetCore.CAP.SqlServer/DotNetCore.CAP.SqlServer.csproj +++ b/src/DotNetCore.CAP.SqlServer/DotNetCore.CAP.SqlServer.csproj @@ -15,9 +15,9 @@ - - - + + + diff --git a/src/DotNetCore.CAP/DotNetCore.CAP.csproj b/src/DotNetCore.CAP/DotNetCore.CAP.csproj index 816da2f..8bf4189 100644 --- a/src/DotNetCore.CAP/DotNetCore.CAP.csproj +++ b/src/DotNetCore.CAP/DotNetCore.CAP.csproj @@ -49,11 +49,11 @@ - - - - - + + + + + diff --git a/test/DotNetCore.CAP.MySql.Test/DotNetCore.CAP.MySql.Test.csproj b/test/DotNetCore.CAP.MySql.Test/DotNetCore.CAP.MySql.Test.csproj index b0dd92a..31710c4 100644 --- a/test/DotNetCore.CAP.MySql.Test/DotNetCore.CAP.MySql.Test.csproj +++ b/test/DotNetCore.CAP.MySql.Test/DotNetCore.CAP.MySql.Test.csproj @@ -15,19 +15,19 @@ - - + + - + - - - - - - + + + + + + \ No newline at end of file diff --git a/test/DotNetCore.CAP.PostgreSql.Test/DotNetCore.CAP.PostgreSql.Test.csproj b/test/DotNetCore.CAP.PostgreSql.Test/DotNetCore.CAP.PostgreSql.Test.csproj index be84f65..2346ca5 100644 --- a/test/DotNetCore.CAP.PostgreSql.Test/DotNetCore.CAP.PostgreSql.Test.csproj +++ b/test/DotNetCore.CAP.PostgreSql.Test/DotNetCore.CAP.PostgreSql.Test.csproj @@ -8,7 +8,7 @@ - + diff --git a/test/DotNetCore.CAP.SqlServer.Test/DotNetCore.CAP.SqlServer.Test.csproj b/test/DotNetCore.CAP.SqlServer.Test/DotNetCore.CAP.SqlServer.Test.csproj index 035ed35..d8e70d4 100644 --- a/test/DotNetCore.CAP.SqlServer.Test/DotNetCore.CAP.SqlServer.Test.csproj +++ b/test/DotNetCore.CAP.SqlServer.Test/DotNetCore.CAP.SqlServer.Test.csproj @@ -12,19 +12,19 @@ - - + + - + - - - - - - + + + + + + diff --git a/test/DotNetCore.CAP.Test/DotNetCore.CAP.Test.csproj b/test/DotNetCore.CAP.Test/DotNetCore.CAP.Test.csproj index 96bfb13..09e9dd9 100644 --- a/test/DotNetCore.CAP.Test/DotNetCore.CAP.Test.csproj +++ b/test/DotNetCore.CAP.Test/DotNetCore.CAP.Test.csproj @@ -8,14 +8,14 @@ - + - + - + From bacb47973a8c071a3eb071644d8ece225769fefb Mon Sep 17 00:00:00 2001 From: Savorboard Date: Wed, 18 Apr 2018 13:01:30 +0800 Subject: [PATCH 012/223] fixed message enqueue exception in v2.2 --- build/version.props | 2 +- .../Diagnostics/DiagnosticListenerExtensions.cs | 6 ++++++ src/DotNetCore.CAP/Diagnostics/EventData.Broker.cs | 1 - src/DotNetCore.CAP/IPublishMessageSender.Base.cs | 2 +- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/build/version.props b/build/version.props index d705cb4..6f4fc75 100644 --- a/build/version.props +++ b/build/version.props @@ -2,7 +2,7 @@ 2 2 - 0 + 1 $(VersionMajor).$(VersionMinor).$(VersionPatch) diff --git a/src/DotNetCore.CAP/Diagnostics/DiagnosticListenerExtensions.cs b/src/DotNetCore.CAP/Diagnostics/DiagnosticListenerExtensions.cs index e833a8e..da0cc0c 100644 --- a/src/DotNetCore.CAP/Diagnostics/DiagnosticListenerExtensions.cs +++ b/src/DotNetCore.CAP/Diagnostics/DiagnosticListenerExtensions.cs @@ -107,6 +107,7 @@ namespace DotNetCore.CAP.Diagnostics { if (@this.IsEnabled(CapBeforePublish)) { + eventData.Headers = new TracingHeaders(); @this.Write(CapBeforePublish, eventData); } } @@ -115,6 +116,7 @@ namespace DotNetCore.CAP.Diagnostics { if (@this.IsEnabled(CapAfterPublish)) { + eventData.Headers = new TracingHeaders(); @this.Write(CapAfterPublish, eventData); } } @@ -123,6 +125,7 @@ namespace DotNetCore.CAP.Diagnostics { if (@this.IsEnabled(CapErrorPublish)) { + eventData.Headers = new TracingHeaders(); @this.Write(CapErrorPublish, eventData); } } @@ -135,6 +138,7 @@ namespace DotNetCore.CAP.Diagnostics { if (@this.IsEnabled(CapBeforeConsume)) { + eventData.Headers = new TracingHeaders(); @this.Write(CapBeforeConsume, eventData); } @@ -145,6 +149,7 @@ namespace DotNetCore.CAP.Diagnostics { if (@this.IsEnabled(CapAfterConsume)) { + eventData.Headers = new TracingHeaders(); @this.Write(CapAfterConsume, eventData); } } @@ -153,6 +158,7 @@ namespace DotNetCore.CAP.Diagnostics { if (@this.IsEnabled(CapErrorConsume)) { + eventData.Headers = new TracingHeaders(); @this.Write(CapErrorConsume, eventData); } } diff --git a/src/DotNetCore.CAP/Diagnostics/EventData.Broker.cs b/src/DotNetCore.CAP/Diagnostics/EventData.Broker.cs index b0a6b20..2db1794 100644 --- a/src/DotNetCore.CAP/Diagnostics/EventData.Broker.cs +++ b/src/DotNetCore.CAP/Diagnostics/EventData.Broker.cs @@ -11,7 +11,6 @@ namespace DotNetCore.CAP.Diagnostics string brokerTopicName, string brokerTopicBody) : base(operationId, operation) { - Headers = new TracingHeaders(); BrokerAddress = brokerAddress; BrokerTopicName = brokerTopicName; BrokerTopicBody = brokerTopicBody; diff --git a/src/DotNetCore.CAP/IPublishMessageSender.Base.cs b/src/DotNetCore.CAP/IPublishMessageSender.Base.cs index eb37cfc..cc82be6 100644 --- a/src/DotNetCore.CAP/IPublishMessageSender.Base.cs +++ b/src/DotNetCore.CAP/IPublishMessageSender.Base.cs @@ -147,7 +147,7 @@ namespace DotNetCore.CAP s_diagnosticListener.WritePublishBefore(eventData); - return (operationId, eventData.Headers); + return (operationId, eventData.Headers); //if not enabled diagnostics ,the header will be null } private void TracingAfter(Guid operationId, string topic, string values, DateTimeOffset startTime, TimeSpan du) From 488df1b067632eb38dbe385a846c76baeb762691 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Wed, 18 Apr 2018 17:50:32 +0800 Subject: [PATCH 013/223] add log to samples --- samples/Sample.Kafka.MySql/Program.cs | 7 ++++- .../Sample.Kafka.MySql.csproj | 6 +++++ samples/Sample.Kafka.MySql/Startup.cs | 3 +-- samples/Sample.Kafka.MySql/nlog.config | 26 +++++++++++++++++++ 4 files changed, 39 insertions(+), 3 deletions(-) create mode 100644 samples/Sample.Kafka.MySql/nlog.config diff --git a/samples/Sample.Kafka.MySql/Program.cs b/samples/Sample.Kafka.MySql/Program.cs index 7d62e55..976b20a 100644 --- a/samples/Sample.Kafka.MySql/Program.cs +++ b/samples/Sample.Kafka.MySql/Program.cs @@ -1,5 +1,6 @@ using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; +using NLog.Web; namespace Sample.Kafka.MySql { @@ -14,7 +15,11 @@ namespace Sample.Kafka.MySql public static IWebHost BuildWebHost(string[] args) => WebHost.CreateDefaultBuilder(args) .UseStartup() + .ConfigureLogging((hostingContext, builder) => + { + hostingContext.HostingEnvironment.ConfigureNLog("nlog.config"); + }) + .UseNLog() .Build(); - } } \ No newline at end of file diff --git a/samples/Sample.Kafka.MySql/Sample.Kafka.MySql.csproj b/samples/Sample.Kafka.MySql/Sample.Kafka.MySql.csproj index ad812ae..88c0c1d 100644 --- a/samples/Sample.Kafka.MySql/Sample.Kafka.MySql.csproj +++ b/samples/Sample.Kafka.MySql/Sample.Kafka.MySql.csproj @@ -10,6 +10,7 @@ + @@ -20,5 +21,10 @@ + + + PreserveNewest + + diff --git a/samples/Sample.Kafka.MySql/Startup.cs b/samples/Sample.Kafka.MySql/Startup.cs index 419e48e..12040d7 100644 --- a/samples/Sample.Kafka.MySql/Startup.cs +++ b/samples/Sample.Kafka.MySql/Startup.cs @@ -1,5 +1,4 @@ -using System; -using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.DependencyInjection; namespace Sample.Kafka.MySql diff --git a/samples/Sample.Kafka.MySql/nlog.config b/samples/Sample.Kafka.MySql/nlog.config new file mode 100644 index 0000000..5bfcf49 --- /dev/null +++ b/samples/Sample.Kafka.MySql/nlog.config @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 852357cc4967b618606fbd31aa9b3f75013792ab Mon Sep 17 00:00:00 2001 From: Savorboard Date: Wed, 18 Apr 2018 17:51:45 +0800 Subject: [PATCH 014/223] Improved log output. #114 --- .../IPublishMessageSender.Base.cs | 33 ++++++++----------- .../Internal/PublisherSentFailedException.cs | 4 +++ src/DotNetCore.CAP/LoggerExtensions.cs | 22 ++++++------- 3 files changed, 28 insertions(+), 31 deletions(-) diff --git a/src/DotNetCore.CAP/IPublishMessageSender.Base.cs b/src/DotNetCore.CAP/IPublishMessageSender.Base.cs index cc82be6..a597169 100644 --- a/src/DotNetCore.CAP/IPublishMessageSender.Base.cs +++ b/src/DotNetCore.CAP/IPublishMessageSender.Base.cs @@ -67,15 +67,13 @@ namespace DotNetCore.CAP } else { - TracingError(operationId, message.Name, sendValues, result.Exception, startTime, stopwatch.Elapsed); - - _logger.MessagePublishException(message.Id, result.Exception); + TracingError(operationId, message, result, startTime, stopwatch.Elapsed); await SetFailedState(message, result.Exception, out bool stillRetry); if (stillRetry) { - _logger.SenderRetrying(3); + _logger.SenderRetrying(message.Id, message.Retries); await SendAsync(message); } @@ -109,20 +107,11 @@ namespace DotNetCore.CAP private Task SetFailedState(CapPublishedMessage message, Exception ex, out bool stillRetry) { IState newState = new FailedState(); - - if (ex is PublisherSentFailedException) - { - stillRetry = false; - message.Retries = _options.FailedRetryCount; // not retry if PublisherSentFailedException - } - else + stillRetry = UpdateMessageForRetryAsync(message); + if (stillRetry) { - stillRetry = UpdateMessageForRetryAsync(message); - if (stillRetry) - { - _logger.ConsumerExecutionFailedWillRetry(ex); - return Task.CompletedTask; - } + _logger.ConsumerExecutionFailedWillRetry(ex); + return Task.CompletedTask; } AddErrorReasonToContent(message, ex); @@ -166,14 +155,18 @@ namespace DotNetCore.CAP _logger.MessageHasBeenSent(du.TotalSeconds); } - private void TracingError(Guid operationId, string topic, string values, Exception ex, DateTimeOffset startTime, TimeSpan du) + private void TracingError(Guid operationId, CapPublishedMessage message, OperateResult result, DateTimeOffset startTime, TimeSpan du) { + var ex = new PublisherSentFailedException(result.ToString(), result.Exception); + + _logger.MessagePublishException(message.Id, result.ToString(), ex); + var eventData = new BrokerPublishErrorEventData( operationId, "", ServersAddress, - topic, - values, + message.Name, + message.Content, ex, startTime, du); diff --git a/src/DotNetCore.CAP/Internal/PublisherSentFailedException.cs b/src/DotNetCore.CAP/Internal/PublisherSentFailedException.cs index b0f0a2c..9be1758 100644 --- a/src/DotNetCore.CAP/Internal/PublisherSentFailedException.cs +++ b/src/DotNetCore.CAP/Internal/PublisherSentFailedException.cs @@ -7,6 +7,10 @@ namespace DotNetCore.CAP.Internal { public class PublisherSentFailedException : Exception { + public PublisherSentFailedException(string message) : base(message) + { + } + public PublisherSentFailedException(string message, Exception ex) : base(message, ex) { } diff --git a/src/DotNetCore.CAP/LoggerExtensions.cs b/src/DotNetCore.CAP/LoggerExtensions.cs index 031be9d..a0096e6 100644 --- a/src/DotNetCore.CAP/LoggerExtensions.cs +++ b/src/DotNetCore.CAP/LoggerExtensions.cs @@ -17,10 +17,10 @@ namespace DotNetCore.CAP private static readonly Action _modelBinderFormattingException; private static readonly Action _consumerFailedWillRetry; private static readonly Action _consumerExecuted; - private static readonly Action _senderRetrying; + private static readonly Action _senderRetrying; private static readonly Action _exceptionOccuredWhileExecuting; private static readonly Action _messageHasBeenSent; - private static readonly Action _messagePublishException; + private static readonly Action _messagePublishException; static LoggerExtensions() { @@ -60,10 +60,10 @@ namespace DotNetCore.CAP "When call subscribe method, a parameter format conversion exception occurs. MethodName:'{MethodName}' ParameterName:'{ParameterName}' Content:'{Content}'." ); - _senderRetrying = LoggerMessage.Define( + _senderRetrying = LoggerMessage.Define( LogLevel.Debug, 3, - "Retrying send a message: {Retries}..."); + "The {Retries}th retrying send a message failed. message id: {MessageId} "); _consumerExecuted = LoggerMessage.Define( LogLevel.Debug, @@ -78,17 +78,17 @@ namespace DotNetCore.CAP _exceptionOccuredWhileExecuting = LoggerMessage.Define( LogLevel.Error, 6, - "An exception occured while trying to store a message: '{MessageId}'. "); + "An exception occured while trying to store a message. message id: {MessageId}"); _messageHasBeenSent = LoggerMessage.Define( LogLevel.Debug, 4, "Message published. Took: {Seconds} secs."); - _messagePublishException = LoggerMessage.Define( + _messagePublishException = LoggerMessage.Define( LogLevel.Error, 6, - "An exception occured while publishing a message: '{MessageId}'. "); + "An exception occured while publishing a message, reason:{Reason}. message id:{MessageId}"); } public static void ConsumerExecutionFailedWillRetry(this ILogger logger, Exception ex) @@ -96,9 +96,9 @@ namespace DotNetCore.CAP _consumerFailedWillRetry(logger, ex); } - public static void SenderRetrying(this ILogger logger, int retries) + public static void SenderRetrying(this ILogger logger, int messageId, int retries) { - _senderRetrying(logger, retries, null); + _senderRetrying(logger, messageId, retries, null); } public static void MessageHasBeenSent(this ILogger logger, double seconds) @@ -106,9 +106,9 @@ namespace DotNetCore.CAP _messageHasBeenSent(logger, seconds, null); } - public static void MessagePublishException(this ILogger logger, int messageId, Exception ex) + public static void MessagePublishException(this ILogger logger, int messageId, string reason, Exception ex) { - _messagePublishException(logger, messageId, ex); + _messagePublishException(logger, messageId, reason, ex); } public static void ConsumerExecuted(this ILogger logger, double seconds) From 0058f42127d040ac20934e55225699234fd744a7 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Wed, 18 Apr 2018 17:52:28 +0800 Subject: [PATCH 015/223] add default timeout configuration for kafka client. --- src/DotNetCore.CAP.Kafka/CAP.KafkaOptions.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/DotNetCore.CAP.Kafka/CAP.KafkaOptions.cs b/src/DotNetCore.CAP.Kafka/CAP.KafkaOptions.cs index 629963b..0e4161b 100644 --- a/src/DotNetCore.CAP.Kafka/CAP.KafkaOptions.cs +++ b/src/DotNetCore.CAP.Kafka/CAP.KafkaOptions.cs @@ -57,6 +57,8 @@ namespace DotNetCore.CAP MainConfig["socket.blocking.max.ms"] = "10"; MainConfig["enable.auto.commit"] = "false"; MainConfig["log.connection.close"] = "false"; + MainConfig["request.timeout.ms"] = "3000"; + MainConfig["message.timeout.ms"] = "5000"; _kafkaConfig = MainConfig.AsEnumerable(); } From 8a0f75bae1f1ebad0065035a02ce2a03a30c06cf Mon Sep 17 00:00:00 2001 From: Savorboard Date: Wed, 18 Apr 2018 17:53:10 +0800 Subject: [PATCH 016/223] fixed retry processor bugs. --- .../Processor/IProcessor.NeedRetry.cs | 57 ++++++++++++++++--- 1 file changed, 50 insertions(+), 7 deletions(-) diff --git a/src/DotNetCore.CAP/Processor/IProcessor.NeedRetry.cs b/src/DotNetCore.CAP/Processor/IProcessor.NeedRetry.cs index b1109a3..dbd2212 100644 --- a/src/DotNetCore.CAP/Processor/IProcessor.NeedRetry.cs +++ b/src/DotNetCore.CAP/Processor/IProcessor.NeedRetry.cs @@ -80,18 +80,29 @@ namespace DotNetCore.CAP.Processor using (var transaction = connection.CreateTransaction()) { - try + var result = await _publishExecutor.PublishAsync(message.Name, message.Content); + if (result.Succeeded) { - await _publishExecutor.PublishAsync(message.Name, message.Content); - _stateChanger.ChangeState(message, new SucceededState(), transaction); + _logger.LogInformation("The message was sent successfully during the retry. MessageId:" + message.Id); } - catch (Exception e) + else { - message.Content = Helper.AddExceptionProperty(message.Content, e); + message.Content = Helper.AddExceptionProperty(message.Content, result.Exception); + message.Retries++; + if (message.StatusName == StatusName.Scheduled) + { + message.ExpiresAt = GetDueTime(message.Added, message.Retries); + message.StatusName = StatusName.Failed; + } transaction.UpdateMessage(message); - } + if (message.Retries >= _options.FailedRetryCount) + { + _logger.LogError($"The message still sent failed after {_options.FailedRetryCount} retries. We will stop retrying the message. " + + "MessageId:" + message.Id); + } + } await transaction.CommitAsync(); } @@ -126,12 +137,44 @@ namespace DotNetCore.CAP.Processor } } - await _subscriberExecutor.ExecuteAsync(message); + using (var transaction = connection.CreateTransaction()) + { + var result = await _subscriberExecutor.ExecuteAsync(message); + if (result.Succeeded) + { + _stateChanger.ChangeState(message, new SucceededState(), transaction); + _logger.LogInformation("The message was execute successfully during the retry. MessageId:" + message.Id); + } + else + { + message.Content = Helper.AddExceptionProperty(message.Content, result.Exception); + message.Retries++; + if (message.StatusName == StatusName.Scheduled) + { + message.ExpiresAt = GetDueTime(message.Added, message.Retries); + message.StatusName = StatusName.Failed; + } + transaction.UpdateMessage(message); + + if (message.Retries >= _options.FailedRetryCount) + { + _logger.LogError($"[Subscriber]The message still executed failed after {_options.FailedRetryCount} retries. " + + "We will stop retrying to execute the message. message id:" + message.Id); + } + } + await transaction.CommitAsync(); + } context.ThrowIfStopping(); await context.WaitAsync(_delay); } } + + public DateTime GetDueTime(DateTime addedTime, int retries) + { + var retryBehavior = RetryBehavior.DefaultRetry; + return addedTime.AddSeconds(retryBehavior.RetryIn(retries)); + } } } \ No newline at end of file From c9f672e08b3e12e35f4be32ccfad062d0e4a03a6 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Wed, 18 Apr 2018 17:55:04 +0800 Subject: [PATCH 017/223] Fixed kafka producer exception log without logging when publish message. --- src/DotNetCore.CAP.Kafka/IPublishMessageSender.Kafka.cs | 8 ++------ src/DotNetCore.CAP/Infrastructure/Helper.cs | 1 - 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/DotNetCore.CAP.Kafka/IPublishMessageSender.Kafka.cs b/src/DotNetCore.CAP.Kafka/IPublishMessageSender.Kafka.cs index f3e4e5d..4527eda 100644 --- a/src/DotNetCore.CAP.Kafka/IPublishMessageSender.Kafka.cs +++ b/src/DotNetCore.CAP.Kafka/IPublishMessageSender.Kafka.cs @@ -36,11 +36,7 @@ namespace DotNetCore.CAP.Kafka if (message.Error.HasError) { - return OperateResult.Failed(new OperateError - { - Code = message.Error.Code.ToString(), - Description = message.Error.Reason - }); + throw new PublisherSentFailedException(message.Error.ToString()); } _logger.LogDebug($"kafka topic message [{keyName}] has been published."); @@ -61,6 +57,6 @@ namespace DotNetCore.CAP.Kafka producer.Dispose(); } } - } + } } } \ No newline at end of file diff --git a/src/DotNetCore.CAP/Infrastructure/Helper.cs b/src/DotNetCore.CAP/Infrastructure/Helper.cs index 83d128f..3f023b9 100644 --- a/src/DotNetCore.CAP/Infrastructure/Helper.cs +++ b/src/DotNetCore.CAP/Infrastructure/Helper.cs @@ -86,7 +86,6 @@ namespace DotNetCore.CAP.Infrastructure return !CanConvertFromString(type); } - public static string AddExceptionProperty(string json, Exception exception) { var jObject = ToJObject(exception); From 997379922af41e7a4b81756fcfc6c9d7ff358ff7 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Wed, 18 Apr 2018 17:55:39 +0800 Subject: [PATCH 018/223] update version num to 2.2.2 --- build/version.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/version.props b/build/version.props index 6f4fc75..ae5b04f 100644 --- a/build/version.props +++ b/build/version.props @@ -2,7 +2,7 @@ 2 2 - 1 + 2 $(VersionMajor).$(VersionMinor).$(VersionPatch) From bfa283e3f893e579b08c3fc955a77bccfce21e23 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Fri, 20 Apr 2018 17:01:50 +0800 Subject: [PATCH 019/223] rename configuration options FailedCallback to FailedThresholdCallback --- src/DotNetCore.CAP/CAP.Options.cs | 4 +- .../Processor/IProcessor.NeedRetry.cs | 57 ++++++++++--------- 2 files changed, 33 insertions(+), 28 deletions(-) diff --git a/src/DotNetCore.CAP/CAP.Options.cs b/src/DotNetCore.CAP/CAP.Options.cs index 267a61f..05c0294 100644 --- a/src/DotNetCore.CAP/CAP.Options.cs +++ b/src/DotNetCore.CAP/CAP.Options.cs @@ -58,9 +58,9 @@ namespace DotNetCore.CAP public int FailedRetryInterval { get; set; } /// - /// We’ll invoke this call-back with message type,name,content when requeue failed message. + /// We’ll invoke this call-back with message type,name,content when retry failed (send or executed) messages equals times. /// - public Action FailedCallback { get; set; } + public Action FailedThresholdCallback { get; set; } /// /// The number of message retries, the retry will stop when the threshold is reached. diff --git a/src/DotNetCore.CAP/Processor/IProcessor.NeedRetry.cs b/src/DotNetCore.CAP/Processor/IProcessor.NeedRetry.cs index dbd2212..2f7b829 100644 --- a/src/DotNetCore.CAP/Processor/IProcessor.NeedRetry.cs +++ b/src/DotNetCore.CAP/Processor/IProcessor.NeedRetry.cs @@ -65,19 +65,6 @@ namespace DotNetCore.CAP.Processor continue; } - if (!hasException) - { - try - { - _options.FailedCallback?.Invoke(MessageType.Publish, message.Name, message.Content); - } - catch (Exception ex) - { - hasException = true; - _logger.LogWarning("Failed call-back method raised an exception:" + ex.Message); - } - } - using (var transaction = connection.CreateTransaction()) { var result = await _publishExecutor.PublishAsync(message.Name, message.Content); @@ -101,6 +88,21 @@ namespace DotNetCore.CAP.Processor { _logger.LogError($"The message still sent failed after {_options.FailedRetryCount} retries. We will stop retrying the message. " + "MessageId:" + message.Id); + if (message.Retries == _options.FailedRetryCount) + { + if (!hasException) + { + try + { + _options.FailedThresholdCallback?.Invoke(MessageType.Publish, message.Name, message.Content); + } + catch (Exception ex) + { + hasException = true; + _logger.LogWarning("Failed call-back method raised an exception:" + ex.Message); + } + } + } } } await transaction.CommitAsync(); @@ -124,19 +126,6 @@ namespace DotNetCore.CAP.Processor continue; } - if (!hasException) - { - try - { - _options.FailedCallback?.Invoke(MessageType.Subscribe, message.Name, message.Content); - } - catch (Exception ex) - { - hasException = true; - _logger.LogWarning("Failed call-back method raised an exception:" + ex.Message); - } - } - using (var transaction = connection.CreateTransaction()) { var result = await _subscriberExecutor.ExecuteAsync(message); @@ -160,6 +149,22 @@ namespace DotNetCore.CAP.Processor { _logger.LogError($"[Subscriber]The message still executed failed after {_options.FailedRetryCount} retries. " + "We will stop retrying to execute the message. message id:" + message.Id); + + if (message.Retries == _options.FailedRetryCount) + { + if (!hasException) + { + try + { + _options.FailedThresholdCallback?.Invoke(MessageType.Subscribe, message.Name, message.Content); + } + catch (Exception ex) + { + hasException = true; + _logger.LogWarning("Failed call-back method raised an exception:" + ex.Message); + } + } + } } } await transaction.CommitAsync(); From b62f4b00daf083e3671653b9e2013c83d3d651ab Mon Sep 17 00:00:00 2001 From: Savorboard Date: Wed, 25 Apr 2018 20:50:18 +0800 Subject: [PATCH 020/223] rename files name. --- .../{ConnectionPool.cs => IConnectionPool.Default.cs} | 0 ...ConnectionChannelPool.cs => IConnectionChannelPool.Default.cs} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename src/DotNetCore.CAP.Kafka/{ConnectionPool.cs => IConnectionPool.Default.cs} (100%) rename src/DotNetCore.CAP.RabbitMQ/{ConnectionChannelPool.cs => IConnectionChannelPool.Default.cs} (100%) diff --git a/src/DotNetCore.CAP.Kafka/ConnectionPool.cs b/src/DotNetCore.CAP.Kafka/IConnectionPool.Default.cs similarity index 100% rename from src/DotNetCore.CAP.Kafka/ConnectionPool.cs rename to src/DotNetCore.CAP.Kafka/IConnectionPool.Default.cs diff --git a/src/DotNetCore.CAP.RabbitMQ/ConnectionChannelPool.cs b/src/DotNetCore.CAP.RabbitMQ/IConnectionChannelPool.Default.cs similarity index 100% rename from src/DotNetCore.CAP.RabbitMQ/ConnectionChannelPool.cs rename to src/DotNetCore.CAP.RabbitMQ/IConnectionChannelPool.Default.cs From 67060b7e88907dd97363b950802a89e896c4ef08 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Wed, 25 Apr 2018 20:50:36 +0800 Subject: [PATCH 021/223] remove unused files. --- src/DotNetCore.CAP/IFetchedMessage.cs | 19 ------------------- 1 file changed, 19 deletions(-) delete mode 100644 src/DotNetCore.CAP/IFetchedMessage.cs diff --git a/src/DotNetCore.CAP/IFetchedMessage.cs b/src/DotNetCore.CAP/IFetchedMessage.cs deleted file mode 100644 index 34a78ca..0000000 --- a/src/DotNetCore.CAP/IFetchedMessage.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) .NET Core Community. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. - -using System; -using DotNetCore.CAP.Models; - -namespace DotNetCore.CAP -{ - public interface IFetchedMessage : IDisposable - { - int MessageId { get; } - - MessageType MessageType { get; } - - void RemoveFromQueue(); - - void Requeue(); - } -} \ No newline at end of file From bbc987339d64a1bbe12bae6f675ea1d69bde534a Mon Sep 17 00:00:00 2001 From: Savorboard Date: Wed, 25 Apr 2018 20:51:05 +0800 Subject: [PATCH 022/223] modify the error comments. --- src/DotNetCore.CAP/Abstractions/CapPublisherBase.cs | 2 +- src/DotNetCore.CAP/ICapPublisher.cs | 4 ++-- src/DotNetCore.CAP/ICapSubscribe.cs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/DotNetCore.CAP/Abstractions/CapPublisherBase.cs b/src/DotNetCore.CAP/Abstractions/CapPublisherBase.cs index 26760b2..4d7a321 100644 --- a/src/DotNetCore.CAP/Abstractions/CapPublisherBase.cs +++ b/src/DotNetCore.CAP/Abstractions/CapPublisherBase.cs @@ -132,7 +132,7 @@ namespace DotNetCore.CAP.Abstractions { throw new InvalidOperationException( "If you are using the EntityFramework, you need to configure the DbContextType first." + - " otherwise you need to use overloaded method with IDbConnection and IDbTransaction."); + " otherwise you need to use overloaded method with IDbTransaction."); } } diff --git a/src/DotNetCore.CAP/ICapPublisher.cs b/src/DotNetCore.CAP/ICapPublisher.cs index 8ba45e1..0688c02 100644 --- a/src/DotNetCore.CAP/ICapPublisher.cs +++ b/src/DotNetCore.CAP/ICapPublisher.cs @@ -15,7 +15,7 @@ namespace DotNetCore.CAP /// (EntityFramework) Asynchronous publish a object message. /// /// If you are using the EntityFramework, you need to configure the DbContextType first. - /// otherwise you need to use overloaded method with IDbConnection and IDbTransaction. + /// otherwise you need to use overloaded method with IDbTransaction. /// /// /// The type of content object. @@ -28,7 +28,7 @@ namespace DotNetCore.CAP /// (EntityFramework) Publish a object message. /// /// If you are using the EntityFramework, you need to configure the DbContextType first. - /// otherwise you need to use overloaded method with IDbConnection and IDbTransaction. + /// otherwise you need to use overloaded method with IDbTransaction. /// /// /// The type of content object. diff --git a/src/DotNetCore.CAP/ICapSubscribe.cs b/src/DotNetCore.CAP/ICapSubscribe.cs index 9b536ad..e223b84 100644 --- a/src/DotNetCore.CAP/ICapSubscribe.cs +++ b/src/DotNetCore.CAP/ICapSubscribe.cs @@ -4,7 +4,7 @@ namespace DotNetCore.CAP { /// - /// An empty interface, which is used to mark the current class have a CAP methods. + /// An empty interface, which is used to mark the current class have a CAP subscriber methods. /// public interface ICapSubscribe { From b217f5bd6bada5d9849cf286a013c7575b2bb911 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Sat, 28 Apr 2018 15:54:45 +0800 Subject: [PATCH 023/223] update samples. --- .../Controllers/ValuesController.cs | 2 +- samples/Sample.Kafka.MySql/Startup.cs | 3 ++- samples/Sample.Kafka.MySql/nlog.config | 2 +- samples/Sample.RabbitMQ.MySql/AppDbContext.cs | 9 ++----- .../Controllers/ValuesController.cs | 4 +-- samples/Sample.RabbitMQ.MySql/Program.cs | 15 +++++------ .../Sample.RabbitMQ.MySql.csproj | 6 +++++ samples/Sample.RabbitMQ.MySql/nlog.config | 26 +++++++++++++++++++ 8 files changed, 47 insertions(+), 20 deletions(-) create mode 100644 samples/Sample.RabbitMQ.MySql/nlog.config diff --git a/samples/Sample.Kafka.MySql/Controllers/ValuesController.cs b/samples/Sample.Kafka.MySql/Controllers/ValuesController.cs index afcec67..6f04946 100644 --- a/samples/Sample.Kafka.MySql/Controllers/ValuesController.cs +++ b/samples/Sample.Kafka.MySql/Controllers/ValuesController.cs @@ -37,7 +37,7 @@ namespace Sample.Kafka.MySql.Controllers [CapSubscribe("xxx.xxx.test2")] public void Test2(int value) { - Console.WriteLine(value); + Console.WriteLine("Subscriber output message: " + value); } } } \ No newline at end of file diff --git a/samples/Sample.Kafka.MySql/Startup.cs b/samples/Sample.Kafka.MySql/Startup.cs index 12040d7..419e48e 100644 --- a/samples/Sample.Kafka.MySql/Startup.cs +++ b/samples/Sample.Kafka.MySql/Startup.cs @@ -1,4 +1,5 @@ -using Microsoft.AspNetCore.Builder; +using System; +using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.DependencyInjection; namespace Sample.Kafka.MySql diff --git a/samples/Sample.Kafka.MySql/nlog.config b/samples/Sample.Kafka.MySql/nlog.config index 5bfcf49..5b91105 100644 --- a/samples/Sample.Kafka.MySql/nlog.config +++ b/samples/Sample.Kafka.MySql/nlog.config @@ -7,7 +7,7 @@ internalLogFile="logs/internal-nlog.txt"> + value="---------------------------------------------------------------------------${newline}Date:${longdate} Level:${uppercase:${level}} User:${aspnet-user-identity}(${aspnet-request-ip}) Logger:${logger} URL:${aspnet-request-method} ${aspnet-request-url:IncludePort=true:IncludeQueryString=true} Action:${aspnet-mvc-action} ${newline}Message:${message} ${newline}${onexception:Exception:${exception:format=toString}}" /> diff --git a/samples/Sample.RabbitMQ.MySql/AppDbContext.cs b/samples/Sample.RabbitMQ.MySql/AppDbContext.cs index f803e26..ec83423 100644 --- a/samples/Sample.RabbitMQ.MySql/AppDbContext.cs +++ b/samples/Sample.RabbitMQ.MySql/AppDbContext.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; namespace Sample.RabbitMQ.MySql { @@ -10,8 +6,7 @@ namespace Sample.RabbitMQ.MySql { protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { - optionsBuilder.UseMySql("Server=localhost;Database=Sample.RabbitMQ.MySql;UserId=root;Password=123123;Allow User Variables=True"); - //optionsBuilder.UseMySql("Server=192.168.2.206;Database=Sample.RabbitMQ.MySql;UserId=root;Password=123123;"); + optionsBuilder.UseMySql("Server=192.168.10.110;Database=testcap;UserId=root;Password=123123;"); } } } diff --git a/samples/Sample.RabbitMQ.MySql/Controllers/ValuesController.cs b/samples/Sample.RabbitMQ.MySql/Controllers/ValuesController.cs index 7b30772..1b47e5e 100644 --- a/samples/Sample.RabbitMQ.MySql/Controllers/ValuesController.cs +++ b/samples/Sample.RabbitMQ.MySql/Controllers/ValuesController.cs @@ -21,7 +21,7 @@ namespace Sample.RabbitMQ.MySql.Controllers public IActionResult PublishMessage() { _capBus.Publish("sample.rabbitmq.mysql", DateTime.Now); - + return Ok(); } @@ -50,7 +50,7 @@ namespace Sample.RabbitMQ.MySql.Controllers [CapSubscribe("sample.rabbitmq.mysql")] public void ReceiveMessage(DateTime time) { - Console.WriteLine("[sample.rabbitmq.mysql] message received: "+ DateTime.Now.ToString() +" , sent time: " + time.ToString()); + Console.WriteLine("[sample.rabbitmq.mysql] message received: " + DateTime.Now + ",sent time: " + time); } } } diff --git a/samples/Sample.RabbitMQ.MySql/Program.cs b/samples/Sample.RabbitMQ.MySql/Program.cs index 94095e2..3cbbe15 100644 --- a/samples/Sample.RabbitMQ.MySql/Program.cs +++ b/samples/Sample.RabbitMQ.MySql/Program.cs @@ -1,12 +1,6 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore; -using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; +using NLog.Web; namespace Sample.RabbitMQ.MySql { @@ -20,6 +14,11 @@ namespace Sample.RabbitMQ.MySql public static IWebHost BuildWebHost(string[] args) => WebHost.CreateDefaultBuilder(args) .UseStartup() + .ConfigureLogging((hostingContext, builder) => + { + hostingContext.HostingEnvironment.ConfigureNLog("nlog.config"); + }) + .UseNLog() .Build(); } } diff --git a/samples/Sample.RabbitMQ.MySql/Sample.RabbitMQ.MySql.csproj b/samples/Sample.RabbitMQ.MySql/Sample.RabbitMQ.MySql.csproj index d3a3987..5c981a3 100644 --- a/samples/Sample.RabbitMQ.MySql/Sample.RabbitMQ.MySql.csproj +++ b/samples/Sample.RabbitMQ.MySql/Sample.RabbitMQ.MySql.csproj @@ -12,6 +12,7 @@ + @@ -21,5 +22,10 @@ + + + PreserveNewest + + diff --git a/samples/Sample.RabbitMQ.MySql/nlog.config b/samples/Sample.RabbitMQ.MySql/nlog.config new file mode 100644 index 0000000..bba5bea --- /dev/null +++ b/samples/Sample.RabbitMQ.MySql/nlog.config @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 96f530acd9f9da223075c710be30f96d6f07c875 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Sat, 28 Apr 2018 15:55:47 +0800 Subject: [PATCH 024/223] add logs. --- src/DotNetCore.CAP/IConsumerHandler.Default.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/DotNetCore.CAP/IConsumerHandler.Default.cs b/src/DotNetCore.CAP/IConsumerHandler.Default.cs index 8d48a37..1d441ff 100644 --- a/src/DotNetCore.CAP/IConsumerHandler.Default.cs +++ b/src/DotNetCore.CAP/IConsumerHandler.Default.cs @@ -178,6 +178,8 @@ namespace DotNetCore.CAP private (Guid, string) TracingBefore(string topic, string values) { + _logger.LogDebug("CAP received topic message:" + topic); + Guid operationId = Guid.NewGuid(); var eventData = new BrokerConsumeEventData( From 43d5e8368633b060cc93799a94ff453b985cde8e Mon Sep 17 00:00:00 2001 From: Savorboard Date: Sun, 29 Apr 2018 20:07:38 +0800 Subject: [PATCH 025/223] Add unit tests for Diagnostics. --- test/DotNetCore.CAP.Test/DiagnosticsTest.cs | 241 ++++++++++++++++++ .../FakeDiagnosticListenerObserver.cs | 71 ++++++ 2 files changed, 312 insertions(+) create mode 100644 test/DotNetCore.CAP.Test/DiagnosticsTest.cs create mode 100644 test/DotNetCore.CAP.Test/FakeDiagnosticListenerObserver.cs diff --git a/test/DotNetCore.CAP.Test/DiagnosticsTest.cs b/test/DotNetCore.CAP.Test/DiagnosticsTest.cs new file mode 100644 index 0000000..b3473a9 --- /dev/null +++ b/test/DotNetCore.CAP.Test/DiagnosticsTest.cs @@ -0,0 +1,241 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Reflection; +using System.Runtime.CompilerServices; +using DotNetCore.CAP.Diagnostics; +using DotNetCore.CAP.Internal; +using Xunit; + +namespace DotNetCore.CAP.Test +{ + + public class DiagnosticsTest + { + private static readonly DiagnosticListener s_diagnosticListener = + new DiagnosticListener(CapDiagnosticListenerExtensions.DiagnosticListenerName); + + [Fact] + public void WritePublishBeforeTest() + { + Guid operationId = Guid.NewGuid(); + + DiagnosticsWapper(() => + { + var eventData = new BrokerPublishEventData(operationId, "", "", "", "", DateTimeOffset.UtcNow); + s_diagnosticListener.WritePublishBefore(eventData); + + }, kvp => + { + if (kvp.Key.Equals(CapDiagnosticListenerExtensions.CapBeforePublish)) + { + Assert.NotNull(kvp.Value); + Assert.IsType(kvp.Value); + Assert.Equal(operationId, ((BrokerPublishEventData)kvp.Value).OperationId); + } + }); + } + + [Fact] + public void WritePublishAfterTest() + { + Guid operationId = Guid.NewGuid(); + + DiagnosticsWapper(() => + { + var eventData = new BrokerPublishEndEventData(operationId, "", "", "", "", DateTimeOffset.UtcNow, TimeSpan.FromMinutes(1)); + s_diagnosticListener.WritePublishAfter(eventData); + + }, kvp => + { + if (kvp.Key.Equals(CapDiagnosticListenerExtensions.CapAfterPublish)) + { + Assert.NotNull(kvp.Value); + Assert.IsType(kvp.Value); + Assert.Equal(operationId, ((BrokerPublishEndEventData)kvp.Value).OperationId); + Assert.Equal(TimeSpan.FromMinutes(1), ((BrokerPublishEndEventData)kvp.Value).Duration); + } + }); + } + + [Fact] + public void WritePublishErrorTest() + { + Guid operationId = Guid.NewGuid(); + var ex = new Exception("WritePublishErrorTest"); + DiagnosticsWapper(() => + { + var eventData = new BrokerPublishErrorEventData(operationId, "", "", "", "", ex, DateTimeOffset.UtcNow, default(TimeSpan)); + s_diagnosticListener.WritePublishError(eventData); + + }, kvp => + { + if (kvp.Key.Equals(CapDiagnosticListenerExtensions.CapErrorPublish)) + { + Assert.NotNull(kvp.Value); + Assert.IsType(kvp.Value); + Assert.Equal(operationId, ((BrokerPublishErrorEventData)kvp.Value).OperationId); + Assert.Equal(ex, ((BrokerPublishErrorEventData)kvp.Value).Exception); + } + }); + } + + [Fact] + public void WriteConsumeBeforeTest() + { + Guid operationId = Guid.NewGuid(); + + DiagnosticsWapper(() => + { + var eventData = new BrokerConsumeEventData(operationId, "", "", "", "", DateTimeOffset.UtcNow); + s_diagnosticListener.WriteConsumeBefore(eventData); + + }, kvp => + { + if (kvp.Key.Equals(CapDiagnosticListenerExtensions.CapBeforeConsume)) + { + Assert.NotNull(kvp.Value); + Assert.IsType(kvp.Value); + Assert.Equal(operationId, ((BrokerConsumeEventData)kvp.Value).OperationId); + } + }); + } + + [Fact] + public void WriteConsumeAfterTest() + { + Guid operationId = Guid.NewGuid(); + + DiagnosticsWapper(() => + { + var eventData = new BrokerConsumeEndEventData(operationId, "", "", "", "", DateTimeOffset.UtcNow, TimeSpan.FromMinutes(1)); + s_diagnosticListener.WriteConsumeAfter(eventData); + + }, kvp => + { + if (kvp.Key.Equals(CapDiagnosticListenerExtensions.CapAfterConsume)) + { + Assert.NotNull(kvp.Value); + Assert.IsType(kvp.Value); + Assert.Equal(operationId, ((BrokerConsumeEndEventData)kvp.Value).OperationId); + Assert.Equal(TimeSpan.FromMinutes(1), ((BrokerConsumeEndEventData)kvp.Value).Duration); + } + }); + } + + [Fact] + public void WriteConsumeErrorTest() + { + Guid operationId = Guid.NewGuid(); + var ex = new Exception("WriteConsumeErrorTest"); + DiagnosticsWapper(() => + { + var eventData = new BrokerConsumeErrorEventData(operationId, "", "", "", "", ex, DateTimeOffset.UtcNow, default(TimeSpan)); + s_diagnosticListener.WriteConsumeError(eventData); + + }, kvp => + { + if (kvp.Key.Equals(CapDiagnosticListenerExtensions.CapErrorPublish)) + { + Assert.NotNull(kvp.Value); + Assert.IsType(kvp.Value); + Assert.Equal(operationId, ((BrokerConsumeErrorEventData)kvp.Value).OperationId); + Assert.Equal(ex, ((BrokerConsumeErrorEventData)kvp.Value).Exception); + } + }); + } + + [Fact] + public void WriteSubscriberInvokeBeforeTest() + { + DiagnosticsWapper(() => + { + s_diagnosticListener.WriteSubscriberInvokeBefore(FackConsumerContext()); + + }, kvp => + { + if (kvp.Key.Equals(CapDiagnosticListenerExtensions.CapBeforeSubscriberInvoke)) + { + Assert.NotNull(kvp.Value); + Assert.IsType(kvp.Value); + } + }); + } + + [Fact] + public void WriteSubscriberInvokeAfterTest() + { + Guid operationId = Guid.NewGuid(); + + DiagnosticsWapper(() => + { + s_diagnosticListener.WriteSubscriberInvokeAfter(operationId, FackConsumerContext(), DateTimeOffset.Now, TimeSpan.FromMinutes(1)); + + }, kvp => + { + if (kvp.Key.Equals(CapDiagnosticListenerExtensions.CapAfterSubscriberInvoke)) + { + Assert.NotNull(kvp.Value); + Assert.IsType(kvp.Value); + Assert.Equal(operationId, ((SubscriberInvokeEndEventData)kvp.Value).OperationId); + + } + }); + } + + [Fact] + public void WriteSubscriberInvokeErrorTest() + { + Guid operationId = Guid.NewGuid(); + + var ex = new Exception("WriteConsumeErrorTest"); + DiagnosticsWapper(() => + { + s_diagnosticListener.WriteSubscriberInvokeError(operationId, FackConsumerContext(), ex, + DateTimeOffset.Now, TimeSpan.MaxValue); + }, kvp => + { + if (kvp.Key.Equals(CapDiagnosticListenerExtensions.CapErrorSubscriberInvoke)) + { + Assert.NotNull(kvp.Value); + Assert.IsType(kvp.Value); + Assert.Equal(operationId, ((SubscriberInvokeErrorEventData)kvp.Value).OperationId); + Assert.Equal(ex, ((SubscriberInvokeErrorEventData)kvp.Value).Exception); + } + }); + } + + private ConsumerContext FackConsumerContext() + { + //Mock description + var description = new ConsumerExecutorDescriptor + { + MethodInfo = GetType().GetMethod("WriteSubscriberInvokeAfterTest"), + Attribute = new CapSubscribeAttribute("xxx"), + ImplTypeInfo = GetType().GetTypeInfo() + }; + + //Mock messageContext + var messageContext = new MessageContext + { + Name= "Name", + Group= "Group", + Content = "Content" + }; + + return new ConsumerContext(description, messageContext); + } + + private void DiagnosticsWapper(Action operation, Action> assert, [CallerMemberName]string methodName = "") + { + FakeDiagnosticListenerObserver diagnosticListenerObserver = new FakeDiagnosticListenerObserver(assert); + + diagnosticListenerObserver.Enable(); + using (DiagnosticListener.AllListeners.Subscribe(diagnosticListenerObserver)) + { + Console.WriteLine(string.Format("Test: {0} Enabled Listeners", methodName)); + operation(); + } + } + } +} diff --git a/test/DotNetCore.CAP.Test/FakeDiagnosticListenerObserver.cs b/test/DotNetCore.CAP.Test/FakeDiagnosticListenerObserver.cs new file mode 100644 index 0000000..81db2c8 --- /dev/null +++ b/test/DotNetCore.CAP.Test/FakeDiagnosticListenerObserver.cs @@ -0,0 +1,71 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Text; +using DotNetCore.CAP.Diagnostics; + +namespace DotNetCore.CAP.Test +{ + public sealed class FakeDiagnosticListenerObserver : IObserver + { + private class FakeDiagnosticSourceWriteObserver : IObserver> + { + private readonly Action> _writeCallback; + + public FakeDiagnosticSourceWriteObserver(Action> writeCallback) + { + _writeCallback = writeCallback; + } + + public void OnCompleted() + { + } + + public void OnError(Exception error) + { + } + + public void OnNext(KeyValuePair value) + { + _writeCallback(value); + } + } + + private readonly Action> _writeCallback; + private bool _writeObserverEnabled; + + public FakeDiagnosticListenerObserver(Action> writeCallback) + { + _writeCallback = writeCallback; + } + + public void OnCompleted() + { + } + + public void OnError(Exception error) + { + } + + public void OnNext(DiagnosticListener value) + { + if (value.Name.Equals(CapDiagnosticListenerExtensions.DiagnosticListenerName)) + { + value.Subscribe(new FakeDiagnosticSourceWriteObserver(_writeCallback), IsEnabled); + } + } + + public void Enable() + { + _writeObserverEnabled = true; + } + public void Disable() + { + _writeObserverEnabled = false; + } + private bool IsEnabled(string s) + { + return _writeObserverEnabled; + } + } +} From 3a9351c22095f33cf9fe123650543367d5a52886 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Tue, 1 May 2018 14:02:39 +0800 Subject: [PATCH 026/223] removed unused files. --- .../Processor/DefaultDispatcherTest.cs | 83 ------------------- 1 file changed, 83 deletions(-) delete mode 100644 test/DotNetCore.CAP.Test/Processor/DefaultDispatcherTest.cs diff --git a/test/DotNetCore.CAP.Test/Processor/DefaultDispatcherTest.cs b/test/DotNetCore.CAP.Test/Processor/DefaultDispatcherTest.cs deleted file mode 100644 index 1f3c77e..0000000 --- a/test/DotNetCore.CAP.Test/Processor/DefaultDispatcherTest.cs +++ /dev/null @@ -1,83 +0,0 @@ -//using System; -//using System.Threading; -//using System.Threading.Tasks; -//using DotNetCore.CAP.Models; -//using DotNetCore.CAP.Processor; -//using Microsoft.Extensions.DependencyInjection; -//using Microsoft.Extensions.Options; -//using Moq; -//using Xunit; - -//namespace DotNetCore.CAP.Test -//{ -// public class DefaultDispatcherTest -// { -// private CancellationTokenSource _cancellationTokenSource; -// private ProcessingContext _context; -// private IServiceProvider _provider; -// private Mock _mockStorageConnection; - -// public DefaultDispatcherTest() -// { -// _mockStorageConnection = new Mock(); - -// _cancellationTokenSource = new CancellationTokenSource(); - -// var services = new ServiceCollection(); -// services.AddLogging(); -// services.Configure>(x => { }); -// services.AddOptions(); -// services.AddSingleton(_mockStorageConnection.Object); -// _provider = services.BuildServiceProvider(); - -// _context = new ProcessingContext(_provider, _cancellationTokenSource.Token); -// } - -// [Fact] -// public void MockTest() -// { -// Assert.NotNull(_provider.GetServices()); -// } - -// [Fact] -// public async void ProcessAsync_CancellationTokenCancelled_ThrowsImmediately() -// { -// // Arrange -// _cancellationTokenSource.Cancel(); -// var fixture = Create(); - -// // Act -// await Assert.ThrowsAsync(() => fixture.ProcessAsync(_context)); -// } - -// [Fact] -// public async Task ProcessAsync() -// { -// // Arrange -// var job = new CapPublishedMessage -// { -// }; - -// var mockFetchedJob = Mock.Get(Mock.Of(fj => fj.MessageId == 42 && fj.MessageType == MessageType.Publish)); - -// _mockStorageConnection -// .Setup(m => m.FetchNextMessageAsync()) -// .ReturnsAsync(mockFetchedJob.Object).Verifiable(); - -// _mockQueueExecutor -// .Setup(x => x.ExecuteAsync(_mockStorageConnection.Object, mockFetchedJob.Object)) -// .Returns(Task.FromResult(OperateResult.Success)); - -// var fixture = Create(); - -// // Act -// await fixture.ProcessAsync(_context); - -// // Assert -// _mockStorageConnection.VerifyAll(); -// } - -// private DefaultDispatcher Create() -// => _provider.GetService(); -// } -//} \ No newline at end of file From 8cb97a237067d0ad5243fb190c4142b02d7f92f5 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Tue, 1 May 2018 14:03:04 +0800 Subject: [PATCH 027/223] add unit tests for Helper.cs --- test/DotNetCore.CAP.Test/HelperTest.cs | 154 +++++++++++++++++++++++++ 1 file changed, 154 insertions(+) create mode 100644 test/DotNetCore.CAP.Test/HelperTest.cs diff --git a/test/DotNetCore.CAP.Test/HelperTest.cs b/test/DotNetCore.CAP.Test/HelperTest.cs new file mode 100644 index 0000000..48ee66f --- /dev/null +++ b/test/DotNetCore.CAP.Test/HelperTest.cs @@ -0,0 +1,154 @@ +using System; +using System.Reflection; +using DotNetCore.CAP.Diagnostics; +using DotNetCore.CAP.Infrastructure; +using Newtonsoft.Json.Linq; +using Xunit; + +namespace DotNetCore.CAP.Test +{ + public class HelperTest + { + + [Fact] + public void ToTimestampTest() + { + //Arrange + var time = DateTime.Parse("2018-01-01 00:00:00"); + + //Act + var result = Helper.ToTimestamp(time); + + //Assert + Assert.Equal(1514764800, result); + } + + [Fact] + public void FromTimestampTest() + { + //Arrange + var time = DateTime.Parse("2018-01-01 00:00:00"); + + //Act + var result = Helper.FromTimestamp(1514764800); + + //Assert + Assert.Equal(time, result); + } + + [Fact] + public void IsControllerTest() + { + //Arrange + var typeInfo = typeof(HomeController).GetTypeInfo(); + + //Act + var result = Helper.IsController(typeInfo); + + //Assert + Assert.True(result); + } + + [Theory] + [InlineData(typeof(string))] + [InlineData(typeof(decimal))] + [InlineData(typeof(DateTime))] + [InlineData(typeof(DateTimeOffset))] + [InlineData(typeof(Guid))] + [InlineData(typeof(TimeSpan))] + [InlineData(typeof(Uri))] + public void IsSimpleTypeTest(Type type) + { + //Act + var result = Helper.IsComplexType(type); + + //Assert + Assert.False(result); + } + + [Theory] + [InlineData(typeof(HomeController))] + [InlineData(typeof(Exception))] + [InlineData(typeof(Person))] + public void IsComplexTypeTest(Type type) + { + //Act + var result = Helper.IsComplexType(type); + + //Assert + Assert.True(result); + } + + [Fact] + public void AddExceptionPropertyTest() + { + //Arrange + var json = "{}"; + var exception = new Exception("Test Exception Message") + { + Source = "Test Source", + InnerException = { } + }; + + var expected = new + { + ExceptionMessage = new + { + Source = "Test Source", + Message = "Test Exception Message", + InnerMessage = new { } + } + }; + + //Act + var result = Helper.AddExceptionProperty(json, exception); + + //Assert + var jObj = JObject.Parse(result); + Assert.Equal(jObj["ExceptionMessage"]["Source"].Value(), expected.ExceptionMessage.Source); + Assert.Equal(jObj["ExceptionMessage"]["Message"].Value(), expected.ExceptionMessage.Message); + } + + [Theory] + [InlineData("10.0.0.1")] + [InlineData("172.16.0.1")] + [InlineData("192.168.1.1")] + public void IsInnerIPTest(string ipAddress) + { + Assert.True(Helper.IsInnerIP(ipAddress)); + } + + [Fact] + public void AddTracingHeaderPropertyTest() + { + //Arrange + var json = "{}"; + var header = new TracingHeaders { { "key", "value" } }; + + //Act + var result = Helper.AddTracingHeaderProperty(json, header); + + //Assert + var expected = "{\"TracingHeaders\":{\"key\":\"value\"}}"; + Assert.Equal(expected, result); + } + + [Fact] + public void TryExtractTracingHeadersTest() + { + //Arrange + var json = "{\"TracingHeaders\":{\"key\":\"value\"}}"; + TracingHeaders header = null; + string removedHeadersJson = ""; + + //Act + var result = Helper.TryExtractTracingHeaders(json, out header, out removedHeadersJson); + + //Assert + Assert.True(result); + Assert.NotNull(header); + Assert.Single(header); + Assert.Equal("{}", removedHeadersJson); + } + } +} From d9f0cfcec3225623458fd7c2865ea3e11df45d94 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Tue, 1 May 2018 14:05:17 +0800 Subject: [PATCH 028/223] modify version to 2.2.3 --- build/version.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/version.props b/build/version.props index ae5b04f..dcd0b81 100644 --- a/build/version.props +++ b/build/version.props @@ -2,7 +2,7 @@ 2 2 - 2 + 3 $(VersionMajor).$(VersionMinor).$(VersionPatch) From 46c8dc89486a81c46d10deb9382fa381200557dd Mon Sep 17 00:00:00 2001 From: Savorboard Date: Fri, 4 May 2018 19:29:15 +0800 Subject: [PATCH 029/223] Modify logs content. --- src/DotNetCore.CAP/ISubscribeExecutor.Default.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/DotNetCore.CAP/ISubscribeExecutor.Default.cs b/src/DotNetCore.CAP/ISubscribeExecutor.Default.cs index 92f988e..fe03a15 100644 --- a/src/DotNetCore.CAP/ISubscribeExecutor.Default.cs +++ b/src/DotNetCore.CAP/ISubscribeExecutor.Default.cs @@ -72,7 +72,7 @@ namespace DotNetCore.CAP } catch (Exception ex) { - _logger.ExceptionOccuredWhileExecuting(message.Name, ex); + _logger.LogError(ex, $"An exception occurred while executing the subscription method. Topic:{message.Name}, Id:{message.Id}"); await SetFailedState(message, ex, out bool stillRetry); if (stillRetry) From 9265cf752b03f06dcc423fc07447b49b337e7167 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Fri, 4 May 2018 19:29:37 +0800 Subject: [PATCH 030/223] refactor and modify logs content. --- src/DotNetCore.CAP/Processor/IDispatcher.Default.cs | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/DotNetCore.CAP/Processor/IDispatcher.Default.cs b/src/DotNetCore.CAP/Processor/IDispatcher.Default.cs index 95e9cdb..433c01b 100644 --- a/src/DotNetCore.CAP/Processor/IDispatcher.Default.cs +++ b/src/DotNetCore.CAP/Processor/IDispatcher.Default.cs @@ -65,7 +65,7 @@ namespace DotNetCore.CAP.Processor } catch (Exception ex) { - _logger.ExceptionOccuredWhileExecuting(message.Name, ex); + _logger.LogError(ex, $"An exception occurred when sending a message to the MQ. Topic:{message.Name}, Id:{message.Id}"); } } } @@ -82,14 +82,7 @@ namespace DotNetCore.CAP.Processor { foreach (var message in _receivedMessageQueue.GetConsumingEnumerable(_cts.Token)) { - try - { - _executor.ExecuteAsync(message); - } - catch (Exception ex) - { - _logger.ExceptionOccuredWhileExecuting(message.Name, ex); - } + _executor.ExecuteAsync(message); } } catch (OperationCanceledException) From 6e397241cc494ea0cca9f2d1792304937601cb33 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Fri, 4 May 2018 19:29:52 +0800 Subject: [PATCH 031/223] remove some log items. --- src/DotNetCore.CAP/LoggerExtensions.cs | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/src/DotNetCore.CAP/LoggerExtensions.cs b/src/DotNetCore.CAP/LoggerExtensions.cs index a0096e6..36b3570 100644 --- a/src/DotNetCore.CAP/LoggerExtensions.cs +++ b/src/DotNetCore.CAP/LoggerExtensions.cs @@ -18,7 +18,6 @@ namespace DotNetCore.CAP private static readonly Action _consumerFailedWillRetry; private static readonly Action _consumerExecuted; private static readonly Action _senderRetrying; - private static readonly Action _exceptionOccuredWhileExecuting; private static readonly Action _messageHasBeenSent; private static readonly Action _messagePublishException; @@ -73,12 +72,7 @@ namespace DotNetCore.CAP _consumerFailedWillRetry = LoggerMessage.Define( LogLevel.Warning, 2, - "Consumer failed to execute. Will retry."); - - _exceptionOccuredWhileExecuting = LoggerMessage.Define( - LogLevel.Error, - 6, - "An exception occured while trying to store a message. message id: {MessageId}"); + "Consumer failed to execute. Will retry."); _messageHasBeenSent = LoggerMessage.Define( LogLevel.Debug, @@ -136,11 +130,6 @@ namespace DotNetCore.CAP _expectedOperationCanceledException(logger, ex.Message, ex); } - public static void ExceptionOccuredWhileExecuting(this ILogger logger, string messageId, Exception ex) - { - _exceptionOccuredWhileExecuting(logger, messageId, ex); - } - public static void ModelBinderFormattingException(this ILogger logger, string methodName, string parameterName, string content, Exception ex) { From 67c7ec976671f7ecdecffa7a51fbad3e9a4c59cf Mon Sep 17 00:00:00 2001 From: AlexLEWIS Date: Sun, 6 May 2018 09:49:14 +0800 Subject: [PATCH 032/223] Update badge to NCC (#128) * Update badge of NCC * Fixed a markdown syntax error in readme file --- README.md | 2 +- README.zh-cn.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index cd25182..4dbadde 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![AppVeyor](https://ci.appveyor.com/api/projects/status/4mpe0tbu7n126vyw?svg=true)](https://ci.appveyor.com/project/yuleyule66/cap) [![NuGet](https://img.shields.io/nuget/v/DotNetCore.CAP.svg)](https://www.nuget.org/packages/DotNetCore.CAP/) [![NuGet Preview](https://img.shields.io/nuget/vpre/DotNetCore.CAP.svg?label=nuget-pre)](https://www.nuget.org/packages/DotNetCore.CAP/) -[![Member project of .NET China Foundation](https://img.shields.io/badge/member_project_of-.NET_CHINA-red.svg?style=flat&colorB=9E20C8)](https://github.com/dotnetcore) +[![Member project of .NET Core Community](https://img.shields.io/badge/member%20project%20of-NCC-9e20c9.svg)](https://github.com/dotnetcore) [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/dotnetcore/CAP/master/LICENSE.txt) CAP is a library based on .Net standard, which is a solution to deal with distributed transactions, also has the function of EventBus, it is lightweight, easy to use, and efficiently. diff --git a/README.zh-cn.md b/README.zh-cn.md index 8ceb14b..639e4c5 100644 --- a/README.zh-cn.md +++ b/README.zh-cn.md @@ -3,7 +3,7 @@ [![AppVeyor](https://ci.appveyor.com/api/projects/status/4mpe0tbu7n126vyw?svg=true)](https://ci.appveyor.com/project/yuleyule66/cap) [![NuGet](https://img.shields.io/nuget/v/DotNetCore.CAP.svg)](https://www.nuget.org/packages/DotNetCore.CAP/) [![NuGet Preview](https://img.shields.io/nuget/vpre/DotNetCore.CAP.svg?label=nuget-pre)](https://www.nuget.org/packages/DotNetCore.CAP/) -[![Member project of .NET China Foundation](https://img.shields.io/badge/member_project_of-.NET_CHINA-red.svg?style=flat&colorB=9E20C8)](https://github.com/dotnetcore) +[![Member project of .NET Core Community](https://img.shields.io/badge/member%20project%20of-NCC-9e20c9.svg)](https://github.com/dotnetcore) [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/dotnetcore/CAP/master/LICENSE.txt) CAP 是一个基于 .NET Standard çš„ C# 库,它是一ç§å¤„ç†åˆ†å¸ƒå¼äº‹åŠ¡çš„解决方案,åŒæ ·å…·æœ‰ EventBus 的功能,它具有轻é‡çº§ã€æ˜“使用ã€é«˜æ€§èƒ½ç­‰ç‰¹ç‚¹ã€‚ From 83980da13a28886b05e33b3c17df6481135b33ac Mon Sep 17 00:00:00 2001 From: Savorboard Date: Mon, 7 May 2018 17:10:08 +0800 Subject: [PATCH 033/223] add github issue template. --- .github/ISSUE_TEMPLATE | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE diff --git a/.github/ISSUE_TEMPLATE b/.github/ISSUE_TEMPLATE new file mode 100644 index 0000000..6cff5d2 --- /dev/null +++ b/.github/ISSUE_TEMPLATE @@ -0,0 +1,40 @@ + + +Please answer these questions before submitting your issue. + +- Why do you submit this issue? +- [ ] Question or discussion +- [ ] Bug +- [ ] Requirement +- [ ] Feature or performance improvement + +___ +### Question +- What do you want to know? + +___ +### Bug +- Which version of SkyWalking, OS and JRE? + +- Which company or project? + +- What happen? +If possible, provide a way for reproducing the error. e.g. demo application, component version. + +___ +### Requirement or improvement +- Please describe about your requirements or improvement suggestions. \ No newline at end of file From 0096f95843d1ee1f16b60de46c7b1d61a4a6a12f Mon Sep 17 00:00:00 2001 From: Savorboard Date: Tue, 8 May 2018 09:31:14 +0800 Subject: [PATCH 034/223] update issue template. --- .github/ISSUE_TEMPLATE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE b/.github/ISSUE_TEMPLATE index 6cff5d2..66cdc24 100644 --- a/.github/ISSUE_TEMPLATE +++ b/.github/ISSUE_TEMPLATE @@ -28,7 +28,7 @@ ___ ___ ### Bug -- Which version of SkyWalking, OS and JRE? +- Which version of CAP, OS and .NET Core? - Which company or project? From cd3a595ea63972b189ff517f22f98207e43c782b Mon Sep 17 00:00:00 2001 From: Savorboard Date: Wed, 9 May 2018 20:59:07 +0800 Subject: [PATCH 035/223] Fixed exception thrown when terminate the program with Ctrl+C. (#130) --- CAP.sln | 1 + .../Processor/IProcessingServer.Cap.cs | 15 ++++++++++----- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/CAP.sln b/CAP.sln index 952416d..828d82a 100644 --- a/CAP.sln +++ b/CAP.sln @@ -17,6 +17,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution CHANGELOG.md = CHANGELOG.md CODE_OF_CONDUCT.md = CODE_OF_CONDUCT.md ConfigureMSDTC.ps1 = ConfigureMSDTC.ps1 + .github\ISSUE_TEMPLATE = .github\ISSUE_TEMPLATE LICENSE.txt = LICENSE.txt README.md = README.md README.zh-cn.md = README.zh-cn.md diff --git a/src/DotNetCore.CAP/Processor/IProcessingServer.Cap.cs b/src/DotNetCore.CAP/Processor/IProcessingServer.Cap.cs index b9d8802..551556e 100644 --- a/src/DotNetCore.CAP/Processor/IProcessingServer.Cap.cs +++ b/src/DotNetCore.CAP/Processor/IProcessingServer.Cap.cs @@ -57,13 +57,14 @@ namespace DotNetCore.CAP.Processor return; } - _disposed = true; - - _logger.ServerShuttingDown(); - _cts.Cancel(); try { - _compositeTask.Wait((int) TimeSpan.FromSeconds(10).TotalMilliseconds); + _disposed = true; + + _logger.ServerShuttingDown(); + _cts.Cancel(); + + _compositeTask?.Wait((int)TimeSpan.FromSeconds(10).TotalMilliseconds); } catch (AggregateException ex) { @@ -73,6 +74,10 @@ namespace DotNetCore.CAP.Processor _logger.ExpectedOperationCanceledException(innerEx); } } + catch (Exception ex) + { + _logger.LogWarning(ex, "An exception was occured when disposing..."); + } } private IProcessor InfiniteRetry(IProcessor inner) From b68e82d55a86d5052413612a95a87a67775209d0 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Thu, 17 May 2018 14:28:45 +0800 Subject: [PATCH 036/223] update samples. --- samples/Sample.Kafka.MySql/Sample.Kafka.MySql.csproj | 3 +-- samples/Sample.Kafka.MySql/Startup.cs | 4 ++-- samples/Sample.RabbitMQ.MySql/AppDbContext.cs | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/samples/Sample.Kafka.MySql/Sample.Kafka.MySql.csproj b/samples/Sample.Kafka.MySql/Sample.Kafka.MySql.csproj index 88c0c1d..bf80ce5 100644 --- a/samples/Sample.Kafka.MySql/Sample.Kafka.MySql.csproj +++ b/samples/Sample.Kafka.MySql/Sample.Kafka.MySql.csproj @@ -9,9 +9,8 @@ - + - diff --git a/samples/Sample.Kafka.MySql/Startup.cs b/samples/Sample.Kafka.MySql/Startup.cs index 419e48e..59c4636 100644 --- a/samples/Sample.Kafka.MySql/Startup.cs +++ b/samples/Sample.Kafka.MySql/Startup.cs @@ -10,8 +10,8 @@ namespace Sample.Kafka.MySql { services.AddCap(x => { - x.UseMySql("Server=192.168.10.110;Database=testcap;UserId=root;Password=123123;"); - x.UseKafka("192.168.10.110:9092"); + x.UseMySql("Server=localhost;Database=testcap;UserId=root;Password=123123;"); + x.UseKafka("localhost:9092"); x.UseDashboard(); }); diff --git a/samples/Sample.RabbitMQ.MySql/AppDbContext.cs b/samples/Sample.RabbitMQ.MySql/AppDbContext.cs index ec83423..d960a8f 100644 --- a/samples/Sample.RabbitMQ.MySql/AppDbContext.cs +++ b/samples/Sample.RabbitMQ.MySql/AppDbContext.cs @@ -6,7 +6,7 @@ namespace Sample.RabbitMQ.MySql { protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { - optionsBuilder.UseMySql("Server=192.168.10.110;Database=testcap;UserId=root;Password=123123;"); + optionsBuilder.UseMySql("Server=localhost;Database=testcap;UserId=root;Password=123123;"); } } } From 3120413132b8b537bd3d945bd72b9d42c4d7e4cc Mon Sep 17 00:00:00 2001 From: Savorboard Date: Thu, 17 May 2018 14:29:18 +0800 Subject: [PATCH 037/223] upgrade MySqlConnector nuget package. --- src/DotNetCore.CAP.MySql/DotNetCore.CAP.MySql.csproj | 2 +- test/DotNetCore.CAP.MySql.Test/DotNetCore.CAP.MySql.Test.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/DotNetCore.CAP.MySql/DotNetCore.CAP.MySql.csproj b/src/DotNetCore.CAP.MySql/DotNetCore.CAP.MySql.csproj index 38482c6..1e85be1 100644 --- a/src/DotNetCore.CAP.MySql/DotNetCore.CAP.MySql.csproj +++ b/src/DotNetCore.CAP.MySql/DotNetCore.CAP.MySql.csproj @@ -17,7 +17,7 @@ - + diff --git a/test/DotNetCore.CAP.MySql.Test/DotNetCore.CAP.MySql.Test.csproj b/test/DotNetCore.CAP.MySql.Test/DotNetCore.CAP.MySql.Test.csproj index 31710c4..f96229e 100644 --- a/test/DotNetCore.CAP.MySql.Test/DotNetCore.CAP.MySql.Test.csproj +++ b/test/DotNetCore.CAP.MySql.Test/DotNetCore.CAP.MySql.Test.csproj @@ -16,7 +16,7 @@ - + From 7e560ec7cfcfa3bebbd99ed1df34f29109619632 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Tue, 22 May 2018 19:36:26 +0800 Subject: [PATCH 038/223] optimized logging content. --- src/DotNetCore.CAP/LoggerExtensions.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/DotNetCore.CAP/LoggerExtensions.cs b/src/DotNetCore.CAP/LoggerExtensions.cs index 36b3570..7ff8cf4 100644 --- a/src/DotNetCore.CAP/LoggerExtensions.cs +++ b/src/DotNetCore.CAP/LoggerExtensions.cs @@ -26,7 +26,7 @@ namespace DotNetCore.CAP _serverStarting = LoggerMessage.Define( LogLevel.Debug, 1, - "Starting the processing server."); + "Starting the processors."); _processorsStartingError = LoggerMessage.Define( LogLevel.Error, @@ -36,7 +36,7 @@ namespace DotNetCore.CAP _serverShuttingDown = LoggerMessage.Define( LogLevel.Information, 2, - "Shutting down the processing server..."); + "Processors is shutting down..."); _expectedOperationCanceledException = LoggerMessage.Define( LogLevel.Warning, @@ -60,7 +60,7 @@ namespace DotNetCore.CAP ); _senderRetrying = LoggerMessage.Define( - LogLevel.Debug, + LogLevel.Information, 3, "The {Retries}th retrying send a message failed. message id: {MessageId} "); @@ -72,7 +72,7 @@ namespace DotNetCore.CAP _consumerFailedWillRetry = LoggerMessage.Define( LogLevel.Warning, 2, - "Consumer failed to execute. Will retry."); + "Consumer failed to execute, it will be retry."); _messageHasBeenSent = LoggerMessage.Define( LogLevel.Debug, From 02a93e90a36ee022b6efabe49c8a85cf7c1bf94f Mon Sep 17 00:00:00 2001 From: Savorboard Date: Mon, 21 May 2018 19:37:00 +0800 Subject: [PATCH 039/223] add cap started and shotdown log. --- src/DotNetCore.CAP/IBootstrapper.Default.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/DotNetCore.CAP/IBootstrapper.Default.cs b/src/DotNetCore.CAP/IBootstrapper.Default.cs index d89986a..9af8f2e 100644 --- a/src/DotNetCore.CAP/IBootstrapper.Default.cs +++ b/src/DotNetCore.CAP/IBootstrapper.Default.cs @@ -59,6 +59,8 @@ namespace DotNetCore.CAP private async Task BootstrapTaskAsync() { + _logger.LogInformation("### CAP starting..."); + await Storage.InitializeAsync(_cts.Token); if (_cts.IsCancellationRequested) @@ -83,6 +85,8 @@ namespace DotNetCore.CAP _ctsRegistration.Dispose(); _cts.Dispose(); + + _logger.LogInformation("### CAP started!"); } protected virtual Task BootstrapCoreAsync() From c07ff1330c6070716f9e3dd7c5bc581fe0b1b7a0 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Sun, 20 May 2018 19:37:28 +0800 Subject: [PATCH 040/223] rename processor. --- src/DotNetCore.CAP/Processor/IProcessingServer.Cap.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/DotNetCore.CAP/Processor/IProcessingServer.Cap.cs b/src/DotNetCore.CAP/Processor/IProcessingServer.Cap.cs index 551556e..a05c302 100644 --- a/src/DotNetCore.CAP/Processor/IProcessingServer.Cap.cs +++ b/src/DotNetCore.CAP/Processor/IProcessingServer.Cap.cs @@ -76,7 +76,11 @@ namespace DotNetCore.CAP.Processor } catch (Exception ex) { - _logger.LogWarning(ex, "An exception was occured when disposing..."); + _logger.LogWarning(ex, "An exception was occured when disposing."); + } + finally + { + _logger.LogInformation("### CAP shutdown!"); } } @@ -90,7 +94,7 @@ namespace DotNetCore.CAP.Processor var returnedProcessors = new List { _provider.GetRequiredService(), - _provider.GetRequiredService() + _provider.GetRequiredService() }; return returnedProcessors.ToArray(); From f608c33e11aaf1eb68405316bfa8b4824f2b18c0 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Sat, 19 May 2018 19:38:00 +0800 Subject: [PATCH 041/223] rename mysql processor --- src/DotNetCore.CAP.MySql/CAP.MySqlCapOptionsExtension.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/DotNetCore.CAP.MySql/CAP.MySqlCapOptionsExtension.cs b/src/DotNetCore.CAP.MySql/CAP.MySqlCapOptionsExtension.cs index 0bf0c2b..0c58540 100644 --- a/src/DotNetCore.CAP.MySql/CAP.MySqlCapOptionsExtension.cs +++ b/src/DotNetCore.CAP.MySql/CAP.MySqlCapOptionsExtension.cs @@ -26,7 +26,7 @@ namespace DotNetCore.CAP services.AddSingleton(); services.AddScoped(); services.AddScoped(); - services.AddTransient(); + services.AddTransient(); AddSingletionMySqlOptions(services); } From 3b98426b9c91a60cddbb53c98cf210546a81828a Mon Sep 17 00:00:00 2001 From: Savorboard Date: Sun, 13 May 2018 19:38:27 +0800 Subject: [PATCH 042/223] rename postgresql processor. --- .../CAP.PostgreSqlCapOptionsExtension.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/DotNetCore.CAP.PostgreSql/CAP.PostgreSqlCapOptionsExtension.cs b/src/DotNetCore.CAP.PostgreSql/CAP.PostgreSqlCapOptionsExtension.cs index 62af675..edc1729 100644 --- a/src/DotNetCore.CAP.PostgreSql/CAP.PostgreSqlCapOptionsExtension.cs +++ b/src/DotNetCore.CAP.PostgreSql/CAP.PostgreSqlCapOptionsExtension.cs @@ -26,7 +26,7 @@ namespace DotNetCore.CAP services.AddSingleton(); services.AddScoped(); services.AddScoped(); - services.AddTransient(); + services.AddTransient(); AddSingletonPostgreSqlOptions(services); } From ee3656b746112b5539babefa35210a0a41ac081c Mon Sep 17 00:00:00 2001 From: Savorboard Date: Mon, 14 May 2018 19:38:49 +0800 Subject: [PATCH 043/223] rename sqlserver processor --- .../CAP.SqlServerCapOptionsExtension.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/DotNetCore.CAP.SqlServer/CAP.SqlServerCapOptionsExtension.cs b/src/DotNetCore.CAP.SqlServer/CAP.SqlServerCapOptionsExtension.cs index d536647..91e04f7 100644 --- a/src/DotNetCore.CAP.SqlServer/CAP.SqlServerCapOptionsExtension.cs +++ b/src/DotNetCore.CAP.SqlServer/CAP.SqlServerCapOptionsExtension.cs @@ -26,7 +26,7 @@ namespace DotNetCore.CAP services.AddSingleton(); services.AddScoped(); services.AddScoped(); - services.AddTransient(); + services.AddTransient(); AddSqlServerOptions(services); } From e3cb3f2fc46237bbc3b091c5acd632f904dd42fd Mon Sep 17 00:00:00 2001 From: Savorboard Date: Fri, 6 Apr 2018 19:40:00 +0800 Subject: [PATCH 044/223] rename IAdditionalProcessor to ICollectProcessor --- .../Processor/{IAdditionalProcessor.cs => ICollectProcessor.cs} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename src/DotNetCore.CAP/Processor/{IAdditionalProcessor.cs => ICollectProcessor.cs} (78%) diff --git a/src/DotNetCore.CAP/Processor/IAdditionalProcessor.cs b/src/DotNetCore.CAP/Processor/ICollectProcessor.cs similarity index 78% rename from src/DotNetCore.CAP/Processor/IAdditionalProcessor.cs rename to src/DotNetCore.CAP/Processor/ICollectProcessor.cs index 51fc1c3..9fd2625 100644 --- a/src/DotNetCore.CAP/Processor/IAdditionalProcessor.cs +++ b/src/DotNetCore.CAP/Processor/ICollectProcessor.cs @@ -3,7 +3,7 @@ namespace DotNetCore.CAP.Processor { - public interface IAdditionalProcessor : IProcessor + public interface ICollectProcessor : IProcessor { } } \ No newline at end of file From d28c2082c4653e9c1e81d2ac2665c3ca8c51c283 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Sun, 8 Apr 2018 18:14:50 +0800 Subject: [PATCH 045/223] support event data for Diagnostics. --- .../Abstractions/CapPublisherBase.cs | 35 ++- src/DotNetCore.CAP/DotNetCore.CAP.csproj | 1 + .../IConsumerHandler.Default.cs | 40 ++- .../IPublishMessageSender.Base.cs | 28 +- .../ISubscribeExecutor.Default.cs | 18 +- .../CapDiagnosticListenerExtensions.cs | 250 ++++++++++++++++++ 6 files changed, 336 insertions(+), 36 deletions(-) create mode 100644 src/DotNetCore.CAP/Internal/CapDiagnosticListenerExtensions.cs diff --git a/src/DotNetCore.CAP/Abstractions/CapPublisherBase.cs b/src/DotNetCore.CAP/Abstractions/CapPublisherBase.cs index b7231dc..e3167d8 100644 --- a/src/DotNetCore.CAP/Abstractions/CapPublisherBase.cs +++ b/src/DotNetCore.CAP/Abstractions/CapPublisherBase.cs @@ -3,8 +3,10 @@ using System; using System.Data; +using System.Diagnostics; using System.Threading.Tasks; using DotNetCore.CAP.Infrastructure; +using DotNetCore.CAP.Internal; using DotNetCore.CAP.Models; using Microsoft.Extensions.Logging; @@ -15,6 +17,11 @@ namespace DotNetCore.CAP.Abstractions private readonly IDispatcher _dispatcher; private readonly ILogger _logger; + // diagnostics listener + // ReSharper disable once InconsistentNaming + private static readonly DiagnosticListener s_diagnosticListener = + new DiagnosticListener(CapDiagnosticListenerExtensions.DiagnosticListenerName); + protected CapPublisherBase(ILogger logger, IDispatcher dispatcher) { _logger = logger; @@ -75,13 +82,13 @@ namespace DotNetCore.CAP.Abstractions protected virtual string Serialize(T obj, string callbackName = null) { - var packer = (IMessagePacker) ServiceProvider.GetService(typeof(IMessagePacker)); + var packer = (IMessagePacker)ServiceProvider.GetService(typeof(IMessagePacker)); string content; if (obj != null) { if (Helper.IsComplexType(obj.GetType())) { - var serializer = (IContentSerializer) ServiceProvider.GetService(typeof(IContentSerializer)); + var serializer = (IContentSerializer)ServiceProvider.GetService(typeof(IContentSerializer)); content = serializer.Serialize(obj); } else @@ -179,16 +186,20 @@ namespace DotNetCore.CAP.Abstractions private void PublishWithTrans(string name, T contentObj, string callbackName = null) { - try + Guid operationId = default(Guid); + + var content = Serialize(contentObj, callbackName); + + var message = new CapPublishedMessage { - var content = Serialize(contentObj, callbackName); + Name = name, + Content = content, + StatusName = StatusName.Scheduled + }; - var message = new CapPublishedMessage - { - Name = name, - Content = content, - StatusName = StatusName.Scheduled - }; + try + { + operationId = s_diagnosticListener.WritePublishMessageStoreBefore(message); var id = Execute(DbConnection, DbTransaction, message); @@ -197,15 +208,15 @@ namespace DotNetCore.CAP.Abstractions if (id > 0) { _logger.LogInformation($"message [{message}] has been persisted in the database."); - + s_diagnosticListener.WritePublishMessageStoreAfter(operationId, message); message.Id = id; - Enqueue(message); } } catch (Exception e) { _logger.LogError("An exception was occurred when publish message. exception message:" + e.Message, e); + s_diagnosticListener.WritePublishMessageStoreError(operationId, message, e); Console.WriteLine(e); throw; } diff --git a/src/DotNetCore.CAP/DotNetCore.CAP.csproj b/src/DotNetCore.CAP/DotNetCore.CAP.csproj index 8bf4189..464a2a5 100644 --- a/src/DotNetCore.CAP/DotNetCore.CAP.csproj +++ b/src/DotNetCore.CAP/DotNetCore.CAP.csproj @@ -55,6 +55,7 @@ + diff --git a/src/DotNetCore.CAP/IConsumerHandler.Default.cs b/src/DotNetCore.CAP/IConsumerHandler.Default.cs index 86baecc..b9bbd2d 100644 --- a/src/DotNetCore.CAP/IConsumerHandler.Default.cs +++ b/src/DotNetCore.CAP/IConsumerHandler.Default.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. See License.txt in the project root for license information. using System; +using System.Diagnostics; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -25,6 +26,11 @@ namespace DotNetCore.CAP private Task _compositeTask; private bool _disposed; + // diagnostics listener + // ReSharper disable once InconsistentNaming + private static readonly DiagnosticListener s_diagnosticListener = + new DiagnosticListener(CapDiagnosticListenerExtensions.DiagnosticListenerName); + public ConsumerHandler(IConsumerClientFactory consumerClientFactory, IDispatcher dispatcher, IStorageConnection connection, @@ -91,21 +97,34 @@ namespace DotNetCore.CAP private void RegisterMessageProcessor(IConsumerClient client) { - client.OnMessageReceived += (sender, message) => + client.OnMessageReceived += (sender, messageContext) => { + Guid operationId = default(Guid); + + var receivedMessage = new CapReceivedMessage(messageContext) + { + StatusName = StatusName.Scheduled + }; + try { - var storedMessage = StoreMessage(message); + operationId = s_diagnosticListener.WriteReceiveMessageStoreBefore(receivedMessage); + + StoreMessage(receivedMessage); client.Commit(); - _dispatcher.EnqueueToExecute(storedMessage); + s_diagnosticListener.WriteReceiveMessageStoreAfter(operationId, receivedMessage); + + _dispatcher.EnqueueToExecute(receivedMessage); } catch (Exception e) { - _logger.LogError(e, "An exception occurred when storage received message. Message:'{0}'.", - message); + _logger.LogError(e, "An exception occurred when storage received message. Message:'{0}'.", messageContext); + client.Reject(); + + s_diagnosticListener.WriteReceiveMessageStoreError(operationId, receivedMessage, e); } }; @@ -139,15 +158,12 @@ namespace DotNetCore.CAP } } - private CapReceivedMessage StoreMessage(MessageContext messageContext) + private void StoreMessage(CapReceivedMessage receivedMessage) { - var receivedMessage = new CapReceivedMessage(messageContext) - { - StatusName = StatusName.Scheduled - }; - var id = _connection.StoreReceivedMessageAsync(receivedMessage).GetAwaiter().GetResult(); + var id = _connection.StoreReceivedMessageAsync(receivedMessage) + .GetAwaiter().GetResult(); + receivedMessage.Id = id; - return receivedMessage; } } } \ No newline at end of file diff --git a/src/DotNetCore.CAP/IPublishMessageSender.Base.cs b/src/DotNetCore.CAP/IPublishMessageSender.Base.cs index d57fc8c..31c1f2a 100644 --- a/src/DotNetCore.CAP/IPublishMessageSender.Base.cs +++ b/src/DotNetCore.CAP/IPublishMessageSender.Base.cs @@ -20,6 +20,11 @@ namespace DotNetCore.CAP private readonly CapOptions _options; private readonly IStateChanger _stateChanger; + // diagnostics listener + // ReSharper disable once InconsistentNaming + private static readonly DiagnosticListener s_diagnosticListener = + new DiagnosticListener(CapDiagnosticListenerExtensions.DiagnosticListenerName); + protected BasePublishMessageSender( ILogger logger, CapOptions options, @@ -37,6 +42,7 @@ namespace DotNetCore.CAP public async Task SendAsync(CapPublishedMessage message) { var sp = Stopwatch.StartNew(); + var operationId = s_diagnosticListener.WritePublishBefore(message); var result = await PublishAsync(message.Name, message.Content); @@ -45,22 +51,26 @@ namespace DotNetCore.CAP if (result.Succeeded) { await SetSuccessfulState(message); + + s_diagnosticListener.WritePublishAfter(operationId, message); _logger.MessageHasBeenSent(sp.Elapsed.TotalSeconds); return OperateResult.Success; } - - _logger.MessagePublishException(message.Id, result.Exception); - - await SetFailedState(message, result.Exception, out bool stillRetry); - - if (stillRetry) + else { - _logger.SenderRetrying(3); + s_diagnosticListener.WritePublishError(operationId, message, result.Exception); + _logger.MessagePublishException(message.Id, result.Exception); - await SendAsync(message); + await SetFailedState(message, result.Exception, out bool stillRetry); + if (stillRetry) + { + _logger.SenderRetrying(3); + + await SendAsync(message); + } + return OperateResult.Failed(result.Exception); } - return OperateResult.Failed(result.Exception); } private static bool UpdateMessageForRetryAsync(CapPublishedMessage message) diff --git a/src/DotNetCore.CAP/ISubscribeExecutor.Default.cs b/src/DotNetCore.CAP/ISubscribeExecutor.Default.cs index 0ea2b13..9133863 100644 --- a/src/DotNetCore.CAP/ISubscribeExecutor.Default.cs +++ b/src/DotNetCore.CAP/ISubscribeExecutor.Default.cs @@ -18,10 +18,14 @@ namespace DotNetCore.CAP private readonly ICallbackMessageSender _callbackMessageSender; private readonly IStorageConnection _connection; private readonly ILogger _logger; + private readonly IStateChanger _stateChanger; private readonly CapOptions _options; - private readonly MethodMatcherCache _selector; - private readonly IStateChanger _stateChanger; + + // diagnostics listener + // ReSharper disable once InconsistentNaming + private static readonly DiagnosticListener s_diagnosticListener = + new DiagnosticListener(CapDiagnosticListenerExtensions.DiagnosticListenerName); public DefaultSubscriberExecutor( ILogger logger, @@ -139,12 +143,18 @@ namespace DotNetCore.CAP var error = $"message can not be found subscriber, Message:{receivedMessage},\r\n see: https://github.com/dotnetcore/CAP/issues/63"; throw new SubscriberNotFoundException(error); } + + Guid operationId = default(Guid); + var consumerContext = new ConsumerContext(executor, receivedMessage.ToMessageContext()); + try { - var consumerContext = new ConsumerContext(executor, receivedMessage.ToMessageContext()); + operationId = s_diagnosticListener.WriteConsumerInvokeBefore(consumerContext); var ret = await Invoker.InvokeAsync(consumerContext); + s_diagnosticListener.WriteConsumerInvokeAfter(operationId,consumerContext); + if (!string.IsNullOrEmpty(ret.CallbackName)) { await _callbackMessageSender.SendAsync(ret.MessageId, ret.CallbackName, ret.Result); @@ -152,6 +162,8 @@ namespace DotNetCore.CAP } catch (Exception ex) { + s_diagnosticListener.WriteConsumerInvokeError(operationId, consumerContext, ex); + throw new SubscriberExecutionFailedException(ex.Message, ex); } } diff --git a/src/DotNetCore.CAP/Internal/CapDiagnosticListenerExtensions.cs b/src/DotNetCore.CAP/Internal/CapDiagnosticListenerExtensions.cs new file mode 100644 index 0000000..3f5999e --- /dev/null +++ b/src/DotNetCore.CAP/Internal/CapDiagnosticListenerExtensions.cs @@ -0,0 +1,250 @@ +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using DotNetCore.CAP.Models; + +namespace DotNetCore.CAP.Internal +{ + /// + /// Extension methods on the DiagnosticListener class to log CAP data + /// + internal static class CapDiagnosticListenerExtensions + { + public const string DiagnosticListenerName = "CapDiagnosticListener"; + + private const string CapPrefix = "DotNetCore.CAP."; + + public const string CapBeforePublishMessageStore = CapPrefix + nameof(WritePublishMessageStoreBefore); + public const string CapAfterPublishMessageStore = CapPrefix + nameof(WritePublishMessageStoreAfter); + public const string CapErrorPublishMessageStore = CapPrefix + nameof(WritePublishMessageStoreError); + + public const string CapBeforePublish = CapPrefix + nameof(WritePublishBefore); + public const string CapAfterPublish = CapPrefix + nameof(WritePublishAfter); + public const string CapErrorPublish = CapPrefix + nameof(WritePublishError); + + public const string CapBeforeReceiveMessageStore = CapPrefix + nameof(WriteReceiveMessageStoreBefore); + public const string CapAfterReceiveMessageStore = CapPrefix + nameof(WriteReceiveMessageStoreAfter); + public const string CapErrorReceiveMessageStore = CapPrefix + nameof(WriteReceiveMessageStoreError); + + public const string CapBeforeConsumerInvoke = CapPrefix + nameof(WriteConsumerInvokeBefore); + public const string CapAfterConsumerInvoke = CapPrefix + nameof(WriteConsumerInvokeAfter); + public const string CapErrorConsumerInvoke = CapPrefix + nameof(WriteConsumerInvokeError); + + public static Guid WritePublishMessageStoreBefore(this DiagnosticListener @this, CapPublishedMessage message, [CallerMemberName] string operation = "") + { + if (@this.IsEnabled(CapBeforePublishMessageStore)) + { + Guid operationId = Guid.NewGuid(); + + @this.Write(CapBeforePublishMessageStore, new + { + OperationId = operationId, + Operation = operation, + MessageName = message.Name, + MessageContent = message.Content + }); + + return operationId; + } + return Guid.Empty; + } + + public static void WritePublishMessageStoreAfter(this DiagnosticListener @this, Guid operationId, CapPublishedMessage message, [CallerMemberName] string operation = "") + { + if (@this.IsEnabled(CapAfterPublishMessageStore)) + { + @this.Write(CapAfterPublishMessageStore, new + { + OperationId = operationId, + Operation = operation, + MessageId = message.Id, + MessageName = message.Name, + MessageContent = message.Content, + Timestamp = Stopwatch.GetTimestamp() + }); + } + } + + public static void WritePublishMessageStoreError(this DiagnosticListener @this, Guid operationId, + CapPublishedMessage message, Exception ex, [CallerMemberName] string operation = "") + { + if (@this.IsEnabled(CapErrorPublishMessageStore)) + { + @this.Write(CapErrorPublishMessageStore, new + { + OperationId = operationId, + Operation = operation, + MessageName = message.Name, + MessageContent = message.Content, + Exception = ex, + Timestamp = Stopwatch.GetTimestamp() + }); + } + } + + public static Guid WritePublishBefore(this DiagnosticListener @this, CapPublishedMessage message, [CallerMemberName] string operation = "") + { + if (@this.IsEnabled(CapBeforePublish)) + { + Guid operationId = Guid.NewGuid(); + + @this.Write(CapBeforePublish, new + { + OperationId = operationId, + Operation = operation, + MessageId = message.Id, + MessageName = message.Name, + MessageContent = message.Content + }); + + return operationId; + } + return Guid.Empty; + } + + public static void WritePublishAfter(this DiagnosticListener @this, Guid operationId, CapPublishedMessage message, [CallerMemberName] string operation = "") + { + if (@this.IsEnabled(CapAfterPublish)) + { + @this.Write(CapAfterPublish, new + { + OperationId = operationId, + Operation = operation, + MessageId = message.Id, + MessageName = message.Name, + MessageContent = message.Content, + Timestamp = Stopwatch.GetTimestamp() + }); + } + } + + public static void WritePublishError(this DiagnosticListener @this, Guid operationId, + CapPublishedMessage message, Exception ex, [CallerMemberName] string operation = "") + { + if (@this.IsEnabled(CapErrorPublish)) + { + @this.Write(CapErrorPublish, new + { + OperationId = operationId, + Operation = operation, + MessageId = message.Id, + MessageName = message.Name, + MessageContent = message.Content, + Exception = ex, + Timestamp = Stopwatch.GetTimestamp() + }); + } + } + + public static Guid WriteReceiveMessageStoreBefore(this DiagnosticListener @this, CapReceivedMessage message, [CallerMemberName] string operation = "") + { + if (@this.IsEnabled(CapBeforeReceiveMessageStore)) + { + Guid operationId = Guid.NewGuid(); + + @this.Write(CapBeforeReceiveMessageStore, new + { + OperationId = operationId, + Operation = operation, + MessageName = message.Name, + MessageContent = message.Content + }); + + return operationId; + } + return Guid.Empty; + } + + public static void WriteReceiveMessageStoreAfter(this DiagnosticListener @this, Guid operationId, CapReceivedMessage message, [CallerMemberName] string operation = "") + { + if (@this.IsEnabled(CapAfterReceiveMessageStore)) + { + @this.Write(CapAfterReceiveMessageStore, new + { + OperationId = operationId, + Operation = operation, + MessageId = message.Id, + MessageName = message.Name, + MessageContent = message.Content, + Timestamp = Stopwatch.GetTimestamp() + }); + } + } + + public static void WriteReceiveMessageStoreError(this DiagnosticListener @this, Guid operationId, + CapReceivedMessage message, Exception ex, [CallerMemberName] string operation = "") + { + if (@this.IsEnabled(CapErrorReceiveMessageStore)) + { + @this.Write(CapErrorReceiveMessageStore, new + { + OperationId = operationId, + Operation = operation, + MessageId = message.Id, + MessageName = message.Name, + MessageContent = message.Content, + Exception = ex, + Timestamp = Stopwatch.GetTimestamp() + }); + } + } + + public static Guid WriteConsumerInvokeBefore(this DiagnosticListener @this, ConsumerContext context, [CallerMemberName] string operation = "") + { + if (@this.IsEnabled(CapBeforeConsumerInvoke)) + { + Guid operationId = Guid.NewGuid(); + + @this.Write(CapBeforeConsumerInvoke, new + { + OperationId = operationId, + Operation = operation, + MethodName = context.ConsumerDescriptor.MethodInfo.Name, + ConsumerGroup = context.ConsumerDescriptor.Attribute.Group, + MessageName = context.DeliverMessage.Name, + MessageContent = context.DeliverMessage.Content, + Timestamp = Stopwatch.GetTimestamp() + }); + + return operationId; + } + return Guid.Empty; + } + + public static void WriteConsumerInvokeAfter(this DiagnosticListener @this, Guid operationId, ConsumerContext context, [CallerMemberName] string operation = "") + { + if (@this.IsEnabled(CapAfterConsumerInvoke)) + { + @this.Write(CapAfterConsumerInvoke, new + { + OperationId = operationId, + Operation = operation, + MethodName = context.ConsumerDescriptor.MethodInfo.Name, + ConsumerGroup = context.ConsumerDescriptor.Attribute.Group, + MessageName = context.DeliverMessage.Name, + MessageContent = context.DeliverMessage.Content, + Timestamp = Stopwatch.GetTimestamp() + }); + } + } + + public static void WriteConsumerInvokeError(this DiagnosticListener @this, Guid operationId, + ConsumerContext context, Exception ex, [CallerMemberName] string operation = "") + { + if (@this.IsEnabled(CapErrorConsumerInvoke)) + { + @this.Write(CapErrorConsumerInvoke, new + { + OperationId = operationId, + Operation = operation, + MethodName = context.ConsumerDescriptor.MethodInfo.Name, + ConsumerGroup = context.ConsumerDescriptor.Attribute.Group, + MessageName = context.DeliverMessage.Name, + MessageContent = context.DeliverMessage.Content, + Exception = ex, + Timestamp = Stopwatch.GetTimestamp() + }); + } + } + } +} From 11abd6dccb705fdfadbdb70e56cf8bf4beb189bd Mon Sep 17 00:00:00 2001 From: Savorboard Date: Sun, 8 Apr 2018 20:13:47 +0800 Subject: [PATCH 046/223] update readme.md --- README.md | 2 ++ README.zh-cn.md | 2 ++ 2 files changed, 4 insertions(+) diff --git a/README.md b/README.md index 14d2d09..04162cf 100644 --- a/README.md +++ b/README.md @@ -218,6 +218,8 @@ services.AddCap(x => }); ``` +The default dashboard address is :[http://localhost:xxx/cap](http://localhost:xxx/cap) , you can also change the `cap` suffix to others with `d.MatchPath` configuration options. + ![dashboard](http://images2017.cnblogs.com/blog/250417/201710/250417-20171004220827302-189215107.png) ![received](http://images2017.cnblogs.com/blog/250417/201710/250417-20171004220934115-1107747665.png) diff --git a/README.zh-cn.md b/README.zh-cn.md index 76d18dc..1a3ef04 100644 --- a/README.zh-cn.md +++ b/README.zh-cn.md @@ -218,6 +218,8 @@ services.AddCap(x => }); ``` +仪表盘默认的访问地å€æ˜¯ï¼š[http://localhost:xxx/cap](http://localhost:xxx/cap),你å¯ä»¥åœ¨`d.MatchPath`é…置项中修改`cap`路径åŽç¼€ä¸ºå…¶ä»–çš„å字。 + ![dashboard](http://images2017.cnblogs.com/blog/250417/201710/250417-20171004220827302-189215107.png) ![received](http://images2017.cnblogs.com/blog/250417/201710/250417-20171004220934115-1107747665.png) From e8846e814b7bc258d854ce39a72a8b80a212b8a2 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Mon, 9 Apr 2018 19:59:40 +0800 Subject: [PATCH 047/223] refactor Diagnostics module. --- .../DiagnosticListenerExtensions.cs | 250 ++++++++++++++++++ .../Diagnostics/EventData.Broker.Consume.cs | 16 ++ .../EventData.Broker.ConsumeEnd.cs | 16 ++ .../EventData.Broker.ConsumeError.cs | 16 ++ .../Diagnostics/EventData.Broker.Publish.cs | 16 ++ .../EventData.Broker.PublishEnd.cs | 16 ++ .../EventData.Broker.PublishError.cs | 16 ++ .../Diagnostics/EventData.Broker.cs | 22 ++ .../Diagnostics/EventData.SubscriberInvoke.cs | 33 +++ .../EventData.SubscriberInvokeEnd.cs | 17 ++ .../EventData.SubscriberInvokeError.cs | 17 ++ src/DotNetCore.CAP/Diagnostics/EventData.cs | 17 ++ .../Diagnostics/IErrorEventData.cs | 11 + 13 files changed, 463 insertions(+) create mode 100644 src/DotNetCore.CAP/Diagnostics/DiagnosticListenerExtensions.cs create mode 100644 src/DotNetCore.CAP/Diagnostics/EventData.Broker.Consume.cs create mode 100644 src/DotNetCore.CAP/Diagnostics/EventData.Broker.ConsumeEnd.cs create mode 100644 src/DotNetCore.CAP/Diagnostics/EventData.Broker.ConsumeError.cs create mode 100644 src/DotNetCore.CAP/Diagnostics/EventData.Broker.Publish.cs create mode 100644 src/DotNetCore.CAP/Diagnostics/EventData.Broker.PublishEnd.cs create mode 100644 src/DotNetCore.CAP/Diagnostics/EventData.Broker.PublishError.cs create mode 100644 src/DotNetCore.CAP/Diagnostics/EventData.Broker.cs create mode 100644 src/DotNetCore.CAP/Diagnostics/EventData.SubscriberInvoke.cs create mode 100644 src/DotNetCore.CAP/Diagnostics/EventData.SubscriberInvokeEnd.cs create mode 100644 src/DotNetCore.CAP/Diagnostics/EventData.SubscriberInvokeError.cs create mode 100644 src/DotNetCore.CAP/Diagnostics/EventData.cs create mode 100644 src/DotNetCore.CAP/Diagnostics/IErrorEventData.cs diff --git a/src/DotNetCore.CAP/Diagnostics/DiagnosticListenerExtensions.cs b/src/DotNetCore.CAP/Diagnostics/DiagnosticListenerExtensions.cs new file mode 100644 index 0000000..b9b5ba4 --- /dev/null +++ b/src/DotNetCore.CAP/Diagnostics/DiagnosticListenerExtensions.cs @@ -0,0 +1,250 @@ +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using DotNetCore.CAP.Internal; +using DotNetCore.CAP.Models; + +namespace DotNetCore.CAP.Diagnostics +{ + /// + /// Extension methods on the DiagnosticListener class to log CAP data + /// + public static class CapDiagnosticListenerExtensions + { + public const string DiagnosticListenerName = "CapDiagnosticListener"; + + private const string CapPrefix = "DotNetCore.CAP."; + + public const string CapBeforePublishMessageStore = CapPrefix + nameof(WritePublishMessageStoreBefore); + public const string CapAfterPublishMessageStore = CapPrefix + nameof(WritePublishMessageStoreAfter); + public const string CapErrorPublishMessageStore = CapPrefix + nameof(WritePublishMessageStoreError); + + public const string CapBeforePublish = CapPrefix + nameof(WritePublishBefore); + public const string CapAfterPublish = CapPrefix + nameof(WritePublishAfter); + public const string CapErrorPublish = CapPrefix + nameof(WritePublishError); + + public const string CapBeforeReceiveMessageStore = CapPrefix + nameof(WriteReceiveMessageStoreBefore); + public const string CapAfterReceiveMessageStore = CapPrefix + nameof(WriteReceiveMessageStoreAfter); + public const string CapErrorReceiveMessageStore = CapPrefix + nameof(WriteReceiveMessageStoreError); + + public const string CapBeforeConsumerInvoke = CapPrefix + nameof(WriteConsumerInvokeBefore); + public const string CapAfterConsumerInvoke = CapPrefix + nameof(WriteConsumerInvokeAfter); + public const string CapErrorConsumerInvoke = CapPrefix + nameof(WriteConsumerInvokeError); + + public static Guid WritePublishMessageStoreBefore(this DiagnosticListener @this, + CapPublishedMessage message, + [CallerMemberName] string operation = "") + { + if (@this.IsEnabled(CapBeforePublishMessageStore)) + { + Guid operationId = Guid.NewGuid(); + + @this.Write(CapBeforePublishMessageStore, new + { + OperationId = operationId, + Operation = operation, + MessageName = message.Name, + MessageContent = message.Content + }); + + return operationId; + } + return Guid.Empty; + } + + public static void WritePublishMessageStoreAfter(this DiagnosticListener @this, + Guid operationId, + CapPublishedMessage message, + [CallerMemberName] string operation = "") + { + if (@this.IsEnabled(CapAfterPublishMessageStore)) + { + @this.Write(CapAfterPublishMessageStore, new + { + OperationId = operationId, + Operation = operation, + MessageId = message.Id, + MessageName = message.Name, + MessageContent = message.Content, + Timestamp = Stopwatch.GetTimestamp() + }); + } + } + + public static void WritePublishMessageStoreError(this DiagnosticListener @this, + Guid operationId, + CapPublishedMessage message, + Exception ex, + [CallerMemberName] string operation = "") + { + if (@this.IsEnabled(CapErrorPublishMessageStore)) + { + @this.Write(CapErrorPublishMessageStore, new + { + OperationId = operationId, + Operation = operation, + MessageName = message.Name, + MessageContent = message.Content, + Exception = ex, + Timestamp = Stopwatch.GetTimestamp() + }); + } + } + + public static Guid WritePublishBefore(this DiagnosticListener @this, + string topic, + string body, + string brokerAddress, + [CallerMemberName] string operation = "") + { + if (@this.IsEnabled(CapBeforePublish)) + { + Guid operationId = Guid.NewGuid(); + + @this.Write(CapBeforePublish, new BrokerPublishEventData(operationId, operation, brokerAddress, topic, body, DateTimeOffset.UtcNow)); + + return operationId; + } + return Guid.Empty; + } + + public static void WritePublishAfter(this DiagnosticListener @this, + Guid operationId, + string topic, + string body, + string brokerAddress, + DateTimeOffset startTime, + TimeSpan duration, + [CallerMemberName] string operation = "") + { + if (@this.IsEnabled(CapAfterPublish)) + { + @this.Write(CapAfterPublish, new BrokerPublishEndEventData(operationId, operation, brokerAddress, topic, body, startTime, duration)); + } + } + + public static void WritePublishError(this DiagnosticListener @this, + Guid operationId, + string topic, + string body, + string brokerAddress, + Exception ex, + DateTimeOffset startTime, + TimeSpan duration, + [CallerMemberName] string operation = "") + { + if (@this.IsEnabled(CapErrorPublish)) + { + @this.Write(CapErrorPublish, new BrokerPublishErrorEventData(operationId, operation, brokerAddress, topic, body, ex, startTime, duration)); + } + } + + public static Guid WriteReceiveMessageStoreBefore(this DiagnosticListener @this, + string topic, + string body, + string groupName, + [CallerMemberName] string operation = "") + { + if (@this.IsEnabled(CapBeforeReceiveMessageStore)) + { + Guid operationId = Guid.NewGuid(); + + @this.Write(CapBeforePublish, new BrokerConsumeEventData(operationId, operation, groupName, topic, body, DateTimeOffset.UtcNow)); + + return operationId; + } + return Guid.Empty; + } + + public static void WriteReceiveMessageStoreAfter(this DiagnosticListener @this, + Guid operationId, + string topic, + string body, + string groupName, + DateTimeOffset startTime, + TimeSpan duration, + [CallerMemberName] string operation = "") + { + if (@this.IsEnabled(CapAfterReceiveMessageStore)) + { + @this.Write(CapAfterPublish, new BrokerConsumeEndEventData(operationId, operation, groupName, topic, body, startTime, duration)); + } + } + + public static void WriteReceiveMessageStoreError(this DiagnosticListener @this, + Guid operationId, + string topic, + string body, + string groupName, + Exception ex, + DateTimeOffset startTime, + TimeSpan duration, + [CallerMemberName] string operation = "") + { + if (@this.IsEnabled(CapErrorReceiveMessageStore)) + { + @this.Write(CapErrorPublish, new BrokerConsumeErrorEventData(operationId, operation, groupName, topic, body, ex, startTime, duration)); + } + } + + public static Guid WriteConsumerInvokeBefore(this DiagnosticListener @this, + ConsumerContext context, + [CallerMemberName] string operation = "") + { + if (@this.IsEnabled(CapBeforeConsumerInvoke)) + { + Guid operationId = Guid.NewGuid(); + + var methodName = context.ConsumerDescriptor.MethodInfo.Name; + var subscribeName = context.ConsumerDescriptor.Attribute.Name; + var subscribeGroup = context.ConsumerDescriptor.Attribute.Group; + var parameterValues = context.DeliverMessage.Content; + + @this.Write(CapBeforePublish, new SubscriberInvokeEventData(operationId, operation, methodName, subscribeName, + subscribeGroup, parameterValues, DateTimeOffset.UtcNow)); + + return operationId; + } + return Guid.Empty; + } + + public static void WriteConsumerInvokeAfter(this DiagnosticListener @this, + Guid operationId, + ConsumerContext context, + DateTimeOffset startTime, + TimeSpan duration, + [CallerMemberName] string operation = "") + { + if (@this.IsEnabled(CapAfterConsumerInvoke)) + { + var methodName = context.ConsumerDescriptor.MethodInfo.Name; + var subscribeName = context.ConsumerDescriptor.Attribute.Name; + var subscribeGroup = context.ConsumerDescriptor.Attribute.Group; + var parameterValues = context.DeliverMessage.Content; + + @this.Write(CapBeforePublish, new SubscriberInvokeEndEventData(operationId, operation, methodName, subscribeName, + subscribeGroup, parameterValues, startTime, duration)); + } + } + + public static void WriteConsumerInvokeError(this DiagnosticListener @this, + Guid operationId, + ConsumerContext context, + Exception ex, + DateTimeOffset startTime, + TimeSpan duration, + [CallerMemberName] string operation = "") + { + if (@this.IsEnabled(CapErrorConsumerInvoke)) + { + var methodName = context.ConsumerDescriptor.MethodInfo.Name; + var subscribeName = context.ConsumerDescriptor.Attribute.Name; + var subscribeGroup = context.ConsumerDescriptor.Attribute.Group; + var parameterValues = context.DeliverMessage.Content; + + @this.Write(CapBeforePublish, new SubscriberInvokeErrorEventData(operationId, operation, methodName, subscribeName, + subscribeGroup, parameterValues, ex, startTime, duration)); + } + } + } +} diff --git a/src/DotNetCore.CAP/Diagnostics/EventData.Broker.Consume.cs b/src/DotNetCore.CAP/Diagnostics/EventData.Broker.Consume.cs new file mode 100644 index 0000000..c6e7454 --- /dev/null +++ b/src/DotNetCore.CAP/Diagnostics/EventData.Broker.Consume.cs @@ -0,0 +1,16 @@ +using System; + +namespace DotNetCore.CAP.Diagnostics +{ + public class BrokerConsumeEventData : BrokerEventData + { + public DateTimeOffset StartTime { get; } + + public BrokerConsumeEventData(Guid operationId, string operation, string groupName, + string brokerTopicName, string brokerTopicBody, DateTimeOffset startTime) + : base(operationId, operation, groupName, brokerTopicName, brokerTopicBody) + { + StartTime = startTime; + } + } +} diff --git a/src/DotNetCore.CAP/Diagnostics/EventData.Broker.ConsumeEnd.cs b/src/DotNetCore.CAP/Diagnostics/EventData.Broker.ConsumeEnd.cs new file mode 100644 index 0000000..e6bbba9 --- /dev/null +++ b/src/DotNetCore.CAP/Diagnostics/EventData.Broker.ConsumeEnd.cs @@ -0,0 +1,16 @@ +using System; + +namespace DotNetCore.CAP.Diagnostics +{ + public class BrokerConsumeEndEventData : BrokerConsumeEventData + { + public TimeSpan Duration { get; } + + public BrokerConsumeEndEventData(Guid operationId, string operation, string groupName, string brokerTopicName, + string brokerTopicBody, DateTimeOffset startTime, TimeSpan duration) + : base(operationId, operation, groupName, brokerTopicName, brokerTopicBody, startTime) + { + Duration = duration; + } + } +} diff --git a/src/DotNetCore.CAP/Diagnostics/EventData.Broker.ConsumeError.cs b/src/DotNetCore.CAP/Diagnostics/EventData.Broker.ConsumeError.cs new file mode 100644 index 0000000..36c1300 --- /dev/null +++ b/src/DotNetCore.CAP/Diagnostics/EventData.Broker.ConsumeError.cs @@ -0,0 +1,16 @@ +using System; + +namespace DotNetCore.CAP.Diagnostics +{ + public class BrokerConsumeErrorEventData : BrokerConsumeEndEventData, IErrorEventData + { + public Exception Exception { get; } + + public BrokerConsumeErrorEventData(Guid operationId, string operation, string groupName, + string brokerTopicName, string brokerTopicBody, Exception exception, DateTimeOffset startTime, TimeSpan duration) + : base(operationId, operation, groupName, brokerTopicName, brokerTopicBody, startTime, duration) + { + Exception = exception; + } + } +} diff --git a/src/DotNetCore.CAP/Diagnostics/EventData.Broker.Publish.cs b/src/DotNetCore.CAP/Diagnostics/EventData.Broker.Publish.cs new file mode 100644 index 0000000..b7f904b --- /dev/null +++ b/src/DotNetCore.CAP/Diagnostics/EventData.Broker.Publish.cs @@ -0,0 +1,16 @@ +using System; + +namespace DotNetCore.CAP.Diagnostics +{ + public class BrokerPublishEventData : BrokerEventData + { + public DateTimeOffset StartTime { get; } + + public BrokerPublishEventData(Guid operationId, string operation, string groupName, + string brokerTopicName, string brokerTopicBody, DateTimeOffset startTime) + : base(operationId, operation, groupName, brokerTopicName, brokerTopicBody) + { + StartTime = startTime; + } + } +} diff --git a/src/DotNetCore.CAP/Diagnostics/EventData.Broker.PublishEnd.cs b/src/DotNetCore.CAP/Diagnostics/EventData.Broker.PublishEnd.cs new file mode 100644 index 0000000..e415647 --- /dev/null +++ b/src/DotNetCore.CAP/Diagnostics/EventData.Broker.PublishEnd.cs @@ -0,0 +1,16 @@ +using System; + +namespace DotNetCore.CAP.Diagnostics +{ + public class BrokerPublishEndEventData : BrokerPublishEventData + { + public TimeSpan Duration { get; } + + public BrokerPublishEndEventData(Guid operationId, string operation, string groupName, string brokerTopicName, + string brokerTopicBody, DateTimeOffset startTime, TimeSpan duration) + : base(operationId, operation, groupName, brokerTopicName, brokerTopicBody, startTime) + { + Duration = duration; + } + } +} diff --git a/src/DotNetCore.CAP/Diagnostics/EventData.Broker.PublishError.cs b/src/DotNetCore.CAP/Diagnostics/EventData.Broker.PublishError.cs new file mode 100644 index 0000000..fa4f8cb --- /dev/null +++ b/src/DotNetCore.CAP/Diagnostics/EventData.Broker.PublishError.cs @@ -0,0 +1,16 @@ +using System; + +namespace DotNetCore.CAP.Diagnostics +{ + public class BrokerPublishErrorEventData : BrokerPublishEndEventData, IErrorEventData + { + public Exception Exception { get; } + + public BrokerPublishErrorEventData(Guid operationId, string operation, string groupName, + string brokerTopicName, string brokerTopicBody, Exception exception, DateTimeOffset startTime, TimeSpan duration) + : base(operationId, operation, groupName, brokerTopicName, brokerTopicBody, startTime, duration) + { + Exception = exception; + } + } +} diff --git a/src/DotNetCore.CAP/Diagnostics/EventData.Broker.cs b/src/DotNetCore.CAP/Diagnostics/EventData.Broker.cs new file mode 100644 index 0000000..eef7ac1 --- /dev/null +++ b/src/DotNetCore.CAP/Diagnostics/EventData.Broker.cs @@ -0,0 +1,22 @@ +using System; + +namespace DotNetCore.CAP.Diagnostics +{ + public class BrokerEventData : EventData + { + public string GroupName { get; set; } + + public string BrokerTopicBody { get; set; } + + public string BrokerTopicName { get; set; } + + public BrokerEventData(Guid operationId, string operation, string groupName, + string brokerTopicName, string brokerTopicBody) + : base(operationId, operation) + { + GroupName = groupName; + BrokerTopicName = brokerTopicName; + BrokerTopicBody = brokerTopicBody; + } + } +} diff --git a/src/DotNetCore.CAP/Diagnostics/EventData.SubscriberInvoke.cs b/src/DotNetCore.CAP/Diagnostics/EventData.SubscriberInvoke.cs new file mode 100644 index 0000000..021fff9 --- /dev/null +++ b/src/DotNetCore.CAP/Diagnostics/EventData.SubscriberInvoke.cs @@ -0,0 +1,33 @@ +using System; + +namespace DotNetCore.CAP.Diagnostics +{ + public class SubscriberInvokeEventData : EventData + { + public DateTimeOffset StartTime { get; } + + public string MethodName { get; set; } + + public string SubscribeName { get; set; } + + public string SubscribeGroup { get; set; } + + public string ParameterValues { get; set; } + + public SubscriberInvokeEventData(Guid operationId, + string operation, + string methodName, + string subscribeName, + string subscribeGroup, + string parameterValues, + DateTimeOffset startTime) + : base(operationId, operation) + { + MethodName = methodName; + SubscribeName = subscribeName; + SubscribeGroup = subscribeGroup; + ParameterValues = parameterValues; + StartTime = startTime; + } + } +} diff --git a/src/DotNetCore.CAP/Diagnostics/EventData.SubscriberInvokeEnd.cs b/src/DotNetCore.CAP/Diagnostics/EventData.SubscriberInvokeEnd.cs new file mode 100644 index 0000000..38358a7 --- /dev/null +++ b/src/DotNetCore.CAP/Diagnostics/EventData.SubscriberInvokeEnd.cs @@ -0,0 +1,17 @@ +using System; + +namespace DotNetCore.CAP.Diagnostics +{ + public class SubscriberInvokeEndEventData : SubscriberInvokeEventData + { + public TimeSpan Duration { get; } + + public SubscriberInvokeEndEventData(Guid operationId, string operation, + string methodName, string subscribeName, string subscribeGroup, + string parameterValues, DateTimeOffset startTime, TimeSpan duration) + : base(operationId, operation, methodName, subscribeName, subscribeGroup, parameterValues, startTime) + { + Duration = duration; + } + } +} diff --git a/src/DotNetCore.CAP/Diagnostics/EventData.SubscriberInvokeError.cs b/src/DotNetCore.CAP/Diagnostics/EventData.SubscriberInvokeError.cs new file mode 100644 index 0000000..be7a361 --- /dev/null +++ b/src/DotNetCore.CAP/Diagnostics/EventData.SubscriberInvokeError.cs @@ -0,0 +1,17 @@ +using System; + +namespace DotNetCore.CAP.Diagnostics +{ + public class SubscriberInvokeErrorEventData : SubscriberInvokeEndEventData, IErrorEventData + { + public Exception Exception { get; } + + public SubscriberInvokeErrorEventData(Guid operationId, string operation, string methodName, + string subscribeName, string subscribeGroup, string parameterValues, Exception exception, + DateTimeOffset startTime, TimeSpan duration) : base(operationId, operation, methodName, subscribeName, + subscribeGroup, parameterValues, startTime, duration) + { + Exception = exception; + } + } +} diff --git a/src/DotNetCore.CAP/Diagnostics/EventData.cs b/src/DotNetCore.CAP/Diagnostics/EventData.cs new file mode 100644 index 0000000..94c01b8 --- /dev/null +++ b/src/DotNetCore.CAP/Diagnostics/EventData.cs @@ -0,0 +1,17 @@ +using System; + +namespace DotNetCore.CAP.Diagnostics +{ + public class EventData + { + public EventData(Guid operationId, string operation) + { + OperationId = operationId; + Operation = operation; + } + + public Guid OperationId { get; set; } + + public string Operation { get; set; } + } +} diff --git a/src/DotNetCore.CAP/Diagnostics/IErrorEventData.cs b/src/DotNetCore.CAP/Diagnostics/IErrorEventData.cs new file mode 100644 index 0000000..56c2278 --- /dev/null +++ b/src/DotNetCore.CAP/Diagnostics/IErrorEventData.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace DotNetCore.CAP.Diagnostics +{ + public interface IErrorEventData + { + Exception Exception { get; } + } +} From 9112fa723c0cc8ffbaed6de0e31253471504dc82 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Mon, 9 Apr 2018 20:00:29 +0800 Subject: [PATCH 048/223] add borkeraddress to connectionpool. --- src/DotNetCore.CAP.Kafka/ConnectionPool.cs | 4 ++++ src/DotNetCore.CAP.Kafka/IConnectionPool.cs | 2 ++ src/DotNetCore.CAP.RabbitMQ/ConnectionChannelPool.cs | 6 ++++++ src/DotNetCore.CAP.RabbitMQ/IConnectionChannelPool.cs | 4 ++++ 4 files changed, 16 insertions(+) diff --git a/src/DotNetCore.CAP.Kafka/ConnectionPool.cs b/src/DotNetCore.CAP.Kafka/ConnectionPool.cs index 6879942..1993661 100644 --- a/src/DotNetCore.CAP.Kafka/ConnectionPool.cs +++ b/src/DotNetCore.CAP.Kafka/ConnectionPool.cs @@ -21,8 +21,12 @@ namespace DotNetCore.CAP.Kafka { _maxSize = options.ConnectionPoolSize; _activator = CreateActivator(options); + + ServersAddress = options.Servers; } + public string ServersAddress { get; } + Producer IConnectionPool.Rent() { return Rent(); diff --git a/src/DotNetCore.CAP.Kafka/IConnectionPool.cs b/src/DotNetCore.CAP.Kafka/IConnectionPool.cs index 7fb01a5..ed86d7f 100644 --- a/src/DotNetCore.CAP.Kafka/IConnectionPool.cs +++ b/src/DotNetCore.CAP.Kafka/IConnectionPool.cs @@ -7,6 +7,8 @@ namespace DotNetCore.CAP.Kafka { public interface IConnectionPool { + string ServersAddress { get; } + Producer Rent(); bool Return(Producer context); diff --git a/src/DotNetCore.CAP.RabbitMQ/ConnectionChannelPool.cs b/src/DotNetCore.CAP.RabbitMQ/ConnectionChannelPool.cs index 4e028d1..e650518 100644 --- a/src/DotNetCore.CAP.RabbitMQ/ConnectionChannelPool.cs +++ b/src/DotNetCore.CAP.RabbitMQ/ConnectionChannelPool.cs @@ -28,6 +28,8 @@ namespace DotNetCore.CAP.RabbitMQ _maxSize = DefaultPoolSize; _connectionActivator = CreateConnection(options); + HostAddress = options.HostName + ":" + options.Port; + Exchange = options.ExchangeName; } IModel IConnectionChannelPool.Rent() @@ -40,6 +42,10 @@ namespace DotNetCore.CAP.RabbitMQ return Return(connection); } + public string HostAddress { get; } + + public string Exchange { get; } + public IConnection GetConnection() { if (_connection != null && _connection.IsOpen) diff --git a/src/DotNetCore.CAP.RabbitMQ/IConnectionChannelPool.cs b/src/DotNetCore.CAP.RabbitMQ/IConnectionChannelPool.cs index d7a6b08..5361529 100644 --- a/src/DotNetCore.CAP.RabbitMQ/IConnectionChannelPool.cs +++ b/src/DotNetCore.CAP.RabbitMQ/IConnectionChannelPool.cs @@ -7,6 +7,10 @@ namespace DotNetCore.CAP.RabbitMQ { public interface IConnectionChannelPool { + string HostAddress { get; } + + string Exchange { get; } + IConnection GetConnection(); IModel Rent(); From b512030eca826cc5955c4ec76c515fed21cf9609 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Mon, 9 Apr 2018 20:01:00 +0800 Subject: [PATCH 049/223] rename topicexchangename to exchangename --- src/DotNetCore.CAP.RabbitMQ/CAP.RabbiMQOptions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/DotNetCore.CAP.RabbitMQ/CAP.RabbiMQOptions.cs b/src/DotNetCore.CAP.RabbitMQ/CAP.RabbiMQOptions.cs index c600a4c..c0abcc8 100644 --- a/src/DotNetCore.CAP.RabbitMQ/CAP.RabbiMQOptions.cs +++ b/src/DotNetCore.CAP.RabbitMQ/CAP.RabbiMQOptions.cs @@ -58,7 +58,7 @@ namespace DotNetCore.CAP /// /// Topic exchange name when declare a topic exchange. /// - public string TopicExchangeName { get; set; } = DefaultExchangeName; + public string ExchangeName { get; set; } = DefaultExchangeName; /// /// Timeout setting for connection attempts (in milliseconds). From 8d81527acf3f9d43d9fe1dc747f85e2e04d9d2db Mon Sep 17 00:00:00 2001 From: Savorboard Date: Mon, 9 Apr 2018 20:01:42 +0800 Subject: [PATCH 050/223] modofiy class access level for diagnostics. --- src/DotNetCore.CAP/Internal/ConsumerContext.cs | 2 +- src/DotNetCore.CAP/Internal/ConsumerExecutorDescriptor.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/DotNetCore.CAP/Internal/ConsumerContext.cs b/src/DotNetCore.CAP/Internal/ConsumerContext.cs index 5610d7a..49762ed 100644 --- a/src/DotNetCore.CAP/Internal/ConsumerContext.cs +++ b/src/DotNetCore.CAP/Internal/ConsumerContext.cs @@ -8,7 +8,7 @@ namespace DotNetCore.CAP.Internal /// /// A context for consumers, it used to be provider wrapper of method description and received message. /// - internal class ConsumerContext + public class ConsumerContext { /// /// create a new instance of . diff --git a/src/DotNetCore.CAP/Internal/ConsumerExecutorDescriptor.cs b/src/DotNetCore.CAP/Internal/ConsumerExecutorDescriptor.cs index 07dca25..6f8f532 100644 --- a/src/DotNetCore.CAP/Internal/ConsumerExecutorDescriptor.cs +++ b/src/DotNetCore.CAP/Internal/ConsumerExecutorDescriptor.cs @@ -9,7 +9,7 @@ namespace DotNetCore.CAP.Internal /// /// A descriptor of user definition method. /// - internal class ConsumerExecutorDescriptor + public class ConsumerExecutorDescriptor { public MethodInfo MethodInfo { get; set; } From 700833defb12feb37c1a53c188c227e76f723967 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Mon, 9 Apr 2018 20:02:36 +0800 Subject: [PATCH 051/223] refactor Diagnostics module. --- .../IPublishMessageSender.Kafka.cs | 19 ++++++++++++-- .../Abstractions/CapPublisherBase.cs | 23 +++++++++------- .../IConsumerHandler.Default.cs | 26 ++++++++++++++++--- .../IPublishMessageSender.Base.cs | 6 ++--- .../ISubscribeExecutor.Default.cs | 10 ++++--- 5 files changed, 62 insertions(+), 22 deletions(-) diff --git a/src/DotNetCore.CAP.Kafka/IPublishMessageSender.Kafka.cs b/src/DotNetCore.CAP.Kafka/IPublishMessageSender.Kafka.cs index cda5755..3e95be2 100644 --- a/src/DotNetCore.CAP.Kafka/IPublishMessageSender.Kafka.cs +++ b/src/DotNetCore.CAP.Kafka/IPublishMessageSender.Kafka.cs @@ -5,6 +5,7 @@ using System; using System.Diagnostics; using System.Text; using System.Threading.Tasks; +using DotNetCore.CAP.Diagnostics; using DotNetCore.CAP.Internal; using DotNetCore.CAP.Processor.States; using Microsoft.Extensions.Logging; @@ -13,24 +14,32 @@ namespace DotNetCore.CAP.Kafka { internal class KafkaPublishMessageSender : BasePublishMessageSender { - private readonly ConnectionPool _connectionPool; + private readonly IConnectionPool _connectionPool; private readonly ILogger _logger; + private readonly string _serversAddress; public KafkaPublishMessageSender( CapOptions options, IStateChanger stateChanger, IStorageConnection connection, - ConnectionPool connectionPool, ILogger logger) + IConnectionPool connectionPool, ILogger logger) : base(logger, options, connection, stateChanger) { _logger = logger; _connectionPool = connectionPool; + _serversAddress = _connectionPool.ServersAddress; } public override async Task PublishAsync(string keyName, string content) { + var startTime = DateTimeOffset.UtcNow; + var stopwatch = Stopwatch.StartNew(); + Guid operationId = Guid.Empty; + var producer = _connectionPool.Rent(); try { + operationId = s_diagnosticListener.WritePublishBefore(keyName, content, _serversAddress); + var contentBytes = Encoding.UTF8.GetBytes(content); var message = await producer.ProduceAsync(keyName, null, contentBytes); @@ -43,13 +52,18 @@ namespace DotNetCore.CAP.Kafka }); } + s_diagnosticListener.WritePublishAfter(operationId, message.Topic, content, _serversAddress, startTime, stopwatch.Elapsed); + _logger.LogDebug($"kafka topic message [{keyName}] has been published."); return OperateResult.Success; } catch (Exception ex) { + s_diagnosticListener.WritePublishError(operationId, keyName, content, _serversAddress, ex, startTime, stopwatch.Elapsed); + var wapperEx = new PublisherSentFailedException(ex.Message, ex); + return OperateResult.Failed(wapperEx); } finally @@ -61,5 +75,6 @@ namespace DotNetCore.CAP.Kafka } } } + } } \ No newline at end of file diff --git a/src/DotNetCore.CAP/Abstractions/CapPublisherBase.cs b/src/DotNetCore.CAP/Abstractions/CapPublisherBase.cs index e3167d8..26760b2 100644 --- a/src/DotNetCore.CAP/Abstractions/CapPublisherBase.cs +++ b/src/DotNetCore.CAP/Abstractions/CapPublisherBase.cs @@ -5,8 +5,8 @@ using System; using System.Data; using System.Diagnostics; using System.Threading.Tasks; +using DotNetCore.CAP.Diagnostics; using DotNetCore.CAP.Infrastructure; -using DotNetCore.CAP.Internal; using DotNetCore.CAP.Models; using Microsoft.Extensions.Logging; @@ -152,16 +152,19 @@ namespace DotNetCore.CAP.Abstractions private async Task PublishWithTransAsync(string name, T contentObj, string callbackName = null) { - try + Guid operationId = default(Guid); + var content = Serialize(contentObj, callbackName); + + var message = new CapPublishedMessage { - var content = Serialize(contentObj, callbackName); + Name = name, + Content = content, + StatusName = StatusName.Scheduled + }; - var message = new CapPublishedMessage - { - Name = name, - Content = content, - StatusName = StatusName.Scheduled - }; + try + { + operationId = s_diagnosticListener.WritePublishMessageStoreBefore(message); var id = await ExecuteAsync(DbConnection, DbTransaction, message); @@ -170,6 +173,7 @@ namespace DotNetCore.CAP.Abstractions if (id > 0) { _logger.LogInformation($"message [{message}] has been persisted in the database."); + s_diagnosticListener.WritePublishMessageStoreAfter(operationId, message); message.Id = id; @@ -179,6 +183,7 @@ namespace DotNetCore.CAP.Abstractions catch (Exception e) { _logger.LogError("An exception was occurred when publish message. exception message:" + e.Message, e); + s_diagnosticListener.WritePublishMessageStoreError(operationId, message, e); Console.WriteLine(e); throw; } diff --git a/src/DotNetCore.CAP/IConsumerHandler.Default.cs b/src/DotNetCore.CAP/IConsumerHandler.Default.cs index b9bbd2d..43e34df 100644 --- a/src/DotNetCore.CAP/IConsumerHandler.Default.cs +++ b/src/DotNetCore.CAP/IConsumerHandler.Default.cs @@ -6,6 +6,7 @@ using System.Diagnostics; using System.Linq; using System.Threading; using System.Threading.Tasks; +using DotNetCore.CAP.Diagnostics; using DotNetCore.CAP.Infrastructure; using DotNetCore.CAP.Internal; using DotNetCore.CAP.Models; @@ -99,7 +100,9 @@ namespace DotNetCore.CAP { client.OnMessageReceived += (sender, messageContext) => { - Guid operationId = default(Guid); + var startTime = DateTimeOffset.UtcNow; + var stopwatch = Stopwatch.StartNew(); + var operationId = Guid.Empty; var receivedMessage = new CapReceivedMessage(messageContext) { @@ -108,13 +111,22 @@ namespace DotNetCore.CAP try { - operationId = s_diagnosticListener.WriteReceiveMessageStoreBefore(receivedMessage); + operationId = s_diagnosticListener.WriteReceiveMessageStoreBefore( + messageContext.Name, + messageContext.Content, + messageContext.Group); StoreMessage(receivedMessage); client.Commit(); - s_diagnosticListener.WriteReceiveMessageStoreAfter(operationId, receivedMessage); + s_diagnosticListener.WriteReceiveMessageStoreAfter( + operationId, + messageContext.Name, + messageContext.Content, + messageContext.Group, + startTime, + stopwatch.Elapsed); _dispatcher.EnqueueToExecute(receivedMessage); } @@ -124,7 +136,13 @@ namespace DotNetCore.CAP client.Reject(); - s_diagnosticListener.WriteReceiveMessageStoreError(operationId, receivedMessage, e); + s_diagnosticListener.WriteReceiveMessageStoreError(operationId, + messageContext.Name, + messageContext.Content, + messageContext.Group, + e, + startTime, + stopwatch.Elapsed); } }; diff --git a/src/DotNetCore.CAP/IPublishMessageSender.Base.cs b/src/DotNetCore.CAP/IPublishMessageSender.Base.cs index 31c1f2a..5b6588d 100644 --- a/src/DotNetCore.CAP/IPublishMessageSender.Base.cs +++ b/src/DotNetCore.CAP/IPublishMessageSender.Base.cs @@ -4,6 +4,7 @@ using System; using System.Diagnostics; using System.Threading.Tasks; +using DotNetCore.CAP.Diagnostics; using DotNetCore.CAP.Infrastructure; using DotNetCore.CAP.Internal; using DotNetCore.CAP.Models; @@ -22,7 +23,7 @@ namespace DotNetCore.CAP // diagnostics listener // ReSharper disable once InconsistentNaming - private static readonly DiagnosticListener s_diagnosticListener = + protected static readonly DiagnosticListener s_diagnosticListener = new DiagnosticListener(CapDiagnosticListenerExtensions.DiagnosticListenerName); protected BasePublishMessageSender( @@ -42,7 +43,6 @@ namespace DotNetCore.CAP public async Task SendAsync(CapPublishedMessage message) { var sp = Stopwatch.StartNew(); - var operationId = s_diagnosticListener.WritePublishBefore(message); var result = await PublishAsync(message.Name, message.Content); @@ -52,14 +52,12 @@ namespace DotNetCore.CAP { await SetSuccessfulState(message); - s_diagnosticListener.WritePublishAfter(operationId, message); _logger.MessageHasBeenSent(sp.Elapsed.TotalSeconds); return OperateResult.Success; } else { - s_diagnosticListener.WritePublishError(operationId, message, result.Exception); _logger.MessagePublishException(message.Id, result.Exception); await SetFailedState(message, result.Exception, out bool stillRetry); diff --git a/src/DotNetCore.CAP/ISubscribeExecutor.Default.cs b/src/DotNetCore.CAP/ISubscribeExecutor.Default.cs index 9133863..0629852 100644 --- a/src/DotNetCore.CAP/ISubscribeExecutor.Default.cs +++ b/src/DotNetCore.CAP/ISubscribeExecutor.Default.cs @@ -4,6 +4,7 @@ using System; using System.Diagnostics; using System.Threading.Tasks; +using DotNetCore.CAP.Diagnostics; using DotNetCore.CAP.Infrastructure; using DotNetCore.CAP.Internal; using DotNetCore.CAP.Models; @@ -144,7 +145,10 @@ namespace DotNetCore.CAP throw new SubscriberNotFoundException(error); } - Guid operationId = default(Guid); + var startTime = DateTimeOffset.UtcNow; + var stopwatch = Stopwatch.StartNew(); + var operationId = Guid.Empty; + var consumerContext = new ConsumerContext(executor, receivedMessage.ToMessageContext()); try @@ -153,7 +157,7 @@ namespace DotNetCore.CAP var ret = await Invoker.InvokeAsync(consumerContext); - s_diagnosticListener.WriteConsumerInvokeAfter(operationId,consumerContext); + s_diagnosticListener.WriteConsumerInvokeAfter(operationId, consumerContext, startTime, stopwatch.Elapsed); if (!string.IsNullOrEmpty(ret.CallbackName)) { @@ -162,7 +166,7 @@ namespace DotNetCore.CAP } catch (Exception ex) { - s_diagnosticListener.WriteConsumerInvokeError(operationId, consumerContext, ex); + s_diagnosticListener.WriteConsumerInvokeError(operationId, consumerContext, ex, startTime, stopwatch.Elapsed); throw new SubscriberExecutionFailedException(ex.Message, ex); } From 27ce0e352497d4263429d592ec04288dd17a4783 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Mon, 9 Apr 2018 20:02:58 +0800 Subject: [PATCH 052/223] refactor --- src/DotNetCore.CAP.Kafka/CAP.KafkaCapOptionsExtension.cs | 2 +- src/DotNetCore.CAP.RabbitMQ/RabbitMQConsumerClient.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/DotNetCore.CAP.Kafka/CAP.KafkaCapOptionsExtension.cs b/src/DotNetCore.CAP.Kafka/CAP.KafkaCapOptionsExtension.cs index 04493f5..88db8fe 100644 --- a/src/DotNetCore.CAP.Kafka/CAP.KafkaCapOptionsExtension.cs +++ b/src/DotNetCore.CAP.Kafka/CAP.KafkaCapOptionsExtension.cs @@ -28,7 +28,7 @@ namespace DotNetCore.CAP services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); - services.AddSingleton(); + services.AddSingleton(); } } } \ No newline at end of file diff --git a/src/DotNetCore.CAP.RabbitMQ/RabbitMQConsumerClient.cs b/src/DotNetCore.CAP.RabbitMQ/RabbitMQConsumerClient.cs index acbff96..402d0fa 100644 --- a/src/DotNetCore.CAP.RabbitMQ/RabbitMQConsumerClient.cs +++ b/src/DotNetCore.CAP.RabbitMQ/RabbitMQConsumerClient.cs @@ -28,7 +28,7 @@ namespace DotNetCore.CAP.RabbitMQ _queueName = queueName; _connectionChannelPool = connectionChannelPool; _rabbitMQOptions = options; - _exchageName = options.TopicExchangeName; + _exchageName = options.ExchangeName; InitClient(); } From 19edfbd2193f5359d22abe253b25c253cd3559cb Mon Sep 17 00:00:00 2001 From: Savorboard Date: Mon, 9 Apr 2018 20:03:30 +0800 Subject: [PATCH 053/223] rename file name --- ...r.cs => IPublishMessageSender.RabbitMQ.cs} | 24 ++++++++++++++----- 1 file changed, 18 insertions(+), 6 deletions(-) rename src/DotNetCore.CAP.RabbitMQ/{PublishQueueExecutor.cs => IPublishMessageSender.RabbitMQ.cs} (66%) diff --git a/src/DotNetCore.CAP.RabbitMQ/PublishQueueExecutor.cs b/src/DotNetCore.CAP.RabbitMQ/IPublishMessageSender.RabbitMQ.cs similarity index 66% rename from src/DotNetCore.CAP.RabbitMQ/PublishQueueExecutor.cs rename to src/DotNetCore.CAP.RabbitMQ/IPublishMessageSender.RabbitMQ.cs index 9afedc7..2cb15c2 100644 --- a/src/DotNetCore.CAP.RabbitMQ/PublishQueueExecutor.cs +++ b/src/DotNetCore.CAP.RabbitMQ/IPublishMessageSender.RabbitMQ.cs @@ -2,8 +2,10 @@ // Licensed under the MIT License. See License.txt in the project root for license information. using System; +using System.Diagnostics; using System.Text; using System.Threading.Tasks; +using DotNetCore.CAP.Diagnostics; using DotNetCore.CAP.Internal; using DotNetCore.CAP.Processor.States; using Microsoft.Extensions.Logging; @@ -15,33 +17,43 @@ namespace DotNetCore.CAP.RabbitMQ { private readonly IConnectionChannelPool _connectionChannelPool; private readonly ILogger _logger; - private readonly RabbitMQOptions _rabbitMQOptions; + private readonly string _exchange; + private readonly string _hostAddress; - public RabbitMQPublishMessageSender(ILogger logger, CapOptions options, RabbitMQOptions rabbitMQOptions, + public RabbitMQPublishMessageSender(ILogger logger, CapOptions options, IStorageConnection connection, IConnectionChannelPool connectionChannelPool, IStateChanger stateChanger) : base(logger, options, connection, stateChanger) { _logger = logger; _connectionChannelPool = connectionChannelPool; - _rabbitMQOptions = rabbitMQOptions; + _exchange = _connectionChannelPool.Exchange; + _hostAddress = _connectionChannelPool.HostAddress; } public override Task PublishAsync(string keyName, string content) { + var startTime = DateTimeOffset.UtcNow; + var stopwatch = Stopwatch.StartNew(); + Guid operationId = Guid.Empty; + var channel = _connectionChannelPool.Rent(); try { - var body = Encoding.UTF8.GetBytes(content); + operationId = s_diagnosticListener.WritePublishBefore(keyName, content, _hostAddress); - channel.ExchangeDeclare(_rabbitMQOptions.TopicExchangeName, RabbitMQOptions.ExchangeType, true); - channel.BasicPublish(_rabbitMQOptions.TopicExchangeName, keyName, null, body); + var body = Encoding.UTF8.GetBytes(content); + channel.ExchangeDeclare(_exchange, RabbitMQOptions.ExchangeType, true); + channel.BasicPublish(_exchange, keyName, null, body); + s_diagnosticListener.WritePublishAfter(operationId, keyName, content, _hostAddress, startTime, stopwatch.Elapsed); _logger.LogDebug($"RabbitMQ topic message [{keyName}] has been published."); return Task.FromResult(OperateResult.Success); } catch (Exception ex) { + s_diagnosticListener.WritePublishError(operationId, keyName, content, _hostAddress, ex, startTime, stopwatch.Elapsed); + var wapperEx = new PublisherSentFailedException(ex.Message, ex); var errors = new OperateError { From b3d0126dc7d88f22d1a8f3cfc6d6eb99e4e8d933 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Mon, 9 Apr 2018 20:03:54 +0800 Subject: [PATCH 054/223] remove file --- .../CapDiagnosticListenerExtensions.cs | 250 ------------------ 1 file changed, 250 deletions(-) delete mode 100644 src/DotNetCore.CAP/Internal/CapDiagnosticListenerExtensions.cs diff --git a/src/DotNetCore.CAP/Internal/CapDiagnosticListenerExtensions.cs b/src/DotNetCore.CAP/Internal/CapDiagnosticListenerExtensions.cs deleted file mode 100644 index 3f5999e..0000000 --- a/src/DotNetCore.CAP/Internal/CapDiagnosticListenerExtensions.cs +++ /dev/null @@ -1,250 +0,0 @@ -using System; -using System.Diagnostics; -using System.Runtime.CompilerServices; -using DotNetCore.CAP.Models; - -namespace DotNetCore.CAP.Internal -{ - /// - /// Extension methods on the DiagnosticListener class to log CAP data - /// - internal static class CapDiagnosticListenerExtensions - { - public const string DiagnosticListenerName = "CapDiagnosticListener"; - - private const string CapPrefix = "DotNetCore.CAP."; - - public const string CapBeforePublishMessageStore = CapPrefix + nameof(WritePublishMessageStoreBefore); - public const string CapAfterPublishMessageStore = CapPrefix + nameof(WritePublishMessageStoreAfter); - public const string CapErrorPublishMessageStore = CapPrefix + nameof(WritePublishMessageStoreError); - - public const string CapBeforePublish = CapPrefix + nameof(WritePublishBefore); - public const string CapAfterPublish = CapPrefix + nameof(WritePublishAfter); - public const string CapErrorPublish = CapPrefix + nameof(WritePublishError); - - public const string CapBeforeReceiveMessageStore = CapPrefix + nameof(WriteReceiveMessageStoreBefore); - public const string CapAfterReceiveMessageStore = CapPrefix + nameof(WriteReceiveMessageStoreAfter); - public const string CapErrorReceiveMessageStore = CapPrefix + nameof(WriteReceiveMessageStoreError); - - public const string CapBeforeConsumerInvoke = CapPrefix + nameof(WriteConsumerInvokeBefore); - public const string CapAfterConsumerInvoke = CapPrefix + nameof(WriteConsumerInvokeAfter); - public const string CapErrorConsumerInvoke = CapPrefix + nameof(WriteConsumerInvokeError); - - public static Guid WritePublishMessageStoreBefore(this DiagnosticListener @this, CapPublishedMessage message, [CallerMemberName] string operation = "") - { - if (@this.IsEnabled(CapBeforePublishMessageStore)) - { - Guid operationId = Guid.NewGuid(); - - @this.Write(CapBeforePublishMessageStore, new - { - OperationId = operationId, - Operation = operation, - MessageName = message.Name, - MessageContent = message.Content - }); - - return operationId; - } - return Guid.Empty; - } - - public static void WritePublishMessageStoreAfter(this DiagnosticListener @this, Guid operationId, CapPublishedMessage message, [CallerMemberName] string operation = "") - { - if (@this.IsEnabled(CapAfterPublishMessageStore)) - { - @this.Write(CapAfterPublishMessageStore, new - { - OperationId = operationId, - Operation = operation, - MessageId = message.Id, - MessageName = message.Name, - MessageContent = message.Content, - Timestamp = Stopwatch.GetTimestamp() - }); - } - } - - public static void WritePublishMessageStoreError(this DiagnosticListener @this, Guid operationId, - CapPublishedMessage message, Exception ex, [CallerMemberName] string operation = "") - { - if (@this.IsEnabled(CapErrorPublishMessageStore)) - { - @this.Write(CapErrorPublishMessageStore, new - { - OperationId = operationId, - Operation = operation, - MessageName = message.Name, - MessageContent = message.Content, - Exception = ex, - Timestamp = Stopwatch.GetTimestamp() - }); - } - } - - public static Guid WritePublishBefore(this DiagnosticListener @this, CapPublishedMessage message, [CallerMemberName] string operation = "") - { - if (@this.IsEnabled(CapBeforePublish)) - { - Guid operationId = Guid.NewGuid(); - - @this.Write(CapBeforePublish, new - { - OperationId = operationId, - Operation = operation, - MessageId = message.Id, - MessageName = message.Name, - MessageContent = message.Content - }); - - return operationId; - } - return Guid.Empty; - } - - public static void WritePublishAfter(this DiagnosticListener @this, Guid operationId, CapPublishedMessage message, [CallerMemberName] string operation = "") - { - if (@this.IsEnabled(CapAfterPublish)) - { - @this.Write(CapAfterPublish, new - { - OperationId = operationId, - Operation = operation, - MessageId = message.Id, - MessageName = message.Name, - MessageContent = message.Content, - Timestamp = Stopwatch.GetTimestamp() - }); - } - } - - public static void WritePublishError(this DiagnosticListener @this, Guid operationId, - CapPublishedMessage message, Exception ex, [CallerMemberName] string operation = "") - { - if (@this.IsEnabled(CapErrorPublish)) - { - @this.Write(CapErrorPublish, new - { - OperationId = operationId, - Operation = operation, - MessageId = message.Id, - MessageName = message.Name, - MessageContent = message.Content, - Exception = ex, - Timestamp = Stopwatch.GetTimestamp() - }); - } - } - - public static Guid WriteReceiveMessageStoreBefore(this DiagnosticListener @this, CapReceivedMessage message, [CallerMemberName] string operation = "") - { - if (@this.IsEnabled(CapBeforeReceiveMessageStore)) - { - Guid operationId = Guid.NewGuid(); - - @this.Write(CapBeforeReceiveMessageStore, new - { - OperationId = operationId, - Operation = operation, - MessageName = message.Name, - MessageContent = message.Content - }); - - return operationId; - } - return Guid.Empty; - } - - public static void WriteReceiveMessageStoreAfter(this DiagnosticListener @this, Guid operationId, CapReceivedMessage message, [CallerMemberName] string operation = "") - { - if (@this.IsEnabled(CapAfterReceiveMessageStore)) - { - @this.Write(CapAfterReceiveMessageStore, new - { - OperationId = operationId, - Operation = operation, - MessageId = message.Id, - MessageName = message.Name, - MessageContent = message.Content, - Timestamp = Stopwatch.GetTimestamp() - }); - } - } - - public static void WriteReceiveMessageStoreError(this DiagnosticListener @this, Guid operationId, - CapReceivedMessage message, Exception ex, [CallerMemberName] string operation = "") - { - if (@this.IsEnabled(CapErrorReceiveMessageStore)) - { - @this.Write(CapErrorReceiveMessageStore, new - { - OperationId = operationId, - Operation = operation, - MessageId = message.Id, - MessageName = message.Name, - MessageContent = message.Content, - Exception = ex, - Timestamp = Stopwatch.GetTimestamp() - }); - } - } - - public static Guid WriteConsumerInvokeBefore(this DiagnosticListener @this, ConsumerContext context, [CallerMemberName] string operation = "") - { - if (@this.IsEnabled(CapBeforeConsumerInvoke)) - { - Guid operationId = Guid.NewGuid(); - - @this.Write(CapBeforeConsumerInvoke, new - { - OperationId = operationId, - Operation = operation, - MethodName = context.ConsumerDescriptor.MethodInfo.Name, - ConsumerGroup = context.ConsumerDescriptor.Attribute.Group, - MessageName = context.DeliverMessage.Name, - MessageContent = context.DeliverMessage.Content, - Timestamp = Stopwatch.GetTimestamp() - }); - - return operationId; - } - return Guid.Empty; - } - - public static void WriteConsumerInvokeAfter(this DiagnosticListener @this, Guid operationId, ConsumerContext context, [CallerMemberName] string operation = "") - { - if (@this.IsEnabled(CapAfterConsumerInvoke)) - { - @this.Write(CapAfterConsumerInvoke, new - { - OperationId = operationId, - Operation = operation, - MethodName = context.ConsumerDescriptor.MethodInfo.Name, - ConsumerGroup = context.ConsumerDescriptor.Attribute.Group, - MessageName = context.DeliverMessage.Name, - MessageContent = context.DeliverMessage.Content, - Timestamp = Stopwatch.GetTimestamp() - }); - } - } - - public static void WriteConsumerInvokeError(this DiagnosticListener @this, Guid operationId, - ConsumerContext context, Exception ex, [CallerMemberName] string operation = "") - { - if (@this.IsEnabled(CapErrorConsumerInvoke)) - { - @this.Write(CapErrorConsumerInvoke, new - { - OperationId = operationId, - Operation = operation, - MethodName = context.ConsumerDescriptor.MethodInfo.Name, - ConsumerGroup = context.ConsumerDescriptor.Attribute.Group, - MessageName = context.DeliverMessage.Name, - MessageContent = context.DeliverMessage.Content, - Exception = ex, - Timestamp = Stopwatch.GetTimestamp() - }); - } - } - } -} From 9d0a453a0c2e17f155f4da72d94af622671db29d Mon Sep 17 00:00:00 2001 From: Savorboard Date: Tue, 10 Apr 2018 20:06:24 +0800 Subject: [PATCH 055/223] add tracingheader helper func --- src/DotNetCore.CAP/Infrastructure/Helper.cs | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/DotNetCore.CAP/Infrastructure/Helper.cs b/src/DotNetCore.CAP/Infrastructure/Helper.cs index 92ae6fb..e67f41c 100644 --- a/src/DotNetCore.CAP/Infrastructure/Helper.cs +++ b/src/DotNetCore.CAP/Infrastructure/Helper.cs @@ -4,6 +4,7 @@ using System; using System.ComponentModel; using System.Reflection; +using DotNetCore.CAP.Diagnostics; using Newtonsoft.Json; using Newtonsoft.Json.Linq; @@ -48,7 +49,7 @@ namespace DotNetCore.CAP.Infrastructure public static long ToTimestamp(DateTime value) { var elapsedTime = value - Epoch; - return (long) elapsedTime.TotalSeconds; + return (long)elapsedTime.TotalSeconds; } @@ -90,6 +91,12 @@ namespace DotNetCore.CAP.Infrastructure return AddJsonProperty(json, "ExceptionMessage", jObject); } + public static string AddTracingHeaderProperty(string json, TracingHeaders headers) + { + var jObject = ToJObject(headers); + return AddJsonProperty(json, "TracingHeaders", jObject); + } + public static bool IsInnerIP(string ipAddress) { bool isInnerIp; @@ -156,6 +163,16 @@ namespace DotNetCore.CAP.Infrastructure }); } + private static JObject ToJObject(TracingHeaders headers) + { + var jobj = new JObject(); + foreach (var keyValuePair in headers) + { + jobj[keyValuePair.Key] = keyValuePair.Value; + } + return jobj; + } + private static string AddJsonProperty(string json, string propertyName, JObject propertyValue) { var jObj = JObject.Parse(json); From af1b1ec6bab0969c96c5b4ab835e6aac9d66705a Mon Sep 17 00:00:00 2001 From: Savorboard Date: Wed, 11 Apr 2018 18:15:23 +0800 Subject: [PATCH 056/223] refactor diagnostics --- .../DiagnosticListenerExtensions.cs | 144 ++++++++---------- .../Diagnostics/EventData.Broker.Consume.cs | 15 +- .../EventData.Broker.ConsumeEnd.cs | 16 +- .../EventData.Broker.ConsumeError.cs | 18 ++- .../Diagnostics/EventData.Broker.Publish.cs | 15 +- .../EventData.Broker.PublishEnd.cs | 16 +- .../EventData.Broker.PublishError.cs | 18 ++- .../Diagnostics/EventData.Broker.cs | 26 ++-- .../Diagnostics/EventData.SubscriberInvoke.cs | 31 ++-- .../EventData.SubscriberInvokeEnd.cs | 11 +- .../EventData.SubscriberInvokeError.cs | 11 +- src/DotNetCore.CAP/Diagnostics/EventData.cs | 7 +- .../Diagnostics/IErrorEventData.cs | 9 +- 13 files changed, 178 insertions(+), 159 deletions(-) diff --git a/src/DotNetCore.CAP/Diagnostics/DiagnosticListenerExtensions.cs b/src/DotNetCore.CAP/Diagnostics/DiagnosticListenerExtensions.cs index b9b5ba4..154309f 100644 --- a/src/DotNetCore.CAP/Diagnostics/DiagnosticListenerExtensions.cs +++ b/src/DotNetCore.CAP/Diagnostics/DiagnosticListenerExtensions.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using System.Diagnostics; using System.Runtime.CompilerServices; using DotNetCore.CAP.Internal; @@ -23,21 +26,25 @@ namespace DotNetCore.CAP.Diagnostics public const string CapAfterPublish = CapPrefix + nameof(WritePublishAfter); public const string CapErrorPublish = CapPrefix + nameof(WritePublishError); - public const string CapBeforeReceiveMessageStore = CapPrefix + nameof(WriteReceiveMessageStoreBefore); - public const string CapAfterReceiveMessageStore = CapPrefix + nameof(WriteReceiveMessageStoreAfter); - public const string CapErrorReceiveMessageStore = CapPrefix + nameof(WriteReceiveMessageStoreError); + public const string CapBeforeConsume = CapPrefix + nameof(WriteConsumeBefore); + public const string CapAfterConsume = CapPrefix + nameof(WriteConsumeAfter); + public const string CapErrorConsume = CapPrefix + nameof(WriteConsumeError); + + public const string CapBeforeSubscriberInvoke = CapPrefix + nameof(WriteSubscriberInvokeBefore); + public const string CapAfterSubscriberInvoke = CapPrefix + nameof(WriteSubscriberInvokeAfter); + public const string CapErrorSubscriberInvoke = CapPrefix + nameof(WriteSubscriberInvokeError); - public const string CapBeforeConsumerInvoke = CapPrefix + nameof(WriteConsumerInvokeBefore); - public const string CapAfterConsumerInvoke = CapPrefix + nameof(WriteConsumerInvokeAfter); - public const string CapErrorConsumerInvoke = CapPrefix + nameof(WriteConsumerInvokeError); - public static Guid WritePublishMessageStoreBefore(this DiagnosticListener @this, + //============================================================================ + //==================== Before publish store message ==================== + //============================================================================ + public static Guid WritePublishMessageStoreBefore(this DiagnosticListener @this, CapPublishedMessage message, [CallerMemberName] string operation = "") { if (@this.IsEnabled(CapBeforePublishMessageStore)) { - Guid operationId = Guid.NewGuid(); + var operationId = Guid.NewGuid(); @this.Write(CapBeforePublishMessageStore, new { @@ -49,6 +56,7 @@ namespace DotNetCore.CAP.Diagnostics return operationId; } + return Guid.Empty; } @@ -73,7 +81,7 @@ namespace DotNetCore.CAP.Diagnostics public static void WritePublishMessageStoreError(this DiagnosticListener @this, Guid operationId, - CapPublishedMessage message, + CapPublishedMessage message, Exception ex, [CallerMemberName] string operation = "") { @@ -91,143 +99,112 @@ namespace DotNetCore.CAP.Diagnostics } } - public static Guid WritePublishBefore(this DiagnosticListener @this, - string topic, - string body, - string brokerAddress, - [CallerMemberName] string operation = "") + + //============================================================================ + //==================== Publish ==================== + //============================================================================ + public static void WritePublishBefore(this DiagnosticListener @this, BrokerPublishEventData eventData) { if (@this.IsEnabled(CapBeforePublish)) { - Guid operationId = Guid.NewGuid(); - - @this.Write(CapBeforePublish, new BrokerPublishEventData(operationId, operation, brokerAddress, topic, body, DateTimeOffset.UtcNow)); - - return operationId; + @this.Write(CapBeforePublish, eventData); } - return Guid.Empty; } - public static void WritePublishAfter(this DiagnosticListener @this, - Guid operationId, - string topic, - string body, - string brokerAddress, - DateTimeOffset startTime, - TimeSpan duration, - [CallerMemberName] string operation = "") + public static void WritePublishAfter(this DiagnosticListener @this, BrokerPublishEndEventData eventData) { if (@this.IsEnabled(CapAfterPublish)) { - @this.Write(CapAfterPublish, new BrokerPublishEndEventData(operationId, operation, brokerAddress, topic, body, startTime, duration)); + @this.Write(CapAfterPublish, eventData); } } - public static void WritePublishError(this DiagnosticListener @this, - Guid operationId, - string topic, - string body, - string brokerAddress, - Exception ex, - DateTimeOffset startTime, - TimeSpan duration, - [CallerMemberName] string operation = "") + public static void WritePublishError(this DiagnosticListener @this, BrokerPublishErrorEventData eventData) { if (@this.IsEnabled(CapErrorPublish)) { - @this.Write(CapErrorPublish, new BrokerPublishErrorEventData(operationId, operation, brokerAddress, topic, body, ex, startTime, duration)); + @this.Write(CapErrorPublish, eventData); } } - public static Guid WriteReceiveMessageStoreBefore(this DiagnosticListener @this, - string topic, - string body, - string groupName, - [CallerMemberName] string operation = "") + + //============================================================================ + //==================== Consume ==================== + //============================================================================ + public static Guid WriteConsumeBefore(this DiagnosticListener @this, BrokerConsumeEventData eventData) { - if (@this.IsEnabled(CapBeforeReceiveMessageStore)) + if (@this.IsEnabled(CapBeforeConsume)) { - Guid operationId = Guid.NewGuid(); - - @this.Write(CapBeforePublish, new BrokerConsumeEventData(operationId, operation, groupName, topic, body, DateTimeOffset.UtcNow)); - - return operationId; + @this.Write(CapBeforeConsume, eventData); } + return Guid.Empty; } - public static void WriteReceiveMessageStoreAfter(this DiagnosticListener @this, - Guid operationId, - string topic, - string body, - string groupName, - DateTimeOffset startTime, - TimeSpan duration, - [CallerMemberName] string operation = "") + public static void WriteConsumeAfter(this DiagnosticListener @this, BrokerConsumeEndEventData eventData) { - if (@this.IsEnabled(CapAfterReceiveMessageStore)) + if (@this.IsEnabled(CapAfterConsume)) { - @this.Write(CapAfterPublish, new BrokerConsumeEndEventData(operationId, operation, groupName, topic, body, startTime, duration)); + @this.Write(CapAfterConsume, eventData); } } - public static void WriteReceiveMessageStoreError(this DiagnosticListener @this, - Guid operationId, - string topic, - string body, - string groupName, - Exception ex, - DateTimeOffset startTime, - TimeSpan duration, - [CallerMemberName] string operation = "") + public static void WriteConsumeError(this DiagnosticListener @this, BrokerConsumeErrorEventData eventData) { - if (@this.IsEnabled(CapErrorReceiveMessageStore)) + if (@this.IsEnabled(CapErrorConsume)) { - @this.Write(CapErrorPublish, new BrokerConsumeErrorEventData(operationId, operation, groupName, topic, body, ex, startTime, duration)); + @this.Write(CapErrorConsume, eventData); } } - public static Guid WriteConsumerInvokeBefore(this DiagnosticListener @this, + + //============================================================================ + //==================== SubscriberInvoke ==================== + //============================================================================ + public static Guid WriteSubscriberInvokeBefore(this DiagnosticListener @this, ConsumerContext context, [CallerMemberName] string operation = "") { - if (@this.IsEnabled(CapBeforeConsumerInvoke)) + if (@this.IsEnabled(CapBeforeSubscriberInvoke)) { - Guid operationId = Guid.NewGuid(); + var operationId = Guid.NewGuid(); var methodName = context.ConsumerDescriptor.MethodInfo.Name; var subscribeName = context.ConsumerDescriptor.Attribute.Name; var subscribeGroup = context.ConsumerDescriptor.Attribute.Group; var parameterValues = context.DeliverMessage.Content; - @this.Write(CapBeforePublish, new SubscriberInvokeEventData(operationId, operation, methodName, subscribeName, + @this.Write(CapBeforePublish, new SubscriberInvokeEventData(operationId, operation, methodName, + subscribeName, subscribeGroup, parameterValues, DateTimeOffset.UtcNow)); return operationId; } + return Guid.Empty; } - public static void WriteConsumerInvokeAfter(this DiagnosticListener @this, + public static void WriteSubscriberInvokeAfter(this DiagnosticListener @this, Guid operationId, ConsumerContext context, DateTimeOffset startTime, TimeSpan duration, [CallerMemberName] string operation = "") { - if (@this.IsEnabled(CapAfterConsumerInvoke)) + if (@this.IsEnabled(CapAfterSubscriberInvoke)) { var methodName = context.ConsumerDescriptor.MethodInfo.Name; var subscribeName = context.ConsumerDescriptor.Attribute.Name; var subscribeGroup = context.ConsumerDescriptor.Attribute.Group; var parameterValues = context.DeliverMessage.Content; - @this.Write(CapBeforePublish, new SubscriberInvokeEndEventData(operationId, operation, methodName, subscribeName, + @this.Write(CapBeforePublish, new SubscriberInvokeEndEventData(operationId, operation, methodName, + subscribeName, subscribeGroup, parameterValues, startTime, duration)); } } - public static void WriteConsumerInvokeError(this DiagnosticListener @this, + public static void WriteSubscriberInvokeError(this DiagnosticListener @this, Guid operationId, ConsumerContext context, Exception ex, @@ -235,16 +212,17 @@ namespace DotNetCore.CAP.Diagnostics TimeSpan duration, [CallerMemberName] string operation = "") { - if (@this.IsEnabled(CapErrorConsumerInvoke)) + if (@this.IsEnabled(CapErrorSubscriberInvoke)) { var methodName = context.ConsumerDescriptor.MethodInfo.Name; var subscribeName = context.ConsumerDescriptor.Attribute.Name; var subscribeGroup = context.ConsumerDescriptor.Attribute.Group; var parameterValues = context.DeliverMessage.Content; - @this.Write(CapBeforePublish, new SubscriberInvokeErrorEventData(operationId, operation, methodName, subscribeName, + @this.Write(CapBeforePublish, new SubscriberInvokeErrorEventData(operationId, operation, methodName, + subscribeName, subscribeGroup, parameterValues, ex, startTime, duration)); } } } -} +} \ No newline at end of file diff --git a/src/DotNetCore.CAP/Diagnostics/EventData.Broker.Consume.cs b/src/DotNetCore.CAP/Diagnostics/EventData.Broker.Consume.cs index c6e7454..b24b01a 100644 --- a/src/DotNetCore.CAP/Diagnostics/EventData.Broker.Consume.cs +++ b/src/DotNetCore.CAP/Diagnostics/EventData.Broker.Consume.cs @@ -1,16 +1,19 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; namespace DotNetCore.CAP.Diagnostics { public class BrokerConsumeEventData : BrokerEventData { - public DateTimeOffset StartTime { get; } - - public BrokerConsumeEventData(Guid operationId, string operation, string groupName, + public BrokerConsumeEventData(Guid operationId, string operation, string brokerAddress, string brokerTopicName, string brokerTopicBody, DateTimeOffset startTime) - : base(operationId, operation, groupName, brokerTopicName, brokerTopicBody) + : base(operationId, operation, brokerAddress, brokerTopicName, brokerTopicBody) { StartTime = startTime; } + + public DateTimeOffset StartTime { get; } } -} +} \ No newline at end of file diff --git a/src/DotNetCore.CAP/Diagnostics/EventData.Broker.ConsumeEnd.cs b/src/DotNetCore.CAP/Diagnostics/EventData.Broker.ConsumeEnd.cs index e6bbba9..013eace 100644 --- a/src/DotNetCore.CAP/Diagnostics/EventData.Broker.ConsumeEnd.cs +++ b/src/DotNetCore.CAP/Diagnostics/EventData.Broker.ConsumeEnd.cs @@ -1,16 +1,20 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; namespace DotNetCore.CAP.Diagnostics { public class BrokerConsumeEndEventData : BrokerConsumeEventData { - public TimeSpan Duration { get; } - - public BrokerConsumeEndEventData(Guid operationId, string operation, string groupName, string brokerTopicName, + public BrokerConsumeEndEventData(Guid operationId, string operation, string brokerAddress, + string brokerTopicName, string brokerTopicBody, DateTimeOffset startTime, TimeSpan duration) - : base(operationId, operation, groupName, brokerTopicName, brokerTopicBody, startTime) + : base(operationId, operation, brokerAddress, brokerTopicName, brokerTopicBody, startTime) { Duration = duration; } + + public TimeSpan Duration { get; } } -} +} \ No newline at end of file diff --git a/src/DotNetCore.CAP/Diagnostics/EventData.Broker.ConsumeError.cs b/src/DotNetCore.CAP/Diagnostics/EventData.Broker.ConsumeError.cs index 36c1300..36af350 100644 --- a/src/DotNetCore.CAP/Diagnostics/EventData.Broker.ConsumeError.cs +++ b/src/DotNetCore.CAP/Diagnostics/EventData.Broker.ConsumeError.cs @@ -1,16 +1,20 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; namespace DotNetCore.CAP.Diagnostics { public class BrokerConsumeErrorEventData : BrokerConsumeEndEventData, IErrorEventData { - public Exception Exception { get; } - - public BrokerConsumeErrorEventData(Guid operationId, string operation, string groupName, - string brokerTopicName, string brokerTopicBody, Exception exception, DateTimeOffset startTime, TimeSpan duration) - : base(operationId, operation, groupName, brokerTopicName, brokerTopicBody, startTime, duration) + public BrokerConsumeErrorEventData(Guid operationId, string operation, string brokerAddress, + string brokerTopicName, string brokerTopicBody, Exception exception, DateTimeOffset startTime, + TimeSpan duration) + : base(operationId, operation, brokerAddress, brokerTopicName, brokerTopicBody, startTime, duration) { Exception = exception; } + + public Exception Exception { get; } } -} +} \ No newline at end of file diff --git a/src/DotNetCore.CAP/Diagnostics/EventData.Broker.Publish.cs b/src/DotNetCore.CAP/Diagnostics/EventData.Broker.Publish.cs index b7f904b..320f956 100644 --- a/src/DotNetCore.CAP/Diagnostics/EventData.Broker.Publish.cs +++ b/src/DotNetCore.CAP/Diagnostics/EventData.Broker.Publish.cs @@ -1,16 +1,19 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; namespace DotNetCore.CAP.Diagnostics { public class BrokerPublishEventData : BrokerEventData { - public DateTimeOffset StartTime { get; } - - public BrokerPublishEventData(Guid operationId, string operation, string groupName, + public BrokerPublishEventData(Guid operationId, string operation, string brokerAddress, string brokerTopicName, string brokerTopicBody, DateTimeOffset startTime) - : base(operationId, operation, groupName, brokerTopicName, brokerTopicBody) + : base(operationId, operation, brokerAddress, brokerTopicName, brokerTopicBody) { StartTime = startTime; } + + public DateTimeOffset StartTime { get; } } -} +} \ No newline at end of file diff --git a/src/DotNetCore.CAP/Diagnostics/EventData.Broker.PublishEnd.cs b/src/DotNetCore.CAP/Diagnostics/EventData.Broker.PublishEnd.cs index e415647..bc3d0cb 100644 --- a/src/DotNetCore.CAP/Diagnostics/EventData.Broker.PublishEnd.cs +++ b/src/DotNetCore.CAP/Diagnostics/EventData.Broker.PublishEnd.cs @@ -1,16 +1,20 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; namespace DotNetCore.CAP.Diagnostics { public class BrokerPublishEndEventData : BrokerPublishEventData { - public TimeSpan Duration { get; } - - public BrokerPublishEndEventData(Guid operationId, string operation, string groupName, string brokerTopicName, + public BrokerPublishEndEventData(Guid operationId, string operation, string brokerAddress, + string brokerTopicName, string brokerTopicBody, DateTimeOffset startTime, TimeSpan duration) - : base(operationId, operation, groupName, brokerTopicName, brokerTopicBody, startTime) + : base(operationId, operation, brokerAddress, brokerTopicName, brokerTopicBody, startTime) { Duration = duration; } + + public TimeSpan Duration { get; } } -} +} \ No newline at end of file diff --git a/src/DotNetCore.CAP/Diagnostics/EventData.Broker.PublishError.cs b/src/DotNetCore.CAP/Diagnostics/EventData.Broker.PublishError.cs index fa4f8cb..ec44e3b 100644 --- a/src/DotNetCore.CAP/Diagnostics/EventData.Broker.PublishError.cs +++ b/src/DotNetCore.CAP/Diagnostics/EventData.Broker.PublishError.cs @@ -1,16 +1,20 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; namespace DotNetCore.CAP.Diagnostics { public class BrokerPublishErrorEventData : BrokerPublishEndEventData, IErrorEventData { - public Exception Exception { get; } - - public BrokerPublishErrorEventData(Guid operationId, string operation, string groupName, - string brokerTopicName, string brokerTopicBody, Exception exception, DateTimeOffset startTime, TimeSpan duration) - : base(operationId, operation, groupName, brokerTopicName, brokerTopicBody, startTime, duration) + public BrokerPublishErrorEventData(Guid operationId, string operation, string brokerAddress, + string brokerTopicName, string brokerTopicBody, Exception exception, DateTimeOffset startTime, + TimeSpan duration) + : base(operationId, operation, brokerAddress, brokerTopicName, brokerTopicBody, startTime, duration) { Exception = exception; } + + public Exception Exception { get; } } -} +} \ No newline at end of file diff --git a/src/DotNetCore.CAP/Diagnostics/EventData.Broker.cs b/src/DotNetCore.CAP/Diagnostics/EventData.Broker.cs index eef7ac1..b0a6b20 100644 --- a/src/DotNetCore.CAP/Diagnostics/EventData.Broker.cs +++ b/src/DotNetCore.CAP/Diagnostics/EventData.Broker.cs @@ -1,22 +1,28 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; namespace DotNetCore.CAP.Diagnostics { public class BrokerEventData : EventData { - public string GroupName { get; set; } - - public string BrokerTopicBody { get; set; } - - public string BrokerTopicName { get; set; } - - public BrokerEventData(Guid operationId, string operation, string groupName, + public BrokerEventData(Guid operationId, string operation, string brokerAddress, string brokerTopicName, string brokerTopicBody) : base(operationId, operation) { - GroupName = groupName; + Headers = new TracingHeaders(); + BrokerAddress = brokerAddress; BrokerTopicName = brokerTopicName; BrokerTopicBody = brokerTopicBody; } + + public TracingHeaders Headers { get; set; } + + public string BrokerAddress { get; set; } + + public string BrokerTopicBody { get; set; } + + public string BrokerTopicName { get; set; } } -} +} \ No newline at end of file diff --git a/src/DotNetCore.CAP/Diagnostics/EventData.SubscriberInvoke.cs b/src/DotNetCore.CAP/Diagnostics/EventData.SubscriberInvoke.cs index 021fff9..2302eec 100644 --- a/src/DotNetCore.CAP/Diagnostics/EventData.SubscriberInvoke.cs +++ b/src/DotNetCore.CAP/Diagnostics/EventData.SubscriberInvoke.cs @@ -1,25 +1,18 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; namespace DotNetCore.CAP.Diagnostics { public class SubscriberInvokeEventData : EventData { - public DateTimeOffset StartTime { get; } - - public string MethodName { get; set; } - - public string SubscribeName { get; set; } - - public string SubscribeGroup { get; set; } - - public string ParameterValues { get; set; } - public SubscriberInvokeEventData(Guid operationId, string operation, - string methodName, + string methodName, string subscribeName, string subscribeGroup, - string parameterValues, + string parameterValues, DateTimeOffset startTime) : base(operationId, operation) { @@ -29,5 +22,15 @@ namespace DotNetCore.CAP.Diagnostics ParameterValues = parameterValues; StartTime = startTime; } + + public DateTimeOffset StartTime { get; } + + public string MethodName { get; set; } + + public string SubscribeName { get; set; } + + public string SubscribeGroup { get; set; } + + public string ParameterValues { get; set; } } -} +} \ No newline at end of file diff --git a/src/DotNetCore.CAP/Diagnostics/EventData.SubscriberInvokeEnd.cs b/src/DotNetCore.CAP/Diagnostics/EventData.SubscriberInvokeEnd.cs index 38358a7..2a24ca1 100644 --- a/src/DotNetCore.CAP/Diagnostics/EventData.SubscriberInvokeEnd.cs +++ b/src/DotNetCore.CAP/Diagnostics/EventData.SubscriberInvokeEnd.cs @@ -1,11 +1,12 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; namespace DotNetCore.CAP.Diagnostics { public class SubscriberInvokeEndEventData : SubscriberInvokeEventData { - public TimeSpan Duration { get; } - public SubscriberInvokeEndEventData(Guid operationId, string operation, string methodName, string subscribeName, string subscribeGroup, string parameterValues, DateTimeOffset startTime, TimeSpan duration) @@ -13,5 +14,7 @@ namespace DotNetCore.CAP.Diagnostics { Duration = duration; } + + public TimeSpan Duration { get; } } -} +} \ No newline at end of file diff --git a/src/DotNetCore.CAP/Diagnostics/EventData.SubscriberInvokeError.cs b/src/DotNetCore.CAP/Diagnostics/EventData.SubscriberInvokeError.cs index be7a361..005f70f 100644 --- a/src/DotNetCore.CAP/Diagnostics/EventData.SubscriberInvokeError.cs +++ b/src/DotNetCore.CAP/Diagnostics/EventData.SubscriberInvokeError.cs @@ -1,11 +1,12 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; namespace DotNetCore.CAP.Diagnostics { public class SubscriberInvokeErrorEventData : SubscriberInvokeEndEventData, IErrorEventData { - public Exception Exception { get; } - public SubscriberInvokeErrorEventData(Guid operationId, string operation, string methodName, string subscribeName, string subscribeGroup, string parameterValues, Exception exception, DateTimeOffset startTime, TimeSpan duration) : base(operationId, operation, methodName, subscribeName, @@ -13,5 +14,7 @@ namespace DotNetCore.CAP.Diagnostics { Exception = exception; } + + public Exception Exception { get; } } -} +} \ No newline at end of file diff --git a/src/DotNetCore.CAP/Diagnostics/EventData.cs b/src/DotNetCore.CAP/Diagnostics/EventData.cs index 94c01b8..b773780 100644 --- a/src/DotNetCore.CAP/Diagnostics/EventData.cs +++ b/src/DotNetCore.CAP/Diagnostics/EventData.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; namespace DotNetCore.CAP.Diagnostics { @@ -14,4 +17,4 @@ namespace DotNetCore.CAP.Diagnostics public string Operation { get; set; } } -} +} \ No newline at end of file diff --git a/src/DotNetCore.CAP/Diagnostics/IErrorEventData.cs b/src/DotNetCore.CAP/Diagnostics/IErrorEventData.cs index 56c2278..e0217c6 100644 --- a/src/DotNetCore.CAP/Diagnostics/IErrorEventData.cs +++ b/src/DotNetCore.CAP/Diagnostics/IErrorEventData.cs @@ -1,6 +1,7 @@ -using System; -using System.Collections.Generic; -using System.Text; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; namespace DotNetCore.CAP.Diagnostics { @@ -8,4 +9,4 @@ namespace DotNetCore.CAP.Diagnostics { Exception Exception { get; } } -} +} \ No newline at end of file From 5e71b8cd1fdf8906a8d9aa24af41a79189f452fd Mon Sep 17 00:00:00 2001 From: Savorboard Date: Thu, 12 Apr 2018 16:46:35 +0800 Subject: [PATCH 057/223] add TryExtractTracingHeaders --- src/DotNetCore.CAP/Infrastructure/Helper.cs | 26 ++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/src/DotNetCore.CAP/Infrastructure/Helper.cs b/src/DotNetCore.CAP/Infrastructure/Helper.cs index e67f41c..83d128f 100644 --- a/src/DotNetCore.CAP/Infrastructure/Helper.cs +++ b/src/DotNetCore.CAP/Infrastructure/Helper.cs @@ -2,7 +2,9 @@ // Licensed under the MIT License. See License.txt in the project root for license information. using System; +using System.Collections.Generic; using System.ComponentModel; +using System.Linq; using System.Reflection; using DotNetCore.CAP.Diagnostics; using Newtonsoft.Json; @@ -94,7 +96,29 @@ namespace DotNetCore.CAP.Infrastructure public static string AddTracingHeaderProperty(string json, TracingHeaders headers) { var jObject = ToJObject(headers); - return AddJsonProperty(json, "TracingHeaders", jObject); + return AddJsonProperty(json, nameof(TracingHeaders), jObject); + } + + public static bool TryExtractTracingHeaders(string json, out TracingHeaders headers, out string removedHeadersJson) + { + var jObj = JObject.Parse(json); + var jToken = jObj[nameof(TracingHeaders)]; + if (jToken != null) + { + headers = new TracingHeaders(); + foreach (var item in jToken.ToObject>()) + { + headers.Add(item.Key, item.Value); + } + + jObj.Remove(nameof(TracingHeaders)); + removedHeadersJson = jObj.ToString(); + return true; + } + + headers = null; + removedHeadersJson = null; + return false; } public static bool IsInnerIP(string ipAddress) From 79e08d78cd11d88832c708b8ae9ac26f605f1175 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Thu, 12 Apr 2018 16:47:18 +0800 Subject: [PATCH 058/223] Add support for Diganostics --- .../IPublishMessageSender.Kafka.cs | 18 +---- .../KafkaConsumerClient.cs | 2 + .../IPublishMessageSender.RabbitMQ.cs | 14 +--- .../RabbitMQConsumerClient.cs | 2 + .../DiagnosticListenerExtensions.cs | 6 +- src/DotNetCore.CAP/IConsumerClient.cs | 2 + .../IConsumerHandler.Default.cs | 75 ++++++++++++++----- .../IPublishMessageSender.Base.cs | 66 +++++++++++++++- .../ISubscribeExecutor.Default.cs | 6 +- 9 files changed, 133 insertions(+), 58 deletions(-) diff --git a/src/DotNetCore.CAP.Kafka/IPublishMessageSender.Kafka.cs b/src/DotNetCore.CAP.Kafka/IPublishMessageSender.Kafka.cs index 3e95be2..f3e4e5d 100644 --- a/src/DotNetCore.CAP.Kafka/IPublishMessageSender.Kafka.cs +++ b/src/DotNetCore.CAP.Kafka/IPublishMessageSender.Kafka.cs @@ -2,10 +2,8 @@ // Licensed under the MIT License. See License.txt in the project root for license information. using System; -using System.Diagnostics; using System.Text; using System.Threading.Tasks; -using DotNetCore.CAP.Diagnostics; using DotNetCore.CAP.Internal; using DotNetCore.CAP.Processor.States; using Microsoft.Extensions.Logging; @@ -16,7 +14,6 @@ namespace DotNetCore.CAP.Kafka { private readonly IConnectionPool _connectionPool; private readonly ILogger _logger; - private readonly string _serversAddress; public KafkaPublishMessageSender( CapOptions options, IStateChanger stateChanger, IStorageConnection connection, @@ -25,21 +22,15 @@ namespace DotNetCore.CAP.Kafka { _logger = logger; _connectionPool = connectionPool; - _serversAddress = _connectionPool.ServersAddress; + ServersAddress = _connectionPool.ServersAddress; } public override async Task PublishAsync(string keyName, string content) { - var startTime = DateTimeOffset.UtcNow; - var stopwatch = Stopwatch.StartNew(); - Guid operationId = Guid.Empty; - var producer = _connectionPool.Rent(); try { - operationId = s_diagnosticListener.WritePublishBefore(keyName, content, _serversAddress); - var contentBytes = Encoding.UTF8.GetBytes(content); var message = await producer.ProduceAsync(keyName, null, contentBytes); @@ -52,16 +43,12 @@ namespace DotNetCore.CAP.Kafka }); } - s_diagnosticListener.WritePublishAfter(operationId, message.Topic, content, _serversAddress, startTime, stopwatch.Elapsed); - _logger.LogDebug($"kafka topic message [{keyName}] has been published."); return OperateResult.Success; } catch (Exception ex) { - s_diagnosticListener.WritePublishError(operationId, keyName, content, _serversAddress, ex, startTime, stopwatch.Elapsed); - var wapperEx = new PublisherSentFailedException(ex.Message, ex); return OperateResult.Failed(wapperEx); @@ -74,7 +61,6 @@ namespace DotNetCore.CAP.Kafka producer.Dispose(); } } - } - + } } } \ No newline at end of file diff --git a/src/DotNetCore.CAP.Kafka/KafkaConsumerClient.cs b/src/DotNetCore.CAP.Kafka/KafkaConsumerClient.cs index ccce448..f279afb 100644 --- a/src/DotNetCore.CAP.Kafka/KafkaConsumerClient.cs +++ b/src/DotNetCore.CAP.Kafka/KafkaConsumerClient.cs @@ -29,6 +29,8 @@ namespace DotNetCore.CAP.Kafka public event EventHandler OnLog; + public string ServersAddress => _kafkaOptions.Servers; + public void Subscribe(IEnumerable topics) { if (topics == null) diff --git a/src/DotNetCore.CAP.RabbitMQ/IPublishMessageSender.RabbitMQ.cs b/src/DotNetCore.CAP.RabbitMQ/IPublishMessageSender.RabbitMQ.cs index 2cb15c2..3dacba3 100644 --- a/src/DotNetCore.CAP.RabbitMQ/IPublishMessageSender.RabbitMQ.cs +++ b/src/DotNetCore.CAP.RabbitMQ/IPublishMessageSender.RabbitMQ.cs @@ -2,10 +2,8 @@ // Licensed under the MIT License. See License.txt in the project root for license information. using System; -using System.Diagnostics; using System.Text; using System.Threading.Tasks; -using DotNetCore.CAP.Diagnostics; using DotNetCore.CAP.Internal; using DotNetCore.CAP.Processor.States; using Microsoft.Extensions.Logging; @@ -18,7 +16,6 @@ namespace DotNetCore.CAP.RabbitMQ private readonly IConnectionChannelPool _connectionChannelPool; private readonly ILogger _logger; private readonly string _exchange; - private readonly string _hostAddress; public RabbitMQPublishMessageSender(ILogger logger, CapOptions options, IStorageConnection connection, IConnectionChannelPool connectionChannelPool, IStateChanger stateChanger) @@ -27,33 +24,24 @@ namespace DotNetCore.CAP.RabbitMQ _logger = logger; _connectionChannelPool = connectionChannelPool; _exchange = _connectionChannelPool.Exchange; - _hostAddress = _connectionChannelPool.HostAddress; + ServersAddress = _connectionChannelPool.HostAddress; } public override Task PublishAsync(string keyName, string content) { - var startTime = DateTimeOffset.UtcNow; - var stopwatch = Stopwatch.StartNew(); - Guid operationId = Guid.Empty; - var channel = _connectionChannelPool.Rent(); try { - operationId = s_diagnosticListener.WritePublishBefore(keyName, content, _hostAddress); - var body = Encoding.UTF8.GetBytes(content); channel.ExchangeDeclare(_exchange, RabbitMQOptions.ExchangeType, true); channel.BasicPublish(_exchange, keyName, null, body); - s_diagnosticListener.WritePublishAfter(operationId, keyName, content, _hostAddress, startTime, stopwatch.Elapsed); _logger.LogDebug($"RabbitMQ topic message [{keyName}] has been published."); return Task.FromResult(OperateResult.Success); } catch (Exception ex) { - s_diagnosticListener.WritePublishError(operationId, keyName, content, _hostAddress, ex, startTime, stopwatch.Elapsed); - var wapperEx = new PublisherSentFailedException(ex.Message, ex); var errors = new OperateError { diff --git a/src/DotNetCore.CAP.RabbitMQ/RabbitMQConsumerClient.cs b/src/DotNetCore.CAP.RabbitMQ/RabbitMQConsumerClient.cs index 402d0fa..a34054f 100644 --- a/src/DotNetCore.CAP.RabbitMQ/RabbitMQConsumerClient.cs +++ b/src/DotNetCore.CAP.RabbitMQ/RabbitMQConsumerClient.cs @@ -37,6 +37,8 @@ namespace DotNetCore.CAP.RabbitMQ public event EventHandler OnLog; + public string ServersAddress => _rabbitMQOptions.HostName; + public void Subscribe(IEnumerable topics) { if (topics == null) diff --git a/src/DotNetCore.CAP/Diagnostics/DiagnosticListenerExtensions.cs b/src/DotNetCore.CAP/Diagnostics/DiagnosticListenerExtensions.cs index 154309f..e833a8e 100644 --- a/src/DotNetCore.CAP/Diagnostics/DiagnosticListenerExtensions.cs +++ b/src/DotNetCore.CAP/Diagnostics/DiagnosticListenerExtensions.cs @@ -174,7 +174,7 @@ namespace DotNetCore.CAP.Diagnostics var subscribeGroup = context.ConsumerDescriptor.Attribute.Group; var parameterValues = context.DeliverMessage.Content; - @this.Write(CapBeforePublish, new SubscriberInvokeEventData(operationId, operation, methodName, + @this.Write(CapBeforeSubscriberInvoke, new SubscriberInvokeEventData(operationId, operation, methodName, subscribeName, subscribeGroup, parameterValues, DateTimeOffset.UtcNow)); @@ -198,7 +198,7 @@ namespace DotNetCore.CAP.Diagnostics var subscribeGroup = context.ConsumerDescriptor.Attribute.Group; var parameterValues = context.DeliverMessage.Content; - @this.Write(CapBeforePublish, new SubscriberInvokeEndEventData(operationId, operation, methodName, + @this.Write(CapAfterSubscriberInvoke, new SubscriberInvokeEndEventData(operationId, operation, methodName, subscribeName, subscribeGroup, parameterValues, startTime, duration)); } @@ -219,7 +219,7 @@ namespace DotNetCore.CAP.Diagnostics var subscribeGroup = context.ConsumerDescriptor.Attribute.Group; var parameterValues = context.DeliverMessage.Content; - @this.Write(CapBeforePublish, new SubscriberInvokeErrorEventData(operationId, operation, methodName, + @this.Write(CapErrorSubscriberInvoke, new SubscriberInvokeErrorEventData(operationId, operation, methodName, subscribeName, subscribeGroup, parameterValues, ex, startTime, duration)); } diff --git a/src/DotNetCore.CAP/IConsumerClient.cs b/src/DotNetCore.CAP/IConsumerClient.cs index 8b46e71..150bc4c 100644 --- a/src/DotNetCore.CAP/IConsumerClient.cs +++ b/src/DotNetCore.CAP/IConsumerClient.cs @@ -13,6 +13,8 @@ namespace DotNetCore.CAP /// public interface IConsumerClient : IDisposable { + string ServersAddress { get; } + /// /// Subscribe to a set of topics to the message queue /// diff --git a/src/DotNetCore.CAP/IConsumerHandler.Default.cs b/src/DotNetCore.CAP/IConsumerHandler.Default.cs index 43e34df..8d48a37 100644 --- a/src/DotNetCore.CAP/IConsumerHandler.Default.cs +++ b/src/DotNetCore.CAP/IConsumerHandler.Default.cs @@ -24,6 +24,7 @@ namespace DotNetCore.CAP private readonly TimeSpan _pollingDelay = TimeSpan.FromSeconds(1); private readonly MethodMatcherCache _selector; + private string _serverAddress; private Task _compositeTask; private bool _disposed; @@ -56,6 +57,8 @@ namespace DotNetCore.CAP { using (var client = _consumerClientFactory.Create(matchGroup.Key)) { + _serverAddress = client.ServersAddress; + RegisterMessageProcessor(client); client.Subscribe(matchGroup.Value.Select(x => x.Attribute.Name)); @@ -102,30 +105,24 @@ namespace DotNetCore.CAP { var startTime = DateTimeOffset.UtcNow; var stopwatch = Stopwatch.StartNew(); - var operationId = Guid.Empty; + + var tracingResult = TracingBefore(messageContext.Name, messageContext.Content); + var operationId = tracingResult.Item1; + var messageBody = tracingResult.Item2; var receivedMessage = new CapReceivedMessage(messageContext) { - StatusName = StatusName.Scheduled + StatusName = StatusName.Scheduled, + Content = messageBody }; try { - operationId = s_diagnosticListener.WriteReceiveMessageStoreBefore( - messageContext.Name, - messageContext.Content, - messageContext.Group); - StoreMessage(receivedMessage); client.Commit(); - s_diagnosticListener.WriteReceiveMessageStoreAfter( - operationId, - messageContext.Name, - messageContext.Content, - messageContext.Group, - startTime, + TracingAfter(operationId, receivedMessage.Name, receivedMessage.Content, startTime, stopwatch.Elapsed); _dispatcher.EnqueueToExecute(receivedMessage); @@ -136,12 +133,7 @@ namespace DotNetCore.CAP client.Reject(); - s_diagnosticListener.WriteReceiveMessageStoreError(operationId, - messageContext.Name, - messageContext.Content, - messageContext.Group, - e, - startTime, + TracingError(operationId, receivedMessage.Name, receivedMessage.Content, e, startTime, stopwatch.Elapsed); } }; @@ -183,5 +175,50 @@ namespace DotNetCore.CAP receivedMessage.Id = id; } + + private (Guid, string) TracingBefore(string topic, string values) + { + Guid operationId = Guid.NewGuid(); + + var eventData = new BrokerConsumeEventData( + operationId, "", + _serverAddress, + topic, + values, + DateTimeOffset.UtcNow); + + s_diagnosticListener.WriteConsumeBefore(eventData); + + return (operationId, eventData.BrokerTopicBody); + } + + private void TracingAfter(Guid operationId, string topic, string values, DateTimeOffset startTime, TimeSpan du) + { + var eventData = new BrokerConsumeEndEventData( + operationId, + "", + _serverAddress, + topic, + values, + startTime, + du); + + s_diagnosticListener.WriteConsumeAfter(eventData); + } + + private void TracingError(Guid operationId, string topic, string values, Exception ex, DateTimeOffset startTime, TimeSpan du) + { + var eventData = new BrokerConsumeErrorEventData( + operationId, + "", + _serverAddress, + topic, + values, + ex, + startTime, + du); + + s_diagnosticListener.WriteConsumeError(eventData); + } } } \ No newline at end of file diff --git a/src/DotNetCore.CAP/IPublishMessageSender.Base.cs b/src/DotNetCore.CAP/IPublishMessageSender.Base.cs index 5b6588d..eb37cfc 100644 --- a/src/DotNetCore.CAP/IPublishMessageSender.Base.cs +++ b/src/DotNetCore.CAP/IPublishMessageSender.Base.cs @@ -21,6 +21,8 @@ namespace DotNetCore.CAP private readonly CapOptions _options; private readonly IStateChanger _stateChanger; + protected string ServersAddress { get; set; } + // diagnostics listener // ReSharper disable once InconsistentNaming protected static readonly DiagnosticListener s_diagnosticListener = @@ -42,25 +44,35 @@ namespace DotNetCore.CAP public async Task SendAsync(CapPublishedMessage message) { - var sp = Stopwatch.StartNew(); + var startTime = DateTimeOffset.UtcNow; + var stopwatch = Stopwatch.StartNew(); + + var tracingResult = TracingBefore(message.Name, message.Content); + var operationId = tracingResult.Item1; - var result = await PublishAsync(message.Name, message.Content); + var sendValues = tracingResult.Item2 != null + ? Helper.AddTracingHeaderProperty(message.Content, tracingResult.Item2) + : message.Content; - sp.Stop(); + var result = await PublishAsync(message.Name, sendValues); + stopwatch.Stop(); if (result.Succeeded) { await SetSuccessfulState(message); - _logger.MessageHasBeenSent(sp.Elapsed.TotalSeconds); + TracingAfter(operationId, message.Name, sendValues, startTime, stopwatch.Elapsed); return OperateResult.Success; } else { + TracingError(operationId, message.Name, sendValues, result.Exception, startTime, stopwatch.Elapsed); + _logger.MessagePublishException(message.Id, result.Exception); await SetFailedState(message, result.Exception, out bool stillRetry); + if (stillRetry) { _logger.SenderRetrying(3); @@ -122,5 +134,51 @@ namespace DotNetCore.CAP { message.Content = Helper.AddExceptionProperty(message.Content, exception); } + + private (Guid, TracingHeaders) TracingBefore(string topic, string values) + { + Guid operationId = Guid.NewGuid(); + + var eventData = new BrokerPublishEventData( + operationId, "", + ServersAddress, topic, + values, + DateTimeOffset.UtcNow); + + s_diagnosticListener.WritePublishBefore(eventData); + + return (operationId, eventData.Headers); + } + + private void TracingAfter(Guid operationId, string topic, string values, DateTimeOffset startTime, TimeSpan du) + { + var eventData = new BrokerPublishEndEventData( + operationId, + "", + ServersAddress, + topic, + values, + startTime, + du); + + s_diagnosticListener.WritePublishAfter(eventData); + + _logger.MessageHasBeenSent(du.TotalSeconds); + } + + private void TracingError(Guid operationId, string topic, string values, Exception ex, DateTimeOffset startTime, TimeSpan du) + { + var eventData = new BrokerPublishErrorEventData( + operationId, + "", + ServersAddress, + topic, + values, + ex, + startTime, + du); + + s_diagnosticListener.WritePublishError(eventData); + } } } \ No newline at end of file diff --git a/src/DotNetCore.CAP/ISubscribeExecutor.Default.cs b/src/DotNetCore.CAP/ISubscribeExecutor.Default.cs index 0629852..92f988e 100644 --- a/src/DotNetCore.CAP/ISubscribeExecutor.Default.cs +++ b/src/DotNetCore.CAP/ISubscribeExecutor.Default.cs @@ -153,11 +153,11 @@ namespace DotNetCore.CAP try { - operationId = s_diagnosticListener.WriteConsumerInvokeBefore(consumerContext); + operationId = s_diagnosticListener.WriteSubscriberInvokeBefore(consumerContext); var ret = await Invoker.InvokeAsync(consumerContext); - s_diagnosticListener.WriteConsumerInvokeAfter(operationId, consumerContext, startTime, stopwatch.Elapsed); + s_diagnosticListener.WriteSubscriberInvokeAfter(operationId, consumerContext, startTime, stopwatch.Elapsed); if (!string.IsNullOrEmpty(ret.CallbackName)) { @@ -166,7 +166,7 @@ namespace DotNetCore.CAP } catch (Exception ex) { - s_diagnosticListener.WriteConsumerInvokeError(operationId, consumerContext, ex, startTime, stopwatch.Elapsed); + s_diagnosticListener.WriteSubscriberInvokeError(operationId, consumerContext, ex, startTime, stopwatch.Elapsed); throw new SubscriberExecutionFailedException(ex.Message, ex); } From 5f5f0916a69eaa1162e55fabcb6e988948efc0c1 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Thu, 12 Apr 2018 16:51:30 +0800 Subject: [PATCH 059/223] Add tracingheader for support Diagnostics --- .../Diagnostics/TracingHeaders.cs | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 src/DotNetCore.CAP/Diagnostics/TracingHeaders.cs diff --git a/src/DotNetCore.CAP/Diagnostics/TracingHeaders.cs b/src/DotNetCore.CAP/Diagnostics/TracingHeaders.cs new file mode 100644 index 0000000..11093d4 --- /dev/null +++ b/src/DotNetCore.CAP/Diagnostics/TracingHeaders.cs @@ -0,0 +1,49 @@ +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +namespace DotNetCore.CAP.Diagnostics +{ + public class TracingHeaders : IEnumerable> + { + private List> _dataStore; + + public IEnumerator> GetEnumerator() + { + return _dataStore.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public void Add(string name, string value) + { + if (_dataStore == null) + { + _dataStore = new List>(); + } + + _dataStore.Add(new KeyValuePair(name, value)); + } + + public bool Contains(string name) + { + return _dataStore != null && _dataStore.Any(x => x.Key == name); + } + + public void Remove(string name) + { + _dataStore?.RemoveAll(x => x.Key == name); + } + + public void Cleaar() + { + _dataStore?.Clear(); + } + } +} \ No newline at end of file From 8bdb1f0ca6aa420b39b6fdf2f7776f89f8e6229a Mon Sep 17 00:00:00 2001 From: Savorboard Date: Mon, 16 Apr 2018 11:25:53 +0800 Subject: [PATCH 060/223] Add notes to readme. --- README.md | 3 +++ README.zh-cn.md | 3 +++ 2 files changed, 6 insertions(+) diff --git a/README.md b/README.md index 04162cf..8bfb3fb 100644 --- a/README.md +++ b/README.md @@ -187,7 +187,10 @@ Then inject your `ISubscriberService` class in Startup.cs ```c# public void ConfigureServices(IServiceCollection services) { + //Note: The injection of services needs before of `services.AddCap(x=>{});` services.AddTransient(); + + services.AddCap(x=>{}); } ``` diff --git a/README.zh-cn.md b/README.zh-cn.md index 1a3ef04..799d771 100644 --- a/README.zh-cn.md +++ b/README.zh-cn.md @@ -187,7 +187,10 @@ namespace xxx.Service ```c# public void ConfigureServices(IServiceCollection services) { + //注æ„: 注入的æœåŠ¡éœ€è¦åœ¨ `services.AddCap(x=>{});` ä¹‹å‰ services.AddTransient(); + + services.AddCap(x=>{}); } ``` From 7eea5e046882e780006293ea2448ff3e8147bb93 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Mon, 16 Apr 2018 11:28:49 +0800 Subject: [PATCH 061/223] add notes to readme. --- README.md | 4 ++-- README.zh-cn.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 8bfb3fb..cd25182 100644 --- a/README.md +++ b/README.md @@ -187,10 +187,10 @@ Then inject your `ISubscriberService` class in Startup.cs ```c# public void ConfigureServices(IServiceCollection services) { - //Note: The injection of services needs before of `services.AddCap(x=>{});` + //Note: The injection of services needs before of `services.AddCap()` services.AddTransient(); - services.AddCap(x=>{}); + services.AddCap(x=>{}); } ``` diff --git a/README.zh-cn.md b/README.zh-cn.md index 799d771..8ceb14b 100644 --- a/README.zh-cn.md +++ b/README.zh-cn.md @@ -187,10 +187,10 @@ namespace xxx.Service ```c# public void ConfigureServices(IServiceCollection services) { - //注æ„: 注入的æœåŠ¡éœ€è¦åœ¨ `services.AddCap(x=>{});` ä¹‹å‰ + //注æ„: 注入的æœåŠ¡éœ€è¦åœ¨ `services.AddCap()` ä¹‹å‰ services.AddTransient(); - services.AddCap(x=>{}); + services.AddCap(x=>{}); } ``` From 0f6ae8add1ec8422c88ce7d4009d34043e8f4baf Mon Sep 17 00:00:00 2001 From: Savorboard Date: Tue, 17 Apr 2018 14:50:20 +0800 Subject: [PATCH 062/223] update samples. --- CAP.sln | 12 ++++---- .../Controllers/ValuesController.cs | 0 .../Program.cs | 0 .../Sample.Kafka.MySql.csproj | 1 + .../Startup.cs | 12 ++------ .../Properties/launchSettings.json | 29 ------------------- 6 files changed, 9 insertions(+), 45 deletions(-) rename samples/{Sample.Kafka.SqlServer => Sample.Kafka.MySql}/Controllers/ValuesController.cs (100%) rename samples/{Sample.Kafka.SqlServer => Sample.Kafka.MySql}/Program.cs (100%) rename samples/{Sample.Kafka.SqlServer => Sample.Kafka.MySql}/Sample.Kafka.MySql.csproj (93%) rename samples/{Sample.Kafka.SqlServer => Sample.Kafka.MySql}/Startup.cs (60%) delete mode 100644 samples/Sample.Kafka.SqlServer/Properties/launchSettings.json diff --git a/CAP.sln b/CAP.sln index 174a366..952416d 100644 --- a/CAP.sln +++ b/CAP.sln @@ -58,7 +58,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNetCore.CAP.PostgreSql", EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNetCore.CAP.PostgreSql.Test", "test\DotNetCore.CAP.PostgreSql.Test\DotNetCore.CAP.PostgreSql.Test.csproj", "{7CA3625D-1817-4695-881D-7E79A1E1DED2}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sample.Kafka.MySql", "samples\Sample.Kafka.SqlServer\Sample.Kafka.MySql.csproj", "{573B4D39-5489-48B3-9B6C-5234249CB980}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sample.Kafka.MySql", "samples\Sample.Kafka.MySql\Sample.Kafka.MySql.csproj", "{9CB51105-A85B-42A4-AFDE-A4FC34D9EA91}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -109,10 +109,10 @@ Global {7CA3625D-1817-4695-881D-7E79A1E1DED2}.Debug|Any CPU.Build.0 = Debug|Any CPU {7CA3625D-1817-4695-881D-7E79A1E1DED2}.Release|Any CPU.ActiveCfg = Release|Any CPU {7CA3625D-1817-4695-881D-7E79A1E1DED2}.Release|Any CPU.Build.0 = Release|Any CPU - {573B4D39-5489-48B3-9B6C-5234249CB980}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {573B4D39-5489-48B3-9B6C-5234249CB980}.Debug|Any CPU.Build.0 = Debug|Any CPU - {573B4D39-5489-48B3-9B6C-5234249CB980}.Release|Any CPU.ActiveCfg = Release|Any CPU - {573B4D39-5489-48B3-9B6C-5234249CB980}.Release|Any CPU.Build.0 = Release|Any CPU + {9CB51105-A85B-42A4-AFDE-A4FC34D9EA91}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9CB51105-A85B-42A4-AFDE-A4FC34D9EA91}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9CB51105-A85B-42A4-AFDE-A4FC34D9EA91}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9CB51105-A85B-42A4-AFDE-A4FC34D9EA91}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -129,7 +129,7 @@ Global {9F3F9BFE-7B6A-4A7A-A6E6-8B517D611873} = {3A6B6931-A123-477A-9469-8B468B5385AF} {82C403AB-ED68-4084-9A1D-11334F9F08F9} = {9B2AE124-6636-4DE9-83A3-70360DABD0C4} {7CA3625D-1817-4695-881D-7E79A1E1DED2} = {C09CDAB0-6DD4-46E9-B7F3-3EF2A4741EA0} - {573B4D39-5489-48B3-9B6C-5234249CB980} = {3A6B6931-A123-477A-9469-8B468B5385AF} + {9CB51105-A85B-42A4-AFDE-A4FC34D9EA91} = {3A6B6931-A123-477A-9469-8B468B5385AF} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {2E70565D-94CF-40B4-BFE1-AC18D5F736AB} diff --git a/samples/Sample.Kafka.SqlServer/Controllers/ValuesController.cs b/samples/Sample.Kafka.MySql/Controllers/ValuesController.cs similarity index 100% rename from samples/Sample.Kafka.SqlServer/Controllers/ValuesController.cs rename to samples/Sample.Kafka.MySql/Controllers/ValuesController.cs diff --git a/samples/Sample.Kafka.SqlServer/Program.cs b/samples/Sample.Kafka.MySql/Program.cs similarity index 100% rename from samples/Sample.Kafka.SqlServer/Program.cs rename to samples/Sample.Kafka.MySql/Program.cs diff --git a/samples/Sample.Kafka.SqlServer/Sample.Kafka.MySql.csproj b/samples/Sample.Kafka.MySql/Sample.Kafka.MySql.csproj similarity index 93% rename from samples/Sample.Kafka.SqlServer/Sample.Kafka.MySql.csproj rename to samples/Sample.Kafka.MySql/Sample.Kafka.MySql.csproj index 1c5a152..ad812ae 100644 --- a/samples/Sample.Kafka.SqlServer/Sample.Kafka.MySql.csproj +++ b/samples/Sample.Kafka.MySql/Sample.Kafka.MySql.csproj @@ -10,6 +10,7 @@ + diff --git a/samples/Sample.Kafka.SqlServer/Startup.cs b/samples/Sample.Kafka.MySql/Startup.cs similarity index 60% rename from samples/Sample.Kafka.SqlServer/Startup.cs rename to samples/Sample.Kafka.MySql/Startup.cs index cdea862..419e48e 100644 --- a/samples/Sample.Kafka.SqlServer/Startup.cs +++ b/samples/Sample.Kafka.MySql/Startup.cs @@ -1,4 +1,5 @@ -using Microsoft.AspNetCore.Builder; +using System; +using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.DependencyInjection; namespace Sample.Kafka.MySql @@ -12,15 +13,6 @@ namespace Sample.Kafka.MySql x.UseMySql("Server=192.168.10.110;Database=testcap;UserId=root;Password=123123;"); x.UseKafka("192.168.10.110:9092"); x.UseDashboard(); - - //x.UseDiscovery(d => - //{ - // d.DiscoveryServerHostName = "localhost"; - // d.DiscoveryServerPort = 8500; - // d.CurrentNodeHostName = "localhost"; - // d.CurrentNodePort = 5820; - // d.NodeName = "CAP 2å·èŠ‚点"; - //}); }); services.AddMvc(); diff --git a/samples/Sample.Kafka.SqlServer/Properties/launchSettings.json b/samples/Sample.Kafka.SqlServer/Properties/launchSettings.json deleted file mode 100644 index 60815cf..0000000 --- a/samples/Sample.Kafka.SqlServer/Properties/launchSettings.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:57172/", - "sslPort": 0 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "launchUrl": "cap", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Sample.Kafka.SqlServer": { - "commandName": "Project", - "launchBrowser": true, - "launchUrl": "cap", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "http://localhost:57174/" - } - } -} \ No newline at end of file From bd66dc5f184d22c2f326484a7ceab789eedf215c Mon Sep 17 00:00:00 2001 From: Savorboard Date: Wed, 18 Apr 2018 13:03:56 +0800 Subject: [PATCH 063/223] fixed message enqueue exception in v2.2 (#115) --- build/version.props | 2 +- .../Diagnostics/DiagnosticListenerExtensions.cs | 6 ++++++ src/DotNetCore.CAP/Diagnostics/EventData.Broker.cs | 1 - src/DotNetCore.CAP/IPublishMessageSender.Base.cs | 2 +- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/build/version.props b/build/version.props index d705cb4..6f4fc75 100644 --- a/build/version.props +++ b/build/version.props @@ -2,7 +2,7 @@ 2 2 - 0 + 1 $(VersionMajor).$(VersionMinor).$(VersionPatch) diff --git a/src/DotNetCore.CAP/Diagnostics/DiagnosticListenerExtensions.cs b/src/DotNetCore.CAP/Diagnostics/DiagnosticListenerExtensions.cs index e833a8e..da0cc0c 100644 --- a/src/DotNetCore.CAP/Diagnostics/DiagnosticListenerExtensions.cs +++ b/src/DotNetCore.CAP/Diagnostics/DiagnosticListenerExtensions.cs @@ -107,6 +107,7 @@ namespace DotNetCore.CAP.Diagnostics { if (@this.IsEnabled(CapBeforePublish)) { + eventData.Headers = new TracingHeaders(); @this.Write(CapBeforePublish, eventData); } } @@ -115,6 +116,7 @@ namespace DotNetCore.CAP.Diagnostics { if (@this.IsEnabled(CapAfterPublish)) { + eventData.Headers = new TracingHeaders(); @this.Write(CapAfterPublish, eventData); } } @@ -123,6 +125,7 @@ namespace DotNetCore.CAP.Diagnostics { if (@this.IsEnabled(CapErrorPublish)) { + eventData.Headers = new TracingHeaders(); @this.Write(CapErrorPublish, eventData); } } @@ -135,6 +138,7 @@ namespace DotNetCore.CAP.Diagnostics { if (@this.IsEnabled(CapBeforeConsume)) { + eventData.Headers = new TracingHeaders(); @this.Write(CapBeforeConsume, eventData); } @@ -145,6 +149,7 @@ namespace DotNetCore.CAP.Diagnostics { if (@this.IsEnabled(CapAfterConsume)) { + eventData.Headers = new TracingHeaders(); @this.Write(CapAfterConsume, eventData); } } @@ -153,6 +158,7 @@ namespace DotNetCore.CAP.Diagnostics { if (@this.IsEnabled(CapErrorConsume)) { + eventData.Headers = new TracingHeaders(); @this.Write(CapErrorConsume, eventData); } } diff --git a/src/DotNetCore.CAP/Diagnostics/EventData.Broker.cs b/src/DotNetCore.CAP/Diagnostics/EventData.Broker.cs index b0a6b20..2db1794 100644 --- a/src/DotNetCore.CAP/Diagnostics/EventData.Broker.cs +++ b/src/DotNetCore.CAP/Diagnostics/EventData.Broker.cs @@ -11,7 +11,6 @@ namespace DotNetCore.CAP.Diagnostics string brokerTopicName, string brokerTopicBody) : base(operationId, operation) { - Headers = new TracingHeaders(); BrokerAddress = brokerAddress; BrokerTopicName = brokerTopicName; BrokerTopicBody = brokerTopicBody; diff --git a/src/DotNetCore.CAP/IPublishMessageSender.Base.cs b/src/DotNetCore.CAP/IPublishMessageSender.Base.cs index eb37cfc..cc82be6 100644 --- a/src/DotNetCore.CAP/IPublishMessageSender.Base.cs +++ b/src/DotNetCore.CAP/IPublishMessageSender.Base.cs @@ -147,7 +147,7 @@ namespace DotNetCore.CAP s_diagnosticListener.WritePublishBefore(eventData); - return (operationId, eventData.Headers); + return (operationId, eventData.Headers); //if not enabled diagnostics ,the header will be null } private void TracingAfter(Guid operationId, string topic, string values, DateTimeOffset startTime, TimeSpan du) From b735a33ca0bdad9273c0e045bd2ec24e32c48a4d Mon Sep 17 00:00:00 2001 From: Savorboard Date: Sat, 21 Apr 2018 19:40:55 +0800 Subject: [PATCH 064/223] rename file. --- ...nalProcessor.Default.cs => ICollectProcessor.MySql.cs} | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) rename src/DotNetCore.CAP.MySql/{IAdditionalProcessor.Default.cs => ICollectProcessor.MySql.cs} (88%) diff --git a/src/DotNetCore.CAP.MySql/IAdditionalProcessor.Default.cs b/src/DotNetCore.CAP.MySql/ICollectProcessor.MySql.cs similarity index 88% rename from src/DotNetCore.CAP.MySql/IAdditionalProcessor.Default.cs rename to src/DotNetCore.CAP.MySql/ICollectProcessor.MySql.cs index fd892cb..4cd0a23 100644 --- a/src/DotNetCore.CAP.MySql/IAdditionalProcessor.Default.cs +++ b/src/DotNetCore.CAP.MySql/ICollectProcessor.MySql.cs @@ -10,7 +10,7 @@ using MySql.Data.MySqlClient; namespace DotNetCore.CAP.MySql { - internal class DefaultAdditionalProcessor : IAdditionalProcessor + internal class MySqlCollectProcessor : ICollectProcessor { private const int MaxBatch = 1000; private readonly TimeSpan _delay = TimeSpan.FromSeconds(1); @@ -18,7 +18,7 @@ namespace DotNetCore.CAP.MySql private readonly MySqlOptions _options; private readonly TimeSpan _waitingInterval = TimeSpan.FromMinutes(5); - public DefaultAdditionalProcessor(ILogger logger, + public MySqlCollectProcessor(ILogger logger, MySqlOptions mysqlOptions) { _logger = logger; @@ -27,8 +27,6 @@ namespace DotNetCore.CAP.MySql public async Task ProcessAsync(ProcessingContext context) { - _logger.LogDebug("Collecting expired entities."); - var tables = new[] { $"{_options.TableNamePrefix}.published", @@ -37,6 +35,8 @@ namespace DotNetCore.CAP.MySql foreach (var table in tables) { + _logger.LogDebug($"Collecting expired data from table [{table}]."); + int removedCount; do { From 0d4578aae34440aadb0fcab3ff7c50fc40f416fb Mon Sep 17 00:00:00 2001 From: Savorboard Date: Sun, 22 Apr 2018 19:41:06 +0800 Subject: [PATCH 065/223] rename file. --- ...cessor.Default.cs => ICollectlProcessor.PostgreSql.cs} | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) rename src/DotNetCore.CAP.PostgreSql/{IAdditionalProcessor.Default.cs => ICollectlProcessor.PostgreSql.cs} (87%) diff --git a/src/DotNetCore.CAP.PostgreSql/IAdditionalProcessor.Default.cs b/src/DotNetCore.CAP.PostgreSql/ICollectlProcessor.PostgreSql.cs similarity index 87% rename from src/DotNetCore.CAP.PostgreSql/IAdditionalProcessor.Default.cs rename to src/DotNetCore.CAP.PostgreSql/ICollectlProcessor.PostgreSql.cs index ef606eb..d82f0b8 100644 --- a/src/DotNetCore.CAP.PostgreSql/IAdditionalProcessor.Default.cs +++ b/src/DotNetCore.CAP.PostgreSql/ICollectlProcessor.PostgreSql.cs @@ -10,7 +10,7 @@ using Npgsql; namespace DotNetCore.CAP.PostgreSql { - internal class DefaultAdditionalProcessor : IAdditionalProcessor + internal class PostgreSqlCollectProcessor : ICollectProcessor { private const int MaxBatch = 1000; @@ -24,7 +24,7 @@ namespace DotNetCore.CAP.PostgreSql private readonly PostgreSqlOptions _options; private readonly TimeSpan _waitingInterval = TimeSpan.FromMinutes(5); - public DefaultAdditionalProcessor(ILogger logger, + public PostgreSqlCollectProcessor(ILogger logger, PostgreSqlOptions sqlServerOptions) { _logger = logger; @@ -33,10 +33,10 @@ namespace DotNetCore.CAP.PostgreSql public async Task ProcessAsync(ProcessingContext context) { - _logger.LogDebug("Collecting expired entities."); - foreach (var table in Tables) { + _logger.LogDebug($"Collecting expired data from table [{_options.Schema}].[{table}]."); + var removedCount = 0; do { From 551ea1d6ee52edf3afb3e4f7e4311792c1baba51 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Mon, 23 Apr 2018 19:41:15 +0800 Subject: [PATCH 066/223] rename file. --- ...rocessor.Default.cs => ICollectProcessor.SqlServer.cs} | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) rename src/DotNetCore.CAP.SqlServer/{IAdditionalProcessor.Default.cs => ICollectProcessor.SqlServer.cs} (87%) diff --git a/src/DotNetCore.CAP.SqlServer/IAdditionalProcessor.Default.cs b/src/DotNetCore.CAP.SqlServer/ICollectProcessor.SqlServer.cs similarity index 87% rename from src/DotNetCore.CAP.SqlServer/IAdditionalProcessor.Default.cs rename to src/DotNetCore.CAP.SqlServer/ICollectProcessor.SqlServer.cs index 8e344d6..c7a4d03 100644 --- a/src/DotNetCore.CAP.SqlServer/IAdditionalProcessor.Default.cs +++ b/src/DotNetCore.CAP.SqlServer/ICollectProcessor.SqlServer.cs @@ -10,7 +10,7 @@ using Microsoft.Extensions.Logging; namespace DotNetCore.CAP.SqlServer { - public class DefaultAdditionalProcessor : IAdditionalProcessor + public class SqlServerCollectProcessor : ICollectProcessor { private const int MaxBatch = 1000; @@ -24,7 +24,7 @@ namespace DotNetCore.CAP.SqlServer private readonly SqlServerOptions _options; private readonly TimeSpan _waitingInterval = TimeSpan.FromMinutes(5); - public DefaultAdditionalProcessor(ILogger logger, + public SqlServerCollectProcessor(ILogger logger, SqlServerOptions sqlServerOptions) { _logger = logger; @@ -33,10 +33,10 @@ namespace DotNetCore.CAP.SqlServer public async Task ProcessAsync(ProcessingContext context) { - _logger.LogDebug("Collecting expired entities."); - foreach (var table in Tables) { + _logger.LogDebug($"Collecting expired data from table [{_options.Schema}].[{table}]."); + int removedCount; do { From c05386016f8cfddd0dec55cc703baa2a971982bf Mon Sep 17 00:00:00 2001 From: Savorboard Date: Sat, 28 Apr 2018 16:03:36 +0800 Subject: [PATCH 067/223] Release 2.2.2 (#121) * fixed message enqueue exception in v2.2 * add log to samples * Improved log output. #114 * add default timeout configuration for kafka client. * fixed retry processor bugs. * Fixed kafka producer exception log without logging when publish message. * update version num to 2.2.2 * rename configuration options FailedCallback to FailedThresholdCallback * rename files name. * remove unused files. * modify the error comments. * update samples. * add logs. --- build/version.props | 2 +- .../Controllers/ValuesController.cs | 2 +- samples/Sample.Kafka.MySql/Program.cs | 7 +- .../Sample.Kafka.MySql.csproj | 6 ++ samples/Sample.Kafka.MySql/nlog.config | 26 +++++ samples/Sample.RabbitMQ.MySql/AppDbContext.cs | 9 +- .../Controllers/ValuesController.cs | 4 +- samples/Sample.RabbitMQ.MySql/Program.cs | 15 ++- .../Sample.RabbitMQ.MySql.csproj | 6 ++ samples/Sample.RabbitMQ.MySql/nlog.config | 26 +++++ src/DotNetCore.CAP.Kafka/CAP.KafkaOptions.cs | 2 + ...tionPool.cs => IConnectionPool.Default.cs} | 0 .../IPublishMessageSender.Kafka.cs | 8 +- ...l.cs => IConnectionChannelPool.Default.cs} | 0 .../Abstractions/CapPublisherBase.cs | 2 +- src/DotNetCore.CAP/CAP.Options.cs | 4 +- src/DotNetCore.CAP/ICapPublisher.cs | 4 +- src/DotNetCore.CAP/ICapSubscribe.cs | 2 +- .../IConsumerHandler.Default.cs | 2 + src/DotNetCore.CAP/IFetchedMessage.cs | 19 ---- .../IPublishMessageSender.Base.cs | 33 +++--- src/DotNetCore.CAP/Infrastructure/Helper.cs | 1 - .../Internal/PublisherSentFailedException.cs | 4 + src/DotNetCore.CAP/LoggerExtensions.cs | 22 ++-- .../Processor/IProcessor.NeedRetry.cs | 102 +++++++++++++----- 25 files changed, 198 insertions(+), 110 deletions(-) create mode 100644 samples/Sample.Kafka.MySql/nlog.config create mode 100644 samples/Sample.RabbitMQ.MySql/nlog.config rename src/DotNetCore.CAP.Kafka/{ConnectionPool.cs => IConnectionPool.Default.cs} (100%) rename src/DotNetCore.CAP.RabbitMQ/{ConnectionChannelPool.cs => IConnectionChannelPool.Default.cs} (100%) delete mode 100644 src/DotNetCore.CAP/IFetchedMessage.cs diff --git a/build/version.props b/build/version.props index 6f4fc75..ae5b04f 100644 --- a/build/version.props +++ b/build/version.props @@ -2,7 +2,7 @@ 2 2 - 1 + 2 $(VersionMajor).$(VersionMinor).$(VersionPatch) diff --git a/samples/Sample.Kafka.MySql/Controllers/ValuesController.cs b/samples/Sample.Kafka.MySql/Controllers/ValuesController.cs index afcec67..6f04946 100644 --- a/samples/Sample.Kafka.MySql/Controllers/ValuesController.cs +++ b/samples/Sample.Kafka.MySql/Controllers/ValuesController.cs @@ -37,7 +37,7 @@ namespace Sample.Kafka.MySql.Controllers [CapSubscribe("xxx.xxx.test2")] public void Test2(int value) { - Console.WriteLine(value); + Console.WriteLine("Subscriber output message: " + value); } } } \ No newline at end of file diff --git a/samples/Sample.Kafka.MySql/Program.cs b/samples/Sample.Kafka.MySql/Program.cs index 7d62e55..976b20a 100644 --- a/samples/Sample.Kafka.MySql/Program.cs +++ b/samples/Sample.Kafka.MySql/Program.cs @@ -1,5 +1,6 @@ using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; +using NLog.Web; namespace Sample.Kafka.MySql { @@ -14,7 +15,11 @@ namespace Sample.Kafka.MySql public static IWebHost BuildWebHost(string[] args) => WebHost.CreateDefaultBuilder(args) .UseStartup() + .ConfigureLogging((hostingContext, builder) => + { + hostingContext.HostingEnvironment.ConfigureNLog("nlog.config"); + }) + .UseNLog() .Build(); - } } \ No newline at end of file diff --git a/samples/Sample.Kafka.MySql/Sample.Kafka.MySql.csproj b/samples/Sample.Kafka.MySql/Sample.Kafka.MySql.csproj index ad812ae..88c0c1d 100644 --- a/samples/Sample.Kafka.MySql/Sample.Kafka.MySql.csproj +++ b/samples/Sample.Kafka.MySql/Sample.Kafka.MySql.csproj @@ -10,6 +10,7 @@ + @@ -20,5 +21,10 @@ + + + PreserveNewest + + diff --git a/samples/Sample.Kafka.MySql/nlog.config b/samples/Sample.Kafka.MySql/nlog.config new file mode 100644 index 0000000..5b91105 --- /dev/null +++ b/samples/Sample.Kafka.MySql/nlog.config @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/samples/Sample.RabbitMQ.MySql/AppDbContext.cs b/samples/Sample.RabbitMQ.MySql/AppDbContext.cs index f803e26..ec83423 100644 --- a/samples/Sample.RabbitMQ.MySql/AppDbContext.cs +++ b/samples/Sample.RabbitMQ.MySql/AppDbContext.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; namespace Sample.RabbitMQ.MySql { @@ -10,8 +6,7 @@ namespace Sample.RabbitMQ.MySql { protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { - optionsBuilder.UseMySql("Server=localhost;Database=Sample.RabbitMQ.MySql;UserId=root;Password=123123;Allow User Variables=True"); - //optionsBuilder.UseMySql("Server=192.168.2.206;Database=Sample.RabbitMQ.MySql;UserId=root;Password=123123;"); + optionsBuilder.UseMySql("Server=192.168.10.110;Database=testcap;UserId=root;Password=123123;"); } } } diff --git a/samples/Sample.RabbitMQ.MySql/Controllers/ValuesController.cs b/samples/Sample.RabbitMQ.MySql/Controllers/ValuesController.cs index 7b30772..1b47e5e 100644 --- a/samples/Sample.RabbitMQ.MySql/Controllers/ValuesController.cs +++ b/samples/Sample.RabbitMQ.MySql/Controllers/ValuesController.cs @@ -21,7 +21,7 @@ namespace Sample.RabbitMQ.MySql.Controllers public IActionResult PublishMessage() { _capBus.Publish("sample.rabbitmq.mysql", DateTime.Now); - + return Ok(); } @@ -50,7 +50,7 @@ namespace Sample.RabbitMQ.MySql.Controllers [CapSubscribe("sample.rabbitmq.mysql")] public void ReceiveMessage(DateTime time) { - Console.WriteLine("[sample.rabbitmq.mysql] message received: "+ DateTime.Now.ToString() +" , sent time: " + time.ToString()); + Console.WriteLine("[sample.rabbitmq.mysql] message received: " + DateTime.Now + ",sent time: " + time); } } } diff --git a/samples/Sample.RabbitMQ.MySql/Program.cs b/samples/Sample.RabbitMQ.MySql/Program.cs index 94095e2..3cbbe15 100644 --- a/samples/Sample.RabbitMQ.MySql/Program.cs +++ b/samples/Sample.RabbitMQ.MySql/Program.cs @@ -1,12 +1,6 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore; -using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; +using NLog.Web; namespace Sample.RabbitMQ.MySql { @@ -20,6 +14,11 @@ namespace Sample.RabbitMQ.MySql public static IWebHost BuildWebHost(string[] args) => WebHost.CreateDefaultBuilder(args) .UseStartup() + .ConfigureLogging((hostingContext, builder) => + { + hostingContext.HostingEnvironment.ConfigureNLog("nlog.config"); + }) + .UseNLog() .Build(); } } diff --git a/samples/Sample.RabbitMQ.MySql/Sample.RabbitMQ.MySql.csproj b/samples/Sample.RabbitMQ.MySql/Sample.RabbitMQ.MySql.csproj index d3a3987..5c981a3 100644 --- a/samples/Sample.RabbitMQ.MySql/Sample.RabbitMQ.MySql.csproj +++ b/samples/Sample.RabbitMQ.MySql/Sample.RabbitMQ.MySql.csproj @@ -12,6 +12,7 @@ + @@ -21,5 +22,10 @@ + + + PreserveNewest + + diff --git a/samples/Sample.RabbitMQ.MySql/nlog.config b/samples/Sample.RabbitMQ.MySql/nlog.config new file mode 100644 index 0000000..bba5bea --- /dev/null +++ b/samples/Sample.RabbitMQ.MySql/nlog.config @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/DotNetCore.CAP.Kafka/CAP.KafkaOptions.cs b/src/DotNetCore.CAP.Kafka/CAP.KafkaOptions.cs index 629963b..0e4161b 100644 --- a/src/DotNetCore.CAP.Kafka/CAP.KafkaOptions.cs +++ b/src/DotNetCore.CAP.Kafka/CAP.KafkaOptions.cs @@ -57,6 +57,8 @@ namespace DotNetCore.CAP MainConfig["socket.blocking.max.ms"] = "10"; MainConfig["enable.auto.commit"] = "false"; MainConfig["log.connection.close"] = "false"; + MainConfig["request.timeout.ms"] = "3000"; + MainConfig["message.timeout.ms"] = "5000"; _kafkaConfig = MainConfig.AsEnumerable(); } diff --git a/src/DotNetCore.CAP.Kafka/ConnectionPool.cs b/src/DotNetCore.CAP.Kafka/IConnectionPool.Default.cs similarity index 100% rename from src/DotNetCore.CAP.Kafka/ConnectionPool.cs rename to src/DotNetCore.CAP.Kafka/IConnectionPool.Default.cs diff --git a/src/DotNetCore.CAP.Kafka/IPublishMessageSender.Kafka.cs b/src/DotNetCore.CAP.Kafka/IPublishMessageSender.Kafka.cs index f3e4e5d..4527eda 100644 --- a/src/DotNetCore.CAP.Kafka/IPublishMessageSender.Kafka.cs +++ b/src/DotNetCore.CAP.Kafka/IPublishMessageSender.Kafka.cs @@ -36,11 +36,7 @@ namespace DotNetCore.CAP.Kafka if (message.Error.HasError) { - return OperateResult.Failed(new OperateError - { - Code = message.Error.Code.ToString(), - Description = message.Error.Reason - }); + throw new PublisherSentFailedException(message.Error.ToString()); } _logger.LogDebug($"kafka topic message [{keyName}] has been published."); @@ -61,6 +57,6 @@ namespace DotNetCore.CAP.Kafka producer.Dispose(); } } - } + } } } \ No newline at end of file diff --git a/src/DotNetCore.CAP.RabbitMQ/ConnectionChannelPool.cs b/src/DotNetCore.CAP.RabbitMQ/IConnectionChannelPool.Default.cs similarity index 100% rename from src/DotNetCore.CAP.RabbitMQ/ConnectionChannelPool.cs rename to src/DotNetCore.CAP.RabbitMQ/IConnectionChannelPool.Default.cs diff --git a/src/DotNetCore.CAP/Abstractions/CapPublisherBase.cs b/src/DotNetCore.CAP/Abstractions/CapPublisherBase.cs index 26760b2..4d7a321 100644 --- a/src/DotNetCore.CAP/Abstractions/CapPublisherBase.cs +++ b/src/DotNetCore.CAP/Abstractions/CapPublisherBase.cs @@ -132,7 +132,7 @@ namespace DotNetCore.CAP.Abstractions { throw new InvalidOperationException( "If you are using the EntityFramework, you need to configure the DbContextType first." + - " otherwise you need to use overloaded method with IDbConnection and IDbTransaction."); + " otherwise you need to use overloaded method with IDbTransaction."); } } diff --git a/src/DotNetCore.CAP/CAP.Options.cs b/src/DotNetCore.CAP/CAP.Options.cs index 267a61f..05c0294 100644 --- a/src/DotNetCore.CAP/CAP.Options.cs +++ b/src/DotNetCore.CAP/CAP.Options.cs @@ -58,9 +58,9 @@ namespace DotNetCore.CAP public int FailedRetryInterval { get; set; } /// - /// We’ll invoke this call-back with message type,name,content when requeue failed message. + /// We’ll invoke this call-back with message type,name,content when retry failed (send or executed) messages equals times. /// - public Action FailedCallback { get; set; } + public Action FailedThresholdCallback { get; set; } /// /// The number of message retries, the retry will stop when the threshold is reached. diff --git a/src/DotNetCore.CAP/ICapPublisher.cs b/src/DotNetCore.CAP/ICapPublisher.cs index 8ba45e1..0688c02 100644 --- a/src/DotNetCore.CAP/ICapPublisher.cs +++ b/src/DotNetCore.CAP/ICapPublisher.cs @@ -15,7 +15,7 @@ namespace DotNetCore.CAP /// (EntityFramework) Asynchronous publish a object message. /// /// If you are using the EntityFramework, you need to configure the DbContextType first. - /// otherwise you need to use overloaded method with IDbConnection and IDbTransaction. + /// otherwise you need to use overloaded method with IDbTransaction. /// /// /// The type of content object. @@ -28,7 +28,7 @@ namespace DotNetCore.CAP /// (EntityFramework) Publish a object message. /// /// If you are using the EntityFramework, you need to configure the DbContextType first. - /// otherwise you need to use overloaded method with IDbConnection and IDbTransaction. + /// otherwise you need to use overloaded method with IDbTransaction. /// /// /// The type of content object. diff --git a/src/DotNetCore.CAP/ICapSubscribe.cs b/src/DotNetCore.CAP/ICapSubscribe.cs index 9b536ad..e223b84 100644 --- a/src/DotNetCore.CAP/ICapSubscribe.cs +++ b/src/DotNetCore.CAP/ICapSubscribe.cs @@ -4,7 +4,7 @@ namespace DotNetCore.CAP { /// - /// An empty interface, which is used to mark the current class have a CAP methods. + /// An empty interface, which is used to mark the current class have a CAP subscriber methods. /// public interface ICapSubscribe { diff --git a/src/DotNetCore.CAP/IConsumerHandler.Default.cs b/src/DotNetCore.CAP/IConsumerHandler.Default.cs index 8d48a37..1d441ff 100644 --- a/src/DotNetCore.CAP/IConsumerHandler.Default.cs +++ b/src/DotNetCore.CAP/IConsumerHandler.Default.cs @@ -178,6 +178,8 @@ namespace DotNetCore.CAP private (Guid, string) TracingBefore(string topic, string values) { + _logger.LogDebug("CAP received topic message:" + topic); + Guid operationId = Guid.NewGuid(); var eventData = new BrokerConsumeEventData( diff --git a/src/DotNetCore.CAP/IFetchedMessage.cs b/src/DotNetCore.CAP/IFetchedMessage.cs deleted file mode 100644 index 34a78ca..0000000 --- a/src/DotNetCore.CAP/IFetchedMessage.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) .NET Core Community. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. - -using System; -using DotNetCore.CAP.Models; - -namespace DotNetCore.CAP -{ - public interface IFetchedMessage : IDisposable - { - int MessageId { get; } - - MessageType MessageType { get; } - - void RemoveFromQueue(); - - void Requeue(); - } -} \ No newline at end of file diff --git a/src/DotNetCore.CAP/IPublishMessageSender.Base.cs b/src/DotNetCore.CAP/IPublishMessageSender.Base.cs index cc82be6..a597169 100644 --- a/src/DotNetCore.CAP/IPublishMessageSender.Base.cs +++ b/src/DotNetCore.CAP/IPublishMessageSender.Base.cs @@ -67,15 +67,13 @@ namespace DotNetCore.CAP } else { - TracingError(operationId, message.Name, sendValues, result.Exception, startTime, stopwatch.Elapsed); - - _logger.MessagePublishException(message.Id, result.Exception); + TracingError(operationId, message, result, startTime, stopwatch.Elapsed); await SetFailedState(message, result.Exception, out bool stillRetry); if (stillRetry) { - _logger.SenderRetrying(3); + _logger.SenderRetrying(message.Id, message.Retries); await SendAsync(message); } @@ -109,20 +107,11 @@ namespace DotNetCore.CAP private Task SetFailedState(CapPublishedMessage message, Exception ex, out bool stillRetry) { IState newState = new FailedState(); - - if (ex is PublisherSentFailedException) - { - stillRetry = false; - message.Retries = _options.FailedRetryCount; // not retry if PublisherSentFailedException - } - else + stillRetry = UpdateMessageForRetryAsync(message); + if (stillRetry) { - stillRetry = UpdateMessageForRetryAsync(message); - if (stillRetry) - { - _logger.ConsumerExecutionFailedWillRetry(ex); - return Task.CompletedTask; - } + _logger.ConsumerExecutionFailedWillRetry(ex); + return Task.CompletedTask; } AddErrorReasonToContent(message, ex); @@ -166,14 +155,18 @@ namespace DotNetCore.CAP _logger.MessageHasBeenSent(du.TotalSeconds); } - private void TracingError(Guid operationId, string topic, string values, Exception ex, DateTimeOffset startTime, TimeSpan du) + private void TracingError(Guid operationId, CapPublishedMessage message, OperateResult result, DateTimeOffset startTime, TimeSpan du) { + var ex = new PublisherSentFailedException(result.ToString(), result.Exception); + + _logger.MessagePublishException(message.Id, result.ToString(), ex); + var eventData = new BrokerPublishErrorEventData( operationId, "", ServersAddress, - topic, - values, + message.Name, + message.Content, ex, startTime, du); diff --git a/src/DotNetCore.CAP/Infrastructure/Helper.cs b/src/DotNetCore.CAP/Infrastructure/Helper.cs index 83d128f..3f023b9 100644 --- a/src/DotNetCore.CAP/Infrastructure/Helper.cs +++ b/src/DotNetCore.CAP/Infrastructure/Helper.cs @@ -86,7 +86,6 @@ namespace DotNetCore.CAP.Infrastructure return !CanConvertFromString(type); } - public static string AddExceptionProperty(string json, Exception exception) { var jObject = ToJObject(exception); diff --git a/src/DotNetCore.CAP/Internal/PublisherSentFailedException.cs b/src/DotNetCore.CAP/Internal/PublisherSentFailedException.cs index b0f0a2c..9be1758 100644 --- a/src/DotNetCore.CAP/Internal/PublisherSentFailedException.cs +++ b/src/DotNetCore.CAP/Internal/PublisherSentFailedException.cs @@ -7,6 +7,10 @@ namespace DotNetCore.CAP.Internal { public class PublisherSentFailedException : Exception { + public PublisherSentFailedException(string message) : base(message) + { + } + public PublisherSentFailedException(string message, Exception ex) : base(message, ex) { } diff --git a/src/DotNetCore.CAP/LoggerExtensions.cs b/src/DotNetCore.CAP/LoggerExtensions.cs index 031be9d..a0096e6 100644 --- a/src/DotNetCore.CAP/LoggerExtensions.cs +++ b/src/DotNetCore.CAP/LoggerExtensions.cs @@ -17,10 +17,10 @@ namespace DotNetCore.CAP private static readonly Action _modelBinderFormattingException; private static readonly Action _consumerFailedWillRetry; private static readonly Action _consumerExecuted; - private static readonly Action _senderRetrying; + private static readonly Action _senderRetrying; private static readonly Action _exceptionOccuredWhileExecuting; private static readonly Action _messageHasBeenSent; - private static readonly Action _messagePublishException; + private static readonly Action _messagePublishException; static LoggerExtensions() { @@ -60,10 +60,10 @@ namespace DotNetCore.CAP "When call subscribe method, a parameter format conversion exception occurs. MethodName:'{MethodName}' ParameterName:'{ParameterName}' Content:'{Content}'." ); - _senderRetrying = LoggerMessage.Define( + _senderRetrying = LoggerMessage.Define( LogLevel.Debug, 3, - "Retrying send a message: {Retries}..."); + "The {Retries}th retrying send a message failed. message id: {MessageId} "); _consumerExecuted = LoggerMessage.Define( LogLevel.Debug, @@ -78,17 +78,17 @@ namespace DotNetCore.CAP _exceptionOccuredWhileExecuting = LoggerMessage.Define( LogLevel.Error, 6, - "An exception occured while trying to store a message: '{MessageId}'. "); + "An exception occured while trying to store a message. message id: {MessageId}"); _messageHasBeenSent = LoggerMessage.Define( LogLevel.Debug, 4, "Message published. Took: {Seconds} secs."); - _messagePublishException = LoggerMessage.Define( + _messagePublishException = LoggerMessage.Define( LogLevel.Error, 6, - "An exception occured while publishing a message: '{MessageId}'. "); + "An exception occured while publishing a message, reason:{Reason}. message id:{MessageId}"); } public static void ConsumerExecutionFailedWillRetry(this ILogger logger, Exception ex) @@ -96,9 +96,9 @@ namespace DotNetCore.CAP _consumerFailedWillRetry(logger, ex); } - public static void SenderRetrying(this ILogger logger, int retries) + public static void SenderRetrying(this ILogger logger, int messageId, int retries) { - _senderRetrying(logger, retries, null); + _senderRetrying(logger, messageId, retries, null); } public static void MessageHasBeenSent(this ILogger logger, double seconds) @@ -106,9 +106,9 @@ namespace DotNetCore.CAP _messageHasBeenSent(logger, seconds, null); } - public static void MessagePublishException(this ILogger logger, int messageId, Exception ex) + public static void MessagePublishException(this ILogger logger, int messageId, string reason, Exception ex) { - _messagePublishException(logger, messageId, ex); + _messagePublishException(logger, messageId, reason, ex); } public static void ConsumerExecuted(this ILogger logger, double seconds) diff --git a/src/DotNetCore.CAP/Processor/IProcessor.NeedRetry.cs b/src/DotNetCore.CAP/Processor/IProcessor.NeedRetry.cs index b1109a3..2f7b829 100644 --- a/src/DotNetCore.CAP/Processor/IProcessor.NeedRetry.cs +++ b/src/DotNetCore.CAP/Processor/IProcessor.NeedRetry.cs @@ -65,33 +65,46 @@ namespace DotNetCore.CAP.Processor continue; } - if (!hasException) - { - try - { - _options.FailedCallback?.Invoke(MessageType.Publish, message.Name, message.Content); - } - catch (Exception ex) - { - hasException = true; - _logger.LogWarning("Failed call-back method raised an exception:" + ex.Message); - } - } - using (var transaction = connection.CreateTransaction()) { - try + var result = await _publishExecutor.PublishAsync(message.Name, message.Content); + if (result.Succeeded) { - await _publishExecutor.PublishAsync(message.Name, message.Content); - _stateChanger.ChangeState(message, new SucceededState(), transaction); + _logger.LogInformation("The message was sent successfully during the retry. MessageId:" + message.Id); } - catch (Exception e) + else { - message.Content = Helper.AddExceptionProperty(message.Content, e); + message.Content = Helper.AddExceptionProperty(message.Content, result.Exception); + message.Retries++; + if (message.StatusName == StatusName.Scheduled) + { + message.ExpiresAt = GetDueTime(message.Added, message.Retries); + message.StatusName = StatusName.Failed; + } transaction.UpdateMessage(message); - } + if (message.Retries >= _options.FailedRetryCount) + { + _logger.LogError($"The message still sent failed after {_options.FailedRetryCount} retries. We will stop retrying the message. " + + "MessageId:" + message.Id); + if (message.Retries == _options.FailedRetryCount) + { + if (!hasException) + { + try + { + _options.FailedThresholdCallback?.Invoke(MessageType.Publish, message.Name, message.Content); + } + catch (Exception ex) + { + hasException = true; + _logger.LogWarning("Failed call-back method raised an exception:" + ex.Message); + } + } + } + } + } await transaction.CommitAsync(); } @@ -113,25 +126,60 @@ namespace DotNetCore.CAP.Processor continue; } - if (!hasException) + using (var transaction = connection.CreateTransaction()) { - try + var result = await _subscriberExecutor.ExecuteAsync(message); + if (result.Succeeded) { - _options.FailedCallback?.Invoke(MessageType.Subscribe, message.Name, message.Content); + _stateChanger.ChangeState(message, new SucceededState(), transaction); + _logger.LogInformation("The message was execute successfully during the retry. MessageId:" + message.Id); } - catch (Exception ex) + else { - hasException = true; - _logger.LogWarning("Failed call-back method raised an exception:" + ex.Message); + message.Content = Helper.AddExceptionProperty(message.Content, result.Exception); + message.Retries++; + if (message.StatusName == StatusName.Scheduled) + { + message.ExpiresAt = GetDueTime(message.Added, message.Retries); + message.StatusName = StatusName.Failed; + } + transaction.UpdateMessage(message); + + if (message.Retries >= _options.FailedRetryCount) + { + _logger.LogError($"[Subscriber]The message still executed failed after {_options.FailedRetryCount} retries. " + + "We will stop retrying to execute the message. message id:" + message.Id); + + if (message.Retries == _options.FailedRetryCount) + { + if (!hasException) + { + try + { + _options.FailedThresholdCallback?.Invoke(MessageType.Subscribe, message.Name, message.Content); + } + catch (Exception ex) + { + hasException = true; + _logger.LogWarning("Failed call-back method raised an exception:" + ex.Message); + } + } + } + } } + await transaction.CommitAsync(); } - await _subscriberExecutor.ExecuteAsync(message); - context.ThrowIfStopping(); await context.WaitAsync(_delay); } } + + public DateTime GetDueTime(DateTime addedTime, int retries) + { + var retryBehavior = RetryBehavior.DefaultRetry; + return addedTime.AddSeconds(retryBehavior.RetryIn(retries)); + } } } \ No newline at end of file From 8f3406f63ceda06c7aaac22df4b26ef805fc7fb2 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Wed, 23 May 2018 19:42:02 +0800 Subject: [PATCH 068/223] add appsettings.json to sample. --- samples/Sample.Kafka.MySql/appsettings.json | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 samples/Sample.Kafka.MySql/appsettings.json diff --git a/samples/Sample.Kafka.MySql/appsettings.json b/samples/Sample.Kafka.MySql/appsettings.json new file mode 100644 index 0000000..20aa907 --- /dev/null +++ b/samples/Sample.Kafka.MySql/appsettings.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "IncludeScopes": false, + "LogLevel": { + "Default": "Debug" + } + } +} From 0a42896941b851a929c230194ff60e01be2dac0b Mon Sep 17 00:00:00 2001 From: Savorboard Date: Thu, 24 May 2018 15:12:17 +0800 Subject: [PATCH 069/223] refactor --- src/DotNetCore.CAP/Internal/IConsumerServiceSelector.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/DotNetCore.CAP/Internal/IConsumerServiceSelector.cs b/src/DotNetCore.CAP/Internal/IConsumerServiceSelector.cs index 3c1dc94..0b53af7 100644 --- a/src/DotNetCore.CAP/Internal/IConsumerServiceSelector.cs +++ b/src/DotNetCore.CAP/Internal/IConsumerServiceSelector.cs @@ -22,8 +22,6 @@ namespace DotNetCore.CAP.Internal /// /// topic or exchange router key. /// the set of candidates. - /// - ConsumerExecutorDescriptor - SelectBestCandidate(string key, IReadOnlyList candidates); + ConsumerExecutorDescriptor SelectBestCandidate(string key, IReadOnlyList candidates); } } \ No newline at end of file From 5591c9ac86940ba30ad7b470742ac1147ff25ad0 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Thu, 24 May 2018 15:17:59 +0800 Subject: [PATCH 070/223] add unit tests for ConsumerServiceSelector --- .../ConsumerServiceSelectorTest.cs | 77 ++++++++++++++++++- 1 file changed, 76 insertions(+), 1 deletion(-) diff --git a/test/DotNetCore.CAP.Test/ConsumerServiceSelectorTest.cs b/test/DotNetCore.CAP.Test/ConsumerServiceSelectorTest.cs index 71154b5..8f255a4 100644 --- a/test/DotNetCore.CAP.Test/ConsumerServiceSelectorTest.cs +++ b/test/DotNetCore.CAP.Test/ConsumerServiceSelectorTest.cs @@ -28,7 +28,7 @@ namespace DotNetCore.CAP.Test var selector = _provider.GetRequiredService(); var candidates = selector.SelectCandidates(); - Assert.Equal(2, candidates.Count); + Assert.Equal(6, candidates.Count); } [Fact] @@ -42,6 +42,66 @@ namespace DotNetCore.CAP.Test Assert.NotNull(bestCandidates.MethodInfo); Assert.Equal(typeof(Task), bestCandidates.MethodInfo.ReturnType); } + + + [Theory] + [InlineData("Candidates.Asterisk")] + [InlineData("candidates.Asterisk")] + [InlineData("AAA.BBB.Asterisk")] + [InlineData("aaa.bbb.Asterisk")] + public void CanFindAsteriskTopic(string topic) + { + var selector = _provider.GetRequiredService(); + var candidates = selector.SelectCandidates(); + + var bestCandidates = selector.SelectBestCandidate(topic, candidates); + Assert.NotNull(bestCandidates); + } + + [Theory] + [InlineData("Candidates.Asterisk.AAA")] + [InlineData("AAA.BBB.CCC.Asterisk")] + [InlineData("aaa.BBB.ccc.Asterisk")] + [InlineData("Asterisk.aaa.bbb")] + public void CanNotFindAsteriskTopic(string topic) + { + var selector = _provider.GetRequiredService(); + var candidates = selector.SelectCandidates(); + + var bestCandidates = selector.SelectBestCandidate(topic, candidates); + Assert.Null(bestCandidates); + } + + [Theory] + [InlineData("Candidates.Pound.AAA")] + [InlineData("Candidates.Pound.AAA.BBB")] + [InlineData("AAA.Pound")] + [InlineData("aaa.Pound")] + [InlineData("aaa.bbb.Pound")] + [InlineData("aaa.BBB.Pound")] + public void CanFindPoundTopic(string topic) + { + var selector = _provider.GetRequiredService(); + var candidates = selector.SelectCandidates(); + + var bestCandidates = selector.SelectBestCandidate(topic, candidates); + Assert.NotNull(bestCandidates); + } + + [Theory] + [InlineData("Pound")] + [InlineData("aaa.Pound.AAA.BBB")] + [InlineData("Pound.AAA")] + [InlineData("Pound.aaa")] + [InlineData("AAA.Pound.aaa")] + public void CanNotFindPoundTopic(string topic) + { + var selector = _provider.GetRequiredService(); + var candidates = selector.SelectCandidates(); + + var bestCandidates = selector.SelectBestCandidate(topic, candidates); + Assert.Null(bestCandidates); + } } public class CandidatesTopic : TopicAttribute @@ -73,6 +133,21 @@ namespace DotNetCore.CAP.Test { Console.WriteLine("GetFoo2() method has bee excuted."); } + + [CandidatesTopic("*.*.Asterisk")] + [CandidatesTopic("*.Asterisk")] + public void GetFooAsterisk() + { + Console.WriteLine("GetFoo2Asterisk() method has bee excuted."); + } + + [CandidatesTopic("Candidates.Pound.#")] + [CandidatesTopic("#.Pound")] + public void GetFooPound() + { + Console.WriteLine("GetFoo2Pound() method has bee excuted."); + } + } public class CandidatesBarTest : IBarTest From 68243b88663c55d1417f1d3d06bee7e0ce36a90a Mon Sep 17 00:00:00 2001 From: Savorboard Date: Thu, 24 May 2018 15:20:45 +0800 Subject: [PATCH 071/223] support pattern matching for consumer. (#132) --- .../IConsumerServiceSelector.Default.cs | 94 ++++++++++++++++--- .../Internal/MethodMatcherCache.cs | 57 +---------- 2 files changed, 88 insertions(+), 63 deletions(-) diff --git a/src/DotNetCore.CAP/Internal/IConsumerServiceSelector.Default.cs b/src/DotNetCore.CAP/Internal/IConsumerServiceSelector.Default.cs index 7bf92f3..f751499 100644 --- a/src/DotNetCore.CAP/Internal/IConsumerServiceSelector.Default.cs +++ b/src/DotNetCore.CAP/Internal/IConsumerServiceSelector.Default.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Reflection; +using System.Text.RegularExpressions; using DotNetCore.CAP.Abstractions; using DotNetCore.CAP.Infrastructure; using Microsoft.Extensions.DependencyInjection; @@ -19,6 +20,8 @@ namespace DotNetCore.CAP.Internal { private readonly CapOptions _capOptions; private readonly IServiceProvider _serviceProvider; + private List> _asteriskList; + private List> _poundList; /// /// Creates a new . @@ -29,17 +32,6 @@ namespace DotNetCore.CAP.Internal _capOptions = capOptions; } - /// - /// Selects the best candidate from for - /// the - /// current message associated. - /// - public ConsumerExecutorDescriptor SelectBestCandidate(string key, - IReadOnlyList executeDescriptor) - { - return executeDescriptor.FirstOrDefault(x => x.Attribute.Name == key); - } - public IReadOnlyList SelectCandidates() { var executorDescriptorList = new List(); @@ -51,6 +43,26 @@ namespace DotNetCore.CAP.Internal return executorDescriptorList; } + public ConsumerExecutorDescriptor SelectBestCandidate(string key, IReadOnlyList executeDescriptor) + { + var result = MatchUsingName(key, executeDescriptor); + if (result != null) + { + return result; + } + + //[*] match with regex, i.e. foo.*.abc + result = MatchAsteriskUsingRegex(key, executeDescriptor); + if (result != null) + { + return result; + } + + //[#] match regex, i.e. foo.# + result = MatchPoundUsingRegex(key, executeDescriptor); + return result; + } + private IEnumerable FindConsumersFromInterfaceTypes( IServiceProvider provider) { @@ -130,5 +142,65 @@ namespace DotNetCore.CAP.Internal return descriptor; } + + private ConsumerExecutorDescriptor MatchUsingName(string key, IReadOnlyList executeDescriptor) + { + return executeDescriptor.FirstOrDefault(x => x.Attribute.Name == key); + } + + private ConsumerExecutorDescriptor MatchAsteriskUsingRegex(string key, IReadOnlyList executeDescriptor) + { + if (_asteriskList == null) + { + _asteriskList = executeDescriptor + .Where(x => x.Attribute.Name.IndexOf('*') >= 0) + .Select(x => new RegexExecuteDescriptor + { + Name = ("^" + x.Attribute.Name + "$").Replace("*", "[a-zA-Z]+").Replace(".", "\\."), + Descriptor = x + }).ToList(); + } + foreach (var red in _asteriskList) + { + if (Regex.IsMatch(key, red.Name, RegexOptions.Singleline)) + { + return red.Descriptor; + } + } + + return null; + } + + private ConsumerExecutorDescriptor MatchPoundUsingRegex(string key, IReadOnlyList executeDescriptor) + { + if (_poundList == null) + { + _poundList = executeDescriptor + .Where(x => x.Attribute.Name.IndexOf('#') >= 0) + .Select(x => new RegexExecuteDescriptor + { + Name = ("^" + x.Attribute.Name + "$").Replace("#", "[a-zA-Z\\.]+"), + Descriptor = x + }).ToList(); + } + + foreach (var red in _poundList) + { + if (Regex.IsMatch(key, red.Name, RegexOptions.Singleline)) + { + return red.Descriptor; + } + } + + return null; + } + + + private class RegexExecuteDescriptor + { + public string Name { get; set; } + + public T Descriptor { get; set; } + } } } \ No newline at end of file diff --git a/src/DotNetCore.CAP/Internal/MethodMatcherCache.cs b/src/DotNetCore.CAP/Internal/MethodMatcherCache.cs index d6b40ba..8ff25c7 100644 --- a/src/DotNetCore.CAP/Internal/MethodMatcherCache.cs +++ b/src/DotNetCore.CAP/Internal/MethodMatcherCache.cs @@ -11,21 +11,20 @@ namespace DotNetCore.CAP.Internal internal class MethodMatcherCache { private readonly IConsumerServiceSelector _selector; - private List _allTopics; public MethodMatcherCache(IConsumerServiceSelector selector) { _selector = selector; - Entries = new ConcurrentDictionary>(); + Entries = new ConcurrentDictionary>(); } - private ConcurrentDictionary> Entries { get; } + private ConcurrentDictionary> Entries { get; } /// /// Get a dictionary of candidates.In the dictionary, /// the Key is the CAPSubscribeAttribute Group, the Value for the current Group of candidates /// - public ConcurrentDictionary> GetCandidatesMethodsOfGroupNameGrouped() + public ConcurrentDictionary> GetCandidatesMethodsOfGroupNameGrouped() { if (Entries.Count != 0) { @@ -44,28 +43,6 @@ namespace DotNetCore.CAP.Internal return Entries; } - /// - /// Get a dictionary of specify topic candidates. - /// The Key is Group name, the value is specify topic candidates. - /// - /// message topic name - public IDictionary> GetTopicExector(string topicName) - { - if (Entries == null) - { - throw new ArgumentNullException(nameof(Entries)); - } - - var dic = new Dictionary>(); - foreach (var item in Entries) - { - var topicCandidates = item.Value.Where(x => x.Attribute.Name == topicName); - dic.Add(item.Key, topicCandidates.ToList()); - } - - return dic; - } - /// /// Attempts to get the topic exector associated with the specified topic name and group name from the /// . @@ -86,36 +63,12 @@ namespace DotNetCore.CAP.Internal if (Entries.TryGetValue(groupName, out var groupMatchTopics)) { - matchTopic = groupMatchTopics.FirstOrDefault(x => x.Attribute.Name == topicName); + matchTopic = _selector.SelectBestCandidate(topicName, groupMatchTopics); + return matchTopic != null; } return false; } - - /// - /// Get all subscribe topics name. - /// - public IEnumerable GetSubscribeTopics() - { - if (_allTopics != null) - { - return _allTopics; - } - - if (Entries == null) - { - throw new ArgumentNullException(nameof(Entries)); - } - - _allTopics = new List(); - - foreach (var descriptors in Entries.Values) - { - _allTopics.AddRange(descriptors.Select(x => x.Attribute.Name)); - } - - return _allTopics; - } } } \ No newline at end of file From 3086589e26cb8db44e5877204b76cd0a0711e4af Mon Sep 17 00:00:00 2001 From: Savorboard Date: Thu, 24 May 2018 18:01:22 +0800 Subject: [PATCH 072/223] update samples. --- samples/Sample.Kafka.MySql/nlog.config | 26 ------------------- .../Sample.RabbitMQ.MySql/appsettings.json | 8 ++++++ samples/Sample.RabbitMQ.MySql/nlog.config | 26 ------------------- 3 files changed, 8 insertions(+), 52 deletions(-) delete mode 100644 samples/Sample.Kafka.MySql/nlog.config create mode 100644 samples/Sample.RabbitMQ.MySql/appsettings.json delete mode 100644 samples/Sample.RabbitMQ.MySql/nlog.config diff --git a/samples/Sample.Kafka.MySql/nlog.config b/samples/Sample.Kafka.MySql/nlog.config deleted file mode 100644 index 5b91105..0000000 --- a/samples/Sample.Kafka.MySql/nlog.config +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/samples/Sample.RabbitMQ.MySql/appsettings.json b/samples/Sample.RabbitMQ.MySql/appsettings.json new file mode 100644 index 0000000..20aa907 --- /dev/null +++ b/samples/Sample.RabbitMQ.MySql/appsettings.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "IncludeScopes": false, + "LogLevel": { + "Default": "Debug" + } + } +} diff --git a/samples/Sample.RabbitMQ.MySql/nlog.config b/samples/Sample.RabbitMQ.MySql/nlog.config deleted file mode 100644 index bba5bea..0000000 --- a/samples/Sample.RabbitMQ.MySql/nlog.config +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - - - - - - \ No newline at end of file From a2efa6b843d0748fb082ec768847264f669061b0 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Thu, 24 May 2018 18:01:54 +0800 Subject: [PATCH 073/223] refactoring log output. --- .../IConnectionPool.Default.cs | 14 ++++++++++---- .../IConnectionChannelPool.Default.cs | 14 +++++++++----- .../RabbitMQConsumerClient.cs | 4 ++-- src/DotNetCore.CAP/IConsumerHandler.Default.cs | 12 ++++++------ 4 files changed, 27 insertions(+), 17 deletions(-) diff --git a/src/DotNetCore.CAP.Kafka/IConnectionPool.Default.cs b/src/DotNetCore.CAP.Kafka/IConnectionPool.Default.cs index 1993661..21b8398 100644 --- a/src/DotNetCore.CAP.Kafka/IConnectionPool.Default.cs +++ b/src/DotNetCore.CAP.Kafka/IConnectionPool.Default.cs @@ -6,23 +6,29 @@ using System.Collections.Concurrent; using System.Diagnostics; using System.Threading; using Confluent.Kafka; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; namespace DotNetCore.CAP.Kafka { public class ConnectionPool : IConnectionPool, IDisposable { + private readonly ILogger _logger; private readonly Func _activator; - private readonly ConcurrentQueue _pool = new ConcurrentQueue(); + private readonly ConcurrentQueue _pool; private int _count; - private int _maxSize; - public ConnectionPool(KafkaOptions options) + public ConnectionPool(ILogger logger, KafkaOptions options) { + _logger = logger; + _pool = new ConcurrentQueue(); _maxSize = options.ConnectionPoolSize; _activator = CreateActivator(options); - ServersAddress = options.Servers; + + _logger.LogDebug("Kafka configuration of CAP :\r\n {0}", + JsonConvert.SerializeObject(options.AsKafkaConfig(), Formatting.Indented)); } public string ServersAddress { get; } diff --git a/src/DotNetCore.CAP.RabbitMQ/IConnectionChannelPool.Default.cs b/src/DotNetCore.CAP.RabbitMQ/IConnectionChannelPool.Default.cs index e650518..e15fdfa 100644 --- a/src/DotNetCore.CAP.RabbitMQ/IConnectionChannelPool.Default.cs +++ b/src/DotNetCore.CAP.RabbitMQ/IConnectionChannelPool.Default.cs @@ -6,6 +6,7 @@ using System.Collections.Concurrent; using System.Diagnostics; using System.Threading; using Microsoft.Extensions.Logging; +using Newtonsoft.Json; using RabbitMQ.Client; namespace DotNetCore.CAP.RabbitMQ @@ -15,21 +16,24 @@ namespace DotNetCore.CAP.RabbitMQ private const int DefaultPoolSize = 15; private readonly Func _connectionActivator; private readonly ILogger _logger; - private readonly ConcurrentQueue _pool = new ConcurrentQueue(); + private readonly ConcurrentQueue _pool; private IConnection _connection; private int _count; private int _maxSize; - public ConnectionChannelPool(ILogger logger, - RabbitMQOptions options) + public ConnectionChannelPool(ILogger logger, RabbitMQOptions options) { _logger = logger; _maxSize = DefaultPoolSize; - + _pool = new ConcurrentQueue(); _connectionActivator = CreateConnection(options); + HostAddress = options.HostName + ":" + options.Port; Exchange = options.ExchangeName; + + _logger.LogDebug("RabbitMQ configuration of CAP :\r\n {0}", + JsonConvert.SerializeObject(options, Formatting.Indented)); } IModel IConnectionChannelPool.Rent() @@ -87,7 +91,7 @@ namespace DotNetCore.CAP.RabbitMQ private void RabbitMQ_ConnectionShutdown(object sender, ShutdownEventArgs e) { - _logger.LogWarning($"RabbitMQ client connection closed! {e}"); + _logger.LogWarning($"RabbitMQ client connection closed! --> {e.ReplyText}"); } public virtual IModel Rent() diff --git a/src/DotNetCore.CAP.RabbitMQ/RabbitMQConsumerClient.cs b/src/DotNetCore.CAP.RabbitMQ/RabbitMQConsumerClient.cs index a34054f..972b6dd 100644 --- a/src/DotNetCore.CAP.RabbitMQ/RabbitMQConsumerClient.cs +++ b/src/DotNetCore.CAP.RabbitMQ/RabbitMQConsumerClient.cs @@ -93,7 +93,7 @@ namespace DotNetCore.CAP.RabbitMQ _connection = _connectionChannelPool.GetConnection(); _channel = _connection.CreateModel(); - + _channel.ExchangeDeclare( _exchageName, RabbitMQOptions.ExchangeType, @@ -155,7 +155,7 @@ namespace DotNetCore.CAP.RabbitMQ var args = new LogMessageEventArgs { LogType = MqLogType.ConsumerShutdown, - Reason = e.ToString() + Reason = e.ReplyText }; OnLog?.Invoke(sender, args); } diff --git a/src/DotNetCore.CAP/IConsumerHandler.Default.cs b/src/DotNetCore.CAP/IConsumerHandler.Default.cs index 1d441ff..e2179ff 100644 --- a/src/DotNetCore.CAP/IConsumerHandler.Default.cs +++ b/src/DotNetCore.CAP/IConsumerHandler.Default.cs @@ -146,22 +146,22 @@ namespace DotNetCore.CAP switch (logmsg.LogType) { case MqLogType.ConsumerCancelled: - _logger.LogWarning("RabbitMQ consumer cancelled. reason: " + logmsg.Reason); + _logger.LogWarning("RabbitMQ consumer cancelled. --> " + logmsg.Reason); break; case MqLogType.ConsumerRegistered: - _logger.LogInformation("RabbitMQ consumer registered. " + logmsg.Reason); + _logger.LogInformation("RabbitMQ consumer registered. --> " + logmsg.Reason); break; case MqLogType.ConsumerUnregistered: - _logger.LogWarning("RabbitMQ consumer unregistered. reason: " + logmsg.Reason); + _logger.LogWarning("RabbitMQ consumer unregistered. --> " + logmsg.Reason); break; case MqLogType.ConsumerShutdown: - _logger.LogWarning("RabbitMQ consumer shutdown. reason:" + logmsg.Reason); + _logger.LogWarning("RabbitMQ consumer shutdown. --> " + logmsg.Reason); break; case MqLogType.ConsumeError: - _logger.LogError("Kakfa client consume error. reason:" + logmsg.Reason); + _logger.LogError("Kakfa client consume error. --> " + logmsg.Reason); break; case MqLogType.ServerConnError: - _logger.LogCritical("Kafka server connection error. reason:" + logmsg.Reason); + _logger.LogCritical("Kafka server connection error. --> " + logmsg.Reason); break; default: throw new ArgumentOutOfRangeException(); From d8c96d8070e5c7958b4c6929edf74ae2f356f074 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Tue, 5 Jun 2018 10:17:30 +0800 Subject: [PATCH 074/223] Upgrade samples to .net core 2.1 --- .../Controllers/ValuesController.cs | 2 +- samples/Sample.Kafka.MySql/Program.cs | 6 ------ .../Sample.Kafka.MySql/Sample.Kafka.MySql.csproj | 15 +++------------ .../Controllers/ValuesController.cs | 3 +-- samples/Sample.RabbitMQ.MySql/Program.cs | 6 ------ .../Sample.RabbitMQ.MySql.csproj | 12 +++--------- samples/Sample.RabbitMQ.MySql/Startup.cs | 3 --- 7 files changed, 8 insertions(+), 39 deletions(-) diff --git a/samples/Sample.Kafka.MySql/Controllers/ValuesController.cs b/samples/Sample.Kafka.MySql/Controllers/ValuesController.cs index 6f04946..21510bb 100644 --- a/samples/Sample.Kafka.MySql/Controllers/ValuesController.cs +++ b/samples/Sample.Kafka.MySql/Controllers/ValuesController.cs @@ -34,7 +34,7 @@ namespace Sample.Kafka.MySql.Controllers return Ok("publish successful!"); } - [CapSubscribe("xxx.xxx.test2")] + [CapSubscribe("#.test2")] public void Test2(int value) { Console.WriteLine("Subscriber output message: " + value); diff --git a/samples/Sample.Kafka.MySql/Program.cs b/samples/Sample.Kafka.MySql/Program.cs index 976b20a..e7be245 100644 --- a/samples/Sample.Kafka.MySql/Program.cs +++ b/samples/Sample.Kafka.MySql/Program.cs @@ -1,6 +1,5 @@ using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; -using NLog.Web; namespace Sample.Kafka.MySql { @@ -15,11 +14,6 @@ namespace Sample.Kafka.MySql public static IWebHost BuildWebHost(string[] args) => WebHost.CreateDefaultBuilder(args) .UseStartup() - .ConfigureLogging((hostingContext, builder) => - { - hostingContext.HostingEnvironment.ConfigureNLog("nlog.config"); - }) - .UseNLog() .Build(); } } \ No newline at end of file diff --git a/samples/Sample.Kafka.MySql/Sample.Kafka.MySql.csproj b/samples/Sample.Kafka.MySql/Sample.Kafka.MySql.csproj index bf80ce5..e4a70df 100644 --- a/samples/Sample.Kafka.MySql/Sample.Kafka.MySql.csproj +++ b/samples/Sample.Kafka.MySql/Sample.Kafka.MySql.csproj @@ -1,29 +1,20 @@  - netcoreapp2.0 + netcoreapp2.1 Sample.Kafka.MySql NU1701 NU1701 - + - - - - - + - - - PreserveNewest - - diff --git a/samples/Sample.RabbitMQ.MySql/Controllers/ValuesController.cs b/samples/Sample.RabbitMQ.MySql/Controllers/ValuesController.cs index 1b47e5e..03f33c0 100644 --- a/samples/Sample.RabbitMQ.MySql/Controllers/ValuesController.cs +++ b/samples/Sample.RabbitMQ.MySql/Controllers/ValuesController.cs @@ -25,7 +25,6 @@ namespace Sample.RabbitMQ.MySql.Controllers return Ok(); } - [Route("~/publish2")] public IActionResult PublishMessage2() { @@ -47,7 +46,7 @@ namespace Sample.RabbitMQ.MySql.Controllers } [NonAction] - [CapSubscribe("sample.rabbitmq.mysql")] + [CapSubscribe("#.rabbitmq.mysql")] public void ReceiveMessage(DateTime time) { Console.WriteLine("[sample.rabbitmq.mysql] message received: " + DateTime.Now + ",sent time: " + time); diff --git a/samples/Sample.RabbitMQ.MySql/Program.cs b/samples/Sample.RabbitMQ.MySql/Program.cs index 3cbbe15..4e25d5c 100644 --- a/samples/Sample.RabbitMQ.MySql/Program.cs +++ b/samples/Sample.RabbitMQ.MySql/Program.cs @@ -1,6 +1,5 @@ using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; -using NLog.Web; namespace Sample.RabbitMQ.MySql { @@ -14,11 +13,6 @@ namespace Sample.RabbitMQ.MySql public static IWebHost BuildWebHost(string[] args) => WebHost.CreateDefaultBuilder(args) .UseStartup() - .ConfigureLogging((hostingContext, builder) => - { - hostingContext.HostingEnvironment.ConfigureNLog("nlog.config"); - }) - .UseNLog() .Build(); } } diff --git a/samples/Sample.RabbitMQ.MySql/Sample.RabbitMQ.MySql.csproj b/samples/Sample.RabbitMQ.MySql/Sample.RabbitMQ.MySql.csproj index 5c981a3..3b11738 100644 --- a/samples/Sample.RabbitMQ.MySql/Sample.RabbitMQ.MySql.csproj +++ b/samples/Sample.RabbitMQ.MySql/Sample.RabbitMQ.MySql.csproj @@ -1,7 +1,7 @@  - netcoreapp2.0 + netcoreapp2.1 @@ -10,9 +10,8 @@ - - - + + @@ -22,10 +21,5 @@ - - - PreserveNewest - - diff --git a/samples/Sample.RabbitMQ.MySql/Startup.cs b/samples/Sample.RabbitMQ.MySql/Startup.cs index 6525770..554a624 100644 --- a/samples/Sample.RabbitMQ.MySql/Startup.cs +++ b/samples/Sample.RabbitMQ.MySql/Startup.cs @@ -23,9 +23,6 @@ namespace Sample.RabbitMQ.MySql public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { - loggerFactory.AddConsole(); - loggerFactory.AddDebug(); - app.UseMvc(); app.UseCap(); From a0bf2d98f73011c92ce5b77332bcb0b0a9716aa9 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Tue, 5 Jun 2018 10:32:19 +0800 Subject: [PATCH 075/223] Add Directory.Build.props --- Directory.Build.props | 18 ++++++++++++++++++ build/common.props | 17 ----------------- .../DotNetCore.CAP.Kafka.csproj | 2 -- .../DotNetCore.CAP.MySql.csproj | 2 -- .../DotNetCore.CAP.PostgreSql.csproj | 2 -- .../DotNetCore.CAP.RabbitMQ.csproj | 2 -- .../DotNetCore.CAP.SqlServer.csproj | 2 -- 7 files changed, 18 insertions(+), 27 deletions(-) create mode 100644 Directory.Build.props delete mode 100644 build/common.props diff --git a/Directory.Build.props b/Directory.Build.props new file mode 100644 index 0000000..c4ae862 --- /dev/null +++ b/Directory.Build.props @@ -0,0 +1,18 @@ + + + + + + CAP + .NET Core Community;Savorboard + https://github.com/dotnetcore/CAP + git + $(MSBuildThisFileDirectory) + https://avatars2.githubusercontent.com/u/19404084 + https://github.com/dotnetcore/CAP + https://github.com/dotnetcore/CAP/blob/master/LICENSE.txt + CAP;EventBus;Distributed Transaction + EventBus and eventually consistency in distributed architectures. + + + \ No newline at end of file diff --git a/build/common.props b/build/common.props deleted file mode 100644 index 80c2d79..0000000 --- a/build/common.props +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - CAP - savorboard;dotnetcore - https://github.com/dotnetcore/CAP - git - https://avatars2.githubusercontent.com/u/19404084 - https://github.com/dotnetcore/CAP - https://github.com/dotnetcore/CAP/blob/master/LICENSE.txt - CAP;EventBus;Distributed Transaction - EventBus and eventually consistency in distributed architectures. - - - diff --git a/src/DotNetCore.CAP.Kafka/DotNetCore.CAP.Kafka.csproj b/src/DotNetCore.CAP.Kafka/DotNetCore.CAP.Kafka.csproj index 72e580e..8c8d5e7 100644 --- a/src/DotNetCore.CAP.Kafka/DotNetCore.CAP.Kafka.csproj +++ b/src/DotNetCore.CAP.Kafka/DotNetCore.CAP.Kafka.csproj @@ -1,7 +1,5 @@  - - netstandard2.0 DotNetCore.CAP.Kafka diff --git a/src/DotNetCore.CAP.MySql/DotNetCore.CAP.MySql.csproj b/src/DotNetCore.CAP.MySql/DotNetCore.CAP.MySql.csproj index 1e85be1..ecc6794 100644 --- a/src/DotNetCore.CAP.MySql/DotNetCore.CAP.MySql.csproj +++ b/src/DotNetCore.CAP.MySql/DotNetCore.CAP.MySql.csproj @@ -1,7 +1,5 @@  - - netstandard2.0 DotNetCore.CAP.MySql diff --git a/src/DotNetCore.CAP.PostgreSql/DotNetCore.CAP.PostgreSql.csproj b/src/DotNetCore.CAP.PostgreSql/DotNetCore.CAP.PostgreSql.csproj index 81b1b5c..fb40b7c 100644 --- a/src/DotNetCore.CAP.PostgreSql/DotNetCore.CAP.PostgreSql.csproj +++ b/src/DotNetCore.CAP.PostgreSql/DotNetCore.CAP.PostgreSql.csproj @@ -1,7 +1,5 @@  - - netstandard2.0 DotNetCore.CAP.PostgreSql diff --git a/src/DotNetCore.CAP.RabbitMQ/DotNetCore.CAP.RabbitMQ.csproj b/src/DotNetCore.CAP.RabbitMQ/DotNetCore.CAP.RabbitMQ.csproj index 6a68173..deccfb7 100644 --- a/src/DotNetCore.CAP.RabbitMQ/DotNetCore.CAP.RabbitMQ.csproj +++ b/src/DotNetCore.CAP.RabbitMQ/DotNetCore.CAP.RabbitMQ.csproj @@ -1,7 +1,5 @@  - - netstandard2.0 DotNetCore.CAP.RabbitMQ diff --git a/src/DotNetCore.CAP.SqlServer/DotNetCore.CAP.SqlServer.csproj b/src/DotNetCore.CAP.SqlServer/DotNetCore.CAP.SqlServer.csproj index 7d03a24..d45d0f4 100644 --- a/src/DotNetCore.CAP.SqlServer/DotNetCore.CAP.SqlServer.csproj +++ b/src/DotNetCore.CAP.SqlServer/DotNetCore.CAP.SqlServer.csproj @@ -1,7 +1,5 @@  - - netstandard2.0 DotNetCore.CAP.SqlServer From c0337ae6089b3d175c0248a8e4753579fd55cb3d Mon Sep 17 00:00:00 2001 From: Savorboard Date: Tue, 5 Jun 2018 10:32:49 +0800 Subject: [PATCH 076/223] refactor csproj structure. --- CAP.sln | 1 - .../Sample.RabbitMQ.MySql.csproj | 6 ++-- src/DotNetCore.CAP/DotNetCore.CAP.csproj | 33 ++++--------------- 3 files changed, 8 insertions(+), 32 deletions(-) diff --git a/CAP.sln b/CAP.sln index 828d82a..225537c 100644 --- a/CAP.sln +++ b/CAP.sln @@ -36,7 +36,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{10C0818D build.cake = build.cake build.ps1 = build.ps1 build.sh = build.sh - build\common.props = build\common.props build\index.cake = build\index.cake build\util.cake = build\util.cake build\version.cake = build\version.cake diff --git a/samples/Sample.RabbitMQ.MySql/Sample.RabbitMQ.MySql.csproj b/samples/Sample.RabbitMQ.MySql/Sample.RabbitMQ.MySql.csproj index 3b11738..cf0add9 100644 --- a/samples/Sample.RabbitMQ.MySql/Sample.RabbitMQ.MySql.csproj +++ b/samples/Sample.RabbitMQ.MySql/Sample.RabbitMQ.MySql.csproj @@ -12,10 +12,8 @@ - - - - + + diff --git a/src/DotNetCore.CAP/DotNetCore.CAP.csproj b/src/DotNetCore.CAP/DotNetCore.CAP.csproj index 464a2a5..20df14b 100644 --- a/src/DotNetCore.CAP/DotNetCore.CAP.csproj +++ b/src/DotNetCore.CAP/DotNetCore.CAP.csproj @@ -1,32 +1,14 @@ - - + + netstandard2.0 - DotNetCore.CAP - $(PackageTags); + bin\$(Configuration)\netstandard2.0\DotNetCore.CAP.xml 1701;1702;1705;CS1591 - - - - - - - - - - - - - - - - - - + @@ -47,6 +29,7 @@ + @@ -60,6 +43,7 @@ + True @@ -143,9 +127,4 @@ Strings.Designer.cs - - - RazorGenerator - - \ No newline at end of file From 15886bd3f937c0e3c1ff67ec08252a9c84b3c43a Mon Sep 17 00:00:00 2001 From: Savorboard Date: Tue, 5 Jun 2018 11:00:41 +0800 Subject: [PATCH 077/223] Fix compilation errors after the upgrade --- src/DotNetCore.CAP.MySql/MySqlMonitoringApi.cs | 8 ++++---- src/DotNetCore.CAP.PostgreSql/PostgreSqlMonitoringApi.cs | 6 +++--- src/DotNetCore.CAP.SqlServer/SqlServerMonitoringApi.cs | 8 ++++---- src/DotNetCore.CAP/Dashboard/TimelineCounter.cs | 8 ++++++++ 4 files changed, 19 insertions(+), 11 deletions(-) create mode 100644 src/DotNetCore.CAP/Dashboard/TimelineCounter.cs diff --git a/src/DotNetCore.CAP.MySql/MySqlMonitoringApi.cs b/src/DotNetCore.CAP.MySql/MySqlMonitoringApi.cs index 6fd0888..a8a4bdf 100644 --- a/src/DotNetCore.CAP.MySql/MySqlMonitoringApi.cs +++ b/src/DotNetCore.CAP.MySql/MySqlMonitoringApi.cs @@ -126,7 +126,7 @@ select count(Id) from `{0}.received` where StatusName = N'Failed';", _prefix); { var sqlQuery = $"select count(Id) from `{_prefix}.{tableName}` where StatusName = @state"; - var count = connection.ExecuteScalar(sqlQuery, new {state = statusName}); + var count = connection.ExecuteScalar(sqlQuery, new { state = statusName }); return count; } @@ -167,10 +167,10 @@ select aggr.* from ( group by date_format(`Added`,'%Y-%m-%d-%H') ) aggr where `Key` in @keys;"; - var valuesMap = connection.Query( + var valuesMap = connection.Query( sqlQuery, - new {keys = keyMaps.Keys, statusName}) - .ToDictionary(x => (string) x.Key, x => (int) x.Count); + new { keys = keyMaps.Keys, statusName }) + .ToDictionary(x => x.Key, x => x.Count); foreach (var key in keyMaps.Keys) { diff --git a/src/DotNetCore.CAP.PostgreSql/PostgreSqlMonitoringApi.cs b/src/DotNetCore.CAP.PostgreSql/PostgreSqlMonitoringApi.cs index 8fcf9f8..2c73bff 100644 --- a/src/DotNetCore.CAP.PostgreSql/PostgreSqlMonitoringApi.cs +++ b/src/DotNetCore.CAP.PostgreSql/PostgreSqlMonitoringApi.cs @@ -128,7 +128,7 @@ select count(""Id"") from ""{0}"".""received"" where ""StatusName"" = N'Failed' var sqlQuery = $"select count(\"Id\") from \"{_options.Schema}\".\"{tableName}\" where Lower(\"StatusName\") = Lower(@state)"; - var count = connection.ExecuteScalar(sqlQuery, new {state = statusName}); + var count = connection.ExecuteScalar(sqlQuery, new { state = statusName }); return count; } @@ -170,9 +170,9 @@ with aggr as ( ) select ""Key"",""Count"" from aggr where ""Key""= Any(@keys);"; - var valuesMap = connection.Query(sqlQuery, new {keys = keyMaps.Keys.ToList(), statusName}) + var valuesMap = connection.Query(sqlQuery, new { keys = keyMaps.Keys.ToList(), statusName }) .ToList() - .ToDictionary(x => (string) x.Key, x => (int) x.Count); + .ToDictionary(x => x.Key, x => x.Count); foreach (var key in keyMaps.Keys) { diff --git a/src/DotNetCore.CAP.SqlServer/SqlServerMonitoringApi.cs b/src/DotNetCore.CAP.SqlServer/SqlServerMonitoringApi.cs index 732143e..31c4bcd 100644 --- a/src/DotNetCore.CAP.SqlServer/SqlServerMonitoringApi.cs +++ b/src/DotNetCore.CAP.SqlServer/SqlServerMonitoringApi.cs @@ -128,7 +128,7 @@ select count(Id) from [{0}].Received with (nolock) where StatusName = N'Failed'; var sqlQuery = $"select count(Id) from [{_options.Schema}].{tableName} with (nolock) where StatusName = @state"; - var count = connection.ExecuteScalar(sqlQuery, new {state = statusName}); + var count = connection.ExecuteScalar(sqlQuery, new { state = statusName }); return count; } @@ -171,10 +171,10 @@ with aggr as ( ) select [Key], [Count] from aggr with (nolock) where [Key] in @keys;"; - var valuesMap = connection.Query( + var valuesMap = connection.Query( sqlQuery, - new {keys = keyMaps.Keys, statusName}) - .ToDictionary(x => (string) x.Key, x => (int) x.Count); + new { keys = keyMaps.Keys, statusName }) + .ToDictionary(x => x.Key, x => x.Count); foreach (var key in keyMaps.Keys) { diff --git a/src/DotNetCore.CAP/Dashboard/TimelineCounter.cs b/src/DotNetCore.CAP/Dashboard/TimelineCounter.cs new file mode 100644 index 0000000..c4c1a0b --- /dev/null +++ b/src/DotNetCore.CAP/Dashboard/TimelineCounter.cs @@ -0,0 +1,8 @@ +namespace DotNetCore.CAP.Dashboard +{ + public class TimelineCounter + { + public string Key { get; set; } + public int Count { get; set; } + } +} From ffd23df4fd29f8d1de650a794a8902e87412c86b Mon Sep 17 00:00:00 2001 From: Savorboard Date: Tue, 5 Jun 2018 11:01:38 +0800 Subject: [PATCH 078/223] Upgrade the dependent nuget package to the latest version --- .../Sample.Kafka.MySql.csproj | 2 +- .../DotNetCore.CAP.MySql.csproj | 8 ++--- .../DotNetCore.CAP.PostgreSql.csproj | 8 ++--- .../DotNetCore.CAP.SqlServer.csproj | 9 +++--- src/DotNetCore.CAP/DotNetCore.CAP.csproj | 14 ++++----- .../DotNetCore.CAP.MySql.Test.csproj | 29 +++++++++---------- .../DotNetCore.CAP.PostgreSql.Test.csproj | 11 ++++--- .../DotNetCore.CAP.SqlServer.Test.csproj | 24 +++++++-------- .../DotNetCore.CAP.Test.csproj | 14 ++++----- 9 files changed, 57 insertions(+), 62 deletions(-) diff --git a/samples/Sample.Kafka.MySql/Sample.Kafka.MySql.csproj b/samples/Sample.Kafka.MySql/Sample.Kafka.MySql.csproj index e4a70df..a657851 100644 --- a/samples/Sample.Kafka.MySql/Sample.Kafka.MySql.csproj +++ b/samples/Sample.Kafka.MySql/Sample.Kafka.MySql.csproj @@ -9,7 +9,7 @@ - + diff --git a/src/DotNetCore.CAP.MySql/DotNetCore.CAP.MySql.csproj b/src/DotNetCore.CAP.MySql/DotNetCore.CAP.MySql.csproj index ecc6794..14734ad 100644 --- a/src/DotNetCore.CAP.MySql/DotNetCore.CAP.MySql.csproj +++ b/src/DotNetCore.CAP.MySql/DotNetCore.CAP.MySql.csproj @@ -12,10 +12,10 @@ - - - - + + + + diff --git a/src/DotNetCore.CAP.PostgreSql/DotNetCore.CAP.PostgreSql.csproj b/src/DotNetCore.CAP.PostgreSql/DotNetCore.CAP.PostgreSql.csproj index fb40b7c..0e3698f 100644 --- a/src/DotNetCore.CAP.PostgreSql/DotNetCore.CAP.PostgreSql.csproj +++ b/src/DotNetCore.CAP.PostgreSql/DotNetCore.CAP.PostgreSql.csproj @@ -12,10 +12,10 @@ - - - - + + + + diff --git a/src/DotNetCore.CAP.SqlServer/DotNetCore.CAP.SqlServer.csproj b/src/DotNetCore.CAP.SqlServer/DotNetCore.CAP.SqlServer.csproj index d45d0f4..27d3ca2 100644 --- a/src/DotNetCore.CAP.SqlServer/DotNetCore.CAP.SqlServer.csproj +++ b/src/DotNetCore.CAP.SqlServer/DotNetCore.CAP.SqlServer.csproj @@ -4,6 +4,7 @@ netstandard2.0 DotNetCore.CAP.SqlServer $(PackageTags);SQL Server + @@ -12,10 +13,10 @@ - - - - + + + + diff --git a/src/DotNetCore.CAP/DotNetCore.CAP.csproj b/src/DotNetCore.CAP/DotNetCore.CAP.csproj index 20df14b..cc1a94d 100644 --- a/src/DotNetCore.CAP/DotNetCore.CAP.csproj +++ b/src/DotNetCore.CAP/DotNetCore.CAP.csproj @@ -32,16 +32,16 @@ - - - - + + + + - + - - + + diff --git a/test/DotNetCore.CAP.MySql.Test/DotNetCore.CAP.MySql.Test.csproj b/test/DotNetCore.CAP.MySql.Test/DotNetCore.CAP.MySql.Test.csproj index f96229e..8678e45 100644 --- a/test/DotNetCore.CAP.MySql.Test/DotNetCore.CAP.MySql.Test.csproj +++ b/test/DotNetCore.CAP.MySql.Test/DotNetCore.CAP.MySql.Test.csproj @@ -1,11 +1,8 @@  - netcoreapp2.0 - true - DotNetCore.CAP.MySql.Test - DotNetCore.CAP.MySql.Test - true + netcoreapp2.1 + false @@ -14,20 +11,20 @@ - - - + + + - - + + - - - - - - + + + + + + \ No newline at end of file diff --git a/test/DotNetCore.CAP.PostgreSql.Test/DotNetCore.CAP.PostgreSql.Test.csproj b/test/DotNetCore.CAP.PostgreSql.Test/DotNetCore.CAP.PostgreSql.Test.csproj index 2346ca5..335f851 100644 --- a/test/DotNetCore.CAP.PostgreSql.Test/DotNetCore.CAP.PostgreSql.Test.csproj +++ b/test/DotNetCore.CAP.PostgreSql.Test/DotNetCore.CAP.PostgreSql.Test.csproj @@ -1,15 +1,14 @@ - + - netcoreapp2.0 - + netcoreapp2.1 false - - - + + + diff --git a/test/DotNetCore.CAP.SqlServer.Test/DotNetCore.CAP.SqlServer.Test.csproj b/test/DotNetCore.CAP.SqlServer.Test/DotNetCore.CAP.SqlServer.Test.csproj index d8e70d4..9375d31 100644 --- a/test/DotNetCore.CAP.SqlServer.Test/DotNetCore.CAP.SqlServer.Test.csproj +++ b/test/DotNetCore.CAP.SqlServer.Test/DotNetCore.CAP.SqlServer.Test.csproj @@ -1,7 +1,7 @@  - netcoreapp2.0 + netcoreapp2.1 false @@ -11,20 +11,20 @@ - - - + + + - - + + - - - - - - + + + + + + diff --git a/test/DotNetCore.CAP.Test/DotNetCore.CAP.Test.csproj b/test/DotNetCore.CAP.Test/DotNetCore.CAP.Test.csproj index 09e9dd9..7216569 100644 --- a/test/DotNetCore.CAP.Test/DotNetCore.CAP.Test.csproj +++ b/test/DotNetCore.CAP.Test/DotNetCore.CAP.Test.csproj @@ -1,21 +1,19 @@  - netcoreapp2.0 - true - DotNetCore.CAP.Test - true + netcoreapp2.1 + false - + - - + + - + From ff4248c598d6f2fe7c76fdef7e8698f781f57125 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Tue, 5 Jun 2018 11:15:56 +0800 Subject: [PATCH 079/223] update ci.yml --- .travis.yml | 34 ++++++---------------------------- 1 file changed, 6 insertions(+), 28 deletions(-) diff --git a/.travis.yml b/.travis.yml index 4d8bd00..f392fbd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,8 @@ -language: cpp +language: csharp sudo: required dist: trusty +solution: CAP.sln +dotnet: 2.1.0 matrix: include: @@ -10,33 +12,9 @@ matrix: - os: osx osx_image: xcode8.3 # macOS 10.12 -env: - global: - - DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true - - DOTNET_CLI_TELEMETRY_OPTOUT: 1 - - CLI_VERSION=2.0.0 - -addons: - apt: - packages: - - gettext - - libcurl4-openssl-dev - - libicu-dev - - libssl-dev - - libunwind8 - - zlib1g - -# Make sure build dependencies are installed. -before_install: - - if test "$TRAVIS_OS_NAME" == "osx"; then ln -s /usr/local/opt/openssl/lib/libcrypto.1.0.0.dylib /usr/local/lib/; ln -s /usr/local/opt/openssl/lib/libssl.1.0.0.dylib /usr/local/lib/; fi - - export DOTNET_INSTALL_DIR="$PWD/.dotnetcli" - - export PATH="$DOTNET_INSTALL_DIR:$PATH" - -install: - - travis_retry curl -sSL https://dot.net/v1/dotnet-install.sh | bash /dev/stdin --channel 2.0 --version "$CLI_VERSION" --install-dir "$DOTNET_INSTALL_DIR" - # Run the build script script: - dotnet --info - - dotnet restore - - dotnet test test/DotNetCore.CAP.Test/DotNetCore.CAP.Test.csproj -f netcoreapp2.0 + - dotnet restore CAP.sln + - dotnet build CAP.sln + - dotnet test test/DotNetCore.CAP.Test/DotNetCore.CAP.Test.csproj From 5457b41a204e58eaa8c3eaf88b542d8dcdecc68c Mon Sep 17 00:00:00 2001 From: Savorboard Date: Tue, 5 Jun 2018 11:20:59 +0800 Subject: [PATCH 080/223] update ci.yml --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index f392fbd..ba8c1fe 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,8 @@ language: csharp sudo: required dist: trusty solution: CAP.sln -dotnet: 2.1.0 +dotnet: 2.1.300 +mono: none matrix: include: From 05852ee5116d7221246525675c9489bd639c1a28 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Tue, 5 Jun 2018 13:43:37 +0800 Subject: [PATCH 081/223] update sample. --- build/version.props | 2 +- samples/Sample.Kafka.MySql/Program.cs | 8 +----- .../Sample.Kafka.MySql.csproj | 20 ++++---------- samples/Sample.Kafka.MySql/nlog.config | 26 ------------------- samples/Sample.RabbitMQ.MySql/Program.cs | 8 +----- .../Sample.RabbitMQ.MySql.csproj | 22 +++------------- samples/Sample.RabbitMQ.MySql/nlog.config | 26 ------------------- 7 files changed, 12 insertions(+), 100 deletions(-) delete mode 100644 samples/Sample.Kafka.MySql/nlog.config delete mode 100644 samples/Sample.RabbitMQ.MySql/nlog.config diff --git a/build/version.props b/build/version.props index ae5b04f..dcd0b81 100644 --- a/build/version.props +++ b/build/version.props @@ -2,7 +2,7 @@ 2 2 - 2 + 3 $(VersionMajor).$(VersionMinor).$(VersionPatch) diff --git a/samples/Sample.Kafka.MySql/Program.cs b/samples/Sample.Kafka.MySql/Program.cs index 976b20a..d92eedd 100644 --- a/samples/Sample.Kafka.MySql/Program.cs +++ b/samples/Sample.Kafka.MySql/Program.cs @@ -1,6 +1,5 @@ using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; -using NLog.Web; namespace Sample.Kafka.MySql { @@ -14,12 +13,7 @@ namespace Sample.Kafka.MySql public static IWebHost BuildWebHost(string[] args) => WebHost.CreateDefaultBuilder(args) - .UseStartup() - .ConfigureLogging((hostingContext, builder) => - { - hostingContext.HostingEnvironment.ConfigureNLog("nlog.config"); - }) - .UseNLog() + .UseStartup() .Build(); } } \ No newline at end of file diff --git a/samples/Sample.Kafka.MySql/Sample.Kafka.MySql.csproj b/samples/Sample.Kafka.MySql/Sample.Kafka.MySql.csproj index 88c0c1d..27a8c6e 100644 --- a/samples/Sample.Kafka.MySql/Sample.Kafka.MySql.csproj +++ b/samples/Sample.Kafka.MySql/Sample.Kafka.MySql.csproj @@ -1,30 +1,20 @@  - netcoreapp2.0 + netcoreapp2.1 Sample.Kafka.MySql NU1701 NU1701 - - - - - - - - + + + - - - - PreserveNewest - - + diff --git a/samples/Sample.Kafka.MySql/nlog.config b/samples/Sample.Kafka.MySql/nlog.config deleted file mode 100644 index 5b91105..0000000 --- a/samples/Sample.Kafka.MySql/nlog.config +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/samples/Sample.RabbitMQ.MySql/Program.cs b/samples/Sample.RabbitMQ.MySql/Program.cs index 3cbbe15..d8e6083 100644 --- a/samples/Sample.RabbitMQ.MySql/Program.cs +++ b/samples/Sample.RabbitMQ.MySql/Program.cs @@ -1,6 +1,5 @@ using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; -using NLog.Web; namespace Sample.RabbitMQ.MySql { @@ -13,12 +12,7 @@ namespace Sample.RabbitMQ.MySql public static IWebHost BuildWebHost(string[] args) => WebHost.CreateDefaultBuilder(args) - .UseStartup() - .ConfigureLogging((hostingContext, builder) => - { - hostingContext.HostingEnvironment.ConfigureNLog("nlog.config"); - }) - .UseNLog() + .UseStartup() .Build(); } } diff --git a/samples/Sample.RabbitMQ.MySql/Sample.RabbitMQ.MySql.csproj b/samples/Sample.RabbitMQ.MySql/Sample.RabbitMQ.MySql.csproj index 5c981a3..5faf15f 100644 --- a/samples/Sample.RabbitMQ.MySql/Sample.RabbitMQ.MySql.csproj +++ b/samples/Sample.RabbitMQ.MySql/Sample.RabbitMQ.MySql.csproj @@ -1,31 +1,17 @@  - netcoreapp2.0 - - - - 1701;1702;1705;3277; - NU1605;MSB3277 + netcoreapp2.1 - - - - - - - + + + - - - PreserveNewest - - diff --git a/samples/Sample.RabbitMQ.MySql/nlog.config b/samples/Sample.RabbitMQ.MySql/nlog.config deleted file mode 100644 index bba5bea..0000000 --- a/samples/Sample.RabbitMQ.MySql/nlog.config +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - - - - - - \ No newline at end of file From b45482000c425872b83317f21907bdfafb14968d Mon Sep 17 00:00:00 2001 From: Savorboard Date: Tue, 5 Jun 2018 14:02:24 +0800 Subject: [PATCH 082/223] update version to 2.2.4 --- build/version.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/version.props b/build/version.props index dcd0b81..537d4af 100644 --- a/build/version.props +++ b/build/version.props @@ -2,7 +2,7 @@ 2 2 - 3 + 4 $(VersionMajor).$(VersionMinor).$(VersionPatch) From 32d71144ecab9a09c21749fd8eda19d0fdbad473 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Tue, 5 Jun 2018 14:02:24 +0800 Subject: [PATCH 083/223] update version to 2.2.4 --- build/version.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/version.props b/build/version.props index dcd0b81..537d4af 100644 --- a/build/version.props +++ b/build/version.props @@ -2,7 +2,7 @@ 2 2 - 3 + 4 $(VersionMajor).$(VersionMinor).$(VersionPatch) From 5a4675d45b929faab4ee046f0102a8327f884d5c Mon Sep 17 00:00:00 2001 From: Savorboard Date: Tue, 5 Jun 2018 14:03:41 +0800 Subject: [PATCH 084/223] update version to 2.2.4 (#138) --- build/version.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/version.props b/build/version.props index dcd0b81..537d4af 100644 --- a/build/version.props +++ b/build/version.props @@ -2,7 +2,7 @@ 2 2 - 3 + 4 $(VersionMajor).$(VersionMinor).$(VersionPatch) From 666fdc46853f7ea55d71413dbff172fcfdf2d016 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Thu, 7 Jun 2018 18:13:30 +0800 Subject: [PATCH 085/223] Fixed Incorrect local IP address judgment of IPv6. (#140) --- src/DotNetCore.CAP/Dashboard/DashboardRequest.cs | 11 +++-------- .../Dashboard/LocalRequestsOnlyAuthorizationFilter.cs | 9 +++++---- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/src/DotNetCore.CAP/Dashboard/DashboardRequest.cs b/src/DotNetCore.CAP/Dashboard/DashboardRequest.cs index 5fdb054..23f46b8 100644 --- a/src/DotNetCore.CAP/Dashboard/DashboardRequest.cs +++ b/src/DotNetCore.CAP/Dashboard/DashboardRequest.cs @@ -28,19 +28,14 @@ namespace DotNetCore.CAP.Dashboard public CapDashboardRequest(HttpContext context) { - if (context == null) - { - throw new ArgumentNullException(nameof(context)); - } - - _context = context; + _context = context ?? throw new ArgumentNullException(nameof(context)); } public override string Method => _context.Request.Method; public override string Path => _context.Request.Path.Value; public override string PathBase => _context.Request.PathBase.Value; - public override string LocalIpAddress => _context.Connection.LocalIpAddress.ToString(); - public override string RemoteIpAddress => _context.Connection.RemoteIpAddress.ToString(); + public override string LocalIpAddress => _context.Connection.LocalIpAddress.MapToIPv4().ToString(); + public override string RemoteIpAddress => _context.Connection.RemoteIpAddress.MapToIPv4().ToString(); public override string GetQuery(string key) { diff --git a/src/DotNetCore.CAP/Dashboard/LocalRequestsOnlyAuthorizationFilter.cs b/src/DotNetCore.CAP/Dashboard/LocalRequestsOnlyAuthorizationFilter.cs index 42746d7..dafd845 100644 --- a/src/DotNetCore.CAP/Dashboard/LocalRequestsOnlyAuthorizationFilter.cs +++ b/src/DotNetCore.CAP/Dashboard/LocalRequestsOnlyAuthorizationFilter.cs @@ -9,26 +9,27 @@ namespace DotNetCore.CAP.Dashboard { public bool Authorize(DashboardContext context) { + var ipAddress = context.Request.RemoteIpAddress; // if unknown, assume not local - if (string.IsNullOrEmpty(context.Request.RemoteIpAddress)) + if (string.IsNullOrEmpty(ipAddress)) { return false; } // check if localhost - if (context.Request.RemoteIpAddress == "127.0.0.1" || context.Request.RemoteIpAddress == "::1") + if (ipAddress == "127.0.0.1" || ipAddress == "0.0.0.1") { return true; } // compare with local address - if (context.Request.RemoteIpAddress == context.Request.LocalIpAddress) + if (ipAddress == context.Request.LocalIpAddress) { return true; } // check if private ip - if (Helper.IsInnerIP(context.Request.RemoteIpAddress)) + if (Helper.IsInnerIP(ipAddress)) { return true; } From 3b73274e5daa92c8fbdfc8ddf60d76fdc4a29b24 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Thu, 7 Jun 2018 18:13:30 +0800 Subject: [PATCH 086/223] Fixed Incorrect local IP address judgment of IPv6. (#140) --- src/DotNetCore.CAP/Dashboard/DashboardRequest.cs | 11 +++-------- .../Dashboard/LocalRequestsOnlyAuthorizationFilter.cs | 9 +++++---- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/src/DotNetCore.CAP/Dashboard/DashboardRequest.cs b/src/DotNetCore.CAP/Dashboard/DashboardRequest.cs index 5fdb054..23f46b8 100644 --- a/src/DotNetCore.CAP/Dashboard/DashboardRequest.cs +++ b/src/DotNetCore.CAP/Dashboard/DashboardRequest.cs @@ -28,19 +28,14 @@ namespace DotNetCore.CAP.Dashboard public CapDashboardRequest(HttpContext context) { - if (context == null) - { - throw new ArgumentNullException(nameof(context)); - } - - _context = context; + _context = context ?? throw new ArgumentNullException(nameof(context)); } public override string Method => _context.Request.Method; public override string Path => _context.Request.Path.Value; public override string PathBase => _context.Request.PathBase.Value; - public override string LocalIpAddress => _context.Connection.LocalIpAddress.ToString(); - public override string RemoteIpAddress => _context.Connection.RemoteIpAddress.ToString(); + public override string LocalIpAddress => _context.Connection.LocalIpAddress.MapToIPv4().ToString(); + public override string RemoteIpAddress => _context.Connection.RemoteIpAddress.MapToIPv4().ToString(); public override string GetQuery(string key) { diff --git a/src/DotNetCore.CAP/Dashboard/LocalRequestsOnlyAuthorizationFilter.cs b/src/DotNetCore.CAP/Dashboard/LocalRequestsOnlyAuthorizationFilter.cs index 42746d7..dafd845 100644 --- a/src/DotNetCore.CAP/Dashboard/LocalRequestsOnlyAuthorizationFilter.cs +++ b/src/DotNetCore.CAP/Dashboard/LocalRequestsOnlyAuthorizationFilter.cs @@ -9,26 +9,27 @@ namespace DotNetCore.CAP.Dashboard { public bool Authorize(DashboardContext context) { + var ipAddress = context.Request.RemoteIpAddress; // if unknown, assume not local - if (string.IsNullOrEmpty(context.Request.RemoteIpAddress)) + if (string.IsNullOrEmpty(ipAddress)) { return false; } // check if localhost - if (context.Request.RemoteIpAddress == "127.0.0.1" || context.Request.RemoteIpAddress == "::1") + if (ipAddress == "127.0.0.1" || ipAddress == "0.0.0.1") { return true; } // compare with local address - if (context.Request.RemoteIpAddress == context.Request.LocalIpAddress) + if (ipAddress == context.Request.LocalIpAddress) { return true; } // check if private ip - if (Helper.IsInnerIP(context.Request.RemoteIpAddress)) + if (Helper.IsInnerIP(ipAddress)) { return true; } From c5bd4c174f5efb55b8ae8dc2bd5fb24eb7006260 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Thu, 7 Jun 2018 18:33:48 +0800 Subject: [PATCH 087/223] Fixed DateTime localization format conversion error to sql.(#139) --- src/DotNetCore.CAP.MySql/MySqlStorageConnection.cs | 2 +- src/DotNetCore.CAP.PostgreSql/PostgreSqlStorageConnection.cs | 2 +- src/DotNetCore.CAP.SqlServer/SqlServerStorageConnection.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/DotNetCore.CAP.MySql/MySqlStorageConnection.cs b/src/DotNetCore.CAP.MySql/MySqlStorageConnection.cs index 691717d..442328e 100644 --- a/src/DotNetCore.CAP.MySql/MySqlStorageConnection.cs +++ b/src/DotNetCore.CAP.MySql/MySqlStorageConnection.cs @@ -42,7 +42,7 @@ namespace DotNetCore.CAP.MySql public async Task> GetPublishedMessagesOfNeedRetry() { - var fourMinsAgo = DateTime.Now.AddMinutes(-4); + var fourMinsAgo = DateTime.Now.AddMinutes(-4).ToString("O"); var sql = $"SELECT * FROM `{_prefix}.published` WHERE `Retries`<{_capOptions.FailedRetryCount} AND `Added`<'{fourMinsAgo}' AND (`StatusName` = '{StatusName.Failed}' OR `StatusName` = '{StatusName.Scheduled}') LIMIT 200;"; diff --git a/src/DotNetCore.CAP.PostgreSql/PostgreSqlStorageConnection.cs b/src/DotNetCore.CAP.PostgreSql/PostgreSqlStorageConnection.cs index 763407c..e6b3d53 100644 --- a/src/DotNetCore.CAP.PostgreSql/PostgreSqlStorageConnection.cs +++ b/src/DotNetCore.CAP.PostgreSql/PostgreSqlStorageConnection.cs @@ -40,7 +40,7 @@ namespace DotNetCore.CAP.PostgreSql public async Task> GetPublishedMessagesOfNeedRetry() { - var fourMinsAgo = DateTime.Now.AddMinutes(-4); + var fourMinsAgo = DateTime.Now.AddMinutes(-4).ToString("O"); var sql = $"SELECT * FROM \"{Options.Schema}\".\"published\" WHERE \"Retries\"<{_capOptions.FailedRetryCount} AND \"Added\"<'{fourMinsAgo}' AND (\"StatusName\"='{StatusName.Failed}' OR \"StatusName\"='{StatusName.Scheduled}') LIMIT 200;"; diff --git a/src/DotNetCore.CAP.SqlServer/SqlServerStorageConnection.cs b/src/DotNetCore.CAP.SqlServer/SqlServerStorageConnection.cs index 9b931c1..2787fd0 100644 --- a/src/DotNetCore.CAP.SqlServer/SqlServerStorageConnection.cs +++ b/src/DotNetCore.CAP.SqlServer/SqlServerStorageConnection.cs @@ -78,7 +78,7 @@ VALUES(@Name,@Group,@Content,@Retries,@Added,@ExpiresAt,@StatusName);SELECT SCOP public async Task> GetReceivedMessagesOfNeedRetry() { - var fourMinsAgo = DateTime.Now.AddMinutes(-4); + var fourMinsAgo = DateTime.Now.AddMinutes(-4).ToString("O"); var sql = $"SELECT TOP (200) * FROM [{Options.Schema}].[Received] WITH (readpast) WHERE Retries<{_capOptions.FailedRetryCount} AND Added<'{fourMinsAgo}' AND (StatusName = '{StatusName.Failed}' OR StatusName = '{StatusName.Scheduled}')"; using (var connection = new SqlConnection(Options.ConnectionString)) From f5eacced4254996d24acbda29f810d0c527e5549 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Thu, 7 Jun 2018 18:33:48 +0800 Subject: [PATCH 088/223] Fixed DateTime localization format conversion error to sql.(#139) --- src/DotNetCore.CAP.MySql/MySqlStorageConnection.cs | 2 +- src/DotNetCore.CAP.PostgreSql/PostgreSqlStorageConnection.cs | 2 +- src/DotNetCore.CAP.SqlServer/SqlServerStorageConnection.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/DotNetCore.CAP.MySql/MySqlStorageConnection.cs b/src/DotNetCore.CAP.MySql/MySqlStorageConnection.cs index 691717d..442328e 100644 --- a/src/DotNetCore.CAP.MySql/MySqlStorageConnection.cs +++ b/src/DotNetCore.CAP.MySql/MySqlStorageConnection.cs @@ -42,7 +42,7 @@ namespace DotNetCore.CAP.MySql public async Task> GetPublishedMessagesOfNeedRetry() { - var fourMinsAgo = DateTime.Now.AddMinutes(-4); + var fourMinsAgo = DateTime.Now.AddMinutes(-4).ToString("O"); var sql = $"SELECT * FROM `{_prefix}.published` WHERE `Retries`<{_capOptions.FailedRetryCount} AND `Added`<'{fourMinsAgo}' AND (`StatusName` = '{StatusName.Failed}' OR `StatusName` = '{StatusName.Scheduled}') LIMIT 200;"; diff --git a/src/DotNetCore.CAP.PostgreSql/PostgreSqlStorageConnection.cs b/src/DotNetCore.CAP.PostgreSql/PostgreSqlStorageConnection.cs index 763407c..e6b3d53 100644 --- a/src/DotNetCore.CAP.PostgreSql/PostgreSqlStorageConnection.cs +++ b/src/DotNetCore.CAP.PostgreSql/PostgreSqlStorageConnection.cs @@ -40,7 +40,7 @@ namespace DotNetCore.CAP.PostgreSql public async Task> GetPublishedMessagesOfNeedRetry() { - var fourMinsAgo = DateTime.Now.AddMinutes(-4); + var fourMinsAgo = DateTime.Now.AddMinutes(-4).ToString("O"); var sql = $"SELECT * FROM \"{Options.Schema}\".\"published\" WHERE \"Retries\"<{_capOptions.FailedRetryCount} AND \"Added\"<'{fourMinsAgo}' AND (\"StatusName\"='{StatusName.Failed}' OR \"StatusName\"='{StatusName.Scheduled}') LIMIT 200;"; diff --git a/src/DotNetCore.CAP.SqlServer/SqlServerStorageConnection.cs b/src/DotNetCore.CAP.SqlServer/SqlServerStorageConnection.cs index 9b931c1..2787fd0 100644 --- a/src/DotNetCore.CAP.SqlServer/SqlServerStorageConnection.cs +++ b/src/DotNetCore.CAP.SqlServer/SqlServerStorageConnection.cs @@ -78,7 +78,7 @@ VALUES(@Name,@Group,@Content,@Retries,@Added,@ExpiresAt,@StatusName);SELECT SCOP public async Task> GetReceivedMessagesOfNeedRetry() { - var fourMinsAgo = DateTime.Now.AddMinutes(-4); + var fourMinsAgo = DateTime.Now.AddMinutes(-4).ToString("O"); var sql = $"SELECT TOP (200) * FROM [{Options.Schema}].[Received] WITH (readpast) WHERE Retries<{_capOptions.FailedRetryCount} AND Added<'{fourMinsAgo}' AND (StatusName = '{StatusName.Failed}' OR StatusName = '{StatusName.Scheduled}')"; using (var connection = new SqlConnection(Options.ConnectionString)) From 06b38379529afefba173c13948553b4e8a910549 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Thu, 7 Jun 2018 18:37:16 +0800 Subject: [PATCH 089/223] update version to 2.2.5 --- build/version.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/version.props b/build/version.props index 537d4af..449a52f 100644 --- a/build/version.props +++ b/build/version.props @@ -2,7 +2,7 @@ 2 2 - 4 + 5 $(VersionMajor).$(VersionMinor).$(VersionPatch) From ff12c0217ab8385e16679d240af426c1d01d937d Mon Sep 17 00:00:00 2001 From: Savorboard Date: Thu, 7 Jun 2018 18:37:16 +0800 Subject: [PATCH 090/223] update version to 2.2.5 --- build/version.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/version.props b/build/version.props index 537d4af..449a52f 100644 --- a/build/version.props +++ b/build/version.props @@ -2,7 +2,7 @@ 2 2 - 4 + 5 $(VersionMajor).$(VersionMinor).$(VersionPatch) From 4db730a7ded3174a1971a0b65e81f3f609170c7b Mon Sep 17 00:00:00 2001 From: Savorboard Date: Tue, 12 Jun 2018 16:59:10 +0800 Subject: [PATCH 091/223] remove unused constructor. --- src/DotNetCore.CAP/Models/CapPublishedMessage.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/DotNetCore.CAP/Models/CapPublishedMessage.cs b/src/DotNetCore.CAP/Models/CapPublishedMessage.cs index 41c554d..d95e72f 100644 --- a/src/DotNetCore.CAP/Models/CapPublishedMessage.cs +++ b/src/DotNetCore.CAP/Models/CapPublishedMessage.cs @@ -15,12 +15,6 @@ namespace DotNetCore.CAP.Models Added = DateTime.Now; } - public CapPublishedMessage(MessageContext message) - { - Name = message.Name; - Content = message.Content; - } - public int Id { get; set; } public string Name { get; set; } From 63e83637bf73c30e5147d63ed92c929c26324038 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Tue, 12 Jun 2018 16:59:10 +0800 Subject: [PATCH 092/223] remove unused constructor. --- src/DotNetCore.CAP/Models/CapPublishedMessage.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/DotNetCore.CAP/Models/CapPublishedMessage.cs b/src/DotNetCore.CAP/Models/CapPublishedMessage.cs index 41c554d..d95e72f 100644 --- a/src/DotNetCore.CAP/Models/CapPublishedMessage.cs +++ b/src/DotNetCore.CAP/Models/CapPublishedMessage.cs @@ -15,12 +15,6 @@ namespace DotNetCore.CAP.Models Added = DateTime.Now; } - public CapPublishedMessage(MessageContext message) - { - Name = message.Name; - Content = message.Content; - } - public int Id { get; set; } public string Name { get; set; } From 9caf7a23082714827561ae504440240768a41793 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Tue, 12 Jun 2018 17:15:29 +0800 Subject: [PATCH 093/223] Fixed DateTime localization format conversion error to sql.(#139) --- src/DotNetCore.CAP.MySql/MySqlStorageConnection.cs | 2 +- src/DotNetCore.CAP.PostgreSql/PostgreSqlStorageConnection.cs | 2 +- src/DotNetCore.CAP.SqlServer/SqlServerStorageConnection.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/DotNetCore.CAP.MySql/MySqlStorageConnection.cs b/src/DotNetCore.CAP.MySql/MySqlStorageConnection.cs index 442328e..77f9fbb 100644 --- a/src/DotNetCore.CAP.MySql/MySqlStorageConnection.cs +++ b/src/DotNetCore.CAP.MySql/MySqlStorageConnection.cs @@ -80,7 +80,7 @@ VALUES(@Name,@Group,@Content,@Retries,@Added,@ExpiresAt,@StatusName);SELECT LAST public async Task> GetReceivedMessagesOfNeedRetry() { - var fourMinsAgo = DateTime.Now.AddMinutes(-4); + var fourMinsAgo = DateTime.Now.AddMinutes(-4).ToString("O"); var sql = $"SELECT * FROM `{_prefix}.received` WHERE `Retries`<{_capOptions.FailedRetryCount} AND `Added`<'{fourMinsAgo}' AND (`StatusName` = '{StatusName.Failed}' OR `StatusName` = '{StatusName.Scheduled}') LIMIT 200;"; using (var connection = new MySqlConnection(Options.ConnectionString)) diff --git a/src/DotNetCore.CAP.PostgreSql/PostgreSqlStorageConnection.cs b/src/DotNetCore.CAP.PostgreSql/PostgreSqlStorageConnection.cs index e6b3d53..3d8fba7 100644 --- a/src/DotNetCore.CAP.PostgreSql/PostgreSqlStorageConnection.cs +++ b/src/DotNetCore.CAP.PostgreSql/PostgreSqlStorageConnection.cs @@ -77,7 +77,7 @@ namespace DotNetCore.CAP.PostgreSql public async Task> GetReceivedMessagesOfNeedRetry() { - var fourMinsAgo = DateTime.Now.AddMinutes(-4); + var fourMinsAgo = DateTime.Now.AddMinutes(-4).ToString("O"); var sql = $"SELECT * FROM \"{Options.Schema}\".\"received\" WHERE \"Retries\"<{_capOptions.FailedRetryCount} AND \"Added\"<'{fourMinsAgo}' AND (\"StatusName\"='{StatusName.Failed}' OR \"StatusName\"='{StatusName.Scheduled}') LIMIT 200;"; using (var connection = new NpgsqlConnection(Options.ConnectionString)) diff --git a/src/DotNetCore.CAP.SqlServer/SqlServerStorageConnection.cs b/src/DotNetCore.CAP.SqlServer/SqlServerStorageConnection.cs index 2787fd0..b204fde 100644 --- a/src/DotNetCore.CAP.SqlServer/SqlServerStorageConnection.cs +++ b/src/DotNetCore.CAP.SqlServer/SqlServerStorageConnection.cs @@ -40,7 +40,7 @@ namespace DotNetCore.CAP.SqlServer public async Task> GetPublishedMessagesOfNeedRetry() { - var fourMinsAgo = DateTime.Now.AddMinutes(-4); + var fourMinsAgo = DateTime.Now.AddMinutes(-4).ToString("O"); var sql = $"SELECT TOP (200) * FROM [{Options.Schema}].[Published] WITH (readpast) WHERE Retries<{_capOptions.FailedRetryCount} AND Added<'{fourMinsAgo}' AND (StatusName = '{StatusName.Failed}' OR StatusName = '{StatusName.Scheduled}')"; From b2941f39659d84dcecae25ff9a39880076a04779 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Tue, 12 Jun 2018 17:15:29 +0800 Subject: [PATCH 094/223] Fixed DateTime localization format conversion error to sql.(#139) --- src/DotNetCore.CAP.MySql/MySqlStorageConnection.cs | 2 +- src/DotNetCore.CAP.PostgreSql/PostgreSqlStorageConnection.cs | 2 +- src/DotNetCore.CAP.SqlServer/SqlServerStorageConnection.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/DotNetCore.CAP.MySql/MySqlStorageConnection.cs b/src/DotNetCore.CAP.MySql/MySqlStorageConnection.cs index 442328e..77f9fbb 100644 --- a/src/DotNetCore.CAP.MySql/MySqlStorageConnection.cs +++ b/src/DotNetCore.CAP.MySql/MySqlStorageConnection.cs @@ -80,7 +80,7 @@ VALUES(@Name,@Group,@Content,@Retries,@Added,@ExpiresAt,@StatusName);SELECT LAST public async Task> GetReceivedMessagesOfNeedRetry() { - var fourMinsAgo = DateTime.Now.AddMinutes(-4); + var fourMinsAgo = DateTime.Now.AddMinutes(-4).ToString("O"); var sql = $"SELECT * FROM `{_prefix}.received` WHERE `Retries`<{_capOptions.FailedRetryCount} AND `Added`<'{fourMinsAgo}' AND (`StatusName` = '{StatusName.Failed}' OR `StatusName` = '{StatusName.Scheduled}') LIMIT 200;"; using (var connection = new MySqlConnection(Options.ConnectionString)) diff --git a/src/DotNetCore.CAP.PostgreSql/PostgreSqlStorageConnection.cs b/src/DotNetCore.CAP.PostgreSql/PostgreSqlStorageConnection.cs index e6b3d53..3d8fba7 100644 --- a/src/DotNetCore.CAP.PostgreSql/PostgreSqlStorageConnection.cs +++ b/src/DotNetCore.CAP.PostgreSql/PostgreSqlStorageConnection.cs @@ -77,7 +77,7 @@ namespace DotNetCore.CAP.PostgreSql public async Task> GetReceivedMessagesOfNeedRetry() { - var fourMinsAgo = DateTime.Now.AddMinutes(-4); + var fourMinsAgo = DateTime.Now.AddMinutes(-4).ToString("O"); var sql = $"SELECT * FROM \"{Options.Schema}\".\"received\" WHERE \"Retries\"<{_capOptions.FailedRetryCount} AND \"Added\"<'{fourMinsAgo}' AND (\"StatusName\"='{StatusName.Failed}' OR \"StatusName\"='{StatusName.Scheduled}') LIMIT 200;"; using (var connection = new NpgsqlConnection(Options.ConnectionString)) diff --git a/src/DotNetCore.CAP.SqlServer/SqlServerStorageConnection.cs b/src/DotNetCore.CAP.SqlServer/SqlServerStorageConnection.cs index 2787fd0..b204fde 100644 --- a/src/DotNetCore.CAP.SqlServer/SqlServerStorageConnection.cs +++ b/src/DotNetCore.CAP.SqlServer/SqlServerStorageConnection.cs @@ -40,7 +40,7 @@ namespace DotNetCore.CAP.SqlServer public async Task> GetPublishedMessagesOfNeedRetry() { - var fourMinsAgo = DateTime.Now.AddMinutes(-4); + var fourMinsAgo = DateTime.Now.AddMinutes(-4).ToString("O"); var sql = $"SELECT TOP (200) * FROM [{Options.Schema}].[Published] WITH (readpast) WHERE Retries<{_capOptions.FailedRetryCount} AND Added<'{fourMinsAgo}' AND (StatusName = '{StatusName.Failed}' OR StatusName = '{StatusName.Scheduled}')"; From 5a2403052ec0a594b0b5f4025b79e674fcd2b3fd Mon Sep 17 00:00:00 2001 From: Savorboard Date: Tue, 19 Jun 2018 13:52:33 +0800 Subject: [PATCH 095/223] Improved logging --- src/DotNetCore.CAP/Abstractions/CapPublisherBase.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/DotNetCore.CAP/Abstractions/CapPublisherBase.cs b/src/DotNetCore.CAP/Abstractions/CapPublisherBase.cs index 4d7a321..77a6c44 100644 --- a/src/DotNetCore.CAP/Abstractions/CapPublisherBase.cs +++ b/src/DotNetCore.CAP/Abstractions/CapPublisherBase.cs @@ -182,7 +182,7 @@ namespace DotNetCore.CAP.Abstractions } catch (Exception e) { - _logger.LogError("An exception was occurred when publish message. exception message:" + e.Message, e); + _logger.LogError(e, "An exception was occurred when publish message async. exception message:" + name); s_diagnosticListener.WritePublishMessageStoreError(operationId, message, e); Console.WriteLine(e); throw; @@ -204,10 +204,11 @@ namespace DotNetCore.CAP.Abstractions try { - operationId = s_diagnosticListener.WritePublishMessageStoreBefore(message); + Console.WriteLine("================22222222222222====================="); + operationId = s_diagnosticListener.WritePublishMessageStoreBefore(message); var id = Execute(DbConnection, DbTransaction, message); - + Console.WriteLine("================777777777777777777777====================="); ClosedCap(); if (id > 0) @@ -220,7 +221,7 @@ namespace DotNetCore.CAP.Abstractions } catch (Exception e) { - _logger.LogError("An exception was occurred when publish message. exception message:" + e.Message, e); + _logger.LogError(e, "An exception was occurred when publish message. message:" + name); s_diagnosticListener.WritePublishMessageStoreError(operationId, message, e); Console.WriteLine(e); throw; From 273e7bf4a8fc41537857aeba1fe4dabbd260d1cf Mon Sep 17 00:00:00 2001 From: Savorboard Date: Tue, 19 Jun 2018 13:52:33 +0800 Subject: [PATCH 096/223] Improved logging --- src/DotNetCore.CAP/Abstractions/CapPublisherBase.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/DotNetCore.CAP/Abstractions/CapPublisherBase.cs b/src/DotNetCore.CAP/Abstractions/CapPublisherBase.cs index 4d7a321..77a6c44 100644 --- a/src/DotNetCore.CAP/Abstractions/CapPublisherBase.cs +++ b/src/DotNetCore.CAP/Abstractions/CapPublisherBase.cs @@ -182,7 +182,7 @@ namespace DotNetCore.CAP.Abstractions } catch (Exception e) { - _logger.LogError("An exception was occurred when publish message. exception message:" + e.Message, e); + _logger.LogError(e, "An exception was occurred when publish message async. exception message:" + name); s_diagnosticListener.WritePublishMessageStoreError(operationId, message, e); Console.WriteLine(e); throw; @@ -204,10 +204,11 @@ namespace DotNetCore.CAP.Abstractions try { - operationId = s_diagnosticListener.WritePublishMessageStoreBefore(message); + Console.WriteLine("================22222222222222====================="); + operationId = s_diagnosticListener.WritePublishMessageStoreBefore(message); var id = Execute(DbConnection, DbTransaction, message); - + Console.WriteLine("================777777777777777777777====================="); ClosedCap(); if (id > 0) @@ -220,7 +221,7 @@ namespace DotNetCore.CAP.Abstractions } catch (Exception e) { - _logger.LogError("An exception was occurred when publish message. exception message:" + e.Message, e); + _logger.LogError(e, "An exception was occurred when publish message. message:" + name); s_diagnosticListener.WritePublishMessageStoreError(operationId, message, e); Console.WriteLine(e); throw; From 0fdbf6ebf1ffac5a6e35ce74c4d35dfe8f54c903 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Thu, 28 Jun 2018 18:45:13 +0800 Subject: [PATCH 097/223] support RabbitMQ cluster configuration. --- src/DotNetCore.CAP.RabbitMQ/CAP.RabbiMQOptions.cs | 5 ++++- .../IConnectionChannelPool.Default.cs | 8 +++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/DotNetCore.CAP.RabbitMQ/CAP.RabbiMQOptions.cs b/src/DotNetCore.CAP.RabbitMQ/CAP.RabbiMQOptions.cs index c0abcc8..afb2fd2 100644 --- a/src/DotNetCore.CAP.RabbitMQ/CAP.RabbiMQOptions.cs +++ b/src/DotNetCore.CAP.RabbitMQ/CAP.RabbiMQOptions.cs @@ -37,7 +37,10 @@ namespace DotNetCore.CAP /// The topic exchange type. public const string ExchangeType = "topic"; - /// The host to connect to. + /// + /// The host to connect to. + /// If you want connect to the cluster, you can assign like “192.168.1.111,192.168.1.112†+ /// public string HostName { get; set; } = "localhost"; /// diff --git a/src/DotNetCore.CAP.RabbitMQ/IConnectionChannelPool.Default.cs b/src/DotNetCore.CAP.RabbitMQ/IConnectionChannelPool.Default.cs index e15fdfa..b50c9dc 100644 --- a/src/DotNetCore.CAP.RabbitMQ/IConnectionChannelPool.Default.cs +++ b/src/DotNetCore.CAP.RabbitMQ/IConnectionChannelPool.Default.cs @@ -76,7 +76,6 @@ namespace DotNetCore.CAP.RabbitMQ { var factory = new ConnectionFactory { - HostName = options.HostName, UserName = options.UserName, Port = options.Port, Password = options.Password, @@ -86,6 +85,13 @@ namespace DotNetCore.CAP.RabbitMQ SocketWriteTimeout = options.SocketWriteTimeout }; + if (options.HostName.Contains(",")) + { + return () => factory.CreateConnection( + options.HostName.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries)); + } + + factory.HostName = options.HostName; return () => factory.CreateConnection(); } From 018035ce85b7c7d39f307fdb23d398a5f24b4543 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Thu, 28 Jun 2018 18:45:13 +0800 Subject: [PATCH 098/223] support RabbitMQ cluster configuration. --- src/DotNetCore.CAP.RabbitMQ/CAP.RabbiMQOptions.cs | 5 ++++- .../IConnectionChannelPool.Default.cs | 8 +++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/DotNetCore.CAP.RabbitMQ/CAP.RabbiMQOptions.cs b/src/DotNetCore.CAP.RabbitMQ/CAP.RabbiMQOptions.cs index c0abcc8..afb2fd2 100644 --- a/src/DotNetCore.CAP.RabbitMQ/CAP.RabbiMQOptions.cs +++ b/src/DotNetCore.CAP.RabbitMQ/CAP.RabbiMQOptions.cs @@ -37,7 +37,10 @@ namespace DotNetCore.CAP /// The topic exchange type. public const string ExchangeType = "topic"; - /// The host to connect to. + /// + /// The host to connect to. + /// If you want connect to the cluster, you can assign like “192.168.1.111,192.168.1.112†+ /// public string HostName { get; set; } = "localhost"; /// diff --git a/src/DotNetCore.CAP.RabbitMQ/IConnectionChannelPool.Default.cs b/src/DotNetCore.CAP.RabbitMQ/IConnectionChannelPool.Default.cs index e15fdfa..b50c9dc 100644 --- a/src/DotNetCore.CAP.RabbitMQ/IConnectionChannelPool.Default.cs +++ b/src/DotNetCore.CAP.RabbitMQ/IConnectionChannelPool.Default.cs @@ -76,7 +76,6 @@ namespace DotNetCore.CAP.RabbitMQ { var factory = new ConnectionFactory { - HostName = options.HostName, UserName = options.UserName, Port = options.Port, Password = options.Password, @@ -86,6 +85,13 @@ namespace DotNetCore.CAP.RabbitMQ SocketWriteTimeout = options.SocketWriteTimeout }; + if (options.HostName.Contains(",")) + { + return () => factory.CreateConnection( + options.HostName.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries)); + } + + factory.HostName = options.HostName; return () => factory.CreateConnection(); } From 2c743162a50f1dff47407f4d77479d50de1c3122 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Wed, 11 Jul 2018 00:10:01 +0800 Subject: [PATCH 099/223] Fixed dashboard message page re-requeue and re-executed operate bug. (#158) --- .../Dashboard/DashboardRoutes.cs | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/DotNetCore.CAP/Dashboard/DashboardRoutes.cs b/src/DotNetCore.CAP/Dashboard/DashboardRoutes.cs index 312da57..9a51faa 100644 --- a/src/DotNetCore.CAP/Dashboard/DashboardRoutes.cs +++ b/src/DotNetCore.CAP/Dashboard/DashboardRoutes.cs @@ -3,7 +3,7 @@ using System.Reflection; using DotNetCore.CAP.Dashboard.Pages; -using DotNetCore.CAP.Infrastructure; +using Microsoft.Extensions.DependencyInjection; namespace DotNetCore.CAP.Dashboard { @@ -83,24 +83,34 @@ namespace DotNetCore.CAP.Dashboard Routes.AddJsonResult("/published/message/(?.+)", x => { var id = int.Parse(x.UriMatch.Groups["Id"].Value); - var message = x.Storage.GetConnection().GetPublishedMessageAsync(id).GetAwaiter().GetResult(); + var message = x.Storage.GetConnection().GetPublishedMessageAsync(id) + .GetAwaiter().GetResult(); return message.Content; }); Routes.AddJsonResult("/received/message/(?.+)", x => { var id = int.Parse(x.UriMatch.Groups["Id"].Value); - var message = x.Storage.GetConnection().GetReceivedMessageAsync(id).GetAwaiter().GetResult(); + var message = x.Storage.GetConnection().GetReceivedMessageAsync(id) + .GetAwaiter().GetResult(); return message.Content; }); Routes.AddPublishBatchCommand( "/published/requeue", (client, messageId) => - client.Storage.GetConnection().ChangePublishedState(messageId, StatusName.Scheduled)); + { + var msg = client.Storage.GetConnection().GetPublishedMessageAsync(messageId) + .GetAwaiter().GetResult(); + client.RequestServices.GetService().EnqueueToPublish(msg); + }); Routes.AddPublishBatchCommand( "/received/requeue", (client, messageId) => - client.Storage.GetConnection().ChangeReceivedState(messageId, StatusName.Scheduled)); + { + var msg = client.Storage.GetConnection().GetReceivedMessageAsync(messageId) + .GetAwaiter().GetResult(); + client.RequestServices.GetService().EnqueueToExecute(msg); + }); Routes.AddRazorPage( "/published/(?.+)", From 16800674bdfe9b83b90d357910de371b59f05662 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Wed, 11 Jul 2018 00:10:01 +0800 Subject: [PATCH 100/223] Fixed dashboard message page re-requeue and re-executed operate bug. (#158) --- .../Dashboard/DashboardRoutes.cs | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/DotNetCore.CAP/Dashboard/DashboardRoutes.cs b/src/DotNetCore.CAP/Dashboard/DashboardRoutes.cs index 312da57..9a51faa 100644 --- a/src/DotNetCore.CAP/Dashboard/DashboardRoutes.cs +++ b/src/DotNetCore.CAP/Dashboard/DashboardRoutes.cs @@ -3,7 +3,7 @@ using System.Reflection; using DotNetCore.CAP.Dashboard.Pages; -using DotNetCore.CAP.Infrastructure; +using Microsoft.Extensions.DependencyInjection; namespace DotNetCore.CAP.Dashboard { @@ -83,24 +83,34 @@ namespace DotNetCore.CAP.Dashboard Routes.AddJsonResult("/published/message/(?.+)", x => { var id = int.Parse(x.UriMatch.Groups["Id"].Value); - var message = x.Storage.GetConnection().GetPublishedMessageAsync(id).GetAwaiter().GetResult(); + var message = x.Storage.GetConnection().GetPublishedMessageAsync(id) + .GetAwaiter().GetResult(); return message.Content; }); Routes.AddJsonResult("/received/message/(?.+)", x => { var id = int.Parse(x.UriMatch.Groups["Id"].Value); - var message = x.Storage.GetConnection().GetReceivedMessageAsync(id).GetAwaiter().GetResult(); + var message = x.Storage.GetConnection().GetReceivedMessageAsync(id) + .GetAwaiter().GetResult(); return message.Content; }); Routes.AddPublishBatchCommand( "/published/requeue", (client, messageId) => - client.Storage.GetConnection().ChangePublishedState(messageId, StatusName.Scheduled)); + { + var msg = client.Storage.GetConnection().GetPublishedMessageAsync(messageId) + .GetAwaiter().GetResult(); + client.RequestServices.GetService().EnqueueToPublish(msg); + }); Routes.AddPublishBatchCommand( "/received/requeue", (client, messageId) => - client.Storage.GetConnection().ChangeReceivedState(messageId, StatusName.Scheduled)); + { + var msg = client.Storage.GetConnection().GetReceivedMessageAsync(messageId) + .GetAwaiter().GetResult(); + client.RequestServices.GetService().EnqueueToExecute(msg); + }); Routes.AddRazorPage( "/published/(?.+)", From 4fe8259ce48776853c4df22bbf6122e226b6c9f0 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Thu, 19 Jul 2018 10:49:17 +0800 Subject: [PATCH 101/223] Fixed SendAsync or ExecuteAsync recursion retries bug. (#160) --- src/DotNetCore.CAP/IPublishMessageSender.Base.cs | 2 +- src/DotNetCore.CAP/ISubscribeExecutor.Default.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/DotNetCore.CAP/IPublishMessageSender.Base.cs b/src/DotNetCore.CAP/IPublishMessageSender.Base.cs index a597169..a64a6a7 100644 --- a/src/DotNetCore.CAP/IPublishMessageSender.Base.cs +++ b/src/DotNetCore.CAP/IPublishMessageSender.Base.cs @@ -75,7 +75,7 @@ namespace DotNetCore.CAP { _logger.SenderRetrying(message.Id, message.Retries); - await SendAsync(message); + return await SendAsync(message); } return OperateResult.Failed(result.Exception); } diff --git a/src/DotNetCore.CAP/ISubscribeExecutor.Default.cs b/src/DotNetCore.CAP/ISubscribeExecutor.Default.cs index fe03a15..d069d95 100644 --- a/src/DotNetCore.CAP/ISubscribeExecutor.Default.cs +++ b/src/DotNetCore.CAP/ISubscribeExecutor.Default.cs @@ -77,7 +77,7 @@ namespace DotNetCore.CAP await SetFailedState(message, ex, out bool stillRetry); if (stillRetry) { - await ExecuteAsync(message); + return await ExecuteAsync(message); } return OperateResult.Failed(ex); From 78554f9115568ebbef7f8d2e505186a5385e2da7 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Sun, 15 Jul 2018 12:52:28 +0800 Subject: [PATCH 102/223] refactor code --- src/DotNetCore.CAP/LoggerExtensions.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/DotNetCore.CAP/LoggerExtensions.cs b/src/DotNetCore.CAP/LoggerExtensions.cs index a0096e6..1840af5 100644 --- a/src/DotNetCore.CAP/LoggerExtensions.cs +++ b/src/DotNetCore.CAP/LoggerExtensions.cs @@ -15,7 +15,7 @@ namespace DotNetCore.CAP private static readonly Action _serverShuttingDown; private static readonly Action _expectedOperationCanceledException; private static readonly Action _modelBinderFormattingException; - private static readonly Action _consumerFailedWillRetry; + private static readonly Action _consumerFailedWillRetry; private static readonly Action _consumerExecuted; private static readonly Action _senderRetrying; private static readonly Action _exceptionOccuredWhileExecuting; @@ -61,7 +61,7 @@ namespace DotNetCore.CAP ); _senderRetrying = LoggerMessage.Define( - LogLevel.Debug, + LogLevel.Warning, 3, "The {Retries}th retrying send a message failed. message id: {MessageId} "); @@ -70,10 +70,10 @@ namespace DotNetCore.CAP 4, "Consumer executed. Took: {Seconds} secs."); - _consumerFailedWillRetry = LoggerMessage.Define( + _consumerFailedWillRetry = LoggerMessage.Define( LogLevel.Warning, 2, - "Consumer failed to execute. Will retry."); + "The {Retries}th retrying consume a message failed. message id: {MessageId}"); _exceptionOccuredWhileExecuting = LoggerMessage.Define( LogLevel.Error, @@ -91,9 +91,9 @@ namespace DotNetCore.CAP "An exception occured while publishing a message, reason:{Reason}. message id:{MessageId}"); } - public static void ConsumerExecutionFailedWillRetry(this ILogger logger, Exception ex) + public static void ConsumerExecutionRetrying(this ILogger logger, int messageId, int retries) { - _consumerFailedWillRetry(logger, ex); + _consumerFailedWillRetry(logger, messageId, retries, null); } public static void SenderRetrying(this ILogger logger, int messageId, int retries) From f0bd253c92dd8e1f66453429be5e91250e08a09b Mon Sep 17 00:00:00 2001 From: Savorboard Date: Thu, 19 Jul 2018 10:49:17 +0800 Subject: [PATCH 103/223] Fixed SendAsync or ExecuteAsync recursion retries bug. (#160) --- src/DotNetCore.CAP/IPublishMessageSender.Base.cs | 2 +- src/DotNetCore.CAP/ISubscribeExecutor.Default.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/DotNetCore.CAP/IPublishMessageSender.Base.cs b/src/DotNetCore.CAP/IPublishMessageSender.Base.cs index a597169..a64a6a7 100644 --- a/src/DotNetCore.CAP/IPublishMessageSender.Base.cs +++ b/src/DotNetCore.CAP/IPublishMessageSender.Base.cs @@ -75,7 +75,7 @@ namespace DotNetCore.CAP { _logger.SenderRetrying(message.Id, message.Retries); - await SendAsync(message); + return await SendAsync(message); } return OperateResult.Failed(result.Exception); } diff --git a/src/DotNetCore.CAP/ISubscribeExecutor.Default.cs b/src/DotNetCore.CAP/ISubscribeExecutor.Default.cs index fe03a15..d069d95 100644 --- a/src/DotNetCore.CAP/ISubscribeExecutor.Default.cs +++ b/src/DotNetCore.CAP/ISubscribeExecutor.Default.cs @@ -77,7 +77,7 @@ namespace DotNetCore.CAP await SetFailedState(message, ex, out bool stillRetry); if (stillRetry) { - await ExecuteAsync(message); + return await ExecuteAsync(message); } return OperateResult.Failed(ex); From 07f82ac6fb3e863405811975aa41c68272d11f10 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Sun, 15 Jul 2018 12:52:28 +0800 Subject: [PATCH 104/223] refactor code --- src/DotNetCore.CAP/LoggerExtensions.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/DotNetCore.CAP/LoggerExtensions.cs b/src/DotNetCore.CAP/LoggerExtensions.cs index a0096e6..1840af5 100644 --- a/src/DotNetCore.CAP/LoggerExtensions.cs +++ b/src/DotNetCore.CAP/LoggerExtensions.cs @@ -15,7 +15,7 @@ namespace DotNetCore.CAP private static readonly Action _serverShuttingDown; private static readonly Action _expectedOperationCanceledException; private static readonly Action _modelBinderFormattingException; - private static readonly Action _consumerFailedWillRetry; + private static readonly Action _consumerFailedWillRetry; private static readonly Action _consumerExecuted; private static readonly Action _senderRetrying; private static readonly Action _exceptionOccuredWhileExecuting; @@ -61,7 +61,7 @@ namespace DotNetCore.CAP ); _senderRetrying = LoggerMessage.Define( - LogLevel.Debug, + LogLevel.Warning, 3, "The {Retries}th retrying send a message failed. message id: {MessageId} "); @@ -70,10 +70,10 @@ namespace DotNetCore.CAP 4, "Consumer executed. Took: {Seconds} secs."); - _consumerFailedWillRetry = LoggerMessage.Define( + _consumerFailedWillRetry = LoggerMessage.Define( LogLevel.Warning, 2, - "Consumer failed to execute. Will retry."); + "The {Retries}th retrying consume a message failed. message id: {MessageId}"); _exceptionOccuredWhileExecuting = LoggerMessage.Define( LogLevel.Error, @@ -91,9 +91,9 @@ namespace DotNetCore.CAP "An exception occured while publishing a message, reason:{Reason}. message id:{MessageId}"); } - public static void ConsumerExecutionFailedWillRetry(this ILogger logger, Exception ex) + public static void ConsumerExecutionRetrying(this ILogger logger, int messageId, int retries) { - _consumerFailedWillRetry(logger, ex); + _consumerFailedWillRetry(logger, messageId, retries, null); } public static void SenderRetrying(this ILogger logger, int messageId, int retries) From 87b9a377600f76ed01fd2cfd6c41c3b78b268749 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Thu, 19 Jul 2018 13:05:36 +0800 Subject: [PATCH 105/223] Fixed SendAsync or ExecuteAsync recursion retries bug. (#160) --- .../IPublishMessageSender.Base.cs | 44 ++++++++--------- .../ISubscribeExecutor.Default.cs | 47 ++++++++++--------- 2 files changed, 47 insertions(+), 44 deletions(-) diff --git a/src/DotNetCore.CAP/IPublishMessageSender.Base.cs b/src/DotNetCore.CAP/IPublishMessageSender.Base.cs index a64a6a7..d36bc51 100644 --- a/src/DotNetCore.CAP/IPublishMessageSender.Base.cs +++ b/src/DotNetCore.CAP/IPublishMessageSender.Base.cs @@ -43,6 +43,23 @@ namespace DotNetCore.CAP public abstract Task PublishAsync(string keyName, string content); public async Task SendAsync(CapPublishedMessage message) + { + bool retry; + OperateResult result; + do + { + result = await SendWithoutRetryAsync(message); + if (result == OperateResult.Success) + { + return result; + } + retry = UpdateMessageForRetry(message); + } while (retry); + + return result; + } + + private async Task SendWithoutRetryAsync(CapPublishedMessage message) { var startTime = DateTimeOffset.UtcNow; var stopwatch = Stopwatch.StartNew(); @@ -69,28 +86,23 @@ namespace DotNetCore.CAP { TracingError(operationId, message, result, startTime, stopwatch.Elapsed); - await SetFailedState(message, result.Exception, out bool stillRetry); - - if (stillRetry) - { - _logger.SenderRetrying(message.Id, message.Retries); + await SetFailedState(message, result.Exception); - return await SendAsync(message); - } return OperateResult.Failed(result.Exception); } } - private static bool UpdateMessageForRetryAsync(CapPublishedMessage message) + private bool UpdateMessageForRetry(CapPublishedMessage message) { var retryBehavior = RetryBehavior.DefaultRetry; - var retries = ++message.Retries; if (retries >= retryBehavior.RetryCount) { return false; } + _logger.SenderRetrying(message.Id, retries); + var due = message.Added.AddSeconds(retryBehavior.RetryIn(retries)); message.ExpiresAt = due; @@ -100,23 +112,13 @@ namespace DotNetCore.CAP private Task SetSuccessfulState(CapPublishedMessage message) { var succeededState = new SucceededState(_options.SucceedMessageExpiredAfter); - return _stateChanger.ChangeStateAsync(message, succeededState, _connection); } - private Task SetFailedState(CapPublishedMessage message, Exception ex, out bool stillRetry) + private Task SetFailedState(CapPublishedMessage message, Exception ex) { - IState newState = new FailedState(); - stillRetry = UpdateMessageForRetryAsync(message); - if (stillRetry) - { - _logger.ConsumerExecutionFailedWillRetry(ex); - return Task.CompletedTask; - } - AddErrorReasonToContent(message, ex); - - return _stateChanger.ChangeStateAsync(message, newState, _connection); + return _stateChanger.ChangeStateAsync(message, new FailedState(), _connection); } private static void AddErrorReasonToContent(CapPublishedMessage message, Exception exception) diff --git a/src/DotNetCore.CAP/ISubscribeExecutor.Default.cs b/src/DotNetCore.CAP/ISubscribeExecutor.Default.cs index d069d95..f56dc73 100644 --- a/src/DotNetCore.CAP/ISubscribeExecutor.Default.cs +++ b/src/DotNetCore.CAP/ISubscribeExecutor.Default.cs @@ -50,6 +50,23 @@ namespace DotNetCore.CAP private IConsumerInvoker Invoker { get; } public async Task ExecuteAsync(CapReceivedMessage message) + { + bool retry; + OperateResult result; + do + { + result = await ExecuteWithoutRetryAsync(message); + if (result == OperateResult.Success) + { + return result; + } + retry = UpdateMessageForRetry(message); + } while (retry); + + return result; + } + + private async Task ExecuteWithoutRetryAsync(CapReceivedMessage message) { if (message == null) { @@ -74,11 +91,7 @@ namespace DotNetCore.CAP { _logger.LogError(ex, $"An exception occurred while executing the subscription method. Topic:{message.Name}, Id:{message.Id}"); - await SetFailedState(message, ex, out bool stillRetry); - if (stillRetry) - { - return await ExecuteAsync(message); - } + await SetFailedState(message, ex); return OperateResult.Failed(ex); } @@ -91,43 +104,31 @@ namespace DotNetCore.CAP return _stateChanger.ChangeStateAsync(message, succeededState, _connection); } - private Task SetFailedState(CapReceivedMessage message, Exception ex, out bool stillRetry) + private Task SetFailedState(CapReceivedMessage message, Exception ex) { - IState newState = new FailedState(); - if (ex is SubscriberNotFoundException) { - stillRetry = false; message.Retries = _options.FailedRetryCount; // not retry if SubscriberNotFoundException } - else - { - stillRetry = UpdateMessageForRetry(message); - if (stillRetry) - { - _logger.ConsumerExecutionFailedWillRetry(ex); - return Task.CompletedTask; - } - } AddErrorReasonToContent(message, ex); - return _stateChanger.ChangeStateAsync(message, newState, _connection); + return _stateChanger.ChangeStateAsync(message, new FailedState(), _connection); } - private static bool UpdateMessageForRetry(CapReceivedMessage message) + private bool UpdateMessageForRetry(CapReceivedMessage message) { var retryBehavior = RetryBehavior.DefaultRetry; var retries = ++message.Retries; - if (retries >= retryBehavior.RetryCount) + var retryCount = Math.Min(_options.FailedRetryCount, retryBehavior.RetryCount); + if (retries >= retryCount) { return false; } - + _logger.ConsumerExecutionRetrying(message.Id, retries); var due = message.Added.AddSeconds(retryBehavior.RetryIn(retries)); message.ExpiresAt = due; - return true; } From 69d1fb1d89c4078a88e461fffb61c16cadf5a9cf Mon Sep 17 00:00:00 2001 From: Savorboard Date: Mon, 16 Jul 2018 15:19:02 +0800 Subject: [PATCH 106/223] refactor log extensions. --- src/DotNetCore.CAP/LoggerExtensions.cs | 111 +++++-------------------- 1 file changed, 20 insertions(+), 91 deletions(-) diff --git a/src/DotNetCore.CAP/LoggerExtensions.cs b/src/DotNetCore.CAP/LoggerExtensions.cs index 1840af5..808096c 100644 --- a/src/DotNetCore.CAP/LoggerExtensions.cs +++ b/src/DotNetCore.CAP/LoggerExtensions.cs @@ -10,141 +10,70 @@ namespace DotNetCore.CAP [SuppressMessage("ReSharper", "InconsistentNaming")] internal static class LoggerExtensions { - private static readonly Action _serverStarting; - private static readonly Action _processorsStartingError; - private static readonly Action _serverShuttingDown; - private static readonly Action _expectedOperationCanceledException; - private static readonly Action _modelBinderFormattingException; - private static readonly Action _consumerFailedWillRetry; - private static readonly Action _consumerExecuted; - private static readonly Action _senderRetrying; - private static readonly Action _exceptionOccuredWhileExecuting; - private static readonly Action _messageHasBeenSent; - private static readonly Action _messagePublishException; - - static LoggerExtensions() + public static void ConsumerExecutedAfterThreshold(this ILogger logger, int messageId, int retries) { - _serverStarting = LoggerMessage.Define( - LogLevel.Debug, - 1, - "Starting the processing server."); - - _processorsStartingError = LoggerMessage.Define( - LogLevel.Error, - 5, - "Starting the processors throw an exception."); - - _serverShuttingDown = LoggerMessage.Define( - LogLevel.Information, - 2, - "Shutting down the processing server..."); - - _expectedOperationCanceledException = LoggerMessage.Define( - LogLevel.Warning, - 3, - "Expected an OperationCanceledException, but found '{ExceptionMessage}'."); - - LoggerMessage.Define( - LogLevel.Error, - 5, - "Consumer method '{methodName}' failed to execute."); - - LoggerMessage.Define( - LogLevel.Error, - 5, - "Received message topic method '{topicName}' failed to execute."); - - _modelBinderFormattingException = LoggerMessage.Define( - LogLevel.Error, - 5, - "When call subscribe method, a parameter format conversion exception occurs. MethodName:'{MethodName}' ParameterName:'{ParameterName}' Content:'{Content}'." - ); - - _senderRetrying = LoggerMessage.Define( - LogLevel.Warning, - 3, - "The {Retries}th retrying send a message failed. message id: {MessageId} "); - - _consumerExecuted = LoggerMessage.Define( - LogLevel.Debug, - 4, - "Consumer executed. Took: {Seconds} secs."); - - _consumerFailedWillRetry = LoggerMessage.Define( - LogLevel.Warning, - 2, - "The {Retries}th retrying consume a message failed. message id: {MessageId}"); - - _exceptionOccuredWhileExecuting = LoggerMessage.Define( - LogLevel.Error, - 6, - "An exception occured while trying to store a message. message id: {MessageId}"); + logger.LogWarning($"The Subscriber of the message({messageId}) still fails after {retries}th executions and we will stop retrying."); + } - _messageHasBeenSent = LoggerMessage.Define( - LogLevel.Debug, - 4, - "Message published. Took: {Seconds} secs."); + public static void SenderAfterThreshold(this ILogger logger, int messageId, int retries) + { + logger.LogWarning($"The Publisher of the message({messageId}) still fails after {retries}th sends and we will stop retrying."); + } - _messagePublishException = LoggerMessage.Define( - LogLevel.Error, - 6, - "An exception occured while publishing a message, reason:{Reason}. message id:{MessageId}"); + public static void ExecutedThresholdCallbackFailed(this ILogger logger, Exception ex) + { + logger.LogWarning(ex, "FailedThresholdCallback action raised an exception:" + ex.Message); } public static void ConsumerExecutionRetrying(this ILogger logger, int messageId, int retries) { - _consumerFailedWillRetry(logger, messageId, retries, null); + logger.LogWarning($"The {retries}th retrying consume a message failed. message id: {messageId}"); } public static void SenderRetrying(this ILogger logger, int messageId, int retries) { - _senderRetrying(logger, messageId, retries, null); + logger.LogWarning($"The {retries}th retrying send a message failed. message id: {messageId} "); } public static void MessageHasBeenSent(this ILogger logger, double seconds) { - _messageHasBeenSent(logger, seconds, null); + logger.LogDebug($"Message published. Took: {seconds} secs."); } public static void MessagePublishException(this ILogger logger, int messageId, string reason, Exception ex) { - _messagePublishException(logger, messageId, reason, ex); + logger.LogError(ex, $"An exception occured while publishing a message, reason:{reason}. message id:{messageId}"); } public static void ConsumerExecuted(this ILogger logger, double seconds) { - _consumerExecuted(logger, seconds, null); + logger.LogDebug($"Consumer executed. Took: {seconds} secs."); } public static void ServerStarting(this ILogger logger) { - _serverStarting(logger, null); + logger.LogInformation("Starting the processing server."); } public static void ProcessorsStartedError(this ILogger logger, Exception ex) { - _processorsStartingError(logger, ex); + logger.LogError(ex, "Starting the processors throw an exception."); } public static void ServerShuttingDown(this ILogger logger) { - _serverShuttingDown(logger, null); + logger.LogInformation("Shutting down the processing server..."); } public static void ExpectedOperationCanceledException(this ILogger logger, Exception ex) { - _expectedOperationCanceledException(logger, ex.Message, ex); - } - - public static void ExceptionOccuredWhileExecuting(this ILogger logger, string messageId, Exception ex) - { - _exceptionOccuredWhileExecuting(logger, messageId, ex); + logger.LogWarning(ex, $"Expected an OperationCanceledException, but found '{ex.Message}'."); } public static void ModelBinderFormattingException(this ILogger logger, string methodName, string parameterName, string content, Exception ex) { - _modelBinderFormattingException(logger, methodName, parameterName, content, ex); + logger.LogError(ex, $"When call subscribe method, a parameter format conversion exception occurs. MethodName:'{methodName}' ParameterName:'{parameterName}' Content:'{content}'."); } } } \ No newline at end of file From ba7a5a2cb365d15018a5e144ddd9c2682c0fcf06 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Thu, 19 Jul 2018 13:05:36 +0800 Subject: [PATCH 107/223] Fixed SendAsync or ExecuteAsync recursion retries bug. (#160) --- .../IPublishMessageSender.Base.cs | 44 ++++++++--------- .../ISubscribeExecutor.Default.cs | 47 ++++++++++--------- 2 files changed, 47 insertions(+), 44 deletions(-) diff --git a/src/DotNetCore.CAP/IPublishMessageSender.Base.cs b/src/DotNetCore.CAP/IPublishMessageSender.Base.cs index a64a6a7..d36bc51 100644 --- a/src/DotNetCore.CAP/IPublishMessageSender.Base.cs +++ b/src/DotNetCore.CAP/IPublishMessageSender.Base.cs @@ -43,6 +43,23 @@ namespace DotNetCore.CAP public abstract Task PublishAsync(string keyName, string content); public async Task SendAsync(CapPublishedMessage message) + { + bool retry; + OperateResult result; + do + { + result = await SendWithoutRetryAsync(message); + if (result == OperateResult.Success) + { + return result; + } + retry = UpdateMessageForRetry(message); + } while (retry); + + return result; + } + + private async Task SendWithoutRetryAsync(CapPublishedMessage message) { var startTime = DateTimeOffset.UtcNow; var stopwatch = Stopwatch.StartNew(); @@ -69,28 +86,23 @@ namespace DotNetCore.CAP { TracingError(operationId, message, result, startTime, stopwatch.Elapsed); - await SetFailedState(message, result.Exception, out bool stillRetry); - - if (stillRetry) - { - _logger.SenderRetrying(message.Id, message.Retries); + await SetFailedState(message, result.Exception); - return await SendAsync(message); - } return OperateResult.Failed(result.Exception); } } - private static bool UpdateMessageForRetryAsync(CapPublishedMessage message) + private bool UpdateMessageForRetry(CapPublishedMessage message) { var retryBehavior = RetryBehavior.DefaultRetry; - var retries = ++message.Retries; if (retries >= retryBehavior.RetryCount) { return false; } + _logger.SenderRetrying(message.Id, retries); + var due = message.Added.AddSeconds(retryBehavior.RetryIn(retries)); message.ExpiresAt = due; @@ -100,23 +112,13 @@ namespace DotNetCore.CAP private Task SetSuccessfulState(CapPublishedMessage message) { var succeededState = new SucceededState(_options.SucceedMessageExpiredAfter); - return _stateChanger.ChangeStateAsync(message, succeededState, _connection); } - private Task SetFailedState(CapPublishedMessage message, Exception ex, out bool stillRetry) + private Task SetFailedState(CapPublishedMessage message, Exception ex) { - IState newState = new FailedState(); - stillRetry = UpdateMessageForRetryAsync(message); - if (stillRetry) - { - _logger.ConsumerExecutionFailedWillRetry(ex); - return Task.CompletedTask; - } - AddErrorReasonToContent(message, ex); - - return _stateChanger.ChangeStateAsync(message, newState, _connection); + return _stateChanger.ChangeStateAsync(message, new FailedState(), _connection); } private static void AddErrorReasonToContent(CapPublishedMessage message, Exception exception) diff --git a/src/DotNetCore.CAP/ISubscribeExecutor.Default.cs b/src/DotNetCore.CAP/ISubscribeExecutor.Default.cs index d069d95..f56dc73 100644 --- a/src/DotNetCore.CAP/ISubscribeExecutor.Default.cs +++ b/src/DotNetCore.CAP/ISubscribeExecutor.Default.cs @@ -50,6 +50,23 @@ namespace DotNetCore.CAP private IConsumerInvoker Invoker { get; } public async Task ExecuteAsync(CapReceivedMessage message) + { + bool retry; + OperateResult result; + do + { + result = await ExecuteWithoutRetryAsync(message); + if (result == OperateResult.Success) + { + return result; + } + retry = UpdateMessageForRetry(message); + } while (retry); + + return result; + } + + private async Task ExecuteWithoutRetryAsync(CapReceivedMessage message) { if (message == null) { @@ -74,11 +91,7 @@ namespace DotNetCore.CAP { _logger.LogError(ex, $"An exception occurred while executing the subscription method. Topic:{message.Name}, Id:{message.Id}"); - await SetFailedState(message, ex, out bool stillRetry); - if (stillRetry) - { - return await ExecuteAsync(message); - } + await SetFailedState(message, ex); return OperateResult.Failed(ex); } @@ -91,43 +104,31 @@ namespace DotNetCore.CAP return _stateChanger.ChangeStateAsync(message, succeededState, _connection); } - private Task SetFailedState(CapReceivedMessage message, Exception ex, out bool stillRetry) + private Task SetFailedState(CapReceivedMessage message, Exception ex) { - IState newState = new FailedState(); - if (ex is SubscriberNotFoundException) { - stillRetry = false; message.Retries = _options.FailedRetryCount; // not retry if SubscriberNotFoundException } - else - { - stillRetry = UpdateMessageForRetry(message); - if (stillRetry) - { - _logger.ConsumerExecutionFailedWillRetry(ex); - return Task.CompletedTask; - } - } AddErrorReasonToContent(message, ex); - return _stateChanger.ChangeStateAsync(message, newState, _connection); + return _stateChanger.ChangeStateAsync(message, new FailedState(), _connection); } - private static bool UpdateMessageForRetry(CapReceivedMessage message) + private bool UpdateMessageForRetry(CapReceivedMessage message) { var retryBehavior = RetryBehavior.DefaultRetry; var retries = ++message.Retries; - if (retries >= retryBehavior.RetryCount) + var retryCount = Math.Min(_options.FailedRetryCount, retryBehavior.RetryCount); + if (retries >= retryCount) { return false; } - + _logger.ConsumerExecutionRetrying(message.Id, retries); var due = message.Added.AddSeconds(retryBehavior.RetryIn(retries)); message.ExpiresAt = due; - return true; } From 9b23617f13d5529550e98cef83216bca8dc3352c Mon Sep 17 00:00:00 2001 From: Savorboard Date: Mon, 16 Jul 2018 15:19:02 +0800 Subject: [PATCH 108/223] refactor log extensions. --- src/DotNetCore.CAP/LoggerExtensions.cs | 111 +++++-------------------- 1 file changed, 20 insertions(+), 91 deletions(-) diff --git a/src/DotNetCore.CAP/LoggerExtensions.cs b/src/DotNetCore.CAP/LoggerExtensions.cs index 1840af5..808096c 100644 --- a/src/DotNetCore.CAP/LoggerExtensions.cs +++ b/src/DotNetCore.CAP/LoggerExtensions.cs @@ -10,141 +10,70 @@ namespace DotNetCore.CAP [SuppressMessage("ReSharper", "InconsistentNaming")] internal static class LoggerExtensions { - private static readonly Action _serverStarting; - private static readonly Action _processorsStartingError; - private static readonly Action _serverShuttingDown; - private static readonly Action _expectedOperationCanceledException; - private static readonly Action _modelBinderFormattingException; - private static readonly Action _consumerFailedWillRetry; - private static readonly Action _consumerExecuted; - private static readonly Action _senderRetrying; - private static readonly Action _exceptionOccuredWhileExecuting; - private static readonly Action _messageHasBeenSent; - private static readonly Action _messagePublishException; - - static LoggerExtensions() + public static void ConsumerExecutedAfterThreshold(this ILogger logger, int messageId, int retries) { - _serverStarting = LoggerMessage.Define( - LogLevel.Debug, - 1, - "Starting the processing server."); - - _processorsStartingError = LoggerMessage.Define( - LogLevel.Error, - 5, - "Starting the processors throw an exception."); - - _serverShuttingDown = LoggerMessage.Define( - LogLevel.Information, - 2, - "Shutting down the processing server..."); - - _expectedOperationCanceledException = LoggerMessage.Define( - LogLevel.Warning, - 3, - "Expected an OperationCanceledException, but found '{ExceptionMessage}'."); - - LoggerMessage.Define( - LogLevel.Error, - 5, - "Consumer method '{methodName}' failed to execute."); - - LoggerMessage.Define( - LogLevel.Error, - 5, - "Received message topic method '{topicName}' failed to execute."); - - _modelBinderFormattingException = LoggerMessage.Define( - LogLevel.Error, - 5, - "When call subscribe method, a parameter format conversion exception occurs. MethodName:'{MethodName}' ParameterName:'{ParameterName}' Content:'{Content}'." - ); - - _senderRetrying = LoggerMessage.Define( - LogLevel.Warning, - 3, - "The {Retries}th retrying send a message failed. message id: {MessageId} "); - - _consumerExecuted = LoggerMessage.Define( - LogLevel.Debug, - 4, - "Consumer executed. Took: {Seconds} secs."); - - _consumerFailedWillRetry = LoggerMessage.Define( - LogLevel.Warning, - 2, - "The {Retries}th retrying consume a message failed. message id: {MessageId}"); - - _exceptionOccuredWhileExecuting = LoggerMessage.Define( - LogLevel.Error, - 6, - "An exception occured while trying to store a message. message id: {MessageId}"); + logger.LogWarning($"The Subscriber of the message({messageId}) still fails after {retries}th executions and we will stop retrying."); + } - _messageHasBeenSent = LoggerMessage.Define( - LogLevel.Debug, - 4, - "Message published. Took: {Seconds} secs."); + public static void SenderAfterThreshold(this ILogger logger, int messageId, int retries) + { + logger.LogWarning($"The Publisher of the message({messageId}) still fails after {retries}th sends and we will stop retrying."); + } - _messagePublishException = LoggerMessage.Define( - LogLevel.Error, - 6, - "An exception occured while publishing a message, reason:{Reason}. message id:{MessageId}"); + public static void ExecutedThresholdCallbackFailed(this ILogger logger, Exception ex) + { + logger.LogWarning(ex, "FailedThresholdCallback action raised an exception:" + ex.Message); } public static void ConsumerExecutionRetrying(this ILogger logger, int messageId, int retries) { - _consumerFailedWillRetry(logger, messageId, retries, null); + logger.LogWarning($"The {retries}th retrying consume a message failed. message id: {messageId}"); } public static void SenderRetrying(this ILogger logger, int messageId, int retries) { - _senderRetrying(logger, messageId, retries, null); + logger.LogWarning($"The {retries}th retrying send a message failed. message id: {messageId} "); } public static void MessageHasBeenSent(this ILogger logger, double seconds) { - _messageHasBeenSent(logger, seconds, null); + logger.LogDebug($"Message published. Took: {seconds} secs."); } public static void MessagePublishException(this ILogger logger, int messageId, string reason, Exception ex) { - _messagePublishException(logger, messageId, reason, ex); + logger.LogError(ex, $"An exception occured while publishing a message, reason:{reason}. message id:{messageId}"); } public static void ConsumerExecuted(this ILogger logger, double seconds) { - _consumerExecuted(logger, seconds, null); + logger.LogDebug($"Consumer executed. Took: {seconds} secs."); } public static void ServerStarting(this ILogger logger) { - _serverStarting(logger, null); + logger.LogInformation("Starting the processing server."); } public static void ProcessorsStartedError(this ILogger logger, Exception ex) { - _processorsStartingError(logger, ex); + logger.LogError(ex, "Starting the processors throw an exception."); } public static void ServerShuttingDown(this ILogger logger) { - _serverShuttingDown(logger, null); + logger.LogInformation("Shutting down the processing server..."); } public static void ExpectedOperationCanceledException(this ILogger logger, Exception ex) { - _expectedOperationCanceledException(logger, ex.Message, ex); - } - - public static void ExceptionOccuredWhileExecuting(this ILogger logger, string messageId, Exception ex) - { - _exceptionOccuredWhileExecuting(logger, messageId, ex); + logger.LogWarning(ex, $"Expected an OperationCanceledException, but found '{ex.Message}'."); } public static void ModelBinderFormattingException(this ILogger logger, string methodName, string parameterName, string content, Exception ex) { - _modelBinderFormattingException(logger, methodName, parameterName, content, ex); + logger.LogError(ex, $"When call subscribe method, a parameter format conversion exception occurs. MethodName:'{methodName}' ParameterName:'{parameterName}' Content:'{content}'."); } } } \ No newline at end of file From a007dfefd3e36acb67e64072f83731a6683f4c4c Mon Sep 17 00:00:00 2001 From: Savorboard Date: Tue, 17 Jul 2018 15:19:54 +0800 Subject: [PATCH 109/223] refactor retry task processor. --- .../Processor/IProcessor.NeedRetry.cs | 127 +----------------- 1 file changed, 7 insertions(+), 120 deletions(-) diff --git a/src/DotNetCore.CAP/Processor/IProcessor.NeedRetry.cs b/src/DotNetCore.CAP/Processor/IProcessor.NeedRetry.cs index 2f7b829..36a6deb 100644 --- a/src/DotNetCore.CAP/Processor/IProcessor.NeedRetry.cs +++ b/src/DotNetCore.CAP/Processor/IProcessor.NeedRetry.cs @@ -3,11 +3,7 @@ using System; using System.Threading.Tasks; -using DotNetCore.CAP.Infrastructure; -using DotNetCore.CAP.Models; -using DotNetCore.CAP.Processor.States; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; namespace DotNetCore.CAP.Processor @@ -15,26 +11,18 @@ namespace DotNetCore.CAP.Processor public class NeedRetryMessageProcessor : IProcessor { private readonly TimeSpan _delay = TimeSpan.FromSeconds(1); - private readonly ILogger _logger; - private readonly CapOptions _options; - private readonly IPublishExecutor _publishExecutor; - private readonly IStateChanger _stateChanger; + private readonly IPublishMessageSender _publishMessageSender; private readonly ISubscriberExecutor _subscriberExecutor; private readonly TimeSpan _waitingInterval; public NeedRetryMessageProcessor( IOptions options, - ILogger logger, - IStateChanger stateChanger, ISubscriberExecutor subscriberExecutor, - IPublishExecutor publishExecutor) + IPublishMessageSender publishMessageSender) { - _options = options.Value; - _logger = logger; - _stateChanger = stateChanger; _subscriberExecutor = subscriberExecutor; - _publishExecutor = publishExecutor; - _waitingInterval = TimeSpan.FromSeconds(_options.FailedRetryInterval); + _publishMessageSender = publishMessageSender; + _waitingInterval = TimeSpan.FromSeconds(options.Value.FailedRetryInterval); } public async Task ProcessAsync(ProcessingContext context) @@ -56,57 +44,10 @@ namespace DotNetCore.CAP.Processor private async Task ProcessPublishedAsync(IStorageConnection connection, ProcessingContext context) { var messages = await connection.GetPublishedMessagesOfNeedRetry(); - var hasException = false; foreach (var message in messages) { - if (message.Retries > _options.FailedRetryCount) - { - continue; - } - - using (var transaction = connection.CreateTransaction()) - { - var result = await _publishExecutor.PublishAsync(message.Name, message.Content); - if (result.Succeeded) - { - _stateChanger.ChangeState(message, new SucceededState(), transaction); - _logger.LogInformation("The message was sent successfully during the retry. MessageId:" + message.Id); - } - else - { - message.Content = Helper.AddExceptionProperty(message.Content, result.Exception); - message.Retries++; - if (message.StatusName == StatusName.Scheduled) - { - message.ExpiresAt = GetDueTime(message.Added, message.Retries); - message.StatusName = StatusName.Failed; - } - transaction.UpdateMessage(message); - - if (message.Retries >= _options.FailedRetryCount) - { - _logger.LogError($"The message still sent failed after {_options.FailedRetryCount} retries. We will stop retrying the message. " + - "MessageId:" + message.Id); - if (message.Retries == _options.FailedRetryCount) - { - if (!hasException) - { - try - { - _options.FailedThresholdCallback?.Invoke(MessageType.Publish, message.Name, message.Content); - } - catch (Exception ex) - { - hasException = true; - _logger.LogWarning("Failed call-back method raised an exception:" + ex.Message); - } - } - } - } - } - await transaction.CommitAsync(); - } + await _publishMessageSender.SendAsync(message); context.ThrowIfStopping(); @@ -117,69 +58,15 @@ namespace DotNetCore.CAP.Processor private async Task ProcessReceivedAsync(IStorageConnection connection, ProcessingContext context) { var messages = await connection.GetReceivedMessagesOfNeedRetry(); - var hasException = false; foreach (var message in messages) { - if (message.Retries > _options.FailedRetryCount) - { - continue; - } - - using (var transaction = connection.CreateTransaction()) - { - var result = await _subscriberExecutor.ExecuteAsync(message); - if (result.Succeeded) - { - _stateChanger.ChangeState(message, new SucceededState(), transaction); - _logger.LogInformation("The message was execute successfully during the retry. MessageId:" + message.Id); - } - else - { - message.Content = Helper.AddExceptionProperty(message.Content, result.Exception); - message.Retries++; - if (message.StatusName == StatusName.Scheduled) - { - message.ExpiresAt = GetDueTime(message.Added, message.Retries); - message.StatusName = StatusName.Failed; - } - transaction.UpdateMessage(message); - - if (message.Retries >= _options.FailedRetryCount) - { - _logger.LogError($"[Subscriber]The message still executed failed after {_options.FailedRetryCount} retries. " + - "We will stop retrying to execute the message. message id:" + message.Id); - - if (message.Retries == _options.FailedRetryCount) - { - if (!hasException) - { - try - { - _options.FailedThresholdCallback?.Invoke(MessageType.Subscribe, message.Name, message.Content); - } - catch (Exception ex) - { - hasException = true; - _logger.LogWarning("Failed call-back method raised an exception:" + ex.Message); - } - } - } - } - } - await transaction.CommitAsync(); - } + await _subscriberExecutor.ExecuteAsync(message); context.ThrowIfStopping(); await context.WaitAsync(_delay); } - } - - public DateTime GetDueTime(DateTime addedTime, int retries) - { - var retryBehavior = RetryBehavior.DefaultRetry; - return addedTime.AddSeconds(retryBehavior.RetryIn(retries)); - } + } } } \ No newline at end of file From 2e491a6adf68cc7bafe807d736ad621d24dfed29 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Tue, 17 Jul 2018 15:19:54 +0800 Subject: [PATCH 110/223] refactor retry task processor. --- .../Processor/IProcessor.NeedRetry.cs | 127 +----------------- 1 file changed, 7 insertions(+), 120 deletions(-) diff --git a/src/DotNetCore.CAP/Processor/IProcessor.NeedRetry.cs b/src/DotNetCore.CAP/Processor/IProcessor.NeedRetry.cs index 2f7b829..36a6deb 100644 --- a/src/DotNetCore.CAP/Processor/IProcessor.NeedRetry.cs +++ b/src/DotNetCore.CAP/Processor/IProcessor.NeedRetry.cs @@ -3,11 +3,7 @@ using System; using System.Threading.Tasks; -using DotNetCore.CAP.Infrastructure; -using DotNetCore.CAP.Models; -using DotNetCore.CAP.Processor.States; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; namespace DotNetCore.CAP.Processor @@ -15,26 +11,18 @@ namespace DotNetCore.CAP.Processor public class NeedRetryMessageProcessor : IProcessor { private readonly TimeSpan _delay = TimeSpan.FromSeconds(1); - private readonly ILogger _logger; - private readonly CapOptions _options; - private readonly IPublishExecutor _publishExecutor; - private readonly IStateChanger _stateChanger; + private readonly IPublishMessageSender _publishMessageSender; private readonly ISubscriberExecutor _subscriberExecutor; private readonly TimeSpan _waitingInterval; public NeedRetryMessageProcessor( IOptions options, - ILogger logger, - IStateChanger stateChanger, ISubscriberExecutor subscriberExecutor, - IPublishExecutor publishExecutor) + IPublishMessageSender publishMessageSender) { - _options = options.Value; - _logger = logger; - _stateChanger = stateChanger; _subscriberExecutor = subscriberExecutor; - _publishExecutor = publishExecutor; - _waitingInterval = TimeSpan.FromSeconds(_options.FailedRetryInterval); + _publishMessageSender = publishMessageSender; + _waitingInterval = TimeSpan.FromSeconds(options.Value.FailedRetryInterval); } public async Task ProcessAsync(ProcessingContext context) @@ -56,57 +44,10 @@ namespace DotNetCore.CAP.Processor private async Task ProcessPublishedAsync(IStorageConnection connection, ProcessingContext context) { var messages = await connection.GetPublishedMessagesOfNeedRetry(); - var hasException = false; foreach (var message in messages) { - if (message.Retries > _options.FailedRetryCount) - { - continue; - } - - using (var transaction = connection.CreateTransaction()) - { - var result = await _publishExecutor.PublishAsync(message.Name, message.Content); - if (result.Succeeded) - { - _stateChanger.ChangeState(message, new SucceededState(), transaction); - _logger.LogInformation("The message was sent successfully during the retry. MessageId:" + message.Id); - } - else - { - message.Content = Helper.AddExceptionProperty(message.Content, result.Exception); - message.Retries++; - if (message.StatusName == StatusName.Scheduled) - { - message.ExpiresAt = GetDueTime(message.Added, message.Retries); - message.StatusName = StatusName.Failed; - } - transaction.UpdateMessage(message); - - if (message.Retries >= _options.FailedRetryCount) - { - _logger.LogError($"The message still sent failed after {_options.FailedRetryCount} retries. We will stop retrying the message. " + - "MessageId:" + message.Id); - if (message.Retries == _options.FailedRetryCount) - { - if (!hasException) - { - try - { - _options.FailedThresholdCallback?.Invoke(MessageType.Publish, message.Name, message.Content); - } - catch (Exception ex) - { - hasException = true; - _logger.LogWarning("Failed call-back method raised an exception:" + ex.Message); - } - } - } - } - } - await transaction.CommitAsync(); - } + await _publishMessageSender.SendAsync(message); context.ThrowIfStopping(); @@ -117,69 +58,15 @@ namespace DotNetCore.CAP.Processor private async Task ProcessReceivedAsync(IStorageConnection connection, ProcessingContext context) { var messages = await connection.GetReceivedMessagesOfNeedRetry(); - var hasException = false; foreach (var message in messages) { - if (message.Retries > _options.FailedRetryCount) - { - continue; - } - - using (var transaction = connection.CreateTransaction()) - { - var result = await _subscriberExecutor.ExecuteAsync(message); - if (result.Succeeded) - { - _stateChanger.ChangeState(message, new SucceededState(), transaction); - _logger.LogInformation("The message was execute successfully during the retry. MessageId:" + message.Id); - } - else - { - message.Content = Helper.AddExceptionProperty(message.Content, result.Exception); - message.Retries++; - if (message.StatusName == StatusName.Scheduled) - { - message.ExpiresAt = GetDueTime(message.Added, message.Retries); - message.StatusName = StatusName.Failed; - } - transaction.UpdateMessage(message); - - if (message.Retries >= _options.FailedRetryCount) - { - _logger.LogError($"[Subscriber]The message still executed failed after {_options.FailedRetryCount} retries. " + - "We will stop retrying to execute the message. message id:" + message.Id); - - if (message.Retries == _options.FailedRetryCount) - { - if (!hasException) - { - try - { - _options.FailedThresholdCallback?.Invoke(MessageType.Subscribe, message.Name, message.Content); - } - catch (Exception ex) - { - hasException = true; - _logger.LogWarning("Failed call-back method raised an exception:" + ex.Message); - } - } - } - } - } - await transaction.CommitAsync(); - } + await _subscriberExecutor.ExecuteAsync(message); context.ThrowIfStopping(); await context.WaitAsync(_delay); } - } - - public DateTime GetDueTime(DateTime addedTime, int retries) - { - var retryBehavior = RetryBehavior.DefaultRetry; - return addedTime.AddSeconds(retryBehavior.RetryIn(retries)); - } + } } } \ No newline at end of file From 00bbf6cce890e7aab8740d72b0116e9e7af57bd2 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Tue, 17 Jul 2018 15:22:44 +0800 Subject: [PATCH 111/223] Fixed configuration options of FailedThresholdCallback could not be invoke when the value less then three. (#161) --- .../IPublishMessageSender.Base.cs | 70 ++++++++++++------- .../ISubscribeExecutor.Default.cs | 46 ++++++++---- 2 files changed, 79 insertions(+), 37 deletions(-) diff --git a/src/DotNetCore.CAP/IPublishMessageSender.Base.cs b/src/DotNetCore.CAP/IPublishMessageSender.Base.cs index d36bc51..cbbc780 100644 --- a/src/DotNetCore.CAP/IPublishMessageSender.Base.cs +++ b/src/DotNetCore.CAP/IPublishMessageSender.Base.cs @@ -48,18 +48,19 @@ namespace DotNetCore.CAP OperateResult result; do { - result = await SendWithoutRetryAsync(message); + var executedResult = await SendWithoutRetryAsync(message); + result = executedResult.Item2; if (result == OperateResult.Success) { return result; } - retry = UpdateMessageForRetry(message); + retry = executedResult.Item1; } while (retry); return result; } - private async Task SendWithoutRetryAsync(CapPublishedMessage message) + private async Task<(bool, OperateResult)> SendWithoutRetryAsync(CapPublishedMessage message) { var startTime = DateTimeOffset.UtcNow; var stopwatch = Stopwatch.StartNew(); @@ -80,34 +81,17 @@ namespace DotNetCore.CAP TracingAfter(operationId, message.Name, sendValues, startTime, stopwatch.Elapsed); - return OperateResult.Success; + return (false, OperateResult.Success); } else { TracingError(operationId, message, result, startTime, stopwatch.Elapsed); - await SetFailedState(message, result.Exception); - - return OperateResult.Failed(result.Exception); + var needRetry = await SetFailedState(message, result.Exception); + return (needRetry, OperateResult.Failed(result.Exception)); } } - private bool UpdateMessageForRetry(CapPublishedMessage message) - { - var retryBehavior = RetryBehavior.DefaultRetry; - var retries = ++message.Retries; - if (retries >= retryBehavior.RetryCount) - { - return false; - } - - _logger.SenderRetrying(message.Id, retries); - - var due = message.Added.AddSeconds(retryBehavior.RetryIn(retries)); - message.ExpiresAt = due; - - return true; - } private Task SetSuccessfulState(CapPublishedMessage message) { @@ -115,10 +99,15 @@ namespace DotNetCore.CAP return _stateChanger.ChangeStateAsync(message, succeededState, _connection); } - private Task SetFailedState(CapPublishedMessage message, Exception ex) + private async Task SetFailedState(CapPublishedMessage message, Exception ex) { AddErrorReasonToContent(message, ex); - return _stateChanger.ChangeStateAsync(message, new FailedState(), _connection); + + var needRetry = UpdateMessageForRetry(message); + + await _stateChanger.ChangeStateAsync(message, new FailedState(), _connection); + + return needRetry; } private static void AddErrorReasonToContent(CapPublishedMessage message, Exception exception) @@ -126,6 +115,37 @@ namespace DotNetCore.CAP message.Content = Helper.AddExceptionProperty(message.Content, exception); } + private bool UpdateMessageForRetry(CapPublishedMessage message) + { + var retryBehavior = RetryBehavior.DefaultRetry; + + var retries = ++message.Retries; + message.ExpiresAt = message.Added.AddSeconds(retryBehavior.RetryIn(retries)); + + var retryCount = Math.Min(_options.FailedRetryCount, retryBehavior.RetryCount); + if (retries >= retryCount) + { + if (retries == _options.FailedRetryCount) + { + try + { + _options.FailedThresholdCallback?.Invoke(MessageType.Subscribe, message.Name, message.Content); + + _logger.SenderAfterThreshold(message.Id, _options.FailedRetryCount); + } + catch (Exception ex) + { + _logger.ExecutedThresholdCallbackFailed(ex); + } + } + return false; + } + + _logger.SenderRetrying(message.Id, retries); + + return true; + } + private (Guid, TracingHeaders) TracingBefore(string topic, string values) { Guid operationId = Guid.NewGuid(); diff --git a/src/DotNetCore.CAP/ISubscribeExecutor.Default.cs b/src/DotNetCore.CAP/ISubscribeExecutor.Default.cs index f56dc73..6bd1a68 100644 --- a/src/DotNetCore.CAP/ISubscribeExecutor.Default.cs +++ b/src/DotNetCore.CAP/ISubscribeExecutor.Default.cs @@ -55,18 +55,24 @@ namespace DotNetCore.CAP OperateResult result; do { - result = await ExecuteWithoutRetryAsync(message); + var executedResult = await ExecuteWithoutRetryAsync(message); + result = executedResult.Item2; if (result == OperateResult.Success) { return result; } - retry = UpdateMessageForRetry(message); + retry = executedResult.Item1; } while (retry); return result; } - private async Task ExecuteWithoutRetryAsync(CapReceivedMessage message) + /// + /// Execute message consumption once. + /// + /// the message rececived of + /// Item1 is need still restry, Item2 is executed result. + private async Task<(bool, OperateResult)> ExecuteWithoutRetryAsync(CapReceivedMessage message) { if (message == null) { @@ -85,26 +91,23 @@ namespace DotNetCore.CAP _logger.ConsumerExecuted(sp.Elapsed.TotalSeconds); - return OperateResult.Success; + return (false, OperateResult.Success); } catch (Exception ex) { _logger.LogError(ex, $"An exception occurred while executing the subscription method. Topic:{message.Name}, Id:{message.Id}"); - await SetFailedState(message, ex); - - return OperateResult.Failed(ex); + return (await SetFailedState(message, ex), OperateResult.Failed(ex)); } } private Task SetSuccessfulState(CapReceivedMessage message) { var succeededState = new SucceededState(_options.SucceedMessageExpiredAfter); - return _stateChanger.ChangeStateAsync(message, succeededState, _connection); } - private Task SetFailedState(CapReceivedMessage message, Exception ex) + private async Task SetFailedState(CapReceivedMessage message, Exception ex) { if (ex is SubscriberNotFoundException) { @@ -113,7 +116,11 @@ namespace DotNetCore.CAP AddErrorReasonToContent(message, ex); - return _stateChanger.ChangeStateAsync(message, new FailedState(), _connection); + var needRetry = UpdateMessageForRetry(message); + + await _stateChanger.ChangeStateAsync(message, new FailedState(), _connection); + + return needRetry; } private bool UpdateMessageForRetry(CapReceivedMessage message) @@ -121,14 +128,29 @@ namespace DotNetCore.CAP var retryBehavior = RetryBehavior.DefaultRetry; var retries = ++message.Retries; + message.ExpiresAt = message.Added.AddSeconds(retryBehavior.RetryIn(retries)); + var retryCount = Math.Min(_options.FailedRetryCount, retryBehavior.RetryCount); if (retries >= retryCount) { + if (retries == _options.FailedRetryCount) + { + try + { + _options.FailedThresholdCallback?.Invoke(MessageType.Subscribe, message.Name, message.Content); + + _logger.ConsumerExecutedAfterThreshold(message.Id, _options.FailedRetryCount); + } + catch (Exception ex) + { + _logger.ExecutedThresholdCallbackFailed(ex); + } + } return false; } + _logger.ConsumerExecutionRetrying(message.Id, retries); - var due = message.Added.AddSeconds(retryBehavior.RetryIn(retries)); - message.ExpiresAt = due; + return true; } From 7089b1ccded6851045c3ebf5d9fb0a6f1235111f Mon Sep 17 00:00:00 2001 From: Savorboard Date: Tue, 17 Jul 2018 15:22:44 +0800 Subject: [PATCH 112/223] Fixed configuration options of FailedThresholdCallback could not be invoke when the value less then three. (#161) --- .../IPublishMessageSender.Base.cs | 70 ++++++++++++------- .../ISubscribeExecutor.Default.cs | 46 ++++++++---- 2 files changed, 79 insertions(+), 37 deletions(-) diff --git a/src/DotNetCore.CAP/IPublishMessageSender.Base.cs b/src/DotNetCore.CAP/IPublishMessageSender.Base.cs index d36bc51..cbbc780 100644 --- a/src/DotNetCore.CAP/IPublishMessageSender.Base.cs +++ b/src/DotNetCore.CAP/IPublishMessageSender.Base.cs @@ -48,18 +48,19 @@ namespace DotNetCore.CAP OperateResult result; do { - result = await SendWithoutRetryAsync(message); + var executedResult = await SendWithoutRetryAsync(message); + result = executedResult.Item2; if (result == OperateResult.Success) { return result; } - retry = UpdateMessageForRetry(message); + retry = executedResult.Item1; } while (retry); return result; } - private async Task SendWithoutRetryAsync(CapPublishedMessage message) + private async Task<(bool, OperateResult)> SendWithoutRetryAsync(CapPublishedMessage message) { var startTime = DateTimeOffset.UtcNow; var stopwatch = Stopwatch.StartNew(); @@ -80,34 +81,17 @@ namespace DotNetCore.CAP TracingAfter(operationId, message.Name, sendValues, startTime, stopwatch.Elapsed); - return OperateResult.Success; + return (false, OperateResult.Success); } else { TracingError(operationId, message, result, startTime, stopwatch.Elapsed); - await SetFailedState(message, result.Exception); - - return OperateResult.Failed(result.Exception); + var needRetry = await SetFailedState(message, result.Exception); + return (needRetry, OperateResult.Failed(result.Exception)); } } - private bool UpdateMessageForRetry(CapPublishedMessage message) - { - var retryBehavior = RetryBehavior.DefaultRetry; - var retries = ++message.Retries; - if (retries >= retryBehavior.RetryCount) - { - return false; - } - - _logger.SenderRetrying(message.Id, retries); - - var due = message.Added.AddSeconds(retryBehavior.RetryIn(retries)); - message.ExpiresAt = due; - - return true; - } private Task SetSuccessfulState(CapPublishedMessage message) { @@ -115,10 +99,15 @@ namespace DotNetCore.CAP return _stateChanger.ChangeStateAsync(message, succeededState, _connection); } - private Task SetFailedState(CapPublishedMessage message, Exception ex) + private async Task SetFailedState(CapPublishedMessage message, Exception ex) { AddErrorReasonToContent(message, ex); - return _stateChanger.ChangeStateAsync(message, new FailedState(), _connection); + + var needRetry = UpdateMessageForRetry(message); + + await _stateChanger.ChangeStateAsync(message, new FailedState(), _connection); + + return needRetry; } private static void AddErrorReasonToContent(CapPublishedMessage message, Exception exception) @@ -126,6 +115,37 @@ namespace DotNetCore.CAP message.Content = Helper.AddExceptionProperty(message.Content, exception); } + private bool UpdateMessageForRetry(CapPublishedMessage message) + { + var retryBehavior = RetryBehavior.DefaultRetry; + + var retries = ++message.Retries; + message.ExpiresAt = message.Added.AddSeconds(retryBehavior.RetryIn(retries)); + + var retryCount = Math.Min(_options.FailedRetryCount, retryBehavior.RetryCount); + if (retries >= retryCount) + { + if (retries == _options.FailedRetryCount) + { + try + { + _options.FailedThresholdCallback?.Invoke(MessageType.Subscribe, message.Name, message.Content); + + _logger.SenderAfterThreshold(message.Id, _options.FailedRetryCount); + } + catch (Exception ex) + { + _logger.ExecutedThresholdCallbackFailed(ex); + } + } + return false; + } + + _logger.SenderRetrying(message.Id, retries); + + return true; + } + private (Guid, TracingHeaders) TracingBefore(string topic, string values) { Guid operationId = Guid.NewGuid(); diff --git a/src/DotNetCore.CAP/ISubscribeExecutor.Default.cs b/src/DotNetCore.CAP/ISubscribeExecutor.Default.cs index f56dc73..6bd1a68 100644 --- a/src/DotNetCore.CAP/ISubscribeExecutor.Default.cs +++ b/src/DotNetCore.CAP/ISubscribeExecutor.Default.cs @@ -55,18 +55,24 @@ namespace DotNetCore.CAP OperateResult result; do { - result = await ExecuteWithoutRetryAsync(message); + var executedResult = await ExecuteWithoutRetryAsync(message); + result = executedResult.Item2; if (result == OperateResult.Success) { return result; } - retry = UpdateMessageForRetry(message); + retry = executedResult.Item1; } while (retry); return result; } - private async Task ExecuteWithoutRetryAsync(CapReceivedMessage message) + /// + /// Execute message consumption once. + /// + /// the message rececived of + /// Item1 is need still restry, Item2 is executed result. + private async Task<(bool, OperateResult)> ExecuteWithoutRetryAsync(CapReceivedMessage message) { if (message == null) { @@ -85,26 +91,23 @@ namespace DotNetCore.CAP _logger.ConsumerExecuted(sp.Elapsed.TotalSeconds); - return OperateResult.Success; + return (false, OperateResult.Success); } catch (Exception ex) { _logger.LogError(ex, $"An exception occurred while executing the subscription method. Topic:{message.Name}, Id:{message.Id}"); - await SetFailedState(message, ex); - - return OperateResult.Failed(ex); + return (await SetFailedState(message, ex), OperateResult.Failed(ex)); } } private Task SetSuccessfulState(CapReceivedMessage message) { var succeededState = new SucceededState(_options.SucceedMessageExpiredAfter); - return _stateChanger.ChangeStateAsync(message, succeededState, _connection); } - private Task SetFailedState(CapReceivedMessage message, Exception ex) + private async Task SetFailedState(CapReceivedMessage message, Exception ex) { if (ex is SubscriberNotFoundException) { @@ -113,7 +116,11 @@ namespace DotNetCore.CAP AddErrorReasonToContent(message, ex); - return _stateChanger.ChangeStateAsync(message, new FailedState(), _connection); + var needRetry = UpdateMessageForRetry(message); + + await _stateChanger.ChangeStateAsync(message, new FailedState(), _connection); + + return needRetry; } private bool UpdateMessageForRetry(CapReceivedMessage message) @@ -121,14 +128,29 @@ namespace DotNetCore.CAP var retryBehavior = RetryBehavior.DefaultRetry; var retries = ++message.Retries; + message.ExpiresAt = message.Added.AddSeconds(retryBehavior.RetryIn(retries)); + var retryCount = Math.Min(_options.FailedRetryCount, retryBehavior.RetryCount); if (retries >= retryCount) { + if (retries == _options.FailedRetryCount) + { + try + { + _options.FailedThresholdCallback?.Invoke(MessageType.Subscribe, message.Name, message.Content); + + _logger.ConsumerExecutedAfterThreshold(message.Id, _options.FailedRetryCount); + } + catch (Exception ex) + { + _logger.ExecutedThresholdCallbackFailed(ex); + } + } return false; } + _logger.ConsumerExecutionRetrying(message.Id, retries); - var due = message.Added.AddSeconds(retryBehavior.RetryIn(retries)); - message.ExpiresAt = due; + return true; } From 3e87d5ee1df618eb8d959e996f47c417e615054d Mon Sep 17 00:00:00 2001 From: Savorboard Date: Wed, 18 Jul 2018 15:24:54 +0800 Subject: [PATCH 113/223] update samples. --- samples/Sample.RabbitMQ.MySql/Startup.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/samples/Sample.RabbitMQ.MySql/Startup.cs b/samples/Sample.RabbitMQ.MySql/Startup.cs index 554a624..007e78e 100644 --- a/samples/Sample.RabbitMQ.MySql/Startup.cs +++ b/samples/Sample.RabbitMQ.MySql/Startup.cs @@ -1,4 +1,5 @@ -using Microsoft.AspNetCore.Builder; +using System; +using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; @@ -16,6 +17,11 @@ namespace Sample.RabbitMQ.MySql x.UseEntityFramework(); x.UseRabbitMQ("localhost"); x.UseDashboard(); + x.FailedRetryCount = 5; + x.FailedThresholdCallback = (type, name, content) => + { + Console.WriteLine($@"A message of type {type} failed after executing {x.FailedRetryCount} several times, requiring manual troubleshooting. Message name: {name}, message body: {content}"); + }; }); services.AddMvc(); From d032b26d139184952907598e02b78f9465fe0a5d Mon Sep 17 00:00:00 2001 From: Savorboard Date: Wed, 18 Jul 2018 15:24:54 +0800 Subject: [PATCH 114/223] update samples. --- samples/Sample.RabbitMQ.MySql/Startup.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/samples/Sample.RabbitMQ.MySql/Startup.cs b/samples/Sample.RabbitMQ.MySql/Startup.cs index 554a624..007e78e 100644 --- a/samples/Sample.RabbitMQ.MySql/Startup.cs +++ b/samples/Sample.RabbitMQ.MySql/Startup.cs @@ -1,4 +1,5 @@ -using Microsoft.AspNetCore.Builder; +using System; +using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; @@ -16,6 +17,11 @@ namespace Sample.RabbitMQ.MySql x.UseEntityFramework(); x.UseRabbitMQ("localhost"); x.UseDashboard(); + x.FailedRetryCount = 5; + x.FailedThresholdCallback = (type, name, content) => + { + Console.WriteLine($@"A message of type {type} failed after executing {x.FailedRetryCount} several times, requiring manual troubleshooting. Message name: {name}, message body: {content}"); + }; }); services.AddMvc(); From 4c4c0433b7056e05233c5163ae24dbc995451f12 Mon Sep 17 00:00:00 2001 From: keke Date: Fri, 20 Jul 2018 14:36:49 +0800 Subject: [PATCH 115/223] Compatible sqlserver2008 (#164) * Replace FORMAT * Replace offset * Use sqlserver 2008 * Remove blank lines * Tweak UseSqlServer2008 --- src/DotNetCore.CAP.SqlServer/CAP.EFOptions.cs | 8 ++++++ .../SqlServerMonitoringApi.cs | 26 +++++++++++++++---- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/src/DotNetCore.CAP.SqlServer/CAP.EFOptions.cs b/src/DotNetCore.CAP.SqlServer/CAP.EFOptions.cs index 243b1c8..3cf44c4 100644 --- a/src/DotNetCore.CAP.SqlServer/CAP.EFOptions.cs +++ b/src/DotNetCore.CAP.SqlServer/CAP.EFOptions.cs @@ -20,5 +20,13 @@ namespace DotNetCore.CAP /// EF dbcontext type. /// internal Type DbContextType { get; set; } + + internal bool IsSqlServer2008 { get; set; } + + public EFOptions UseSqlServer2008() + { + IsSqlServer2008 = true; + return this; + } } } \ No newline at end of file diff --git a/src/DotNetCore.CAP.SqlServer/SqlServerMonitoringApi.cs b/src/DotNetCore.CAP.SqlServer/SqlServerMonitoringApi.cs index 31c4bcd..67c7d22 100644 --- a/src/DotNetCore.CAP.SqlServer/SqlServerMonitoringApi.cs +++ b/src/DotNetCore.CAP.SqlServer/SqlServerMonitoringApi.cs @@ -89,10 +89,17 @@ select count(Id) from [{0}].Received with (nolock) where StatusName = N'Failed'; where += " and content like '%@Content%'"; } + var sqlQuery2008 = + $@"select * from + (SELECT t.*, ROW_NUMBER() OVER(order by t.Added desc) AS rownumber + from [{_options.Schema}].{tableName} as t + where 1=1 {where}) as tbl + where tbl.rownumber between @offset and @offset + @limit"; + var sqlQuery = $"select * from [{_options.Schema}].{tableName} where 1=1 {where} order by Added desc offset @Offset rows fetch next @Limit rows only"; - return UseConnection(conn => conn.Query(sqlQuery, new + return UseConnection(conn => conn.Query(_options.IsSqlServer2008 ? sqlQuery2008 : sqlQuery, new { queryDto.StatusName, queryDto.Group, @@ -159,9 +166,18 @@ select count(Id) from [{0}].Received with (nolock) where StatusName = N'Failed'; string statusName, IDictionary keyMaps) { - //SQL Server 2012+ - var sqlQuery = - $@" + var sqlQuery2008 = $@" +with aggr as ( + select replace(convert(varchar, Added, 111), '/','-') + '-' + CONVERT(varchar, DATEPART(hh, Added)) as [Key], + count(id) [Count] + from [{_options.Schema}].{tableName} + where StatusName = @statusName + group by replace(convert(varchar, Added, 111), '/','-') + '-' + CONVERT(varchar, DATEPART(hh, Added)) +) +select [Key], [Count] from aggr with (nolock) where [Key] in @keys;"; + + //SQL Server 2012+ + var sqlQuery = $@" with aggr as ( select FORMAT(Added,'yyyy-MM-dd-HH') as [Key], count(id) [Count] @@ -172,7 +188,7 @@ with aggr as ( select [Key], [Count] from aggr with (nolock) where [Key] in @keys;"; var valuesMap = connection.Query( - sqlQuery, + _options.IsSqlServer2008 ? sqlQuery2008 : sqlQuery, new { keys = keyMaps.Keys, statusName }) .ToDictionary(x => x.Key, x => x.Count); From 7a4a62baf10c262db0e58c6f51766c32540f5735 Mon Sep 17 00:00:00 2001 From: keke Date: Fri, 20 Jul 2018 14:36:49 +0800 Subject: [PATCH 116/223] Compatible sqlserver2008 (#164) * Replace FORMAT * Replace offset * Use sqlserver 2008 * Remove blank lines * Tweak UseSqlServer2008 --- src/DotNetCore.CAP.SqlServer/CAP.EFOptions.cs | 8 ++++++ .../SqlServerMonitoringApi.cs | 26 +++++++++++++++---- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/src/DotNetCore.CAP.SqlServer/CAP.EFOptions.cs b/src/DotNetCore.CAP.SqlServer/CAP.EFOptions.cs index 243b1c8..3cf44c4 100644 --- a/src/DotNetCore.CAP.SqlServer/CAP.EFOptions.cs +++ b/src/DotNetCore.CAP.SqlServer/CAP.EFOptions.cs @@ -20,5 +20,13 @@ namespace DotNetCore.CAP /// EF dbcontext type. /// internal Type DbContextType { get; set; } + + internal bool IsSqlServer2008 { get; set; } + + public EFOptions UseSqlServer2008() + { + IsSqlServer2008 = true; + return this; + } } } \ No newline at end of file diff --git a/src/DotNetCore.CAP.SqlServer/SqlServerMonitoringApi.cs b/src/DotNetCore.CAP.SqlServer/SqlServerMonitoringApi.cs index 31c4bcd..67c7d22 100644 --- a/src/DotNetCore.CAP.SqlServer/SqlServerMonitoringApi.cs +++ b/src/DotNetCore.CAP.SqlServer/SqlServerMonitoringApi.cs @@ -89,10 +89,17 @@ select count(Id) from [{0}].Received with (nolock) where StatusName = N'Failed'; where += " and content like '%@Content%'"; } + var sqlQuery2008 = + $@"select * from + (SELECT t.*, ROW_NUMBER() OVER(order by t.Added desc) AS rownumber + from [{_options.Schema}].{tableName} as t + where 1=1 {where}) as tbl + where tbl.rownumber between @offset and @offset + @limit"; + var sqlQuery = $"select * from [{_options.Schema}].{tableName} where 1=1 {where} order by Added desc offset @Offset rows fetch next @Limit rows only"; - return UseConnection(conn => conn.Query(sqlQuery, new + return UseConnection(conn => conn.Query(_options.IsSqlServer2008 ? sqlQuery2008 : sqlQuery, new { queryDto.StatusName, queryDto.Group, @@ -159,9 +166,18 @@ select count(Id) from [{0}].Received with (nolock) where StatusName = N'Failed'; string statusName, IDictionary keyMaps) { - //SQL Server 2012+ - var sqlQuery = - $@" + var sqlQuery2008 = $@" +with aggr as ( + select replace(convert(varchar, Added, 111), '/','-') + '-' + CONVERT(varchar, DATEPART(hh, Added)) as [Key], + count(id) [Count] + from [{_options.Schema}].{tableName} + where StatusName = @statusName + group by replace(convert(varchar, Added, 111), '/','-') + '-' + CONVERT(varchar, DATEPART(hh, Added)) +) +select [Key], [Count] from aggr with (nolock) where [Key] in @keys;"; + + //SQL Server 2012+ + var sqlQuery = $@" with aggr as ( select FORMAT(Added,'yyyy-MM-dd-HH') as [Key], count(id) [Count] @@ -172,7 +188,7 @@ with aggr as ( select [Key], [Count] from aggr with (nolock) where [Key] in @keys;"; var valuesMap = connection.Query( - sqlQuery, + _options.IsSqlServer2008 ? sqlQuery2008 : sqlQuery, new { keys = keyMaps.Keys, statusName }) .ToDictionary(x => x.Key, x => x.Count); From e6af8ed5bd3d675b46fde3a0e0c75913939b474f Mon Sep 17 00:00:00 2001 From: Savorboard Date: Sat, 21 Jul 2018 18:01:13 +0800 Subject: [PATCH 117/223] refactor log extension --- src/DotNetCore.CAP/LoggerExtensions.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/DotNetCore.CAP/LoggerExtensions.cs b/src/DotNetCore.CAP/LoggerExtensions.cs index 808096c..ab80367 100644 --- a/src/DotNetCore.CAP/LoggerExtensions.cs +++ b/src/DotNetCore.CAP/LoggerExtensions.cs @@ -35,9 +35,9 @@ namespace DotNetCore.CAP logger.LogWarning($"The {retries}th retrying send a message failed. message id: {messageId} "); } - public static void MessageHasBeenSent(this ILogger logger, double seconds) + public static void MessageHasBeenSent(this ILogger logger, string name, string content) { - logger.LogDebug($"Message published. Took: {seconds} secs."); + logger.LogDebug($"Message published. name: {name}, content:{content}."); } public static void MessagePublishException(this ILogger logger, int messageId, string reason, Exception ex) From 584852b61f8583eab2ba6ba753f33550a1f869d2 Mon Sep 17 00:00:00 2001 From: keke Date: Mon, 23 Jul 2018 09:10:03 +0800 Subject: [PATCH 118/223] Feature/support mongodb (#163) * Ignore .vscode * Add MongoDBTest * New DotNetCore.CAP.MongoDB * Use MongoDB * Initialize MongoDB * GetPublishedMessageAsync * GetPublishedMessagesOfNeedRetry * Get received message * Change published state * Chang received state * Store received message * MongoDBStorageTransaction * Implement CapPublisher * Fix CAP.BuilderTest * Implement ICollectProcessor * Configure MongoDBOptions * Test MongoDBHelper * Remove useless code * GetMonitoringApi * Test MongoDbStorage * Test MongoDBStorageConnection * Test MongoDBStorageConnection * Add sample for MongoDB * Tweak CapPublisher * Wati interval * Tweak Mongo extension * Publish without mongo session * Publish rollback * Publish without session & subscribe msg * Tweak rabbitmq config * .gitignore * Implement & test HourlyFailedJobs * Rename MongoDBUtil * Rename MongoDBUtilTest * Tweak pipeline * Implement HourlyJobs * Remove redundant code * Implement Messages * Implement MessagesCount * Fix non-standard StatusName * To local time * Tweak two properties name * Tweak two methods name * Supplement xml comments * Fix wrong names --- .gitignore | 2 + CAP.sln | 21 +++ .../Controllers/ValuesController.cs | 74 ++++++++ samples/Sample.RabbitMQ.MongoDB/Program.cs | 24 +++ .../Sample.RabbitMQ.MongoDB.csproj | 21 +++ samples/Sample.RabbitMQ.MongoDB/Startup.cs | 60 ++++++ .../Sample.RabbitMQ.MongoDB/appsettings.json | 17 ++ .../CAP.MongoDBCapOptionsExtension.cs | 31 +++ .../CAP.MongoDBOptions.cs | 19 ++ .../CAP.Options.Extensions.cs | 26 +++ src/DotNetCore.CAP.MongoDB/CapPublisher.cs | 178 ++++++++++++++++++ .../DotNetCore.CAP.MongoDB.csproj | 17 ++ .../MongoDBCollectProcessor.cs | 46 +++++ .../MongoDBMonitoringApi.cs | 176 +++++++++++++++++ src/DotNetCore.CAP.MongoDB/MongoDBStorage.cs | 69 +++++++ .../MongoDBStorageConnection.cs | 118 ++++++++++++ .../MongoDBStorageTransaction.cs | 70 +++++++ src/DotNetCore.CAP.MongoDB/MongoDBUtil.cs | 63 +++++++ .../Abstractions/CapPublisherBase.cs | 16 +- src/DotNetCore.CAP/ICapPublisher.cs | 26 ++- .../Infrastructure/StatusName.cs | 12 ++ .../ConnectionUtil.cs | 7 + .../DotNetCore.CAP.MongoDB.Test.csproj | 23 +++ .../MongoDBMonitoringApiTest.cs | 83 ++++++++ .../MongoDBStorageConnectionTest.cs | 67 +++++++ .../MongoDBStorageTest.cs | 38 ++++ .../MongoDBTest.cs | 82 ++++++++ .../MongoDBUtilTest.cs | 50 +++++ test/DotNetCore.CAP.Test/CAP.BuilderTest.cs | 19 +- 29 files changed, 1446 insertions(+), 9 deletions(-) create mode 100644 samples/Sample.RabbitMQ.MongoDB/Controllers/ValuesController.cs create mode 100644 samples/Sample.RabbitMQ.MongoDB/Program.cs create mode 100644 samples/Sample.RabbitMQ.MongoDB/Sample.RabbitMQ.MongoDB.csproj create mode 100644 samples/Sample.RabbitMQ.MongoDB/Startup.cs create mode 100644 samples/Sample.RabbitMQ.MongoDB/appsettings.json create mode 100644 src/DotNetCore.CAP.MongoDB/CAP.MongoDBCapOptionsExtension.cs create mode 100644 src/DotNetCore.CAP.MongoDB/CAP.MongoDBOptions.cs create mode 100644 src/DotNetCore.CAP.MongoDB/CAP.Options.Extensions.cs create mode 100644 src/DotNetCore.CAP.MongoDB/CapPublisher.cs create mode 100644 src/DotNetCore.CAP.MongoDB/DotNetCore.CAP.MongoDB.csproj create mode 100644 src/DotNetCore.CAP.MongoDB/MongoDBCollectProcessor.cs create mode 100644 src/DotNetCore.CAP.MongoDB/MongoDBMonitoringApi.cs create mode 100644 src/DotNetCore.CAP.MongoDB/MongoDBStorage.cs create mode 100644 src/DotNetCore.CAP.MongoDB/MongoDBStorageConnection.cs create mode 100644 src/DotNetCore.CAP.MongoDB/MongoDBStorageTransaction.cs create mode 100644 src/DotNetCore.CAP.MongoDB/MongoDBUtil.cs create mode 100644 test/DotNetCore.CAP.MongoDB.Test/ConnectionUtil.cs create mode 100644 test/DotNetCore.CAP.MongoDB.Test/DotNetCore.CAP.MongoDB.Test.csproj create mode 100644 test/DotNetCore.CAP.MongoDB.Test/MongoDBMonitoringApiTest.cs create mode 100644 test/DotNetCore.CAP.MongoDB.Test/MongoDBStorageConnectionTest.cs create mode 100644 test/DotNetCore.CAP.MongoDB.Test/MongoDBStorageTest.cs create mode 100644 test/DotNetCore.CAP.MongoDB.Test/MongoDBTest.cs create mode 100644 test/DotNetCore.CAP.MongoDB.Test/MongoDBUtilTest.cs diff --git a/.gitignore b/.gitignore index af4f8ef..27ab035 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,5 @@ Properties /src/DotNetCore.CAP/packages.config /src/DotNetCore.CAP/DotNetCore.CAP.Net47.csproj /NuGet.config +.vscode/* +samples/Sample.RabbitMQ.MongoDB/appsettings.Development.json diff --git a/CAP.sln b/CAP.sln index 225537c..bf7efad 100644 --- a/CAP.sln +++ b/CAP.sln @@ -60,6 +60,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNetCore.CAP.PostgreSql.T EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sample.Kafka.MySql", "samples\Sample.Kafka.MySql\Sample.Kafka.MySql.csproj", "{9CB51105-A85B-42A4-AFDE-A4FC34D9EA91}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotNetCore.CAP.MongoDB.Test", "test\DotNetCore.CAP.MongoDB.Test\DotNetCore.CAP.MongoDB.Test.csproj", "{C143FCDF-E7F3-46F8-987E-A1BA38C1639D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotNetCore.CAP.MongoDB", "src\DotNetCore.CAP.MongoDB\DotNetCore.CAP.MongoDB.csproj", "{77C0AC02-C44B-49D5-B969-7D5305FC20A5}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sample.RabbitMQ.MongoDB", "samples\Sample.RabbitMQ.MongoDB\Sample.RabbitMQ.MongoDB.csproj", "{4473DE19-E8D2-4B57-80A8-C8AAA2BFA20F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -113,6 +119,18 @@ Global {9CB51105-A85B-42A4-AFDE-A4FC34D9EA91}.Debug|Any CPU.Build.0 = Debug|Any CPU {9CB51105-A85B-42A4-AFDE-A4FC34D9EA91}.Release|Any CPU.ActiveCfg = Release|Any CPU {9CB51105-A85B-42A4-AFDE-A4FC34D9EA91}.Release|Any CPU.Build.0 = Release|Any CPU + {C143FCDF-E7F3-46F8-987E-A1BA38C1639D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C143FCDF-E7F3-46F8-987E-A1BA38C1639D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C143FCDF-E7F3-46F8-987E-A1BA38C1639D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C143FCDF-E7F3-46F8-987E-A1BA38C1639D}.Release|Any CPU.Build.0 = Release|Any CPU + {77C0AC02-C44B-49D5-B969-7D5305FC20A5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77C0AC02-C44B-49D5-B969-7D5305FC20A5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77C0AC02-C44B-49D5-B969-7D5305FC20A5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77C0AC02-C44B-49D5-B969-7D5305FC20A5}.Release|Any CPU.Build.0 = Release|Any CPU + {4473DE19-E8D2-4B57-80A8-C8AAA2BFA20F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4473DE19-E8D2-4B57-80A8-C8AAA2BFA20F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4473DE19-E8D2-4B57-80A8-C8AAA2BFA20F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4473DE19-E8D2-4B57-80A8-C8AAA2BFA20F}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -130,6 +148,9 @@ Global {82C403AB-ED68-4084-9A1D-11334F9F08F9} = {9B2AE124-6636-4DE9-83A3-70360DABD0C4} {7CA3625D-1817-4695-881D-7E79A1E1DED2} = {C09CDAB0-6DD4-46E9-B7F3-3EF2A4741EA0} {9CB51105-A85B-42A4-AFDE-A4FC34D9EA91} = {3A6B6931-A123-477A-9469-8B468B5385AF} + {C143FCDF-E7F3-46F8-987E-A1BA38C1639D} = {C09CDAB0-6DD4-46E9-B7F3-3EF2A4741EA0} + {77C0AC02-C44B-49D5-B969-7D5305FC20A5} = {9B2AE124-6636-4DE9-83A3-70360DABD0C4} + {4473DE19-E8D2-4B57-80A8-C8AAA2BFA20F} = {3A6B6931-A123-477A-9469-8B468B5385AF} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {2E70565D-94CF-40B4-BFE1-AC18D5F736AB} diff --git a/samples/Sample.RabbitMQ.MongoDB/Controllers/ValuesController.cs b/samples/Sample.RabbitMQ.MongoDB/Controllers/ValuesController.cs new file mode 100644 index 0000000..374100d --- /dev/null +++ b/samples/Sample.RabbitMQ.MongoDB/Controllers/ValuesController.cs @@ -0,0 +1,74 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using DotNetCore.CAP; +using Microsoft.AspNetCore.Mvc; +using MongoDB.Bson; +using MongoDB.Driver; + +namespace Sample.RabbitMQ.MongoDB.Controllers +{ + [Route("api/[controller]")] + [ApiController] + public class ValuesController : ControllerBase + { + private readonly IMongoClient _client; + private readonly ICapPublisher _capPublisher; + + public ValuesController(IMongoClient client, ICapPublisher capPublisher) + { + _client = client; + _capPublisher = capPublisher; + } + + [Route("~/publish")] + public IActionResult PublishWithSession() + { + using (var session = _client.StartSession()) + { + session.StartTransaction(); + var collection = _client.GetDatabase("TEST").GetCollection("test"); + collection.InsertOne(session, new BsonDocument { { "hello", "world" } }); + + _capPublisher.PublishWithMongo("sample.rabbitmq.mongodb", DateTime.Now, session); + + session.CommitTransaction(); + } + return Ok(); + } + + [Route("~/publish_rollback")] + public IActionResult PublishRollback() + { + using (var session = _client.StartSession()) + { + try + { + session.StartTransaction(); + _capPublisher.PublishWithMongo("sample.rabbitmq.mongodb", DateTime.Now, session); + throw new Exception("Foo"); + } + catch (System.Exception ex) + { + session.AbortTransaction(); + return StatusCode(500, ex.Message); + } + } + } + + [Route("~/publish_without_session")] + public IActionResult PublishWithoutSession() + { + _capPublisher.PublishWithMongo("sample.rabbitmq.mongodb", DateTime.Now); + return Ok(); + } + + [NonAction] + [CapSubscribe("sample.rabbitmq.mongodb")] + public void ReceiveMessage(DateTime time) + { + Console.WriteLine("[sample.rabbitmq.mongodb] message received: " + DateTime.Now + ",sent time: " + time); + } + } +} diff --git a/samples/Sample.RabbitMQ.MongoDB/Program.cs b/samples/Sample.RabbitMQ.MongoDB/Program.cs new file mode 100644 index 0000000..7f32066 --- /dev/null +++ b/samples/Sample.RabbitMQ.MongoDB/Program.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; + +namespace Sample.RabbitMQ.MongoDB +{ + public class Program + { + public static void Main(string[] args) + { + CreateWebHostBuilder(args).Build().Run(); + } + + public static IWebHostBuilder CreateWebHostBuilder(string[] args) => + WebHost.CreateDefaultBuilder(args) + .UseStartup(); + } +} diff --git a/samples/Sample.RabbitMQ.MongoDB/Sample.RabbitMQ.MongoDB.csproj b/samples/Sample.RabbitMQ.MongoDB/Sample.RabbitMQ.MongoDB.csproj new file mode 100644 index 0000000..fdaa849 --- /dev/null +++ b/samples/Sample.RabbitMQ.MongoDB/Sample.RabbitMQ.MongoDB.csproj @@ -0,0 +1,21 @@ + + + + netcoreapp2.1 + + + + + + + + + + + + + + + + + diff --git a/samples/Sample.RabbitMQ.MongoDB/Startup.cs b/samples/Sample.RabbitMQ.MongoDB/Startup.cs new file mode 100644 index 0000000..4f4bd69 --- /dev/null +++ b/samples/Sample.RabbitMQ.MongoDB/Startup.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using DotNetCore.CAP; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.HttpsPolicy; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using MongoDB.Driver; + +namespace Sample.RabbitMQ.MongoDB +{ + public class Startup + { + public Startup(IConfiguration configuration) + { + Configuration = configuration; + } + + public IConfiguration Configuration { get; } + + public void ConfigureServices(IServiceCollection services) + { + services.AddSingleton(new MongoClient(Configuration.GetConnectionString("MongoDB"))); + services.AddCap(x => + { + x.UseMongoDB(); + + var mq = new RabbitMQOptions(); + Configuration.GetSection("RabbitMQ").Bind(mq); + x.UseRabbitMQ(cfg => + { + cfg.HostName = mq.HostName; + cfg.Port = mq.Port; + cfg.UserName = mq.UserName; + cfg.Password = mq.Password; + }); + + x.UseDashboard(); + }); + services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); + } + + public void Configure(IApplicationBuilder app, IHostingEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + app.UseMvc(); + + app.UseCap(); + } + } +} diff --git a/samples/Sample.RabbitMQ.MongoDB/appsettings.json b/samples/Sample.RabbitMQ.MongoDB/appsettings.json new file mode 100644 index 0000000..5707bfb --- /dev/null +++ b/samples/Sample.RabbitMQ.MongoDB/appsettings.json @@ -0,0 +1,17 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Warning" + } + }, + "AllowedHosts": "*", + "ConnectionStrings": { + "MongoDB": "mongodb://localhost:27017,localhost:27018,localhost:27019/?replicaSet=rs0" + }, + "RabbitMQ": { + "HostName": "localhost", + "Port": 5672, + "UserName": "", + "Password": "" + } +} \ No newline at end of file diff --git a/src/DotNetCore.CAP.MongoDB/CAP.MongoDBCapOptionsExtension.cs b/src/DotNetCore.CAP.MongoDB/CAP.MongoDBCapOptionsExtension.cs new file mode 100644 index 0000000..3bbb891 --- /dev/null +++ b/src/DotNetCore.CAP.MongoDB/CAP.MongoDBCapOptionsExtension.cs @@ -0,0 +1,31 @@ +using System; +using DotNetCore.CAP; +using DotNetCore.CAP.Processor; +using Microsoft.Extensions.DependencyInjection; + +namespace DotNetCore.CAP.MongoDB +{ + public class MongoDBCapOptionsExtension : ICapOptionsExtension + { + private Action _configure; + + public MongoDBCapOptionsExtension(Action configure) + { + _configure = configure; + } + + public void AddServices(IServiceCollection services) + { + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddScoped(); + services.AddScoped(); + services.AddTransient(); + + var options = new MongoDBOptions(); + _configure?.Invoke(options); + services.AddSingleton(options); + } + } +} \ No newline at end of file diff --git a/src/DotNetCore.CAP.MongoDB/CAP.MongoDBOptions.cs b/src/DotNetCore.CAP.MongoDB/CAP.MongoDBOptions.cs new file mode 100644 index 0000000..4e6ef3a --- /dev/null +++ b/src/DotNetCore.CAP.MongoDB/CAP.MongoDBOptions.cs @@ -0,0 +1,19 @@ +using System; + +namespace DotNetCore.CAP.MongoDB +{ + public class MongoDBOptions + { + public const string DefaultDatabase = "Cap"; + + /// + /// Gets or sets the database to use when creating database objects. + /// Default is . + /// + public string Database { get; set; } = DefaultDatabase; + + public string ReceivedCollection { get; } = "Received"; + + public string PublishedCollection { get; } = "Published"; + } +} \ No newline at end of file diff --git a/src/DotNetCore.CAP.MongoDB/CAP.Options.Extensions.cs b/src/DotNetCore.CAP.MongoDB/CAP.Options.Extensions.cs new file mode 100644 index 0000000..af69c02 --- /dev/null +++ b/src/DotNetCore.CAP.MongoDB/CAP.Options.Extensions.cs @@ -0,0 +1,26 @@ +using System; +using DotNetCore.CAP; +using DotNetCore.CAP.MongoDB; + +namespace Microsoft.Extensions.DependencyInjection +{ + public static class CapOptionsExtensions + { + public static CapOptions UseMongoDB(this CapOptions options) + { + return options.UseMongoDB(x => { }); + } + + public static CapOptions UseMongoDB(this CapOptions options, Action configure) + { + if (configure == null) + { + throw new ArgumentNullException(nameof(configure)); + } + + options.RegisterExtension(new MongoDBCapOptionsExtension(configure)); + + return options; + } + } +} \ No newline at end of file diff --git a/src/DotNetCore.CAP.MongoDB/CapPublisher.cs b/src/DotNetCore.CAP.MongoDB/CapPublisher.cs new file mode 100644 index 0000000..1cda633 --- /dev/null +++ b/src/DotNetCore.CAP.MongoDB/CapPublisher.cs @@ -0,0 +1,178 @@ +using System; +using System.Data; +using System.Threading.Tasks; +using DotNetCore.CAP.Abstractions; +using DotNetCore.CAP.Diagnostics; +using DotNetCore.CAP.Infrastructure; +using DotNetCore.CAP.Models; +using Microsoft.Extensions.Logging; +using MongoDB.Driver; + +namespace DotNetCore.CAP.MongoDB +{ + public class CapPublisher : CapPublisherBase, ICallbackPublisher + { + private readonly IMongoClient _client; + private readonly MongoDBOptions _options; + private readonly IMongoDatabase _database; + private bool _isInTransaction = true; + + public CapPublisher(ILogger logger, IDispatcher dispatcher, + IMongoClient client, MongoDBOptions options, IServiceProvider provider) + : base(logger, dispatcher) + { + _client = client; + _options = options; + _database = client.GetDatabase(_options.Database); + ServiceProvider = provider; + } + + public async Task PublishCallbackAsync(CapPublishedMessage message) + { + var collection = _database.GetCollection(_options.PublishedCollection); + message.Id = await new MongoDBUtil().GetNextSequenceValueAsync(_database, _options.PublishedCollection); + collection.InsertOne(message); + Enqueue(message); + } + + protected override int Execute(IDbConnection dbConnection, IDbTransaction dbTransaction, CapPublishedMessage message) + { + throw new System.NotImplementedException("Not work for MongoDB"); + } + + protected override Task ExecuteAsync(IDbConnection dbConnection, IDbTransaction dbTransaction, CapPublishedMessage message) + { + throw new System.NotImplementedException("Not work for MongoDB"); + } + + protected override void PrepareConnectionForEF() + { + throw new System.NotImplementedException("Not work for MongoDB"); + } + + public override void PublishWithMongo(string name, T contentObj, object mongoSession = null, string callbackName = null) + { + var session = mongoSession as IClientSessionHandle; + if (session == null) + { + _isInTransaction = false; + } + + PublishWithSession(name, contentObj, session, callbackName); + } + + public override async Task PublishWithMongoAsync(string name, T contentObj, object mongoSession = null, string callbackName = null) + { + var session = mongoSession as IClientSessionHandle; + if (session == null) + { + _isInTransaction = false; + } + + await PublishWithSessionAsync(name, contentObj, session, callbackName); + } + + private void PublishWithSession(string name, T contentObj, IClientSessionHandle session, string callbackName) + { + Guid operationId = default(Guid); + + var content = Serialize(contentObj, callbackName); + + var message = new CapPublishedMessage + { + Name = name, + Content = content, + StatusName = StatusName.Scheduled + }; + + try + { + operationId = s_diagnosticListener.WritePublishMessageStoreBefore(message); + var id = Execute(session, message); + + if (!_isInTransaction && id > 0) + { + _logger.LogInformation($"message [{message}] has been persisted in the database."); + s_diagnosticListener.WritePublishMessageStoreAfter(operationId, message); + message.Id = id; + Enqueue(message); + } + } + catch (System.Exception e) + { + _logger.LogError(e, "An exception was occurred when publish message. message:" + name); + s_diagnosticListener.WritePublishMessageStoreError(operationId, message, e); + throw; + } + } + + private int Execute(IClientSessionHandle session, CapPublishedMessage message) + { + message.Id = new MongoDBUtil().GetNextSequenceValue(_database, _options.PublishedCollection, session); + + var collection = _database.GetCollection(_options.PublishedCollection); + if (_isInTransaction) + { + collection.InsertOne(session, message); + } + else + { + collection.InsertOne(message); + } + return message.Id; + } + + + private async Task PublishWithSessionAsync(string name, T contentObj, IClientSessionHandle session, string callbackName) + { + Guid operationId = default(Guid); + var content = Serialize(contentObj, callbackName); + + var message = new CapPublishedMessage + { + Name = name, + Content = content, + StatusName = StatusName.Scheduled + }; + + try + { + operationId = s_diagnosticListener.WritePublishMessageStoreBefore(message); + + var id = await ExecuteAsync(session, message); + + if (!_isInTransaction && id > 0) + { + _logger.LogInformation($"message [{message}] has been persisted in the database."); + s_diagnosticListener.WritePublishMessageStoreAfter(operationId, message); + + message.Id = id; + + Enqueue(message); + } + } + catch (System.Exception e) + { + _logger.LogError(e, "An exception was occurred when publish message async. exception message:" + name); + s_diagnosticListener.WritePublishMessageStoreError(operationId, message, e); + Console.WriteLine(e); + throw; + } + } + + private async Task ExecuteAsync(IClientSessionHandle session, CapPublishedMessage message) + { + message.Id = await new MongoDBUtil().GetNextSequenceValueAsync(_database, _options.PublishedCollection, session); + var collection = _database.GetCollection(_options.PublishedCollection); + if (_isInTransaction) + { + await collection.InsertOneAsync(session, message); + } + else + { + await collection.InsertOneAsync(message); + } + return message.Id; + } + } +} \ No newline at end of file diff --git a/src/DotNetCore.CAP.MongoDB/DotNetCore.CAP.MongoDB.csproj b/src/DotNetCore.CAP.MongoDB/DotNetCore.CAP.MongoDB.csproj new file mode 100644 index 0000000..ef1b0c1 --- /dev/null +++ b/src/DotNetCore.CAP.MongoDB/DotNetCore.CAP.MongoDB.csproj @@ -0,0 +1,17 @@ + + + + + + + + netstandard2.0 + + + + + + + + + diff --git a/src/DotNetCore.CAP.MongoDB/MongoDBCollectProcessor.cs b/src/DotNetCore.CAP.MongoDB/MongoDBCollectProcessor.cs new file mode 100644 index 0000000..a0519bc --- /dev/null +++ b/src/DotNetCore.CAP.MongoDB/MongoDBCollectProcessor.cs @@ -0,0 +1,46 @@ +using System; +using System.Threading.Tasks; +using DotNetCore.CAP.Models; +using DotNetCore.CAP.Processor; +using Microsoft.Extensions.Logging; +using MongoDB.Driver; + +namespace DotNetCore.CAP.MongoDB +{ + public class MongoDBCollectProcessor : ICollectProcessor + { + private readonly IMongoClient _client; + private readonly MongoDBOptions _options; + private readonly ILogger _logger; + private readonly IMongoDatabase _database; + private readonly TimeSpan _waitingInterval = TimeSpan.FromMinutes(5); + + public MongoDBCollectProcessor(IMongoClient client, MongoDBOptions options, + ILogger logger) + { + _client = client; + _options = options; + _logger = logger; + _database = client.GetDatabase(_options.Database); + } + + public async Task ProcessAsync(ProcessingContext context) + { + _logger.LogDebug($"Collecting expired data from collection [{_options.Database}].[{_options.PublishedCollection}]."); + + var publishedCollection = _database.GetCollection(_options.PublishedCollection); + var receivedCollection = _database.GetCollection(_options.ReceivedCollection); + + await publishedCollection.BulkWriteAsync(new[] + { + new DeleteManyModel(Builders.Filter.Lt(x => x.ExpiresAt, DateTime.Now)) + }); + await receivedCollection.BulkWriteAsync(new[] + { + new DeleteManyModel(Builders.Filter.Lt(x => x.ExpiresAt, DateTime.Now)) + }); + + await context.WaitAsync(_waitingInterval); + } + } +} \ No newline at end of file diff --git a/src/DotNetCore.CAP.MongoDB/MongoDBMonitoringApi.cs b/src/DotNetCore.CAP.MongoDB/MongoDBMonitoringApi.cs new file mode 100644 index 0000000..7dfcff4 --- /dev/null +++ b/src/DotNetCore.CAP.MongoDB/MongoDBMonitoringApi.cs @@ -0,0 +1,176 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using DotNetCore.CAP.Dashboard; +using DotNetCore.CAP.Dashboard.Monitoring; +using DotNetCore.CAP.Infrastructure; +using DotNetCore.CAP.Models; +using MongoDB.Bson; +using MongoDB.Driver; + +namespace DotNetCore.CAP.MongoDB +{ + public class MongoDBMonitoringApi : IMonitoringApi + { + private IMongoClient _client; + private MongoDBOptions _options; + private IMongoDatabase _database; + + public MongoDBMonitoringApi(IMongoClient client, MongoDBOptions options) + { + _client = client ?? throw new ArgumentNullException(nameof(client)); + _options = options ?? throw new ArgumentNullException(nameof(options)); + + _database = _client.GetDatabase(_options.Database); + } + + public StatisticsDto GetStatistics() + { + var publishedCollection = _database.GetCollection(_options.PublishedCollection); + var receivedCollection = _database.GetCollection(_options.ReceivedCollection); + + var statistics = new StatisticsDto(); + + { + if (int.TryParse(publishedCollection.CountDocuments(x => x.StatusName == StatusName.Succeeded).ToString(), out var count)) + statistics.PublishedSucceeded = count; + } + { + if (int.TryParse(publishedCollection.CountDocuments(x => x.StatusName == StatusName.Failed).ToString(), out var count)) + statistics.PublishedFailed = count; + } + { + if (int.TryParse(receivedCollection.CountDocuments(x => x.StatusName == StatusName.Succeeded).ToString(), out var count)) + statistics.ReceivedSucceeded = count; + } + { + if (int.TryParse(receivedCollection.CountDocuments(x => x.StatusName == StatusName.Failed).ToString(), out var count)) + statistics.ReceivedFailed = count; + } + + return statistics; + } + + public IDictionary HourlyFailedJobs(MessageType type) + { + return GetHourlyTimelineStats(type, StatusName.Failed); + } + + public IDictionary HourlySucceededJobs(MessageType type) + { + return GetHourlyTimelineStats(type, StatusName.Succeeded); + } + + public IList Messages(MessageQueryDto queryDto) + { + queryDto.StatusName = StatusName.Standardized(queryDto.StatusName); + + var name = queryDto.MessageType == MessageType.Publish ? _options.PublishedCollection : _options.ReceivedCollection; + var collection = _database.GetCollection(name); + + var builder = Builders.Filter; + var filter = builder.Empty; + if (!string.IsNullOrEmpty(queryDto.StatusName)) + { + filter = filter & builder.Eq(x => x.StatusName, queryDto.StatusName); + } + if (!string.IsNullOrEmpty(queryDto.Name)) + { + filter = filter & builder.Eq(x => x.Name, queryDto.Name); + } + if (!string.IsNullOrEmpty(queryDto.Group)) + { + filter = filter & builder.Eq(x => x.Group, queryDto.Group); + } + if (!string.IsNullOrEmpty(queryDto.Content)) + { + filter = filter & builder.Regex(x => x.Content, ".*" + queryDto.Content + ".*"); + } + + var result = + collection.Find(filter) + .SortByDescending(x => x.Added) + .Skip(queryDto.PageSize * queryDto.CurrentPage) + .Limit(queryDto.PageSize) + .ToList(); + + return result; + } + + public int PublishedFailedCount() + { + return GetNumberOfMessage(_options.PublishedCollection, StatusName.Failed); + } + + public int PublishedSucceededCount() + { + return GetNumberOfMessage(_options.PublishedCollection, StatusName.Succeeded); + } + + public int ReceivedFailedCount() + { + return GetNumberOfMessage(_options.ReceivedCollection, StatusName.Failed); + } + + public int ReceivedSucceededCount() + { + return GetNumberOfMessage(_options.ReceivedCollection, StatusName.Succeeded); + } + + private int GetNumberOfMessage(string collectionName, string statusName) + { + var collection = _database.GetCollection(collectionName); + var count = collection.CountDocuments(new BsonDocument { { "StatusName", statusName } }); + return int.Parse(count.ToString()); + } + + private IDictionary GetHourlyTimelineStats(MessageType type, string statusName) + { + var collectionName = type == MessageType.Publish ? _options.PublishedCollection : _options.ReceivedCollection; + var endDate = DateTime.UtcNow; + + var groupby = new BsonDocument { + { "$group", new BsonDocument{ + { "_id", new BsonDocument { + { "Key", new BsonDocument { + { "$dateToString", new BsonDocument { + { "format", "%Y-%m-%d %H:00:00"}, + { "date", "$Added"} + }} + }} + } + }, + { "Count", new BsonDocument{ + { "$sum", 1} + }} + }} + }; + + var match = new BsonDocument { { "$match", new BsonDocument { + { "Added", new BsonDocument { { "$gt", endDate.AddHours(-24) } } }, + { "StatusName", new BsonDocument { { "$eq", statusName} } + } } } }; + var pipeline = new BsonDocument[] { match, groupby }; + + var collection = _database.GetCollection(collectionName); + var result = collection.Aggregate(pipeline: pipeline).ToList(); + + var dic = new Dictionary(); + for (var i = 0; i < 24; i++) + { + dic.Add(DateTime.Parse(endDate.ToLocalTime().ToString("yyyy-MM-dd HH:00:00")), 0); + endDate = endDate.AddHours(-1); + } + result.ForEach(d => + { + var key = d["_id"].AsBsonDocument["Key"].AsString; + if (DateTime.TryParse(key, out var dateTime)) + { + dic[dateTime.ToLocalTime()] = d["Count"].AsInt32; + } + }); + + return dic; + } + } +} \ No newline at end of file diff --git a/src/DotNetCore.CAP.MongoDB/MongoDBStorage.cs b/src/DotNetCore.CAP.MongoDB/MongoDBStorage.cs new file mode 100644 index 0000000..27084b8 --- /dev/null +++ b/src/DotNetCore.CAP.MongoDB/MongoDBStorage.cs @@ -0,0 +1,69 @@ +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using DotNetCore.CAP.Dashboard; +using Microsoft.Extensions.Logging; +using MongoDB.Bson; +using MongoDB.Driver; + +namespace DotNetCore.CAP.MongoDB +{ + public class MongoDBStorage : IStorage + { + private readonly CapOptions _capOptions; + private readonly MongoDBOptions _options; + private readonly IMongoClient _client; + private readonly ILogger _logger; + + public MongoDBStorage(CapOptions capOptions, + MongoDBOptions options, + IMongoClient client, + ILogger logger) + { + _capOptions = capOptions; + _options = options; + _client = client; + _logger = logger; + } + + public IStorageConnection GetConnection() + { + return new MongoDBStorageConnection(_capOptions, _options, _client); + } + + public IMonitoringApi GetMonitoringApi() + { + return new MongoDBMonitoringApi(_client, _options); + } + + public async Task InitializeAsync(CancellationToken cancellationToken) + { + if (cancellationToken.IsCancellationRequested) + { + return; + } + + var database = _client.GetDatabase(_options.Database); + var names = (await database.ListCollectionNamesAsync())?.ToList(); + + if (!names.Any(n => n == _options.ReceivedCollection)) + { + await database.CreateCollectionAsync(_options.ReceivedCollection); + } + if (!names.Any(n => n == _options.PublishedCollection)) + { + await database.CreateCollectionAsync(_options.PublishedCollection); + } + if (!names.Any(n => n == "Counter")) + { + await database.CreateCollectionAsync("Counter"); + var collection = database.GetCollection("Counter"); + await collection.InsertManyAsync(new BsonDocument[] + { + new BsonDocument{{"_id", _options.PublishedCollection}, {"sequence_value", 0}}, + new BsonDocument{{"_id", _options.ReceivedCollection}, {"sequence_value", 0}} + }); + } + } + } +} \ No newline at end of file diff --git a/src/DotNetCore.CAP.MongoDB/MongoDBStorageConnection.cs b/src/DotNetCore.CAP.MongoDB/MongoDBStorageConnection.cs new file mode 100644 index 0000000..11e8d8e --- /dev/null +++ b/src/DotNetCore.CAP.MongoDB/MongoDBStorageConnection.cs @@ -0,0 +1,118 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using DotNetCore.CAP.Infrastructure; +using DotNetCore.CAP.Models; +using MongoDB.Bson; +using MongoDB.Driver; + +namespace DotNetCore.CAP.MongoDB +{ + public class MongoDBStorageConnection : IStorageConnection + { + private CapOptions _capOptions; + private MongoDBOptions _options; + private readonly IMongoClient _client; + private readonly IMongoDatabase _database; + + public MongoDBStorageConnection(CapOptions capOptions, MongoDBOptions options, IMongoClient client) + { + _capOptions = capOptions; + _options = options; + _client = client; + _database = _client.GetDatabase(_options.Database); + } + + public bool ChangePublishedState(int messageId, string state) + { + var collection = _database.GetCollection(_options.PublishedCollection); + + var updateDef = Builders + .Update.Inc(x => x.Retries, 1) + .Set(x => x.ExpiresAt, null) + .Set(x => x.StatusName, state); + + var result = + collection.UpdateOne(x => x.Id == messageId, updateDef); + + return result.ModifiedCount > 0; + } + + public bool ChangeReceivedState(int messageId, string state) + { + var collection = _database.GetCollection(_options.ReceivedCollection); + + var updateDef = Builders + .Update.Inc(x => x.Retries, 1) + .Set(x => x.ExpiresAt, null) + .Set(x => x.StatusName, state); + + var result = + collection.UpdateOne(x => x.Id == messageId, updateDef); + + return result.ModifiedCount > 0; + } + + public IStorageTransaction CreateTransaction() + { + return new MongoDBStorageTransaction(_client, _options); + } + + public void Dispose() + { + } + + public async Task GetPublishedMessageAsync(int id) + { + var collection = _database.GetCollection(_options.PublishedCollection); + return await collection.Find(x => x.Id == id).FirstOrDefaultAsync(); + } + + public async Task> GetPublishedMessagesOfNeedRetry() + { + var fourMinsAgo = DateTime.Now.AddMinutes(-4); + var collection = _database.GetCollection(_options.PublishedCollection); + return await + collection.Find(x => + x.Retries < _capOptions.FailedRetryCount + && x.Added < fourMinsAgo + && (x.StatusName == StatusName.Failed || x.StatusName == StatusName.Scheduled)) + .Limit(200) + .ToListAsync(); + } + + public async Task GetReceivedMessageAsync(int id) + { + var collection = _database.GetCollection(_options.ReceivedCollection); + return await collection.Find(x => x.Id == id).FirstOrDefaultAsync(); + } + + public async Task> GetReceivedMessagesOfNeedRetry() + { + var fourMinsAgo = DateTime.Now.AddMinutes(-4); + var collection = _database.GetCollection(_options.ReceivedCollection); + + return await + collection.Find(x => + x.Retries < _capOptions.FailedRetryCount + && x.Added < fourMinsAgo + && (x.StatusName == StatusName.Failed || x.StatusName == StatusName.Scheduled) + ).Limit(200).ToListAsync(); + } + + public async Task StoreReceivedMessageAsync(CapReceivedMessage message) + { + if (message == null) + { + throw new ArgumentNullException(nameof(message)); + } + var collection = _database.GetCollection(_options.ReceivedCollection); + + message.Id = await new MongoDBUtil().GetNextSequenceValueAsync(_database, _options.ReceivedCollection); + + collection.InsertOne(message); + + return message.Id; + } + } +} \ No newline at end of file diff --git a/src/DotNetCore.CAP.MongoDB/MongoDBStorageTransaction.cs b/src/DotNetCore.CAP.MongoDB/MongoDBStorageTransaction.cs new file mode 100644 index 0000000..5ea0a56 --- /dev/null +++ b/src/DotNetCore.CAP.MongoDB/MongoDBStorageTransaction.cs @@ -0,0 +1,70 @@ +using System.Threading.Tasks; +using DotNetCore.CAP.Models; +using MongoDB.Driver; +using System; + +namespace DotNetCore.CAP.MongoDB +{ + internal class MongoDBStorageTransaction : IStorageTransaction + { + private IMongoClient _client; + private readonly MongoDBOptions _options; + private readonly IMongoDatabase _database; + private readonly IClientSessionHandle _session; + + public MongoDBStorageTransaction(IMongoClient client, MongoDBOptions options) + { + _client = client; + _options = options; + _database = client.GetDatabase(options.Database); + _session = _client.StartSession(); + _session.StartTransaction(); + } + + public async Task CommitAsync() + { + await _session.CommitTransactionAsync(); + } + + public void Dispose() + { + _session.Dispose(); + } + + public void UpdateMessage(CapPublishedMessage message) + { + if (message == null) + { + throw new ArgumentNullException(nameof(message)); + } + + var collection = _database.GetCollection(_options.PublishedCollection); + + var updateDef = Builders.Update + .Set(x => x.Retries, message.Retries) + .Set(x => x.Content, message.Content) + .Set(x => x.ExpiresAt, message.ExpiresAt) + .Set(x => x.StatusName, message.StatusName); + + collection.FindOneAndUpdate(_session, x => x.Id == message.Id, updateDef); + } + + public void UpdateMessage(CapReceivedMessage message) + { + if (message == null) + { + throw new ArgumentNullException(nameof(message)); + } + + var collection = _database.GetCollection(_options.ReceivedCollection); + + var updateDef = Builders.Update + .Set(x => x.Retries, message.Retries) + .Set(x => x.Content, message.Content) + .Set(x => x.ExpiresAt, message.ExpiresAt) + .Set(x => x.StatusName, message.StatusName); + + collection.FindOneAndUpdate(_session, x => x.Id == message.Id, updateDef); + } + } +} \ No newline at end of file diff --git a/src/DotNetCore.CAP.MongoDB/MongoDBUtil.cs b/src/DotNetCore.CAP.MongoDB/MongoDBUtil.cs new file mode 100644 index 0000000..8d862ad --- /dev/null +++ b/src/DotNetCore.CAP.MongoDB/MongoDBUtil.cs @@ -0,0 +1,63 @@ +using System; +using System.Threading.Tasks; +using MongoDB.Bson; +using MongoDB.Driver; + +namespace DotNetCore.CAP.MongoDB +{ + public class MongoDBUtil + { + FindOneAndUpdateOptions _options = new FindOneAndUpdateOptions() + { + ReturnDocument = ReturnDocument.After + }; + public async Task GetNextSequenceValueAsync(IMongoDatabase database, string collectionName, IClientSessionHandle session = null) + { + //https://www.tutorialspoint.com/mongodb/mongodb_autoincrement_sequence.htm + var collection = database.GetCollection("Counter"); + + var updateDef = Builders.Update.Inc("sequence_value", 1); + var filter = new BsonDocument { { "_id", collectionName } }; + + BsonDocument result; + if (session == null) + { + result = await collection.FindOneAndUpdateAsync(filter, updateDef, _options); + } + else + { + result = await collection.FindOneAndUpdateAsync(session, filter, updateDef, _options); + } + + if (result.TryGetValue("sequence_value", out var value)) + { + return value.ToInt32(); + } + throw new Exception("Unable to get next sequence value."); + } + + public int GetNextSequenceValue(IMongoDatabase database, string collectionName, IClientSessionHandle session = null) + { + var collection = database.GetCollection("Counter"); + + var filter = new BsonDocument { { "_id", collectionName } }; + var updateDef = Builders.Update.Inc("sequence_value", 1); + + BsonDocument result; + if (session == null) + { + result = collection.FindOneAndUpdate(filter, updateDef, _options); + } + else + { + result = collection.FindOneAndUpdate(session, filter, updateDef, _options); + } + + if (result.TryGetValue("sequence_value", out var value)) + { + return value.ToInt32(); + } + throw new Exception("Unable to get next sequence value."); + } + } +} \ No newline at end of file diff --git a/src/DotNetCore.CAP/Abstractions/CapPublisherBase.cs b/src/DotNetCore.CAP/Abstractions/CapPublisherBase.cs index 77a6c44..bc53ca8 100644 --- a/src/DotNetCore.CAP/Abstractions/CapPublisherBase.cs +++ b/src/DotNetCore.CAP/Abstractions/CapPublisherBase.cs @@ -15,11 +15,11 @@ namespace DotNetCore.CAP.Abstractions public abstract class CapPublisherBase : ICapPublisher, IDisposable { private readonly IDispatcher _dispatcher; - private readonly ILogger _logger; + protected readonly ILogger _logger; // diagnostics listener // ReSharper disable once InconsistentNaming - private static readonly DiagnosticListener s_diagnosticListener = + protected static readonly DiagnosticListener s_diagnosticListener = new DiagnosticListener(CapDiagnosticListenerExtensions.DiagnosticListenerName); protected CapPublisherBase(ILogger logger, IDispatcher dispatcher) @@ -67,6 +67,16 @@ namespace DotNetCore.CAP.Abstractions return PublishWithTransAsync(name, contentObj, callbackName); } + public virtual void PublishWithMongo(string name, T contentObj, object mongoSession = null, string callbackName = null) + { + throw new NotImplementedException("Work for MongoDB only."); + } + + public virtual Task PublishWithMongoAsync(string name, T contentObj, object mongoSession = null, string callbackName = null) + { + throw new NotImplementedException("Work for MongoDB only."); + } + protected void Enqueue(CapPublishedMessage message) { _dispatcher.EnqueueToPublish(message); @@ -205,7 +215,7 @@ namespace DotNetCore.CAP.Abstractions try { Console.WriteLine("================22222222222222====================="); - operationId = s_diagnosticListener.WritePublishMessageStoreBefore(message); + operationId = s_diagnosticListener.WritePublishMessageStoreBefore(message); var id = Execute(DbConnection, DbTransaction, message); Console.WriteLine("================777777777777777777777====================="); diff --git a/src/DotNetCore.CAP/ICapPublisher.cs b/src/DotNetCore.CAP/ICapPublisher.cs index 0688c02..4386926 100644 --- a/src/DotNetCore.CAP/ICapPublisher.cs +++ b/src/DotNetCore.CAP/ICapPublisher.cs @@ -12,7 +12,7 @@ namespace DotNetCore.CAP public interface ICapPublisher { /// - /// (EntityFramework) Asynchronous publish a object message. + /// (EntityFramework) Asynchronous publish an object message. /// /// If you are using the EntityFramework, you need to configure the DbContextType first. /// otherwise you need to use overloaded method with IDbTransaction. @@ -25,7 +25,7 @@ namespace DotNetCore.CAP Task PublishAsync(string name, T contentObj, string callbackName = null); /// - /// (EntityFramework) Publish a object message. + /// (EntityFramework) Publish an object message. /// /// If you are using the EntityFramework, you need to configure the DbContextType first. /// otherwise you need to use overloaded method with IDbTransaction. @@ -38,7 +38,7 @@ namespace DotNetCore.CAP void Publish(string name, T contentObj, string callbackName = null); /// - /// (ado.net) Asynchronous publish a object message. + /// (ado.net) Asynchronous publish an object message. /// /// the topic name or exchange router key. /// message body content, that will be serialized of json. @@ -47,12 +47,30 @@ namespace DotNetCore.CAP Task PublishAsync(string name, T contentObj, IDbTransaction dbTransaction, string callbackName = null); /// - /// (ado.net) Publish a object message. + /// (ado.net) Publish an object message. /// /// the topic name or exchange router key. /// message body content, that will be serialized of json. /// the transaction of /// callback subscriber name void Publish(string name, T contentObj, IDbTransaction dbTransaction, string callbackName = null); + + /// + /// Publish an object message with mongo. + /// + /// the topic name or exchange router key. + /// message body content, that will be serialized of json. + /// if seesion was set null, the message will be published directly. + /// callback subscriber name + void PublishWithMongo(string name, T contentObj, object mongoSession = null, string callbackName = null); + + /// + /// Asynchronous publish an object message with mongo. + /// + /// the topic name or exchange router key. + /// message body content, that will be serialized of json. + /// if seesion was set null, the message will be published directly. + /// callback subscriber name + Task PublishWithMongoAsync(string name, T contentObj, object mongoSession = null, string callbackName = null); } } \ No newline at end of file diff --git a/src/DotNetCore.CAP/Infrastructure/StatusName.cs b/src/DotNetCore.CAP/Infrastructure/StatusName.cs index 65d9427..f8c5142 100644 --- a/src/DotNetCore.CAP/Infrastructure/StatusName.cs +++ b/src/DotNetCore.CAP/Infrastructure/StatusName.cs @@ -11,5 +11,17 @@ namespace DotNetCore.CAP.Infrastructure public const string Scheduled = nameof(Scheduled); public const string Succeeded = nameof(Succeeded); public const string Failed = nameof(Failed); + + public static string Standardized(string input) + { + foreach (var item in typeof(StatusName).GetFields()) + { + if (item.Name.ToLower() == input.ToLower()) + { + return item.Name; + } + } + return string.Empty; + } } } \ No newline at end of file diff --git a/test/DotNetCore.CAP.MongoDB.Test/ConnectionUtil.cs b/test/DotNetCore.CAP.MongoDB.Test/ConnectionUtil.cs new file mode 100644 index 0000000..9c8aa29 --- /dev/null +++ b/test/DotNetCore.CAP.MongoDB.Test/ConnectionUtil.cs @@ -0,0 +1,7 @@ +namespace DotNetCore.CAP.MongoDB.Test +{ + public class ConnectionUtil + { + public static string ConnectionString = "mongodb://mongo1:27017,mongo2:27018,mongo3:27019/?replicaSet=my-mongo-set"; + } +} \ No newline at end of file diff --git a/test/DotNetCore.CAP.MongoDB.Test/DotNetCore.CAP.MongoDB.Test.csproj b/test/DotNetCore.CAP.MongoDB.Test/DotNetCore.CAP.MongoDB.Test.csproj new file mode 100644 index 0000000..5ec46fc --- /dev/null +++ b/test/DotNetCore.CAP.MongoDB.Test/DotNetCore.CAP.MongoDB.Test.csproj @@ -0,0 +1,23 @@ + + + + netcoreapp2.1 + + false + + + + + + + + + + + + + + + + + diff --git a/test/DotNetCore.CAP.MongoDB.Test/MongoDBMonitoringApiTest.cs b/test/DotNetCore.CAP.MongoDB.Test/MongoDBMonitoringApiTest.cs new file mode 100644 index 0000000..a4b6817 --- /dev/null +++ b/test/DotNetCore.CAP.MongoDB.Test/MongoDBMonitoringApiTest.cs @@ -0,0 +1,83 @@ +using MongoDB.Driver; +using DotNetCore.CAP.MongoDB; +using Xunit; +using System; +using DotNetCore.CAP.Models; +using FluentAssertions; +using DotNetCore.CAP.Dashboard.Monitoring; +using DotNetCore.CAP.Infrastructure; +using System.Linq; + +namespace DotNetCore.CAP.MongoDB.Test +{ + public class MongoDBMonitoringApiTest + { + private MongoClient _client; + private MongoDBOptions _options; + private MongoDBMonitoringApi _api; + + public MongoDBMonitoringApiTest() + { + _client = new MongoClient(ConnectionUtil.ConnectionString); + _options = new MongoDBOptions(); + _api = new MongoDBMonitoringApi(_client, _options); + + Init(); + } + + private void Init() + { + var helper = new MongoDBUtil(); + var database = _client.GetDatabase(_options.Database); + var collection = database.GetCollection(_options.PublishedCollection); + collection.InsertMany(new CapPublishedMessage[] + { + new CapPublishedMessage + { + Id = helper.GetNextSequenceValue(database,_options.PublishedCollection), + Added = DateTime.Now.AddHours(-1), + StatusName = "Failed", + Content = "abc" + }, + new CapPublishedMessage + { + Id = helper.GetNextSequenceValue(database,_options.PublishedCollection), + Added = DateTime.Now, + StatusName = "Failed", + Content = "bbc" + } + }); + } + + [Fact] + public void HourlyFailedJobs_Test() + { + var result = _api.HourlyFailedJobs(MessageType.Publish); + result.Should().HaveCount(24); + } + + [Fact] + public void Messages_Test() + { + var messages = + _api.Messages(new MessageQueryDto + { + MessageType = MessageType.Publish, + StatusName = StatusName.Failed, + Content = "b", + CurrentPage = 1, + PageSize = 1 + }); + + messages.Should().HaveCount(1); + messages.First().Content.Should().Contain("b"); + } + + [Fact] + public void PublishedFailedCount_Test() + { + var count = _api.PublishedFailedCount(); + count.Should().BeGreaterThan(1); + } + } +} \ No newline at end of file diff --git a/test/DotNetCore.CAP.MongoDB.Test/MongoDBStorageConnectionTest.cs b/test/DotNetCore.CAP.MongoDB.Test/MongoDBStorageConnectionTest.cs new file mode 100644 index 0000000..1c02736 --- /dev/null +++ b/test/DotNetCore.CAP.MongoDB.Test/MongoDBStorageConnectionTest.cs @@ -0,0 +1,67 @@ +using System.Threading; +using DotNetCore.CAP.Infrastructure; +using DotNetCore.CAP.Models; +using FluentAssertions; +using Microsoft.Extensions.Logging.Abstractions; +using MongoDB.Driver; +using Xunit; +using Xunit.Priority; + +namespace DotNetCore.CAP.MongoDB.Test +{ + [TestCaseOrderer(PriorityOrderer.Name, PriorityOrderer.Assembly)] + public class MongoDBStorageConnectionTest + { + private MongoClient _client; + private MongoDBOptions _options; + private MongoDBStorage _storage; + private IStorageConnection _connection; + + public MongoDBStorageConnectionTest() + { + _client = new MongoClient(ConnectionUtil.ConnectionString); + _options = new MongoDBOptions(); + _storage = new MongoDBStorage(new CapOptions(), _options, _client, NullLogger.Instance); + _connection = _storage.GetConnection(); + } + + [Fact, Priority(1)] + public async void StoreReceivedMessageAsync_TestAsync() + { + await _storage.InitializeAsync(default(CancellationToken)); + + var id = await + _connection.StoreReceivedMessageAsync(new CapReceivedMessage(new MessageContext + { + Group = "test", + Name = "test", + Content = "test-content" + })); + id.Should().BeGreaterThan(0); + } + + [Fact, Priority(2)] + public void ChangeReceivedState_Test() + { + var collection = _client.GetDatabase(_options.Database).GetCollection(_options.ReceivedCollection); + + var msg = collection.Find(x => true).FirstOrDefault(); + _connection.ChangeReceivedState(msg.Id, StatusName.Scheduled).Should().BeTrue(); + collection.Find(x => x.Id == msg.Id).FirstOrDefault()?.StatusName.Should().Be(StatusName.Scheduled); + } + + [Fact, Priority(3)] + public async void GetReceivedMessagesOfNeedRetry_TestAsync() + { + var msgs = await _connection.GetReceivedMessagesOfNeedRetry(); + msgs.Should().HaveCountGreaterThan(0); + } + + [Fact, Priority(4)] + public void GetReceivedMessageAsync_Test() + { + var msg = _connection.GetReceivedMessageAsync(1); + msg.Should().NotBeNull(); + } + } +} \ No newline at end of file diff --git a/test/DotNetCore.CAP.MongoDB.Test/MongoDBStorageTest.cs b/test/DotNetCore.CAP.MongoDB.Test/MongoDBStorageTest.cs new file mode 100644 index 0000000..630956a --- /dev/null +++ b/test/DotNetCore.CAP.MongoDB.Test/MongoDBStorageTest.cs @@ -0,0 +1,38 @@ +using System.Threading; +using FluentAssertions; +using Microsoft.Extensions.Logging.Abstractions; +using MongoDB.Bson; +using MongoDB.Driver; +using Xunit; + +namespace DotNetCore.CAP.MongoDB.Test +{ + public class MongoDBStorageTest + { + private MongoClient _client; + + public MongoDBStorageTest() + { + _client = new MongoClient(ConnectionUtil.ConnectionString); + } + + [Fact] + public async void InitializeAsync_Test() + { + var options = new MongoDBOptions(); + var storage = new MongoDBStorage(new CapOptions(), options, _client, NullLogger.Instance); + await storage.InitializeAsync(default(CancellationToken)); + var names = _client.ListDatabaseNames()?.ToList(); + names.Should().Contain(options.Database); + + var collections = _client.GetDatabase(options.Database).ListCollectionNames()?.ToList(); + collections.Should().Contain(options.PublishedCollection); + collections.Should().Contain(options.ReceivedCollection); + collections.Should().Contain("Counter"); + + var collection = _client.GetDatabase(options.Database).GetCollection("Counter"); + collection.CountDocuments(new BsonDocument { { "_id", options.PublishedCollection } }).Should().Be(1); + collection.CountDocuments(new BsonDocument { { "_id", options.ReceivedCollection } }).Should().Be(1); + } + } +} \ No newline at end of file diff --git a/test/DotNetCore.CAP.MongoDB.Test/MongoDBTest.cs b/test/DotNetCore.CAP.MongoDB.Test/MongoDBTest.cs new file mode 100644 index 0000000..1a91bba --- /dev/null +++ b/test/DotNetCore.CAP.MongoDB.Test/MongoDBTest.cs @@ -0,0 +1,82 @@ +using System; +using FluentAssertions; +using MongoDB.Bson; +using MongoDB.Driver; +using Xunit; + +namespace DotNetCore.CAP.MongoDB.Test +{ + public class MongoDBTest + { + private MongoClient _client; + + public MongoDBTest() + { + _client = new MongoClient(ConnectionUtil.ConnectionString); + } + + [Fact] + public void MongoDB_Connection_Test() + { + var names = _client.ListDatabaseNames(); + names.ToList().Should().NotBeNullOrEmpty(); + } + + [Fact] + public void Transaction_Test() + { + var document = new BsonDocument + { + { "name", "MongoDB" }, + { "type", "Database" }, + { "count", 1 }, + { "info", new BsonDocument + { + { "x", 203 }, + { "y", 102 } + }} + }; + var db = _client.GetDatabase("test"); + var collection1 = db.GetCollection("test1"); + var collection2 = db.GetCollection("test2"); + using (var sesstion = _client.StartSession()) + { + sesstion.StartTransaction(); + collection1.InsertOne(document); + collection2.InsertOne(document); + sesstion.CommitTransaction(); + } + var filter = new BsonDocument("name", "MongoDB"); + collection1.CountDocuments(filter).Should().BeGreaterThan(0); + collection2.CountDocuments(filter).Should().BeGreaterThan(0); + } + + [Fact] + public void Transaction_Rollback_Test() + { + var document = new BsonDocument + { + {"name", "MongoDB"}, + {"date", DateTimeOffset.Now.ToString()} + }; + var db = _client.GetDatabase("test"); + + var collection = db.GetCollection("test3"); + var collection4 = db.GetCollection("test4"); + + using (var session = _client.StartSession()) + { + session.IsInTransaction.Should().BeFalse(); + session.StartTransaction(); + session.IsInTransaction.Should().BeTrue(); + collection.InsertOne(session, document); + collection4.InsertOne(session, new BsonDocument { { "name", "MongoDB" } }); + + session.AbortTransaction(); + } + var filter = new BsonDocument("name", "MongoDB"); + collection.CountDocuments(filter).Should().Be(0); + collection4.CountDocuments(filter).Should().Be(0); + } + } +} diff --git a/test/DotNetCore.CAP.MongoDB.Test/MongoDBUtilTest.cs b/test/DotNetCore.CAP.MongoDB.Test/MongoDBUtilTest.cs new file mode 100644 index 0000000..49e221b --- /dev/null +++ b/test/DotNetCore.CAP.MongoDB.Test/MongoDBUtilTest.cs @@ -0,0 +1,50 @@ +using System.Collections.Concurrent; +using System.Linq; +using System.Threading.Tasks; +using DotNetCore.CAP.Models; +using FluentAssertions; +using MongoDB.Bson; +using MongoDB.Driver; +using Xunit; + +namespace DotNetCore.CAP.MongoDB.Test +{ + public class MongoDBUtilTest + { + private readonly MongoClient _client; + private readonly IMongoDatabase _database; + string _recieved = "ReceivedTest"; + + public MongoDBUtilTest() + { + _client = new MongoClient(ConnectionUtil.ConnectionString); + _database = _client.GetDatabase("CAP_Test"); + + //Initialize MongoDB + if (!_database.ListCollectionNames().ToList().Any(x => x == "Counter")) + { + var collection = _database.GetCollection("Counter"); + collection.InsertOne(new BsonDocument { { "_id", _recieved }, { "sequence_value", 0 } }); + } + } + + [Fact] + public async void GetNextSequenceValueAsync_Test() + { + var id = await new MongoDBUtil().GetNextSequenceValueAsync(_database, _recieved); + id.Should().BeGreaterThan(0); + } + + [Fact] + public void GetNextSequenceValue_Concurrency_Test() + { + var dic = new ConcurrentDictionary(); + Parallel.For(0, 30, (x) => + { + var id = new MongoDBUtil().GetNextSequenceValue(_database, _recieved); + id.Should().BeGreaterThan(0); + dic.TryAdd(id, x).Should().BeTrue(); //The id shouldn't be same. + }); + } + } +} \ No newline at end of file diff --git a/test/DotNetCore.CAP.Test/CAP.BuilderTest.cs b/test/DotNetCore.CAP.Test/CAP.BuilderTest.cs index bc7ad66..e32ff4a 100644 --- a/test/DotNetCore.CAP.Test/CAP.BuilderTest.cs +++ b/test/DotNetCore.CAP.Test/CAP.BuilderTest.cs @@ -15,13 +15,13 @@ namespace DotNetCore.CAP.Test public void CanCreateInstanceAndGetService() { var services = new ServiceCollection(); - + services.AddSingleton(); var builder = new CapBuilder(services); Assert.NotNull(builder); var count = builder.Services.Count; - Assert.Equal(1, count); + Assert.Equal(1, count); var provider = services.BuildServiceProvider(); var capPublisher = provider.GetService(); @@ -129,6 +129,11 @@ namespace DotNetCore.CAP.Test throw new NotImplementedException(); } + public void Publish(string name, T contentObj, object mongoSession, string callbackName = null) + { + throw new NotImplementedException(); + } + public Task PublishAsync(string topic, string content) { throw new NotImplementedException(); @@ -163,6 +168,16 @@ namespace DotNetCore.CAP.Test { throw new NotImplementedException(); } + + public void PublishWithMongo(string name, T contentObj, object mongoSession = null, string callbackName = null) + { + throw new NotImplementedException(); + } + + public Task PublishWithMongoAsync(string name, T contentObj, object mongoSession = null, string callbackName = null) + { + throw new NotImplementedException(); + } } } } \ No newline at end of file From 1242d2d144c4d359c0606f5c4b739d5b0e60ae33 Mon Sep 17 00:00:00 2001 From: keke Date: Mon, 23 Jul 2018 09:10:03 +0800 Subject: [PATCH 119/223] Feature/support mongodb (#163) * Ignore .vscode * Add MongoDBTest * New DotNetCore.CAP.MongoDB * Use MongoDB * Initialize MongoDB * GetPublishedMessageAsync * GetPublishedMessagesOfNeedRetry * Get received message * Change published state * Chang received state * Store received message * MongoDBStorageTransaction * Implement CapPublisher * Fix CAP.BuilderTest * Implement ICollectProcessor * Configure MongoDBOptions * Test MongoDBHelper * Remove useless code * GetMonitoringApi * Test MongoDbStorage * Test MongoDBStorageConnection * Test MongoDBStorageConnection * Add sample for MongoDB * Tweak CapPublisher * Wati interval * Tweak Mongo extension * Publish without mongo session * Publish rollback * Publish without session & subscribe msg * Tweak rabbitmq config * .gitignore * Implement & test HourlyFailedJobs * Rename MongoDBUtil * Rename MongoDBUtilTest * Tweak pipeline * Implement HourlyJobs * Remove redundant code * Implement Messages * Implement MessagesCount * Fix non-standard StatusName * To local time * Tweak two properties name * Tweak two methods name * Supplement xml comments * Fix wrong names --- .gitignore | 2 + CAP.sln | 21 +++ .../Controllers/ValuesController.cs | 74 ++++++++ samples/Sample.RabbitMQ.MongoDB/Program.cs | 24 +++ .../Sample.RabbitMQ.MongoDB.csproj | 21 +++ samples/Sample.RabbitMQ.MongoDB/Startup.cs | 60 ++++++ .../Sample.RabbitMQ.MongoDB/appsettings.json | 17 ++ .../CAP.MongoDBCapOptionsExtension.cs | 31 +++ .../CAP.MongoDBOptions.cs | 19 ++ .../CAP.Options.Extensions.cs | 26 +++ src/DotNetCore.CAP.MongoDB/CapPublisher.cs | 178 ++++++++++++++++++ .../DotNetCore.CAP.MongoDB.csproj | 17 ++ .../MongoDBCollectProcessor.cs | 46 +++++ .../MongoDBMonitoringApi.cs | 176 +++++++++++++++++ src/DotNetCore.CAP.MongoDB/MongoDBStorage.cs | 69 +++++++ .../MongoDBStorageConnection.cs | 118 ++++++++++++ .../MongoDBStorageTransaction.cs | 70 +++++++ src/DotNetCore.CAP.MongoDB/MongoDBUtil.cs | 63 +++++++ .../Abstractions/CapPublisherBase.cs | 16 +- src/DotNetCore.CAP/ICapPublisher.cs | 26 ++- .../Infrastructure/StatusName.cs | 12 ++ .../ConnectionUtil.cs | 7 + .../DotNetCore.CAP.MongoDB.Test.csproj | 23 +++ .../MongoDBMonitoringApiTest.cs | 83 ++++++++ .../MongoDBStorageConnectionTest.cs | 67 +++++++ .../MongoDBStorageTest.cs | 38 ++++ .../MongoDBTest.cs | 82 ++++++++ .../MongoDBUtilTest.cs | 50 +++++ test/DotNetCore.CAP.Test/CAP.BuilderTest.cs | 19 +- 29 files changed, 1446 insertions(+), 9 deletions(-) create mode 100644 samples/Sample.RabbitMQ.MongoDB/Controllers/ValuesController.cs create mode 100644 samples/Sample.RabbitMQ.MongoDB/Program.cs create mode 100644 samples/Sample.RabbitMQ.MongoDB/Sample.RabbitMQ.MongoDB.csproj create mode 100644 samples/Sample.RabbitMQ.MongoDB/Startup.cs create mode 100644 samples/Sample.RabbitMQ.MongoDB/appsettings.json create mode 100644 src/DotNetCore.CAP.MongoDB/CAP.MongoDBCapOptionsExtension.cs create mode 100644 src/DotNetCore.CAP.MongoDB/CAP.MongoDBOptions.cs create mode 100644 src/DotNetCore.CAP.MongoDB/CAP.Options.Extensions.cs create mode 100644 src/DotNetCore.CAP.MongoDB/CapPublisher.cs create mode 100644 src/DotNetCore.CAP.MongoDB/DotNetCore.CAP.MongoDB.csproj create mode 100644 src/DotNetCore.CAP.MongoDB/MongoDBCollectProcessor.cs create mode 100644 src/DotNetCore.CAP.MongoDB/MongoDBMonitoringApi.cs create mode 100644 src/DotNetCore.CAP.MongoDB/MongoDBStorage.cs create mode 100644 src/DotNetCore.CAP.MongoDB/MongoDBStorageConnection.cs create mode 100644 src/DotNetCore.CAP.MongoDB/MongoDBStorageTransaction.cs create mode 100644 src/DotNetCore.CAP.MongoDB/MongoDBUtil.cs create mode 100644 test/DotNetCore.CAP.MongoDB.Test/ConnectionUtil.cs create mode 100644 test/DotNetCore.CAP.MongoDB.Test/DotNetCore.CAP.MongoDB.Test.csproj create mode 100644 test/DotNetCore.CAP.MongoDB.Test/MongoDBMonitoringApiTest.cs create mode 100644 test/DotNetCore.CAP.MongoDB.Test/MongoDBStorageConnectionTest.cs create mode 100644 test/DotNetCore.CAP.MongoDB.Test/MongoDBStorageTest.cs create mode 100644 test/DotNetCore.CAP.MongoDB.Test/MongoDBTest.cs create mode 100644 test/DotNetCore.CAP.MongoDB.Test/MongoDBUtilTest.cs diff --git a/.gitignore b/.gitignore index af4f8ef..27ab035 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,5 @@ Properties /src/DotNetCore.CAP/packages.config /src/DotNetCore.CAP/DotNetCore.CAP.Net47.csproj /NuGet.config +.vscode/* +samples/Sample.RabbitMQ.MongoDB/appsettings.Development.json diff --git a/CAP.sln b/CAP.sln index 225537c..bf7efad 100644 --- a/CAP.sln +++ b/CAP.sln @@ -60,6 +60,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNetCore.CAP.PostgreSql.T EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sample.Kafka.MySql", "samples\Sample.Kafka.MySql\Sample.Kafka.MySql.csproj", "{9CB51105-A85B-42A4-AFDE-A4FC34D9EA91}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotNetCore.CAP.MongoDB.Test", "test\DotNetCore.CAP.MongoDB.Test\DotNetCore.CAP.MongoDB.Test.csproj", "{C143FCDF-E7F3-46F8-987E-A1BA38C1639D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotNetCore.CAP.MongoDB", "src\DotNetCore.CAP.MongoDB\DotNetCore.CAP.MongoDB.csproj", "{77C0AC02-C44B-49D5-B969-7D5305FC20A5}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sample.RabbitMQ.MongoDB", "samples\Sample.RabbitMQ.MongoDB\Sample.RabbitMQ.MongoDB.csproj", "{4473DE19-E8D2-4B57-80A8-C8AAA2BFA20F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -113,6 +119,18 @@ Global {9CB51105-A85B-42A4-AFDE-A4FC34D9EA91}.Debug|Any CPU.Build.0 = Debug|Any CPU {9CB51105-A85B-42A4-AFDE-A4FC34D9EA91}.Release|Any CPU.ActiveCfg = Release|Any CPU {9CB51105-A85B-42A4-AFDE-A4FC34D9EA91}.Release|Any CPU.Build.0 = Release|Any CPU + {C143FCDF-E7F3-46F8-987E-A1BA38C1639D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C143FCDF-E7F3-46F8-987E-A1BA38C1639D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C143FCDF-E7F3-46F8-987E-A1BA38C1639D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C143FCDF-E7F3-46F8-987E-A1BA38C1639D}.Release|Any CPU.Build.0 = Release|Any CPU + {77C0AC02-C44B-49D5-B969-7D5305FC20A5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77C0AC02-C44B-49D5-B969-7D5305FC20A5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77C0AC02-C44B-49D5-B969-7D5305FC20A5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77C0AC02-C44B-49D5-B969-7D5305FC20A5}.Release|Any CPU.Build.0 = Release|Any CPU + {4473DE19-E8D2-4B57-80A8-C8AAA2BFA20F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4473DE19-E8D2-4B57-80A8-C8AAA2BFA20F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4473DE19-E8D2-4B57-80A8-C8AAA2BFA20F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4473DE19-E8D2-4B57-80A8-C8AAA2BFA20F}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -130,6 +148,9 @@ Global {82C403AB-ED68-4084-9A1D-11334F9F08F9} = {9B2AE124-6636-4DE9-83A3-70360DABD0C4} {7CA3625D-1817-4695-881D-7E79A1E1DED2} = {C09CDAB0-6DD4-46E9-B7F3-3EF2A4741EA0} {9CB51105-A85B-42A4-AFDE-A4FC34D9EA91} = {3A6B6931-A123-477A-9469-8B468B5385AF} + {C143FCDF-E7F3-46F8-987E-A1BA38C1639D} = {C09CDAB0-6DD4-46E9-B7F3-3EF2A4741EA0} + {77C0AC02-C44B-49D5-B969-7D5305FC20A5} = {9B2AE124-6636-4DE9-83A3-70360DABD0C4} + {4473DE19-E8D2-4B57-80A8-C8AAA2BFA20F} = {3A6B6931-A123-477A-9469-8B468B5385AF} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {2E70565D-94CF-40B4-BFE1-AC18D5F736AB} diff --git a/samples/Sample.RabbitMQ.MongoDB/Controllers/ValuesController.cs b/samples/Sample.RabbitMQ.MongoDB/Controllers/ValuesController.cs new file mode 100644 index 0000000..374100d --- /dev/null +++ b/samples/Sample.RabbitMQ.MongoDB/Controllers/ValuesController.cs @@ -0,0 +1,74 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using DotNetCore.CAP; +using Microsoft.AspNetCore.Mvc; +using MongoDB.Bson; +using MongoDB.Driver; + +namespace Sample.RabbitMQ.MongoDB.Controllers +{ + [Route("api/[controller]")] + [ApiController] + public class ValuesController : ControllerBase + { + private readonly IMongoClient _client; + private readonly ICapPublisher _capPublisher; + + public ValuesController(IMongoClient client, ICapPublisher capPublisher) + { + _client = client; + _capPublisher = capPublisher; + } + + [Route("~/publish")] + public IActionResult PublishWithSession() + { + using (var session = _client.StartSession()) + { + session.StartTransaction(); + var collection = _client.GetDatabase("TEST").GetCollection("test"); + collection.InsertOne(session, new BsonDocument { { "hello", "world" } }); + + _capPublisher.PublishWithMongo("sample.rabbitmq.mongodb", DateTime.Now, session); + + session.CommitTransaction(); + } + return Ok(); + } + + [Route("~/publish_rollback")] + public IActionResult PublishRollback() + { + using (var session = _client.StartSession()) + { + try + { + session.StartTransaction(); + _capPublisher.PublishWithMongo("sample.rabbitmq.mongodb", DateTime.Now, session); + throw new Exception("Foo"); + } + catch (System.Exception ex) + { + session.AbortTransaction(); + return StatusCode(500, ex.Message); + } + } + } + + [Route("~/publish_without_session")] + public IActionResult PublishWithoutSession() + { + _capPublisher.PublishWithMongo("sample.rabbitmq.mongodb", DateTime.Now); + return Ok(); + } + + [NonAction] + [CapSubscribe("sample.rabbitmq.mongodb")] + public void ReceiveMessage(DateTime time) + { + Console.WriteLine("[sample.rabbitmq.mongodb] message received: " + DateTime.Now + ",sent time: " + time); + } + } +} diff --git a/samples/Sample.RabbitMQ.MongoDB/Program.cs b/samples/Sample.RabbitMQ.MongoDB/Program.cs new file mode 100644 index 0000000..7f32066 --- /dev/null +++ b/samples/Sample.RabbitMQ.MongoDB/Program.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; + +namespace Sample.RabbitMQ.MongoDB +{ + public class Program + { + public static void Main(string[] args) + { + CreateWebHostBuilder(args).Build().Run(); + } + + public static IWebHostBuilder CreateWebHostBuilder(string[] args) => + WebHost.CreateDefaultBuilder(args) + .UseStartup(); + } +} diff --git a/samples/Sample.RabbitMQ.MongoDB/Sample.RabbitMQ.MongoDB.csproj b/samples/Sample.RabbitMQ.MongoDB/Sample.RabbitMQ.MongoDB.csproj new file mode 100644 index 0000000..fdaa849 --- /dev/null +++ b/samples/Sample.RabbitMQ.MongoDB/Sample.RabbitMQ.MongoDB.csproj @@ -0,0 +1,21 @@ + + + + netcoreapp2.1 + + + + + + + + + + + + + + + + + diff --git a/samples/Sample.RabbitMQ.MongoDB/Startup.cs b/samples/Sample.RabbitMQ.MongoDB/Startup.cs new file mode 100644 index 0000000..4f4bd69 --- /dev/null +++ b/samples/Sample.RabbitMQ.MongoDB/Startup.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using DotNetCore.CAP; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.HttpsPolicy; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using MongoDB.Driver; + +namespace Sample.RabbitMQ.MongoDB +{ + public class Startup + { + public Startup(IConfiguration configuration) + { + Configuration = configuration; + } + + public IConfiguration Configuration { get; } + + public void ConfigureServices(IServiceCollection services) + { + services.AddSingleton(new MongoClient(Configuration.GetConnectionString("MongoDB"))); + services.AddCap(x => + { + x.UseMongoDB(); + + var mq = new RabbitMQOptions(); + Configuration.GetSection("RabbitMQ").Bind(mq); + x.UseRabbitMQ(cfg => + { + cfg.HostName = mq.HostName; + cfg.Port = mq.Port; + cfg.UserName = mq.UserName; + cfg.Password = mq.Password; + }); + + x.UseDashboard(); + }); + services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); + } + + public void Configure(IApplicationBuilder app, IHostingEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + app.UseMvc(); + + app.UseCap(); + } + } +} diff --git a/samples/Sample.RabbitMQ.MongoDB/appsettings.json b/samples/Sample.RabbitMQ.MongoDB/appsettings.json new file mode 100644 index 0000000..5707bfb --- /dev/null +++ b/samples/Sample.RabbitMQ.MongoDB/appsettings.json @@ -0,0 +1,17 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Warning" + } + }, + "AllowedHosts": "*", + "ConnectionStrings": { + "MongoDB": "mongodb://localhost:27017,localhost:27018,localhost:27019/?replicaSet=rs0" + }, + "RabbitMQ": { + "HostName": "localhost", + "Port": 5672, + "UserName": "", + "Password": "" + } +} \ No newline at end of file diff --git a/src/DotNetCore.CAP.MongoDB/CAP.MongoDBCapOptionsExtension.cs b/src/DotNetCore.CAP.MongoDB/CAP.MongoDBCapOptionsExtension.cs new file mode 100644 index 0000000..3bbb891 --- /dev/null +++ b/src/DotNetCore.CAP.MongoDB/CAP.MongoDBCapOptionsExtension.cs @@ -0,0 +1,31 @@ +using System; +using DotNetCore.CAP; +using DotNetCore.CAP.Processor; +using Microsoft.Extensions.DependencyInjection; + +namespace DotNetCore.CAP.MongoDB +{ + public class MongoDBCapOptionsExtension : ICapOptionsExtension + { + private Action _configure; + + public MongoDBCapOptionsExtension(Action configure) + { + _configure = configure; + } + + public void AddServices(IServiceCollection services) + { + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddScoped(); + services.AddScoped(); + services.AddTransient(); + + var options = new MongoDBOptions(); + _configure?.Invoke(options); + services.AddSingleton(options); + } + } +} \ No newline at end of file diff --git a/src/DotNetCore.CAP.MongoDB/CAP.MongoDBOptions.cs b/src/DotNetCore.CAP.MongoDB/CAP.MongoDBOptions.cs new file mode 100644 index 0000000..4e6ef3a --- /dev/null +++ b/src/DotNetCore.CAP.MongoDB/CAP.MongoDBOptions.cs @@ -0,0 +1,19 @@ +using System; + +namespace DotNetCore.CAP.MongoDB +{ + public class MongoDBOptions + { + public const string DefaultDatabase = "Cap"; + + /// + /// Gets or sets the database to use when creating database objects. + /// Default is . + /// + public string Database { get; set; } = DefaultDatabase; + + public string ReceivedCollection { get; } = "Received"; + + public string PublishedCollection { get; } = "Published"; + } +} \ No newline at end of file diff --git a/src/DotNetCore.CAP.MongoDB/CAP.Options.Extensions.cs b/src/DotNetCore.CAP.MongoDB/CAP.Options.Extensions.cs new file mode 100644 index 0000000..af69c02 --- /dev/null +++ b/src/DotNetCore.CAP.MongoDB/CAP.Options.Extensions.cs @@ -0,0 +1,26 @@ +using System; +using DotNetCore.CAP; +using DotNetCore.CAP.MongoDB; + +namespace Microsoft.Extensions.DependencyInjection +{ + public static class CapOptionsExtensions + { + public static CapOptions UseMongoDB(this CapOptions options) + { + return options.UseMongoDB(x => { }); + } + + public static CapOptions UseMongoDB(this CapOptions options, Action configure) + { + if (configure == null) + { + throw new ArgumentNullException(nameof(configure)); + } + + options.RegisterExtension(new MongoDBCapOptionsExtension(configure)); + + return options; + } + } +} \ No newline at end of file diff --git a/src/DotNetCore.CAP.MongoDB/CapPublisher.cs b/src/DotNetCore.CAP.MongoDB/CapPublisher.cs new file mode 100644 index 0000000..1cda633 --- /dev/null +++ b/src/DotNetCore.CAP.MongoDB/CapPublisher.cs @@ -0,0 +1,178 @@ +using System; +using System.Data; +using System.Threading.Tasks; +using DotNetCore.CAP.Abstractions; +using DotNetCore.CAP.Diagnostics; +using DotNetCore.CAP.Infrastructure; +using DotNetCore.CAP.Models; +using Microsoft.Extensions.Logging; +using MongoDB.Driver; + +namespace DotNetCore.CAP.MongoDB +{ + public class CapPublisher : CapPublisherBase, ICallbackPublisher + { + private readonly IMongoClient _client; + private readonly MongoDBOptions _options; + private readonly IMongoDatabase _database; + private bool _isInTransaction = true; + + public CapPublisher(ILogger logger, IDispatcher dispatcher, + IMongoClient client, MongoDBOptions options, IServiceProvider provider) + : base(logger, dispatcher) + { + _client = client; + _options = options; + _database = client.GetDatabase(_options.Database); + ServiceProvider = provider; + } + + public async Task PublishCallbackAsync(CapPublishedMessage message) + { + var collection = _database.GetCollection(_options.PublishedCollection); + message.Id = await new MongoDBUtil().GetNextSequenceValueAsync(_database, _options.PublishedCollection); + collection.InsertOne(message); + Enqueue(message); + } + + protected override int Execute(IDbConnection dbConnection, IDbTransaction dbTransaction, CapPublishedMessage message) + { + throw new System.NotImplementedException("Not work for MongoDB"); + } + + protected override Task ExecuteAsync(IDbConnection dbConnection, IDbTransaction dbTransaction, CapPublishedMessage message) + { + throw new System.NotImplementedException("Not work for MongoDB"); + } + + protected override void PrepareConnectionForEF() + { + throw new System.NotImplementedException("Not work for MongoDB"); + } + + public override void PublishWithMongo(string name, T contentObj, object mongoSession = null, string callbackName = null) + { + var session = mongoSession as IClientSessionHandle; + if (session == null) + { + _isInTransaction = false; + } + + PublishWithSession(name, contentObj, session, callbackName); + } + + public override async Task PublishWithMongoAsync(string name, T contentObj, object mongoSession = null, string callbackName = null) + { + var session = mongoSession as IClientSessionHandle; + if (session == null) + { + _isInTransaction = false; + } + + await PublishWithSessionAsync(name, contentObj, session, callbackName); + } + + private void PublishWithSession(string name, T contentObj, IClientSessionHandle session, string callbackName) + { + Guid operationId = default(Guid); + + var content = Serialize(contentObj, callbackName); + + var message = new CapPublishedMessage + { + Name = name, + Content = content, + StatusName = StatusName.Scheduled + }; + + try + { + operationId = s_diagnosticListener.WritePublishMessageStoreBefore(message); + var id = Execute(session, message); + + if (!_isInTransaction && id > 0) + { + _logger.LogInformation($"message [{message}] has been persisted in the database."); + s_diagnosticListener.WritePublishMessageStoreAfter(operationId, message); + message.Id = id; + Enqueue(message); + } + } + catch (System.Exception e) + { + _logger.LogError(e, "An exception was occurred when publish message. message:" + name); + s_diagnosticListener.WritePublishMessageStoreError(operationId, message, e); + throw; + } + } + + private int Execute(IClientSessionHandle session, CapPublishedMessage message) + { + message.Id = new MongoDBUtil().GetNextSequenceValue(_database, _options.PublishedCollection, session); + + var collection = _database.GetCollection(_options.PublishedCollection); + if (_isInTransaction) + { + collection.InsertOne(session, message); + } + else + { + collection.InsertOne(message); + } + return message.Id; + } + + + private async Task PublishWithSessionAsync(string name, T contentObj, IClientSessionHandle session, string callbackName) + { + Guid operationId = default(Guid); + var content = Serialize(contentObj, callbackName); + + var message = new CapPublishedMessage + { + Name = name, + Content = content, + StatusName = StatusName.Scheduled + }; + + try + { + operationId = s_diagnosticListener.WritePublishMessageStoreBefore(message); + + var id = await ExecuteAsync(session, message); + + if (!_isInTransaction && id > 0) + { + _logger.LogInformation($"message [{message}] has been persisted in the database."); + s_diagnosticListener.WritePublishMessageStoreAfter(operationId, message); + + message.Id = id; + + Enqueue(message); + } + } + catch (System.Exception e) + { + _logger.LogError(e, "An exception was occurred when publish message async. exception message:" + name); + s_diagnosticListener.WritePublishMessageStoreError(operationId, message, e); + Console.WriteLine(e); + throw; + } + } + + private async Task ExecuteAsync(IClientSessionHandle session, CapPublishedMessage message) + { + message.Id = await new MongoDBUtil().GetNextSequenceValueAsync(_database, _options.PublishedCollection, session); + var collection = _database.GetCollection(_options.PublishedCollection); + if (_isInTransaction) + { + await collection.InsertOneAsync(session, message); + } + else + { + await collection.InsertOneAsync(message); + } + return message.Id; + } + } +} \ No newline at end of file diff --git a/src/DotNetCore.CAP.MongoDB/DotNetCore.CAP.MongoDB.csproj b/src/DotNetCore.CAP.MongoDB/DotNetCore.CAP.MongoDB.csproj new file mode 100644 index 0000000..ef1b0c1 --- /dev/null +++ b/src/DotNetCore.CAP.MongoDB/DotNetCore.CAP.MongoDB.csproj @@ -0,0 +1,17 @@ + + + + + + + + netstandard2.0 + + + + + + + + + diff --git a/src/DotNetCore.CAP.MongoDB/MongoDBCollectProcessor.cs b/src/DotNetCore.CAP.MongoDB/MongoDBCollectProcessor.cs new file mode 100644 index 0000000..a0519bc --- /dev/null +++ b/src/DotNetCore.CAP.MongoDB/MongoDBCollectProcessor.cs @@ -0,0 +1,46 @@ +using System; +using System.Threading.Tasks; +using DotNetCore.CAP.Models; +using DotNetCore.CAP.Processor; +using Microsoft.Extensions.Logging; +using MongoDB.Driver; + +namespace DotNetCore.CAP.MongoDB +{ + public class MongoDBCollectProcessor : ICollectProcessor + { + private readonly IMongoClient _client; + private readonly MongoDBOptions _options; + private readonly ILogger _logger; + private readonly IMongoDatabase _database; + private readonly TimeSpan _waitingInterval = TimeSpan.FromMinutes(5); + + public MongoDBCollectProcessor(IMongoClient client, MongoDBOptions options, + ILogger logger) + { + _client = client; + _options = options; + _logger = logger; + _database = client.GetDatabase(_options.Database); + } + + public async Task ProcessAsync(ProcessingContext context) + { + _logger.LogDebug($"Collecting expired data from collection [{_options.Database}].[{_options.PublishedCollection}]."); + + var publishedCollection = _database.GetCollection(_options.PublishedCollection); + var receivedCollection = _database.GetCollection(_options.ReceivedCollection); + + await publishedCollection.BulkWriteAsync(new[] + { + new DeleteManyModel(Builders.Filter.Lt(x => x.ExpiresAt, DateTime.Now)) + }); + await receivedCollection.BulkWriteAsync(new[] + { + new DeleteManyModel(Builders.Filter.Lt(x => x.ExpiresAt, DateTime.Now)) + }); + + await context.WaitAsync(_waitingInterval); + } + } +} \ No newline at end of file diff --git a/src/DotNetCore.CAP.MongoDB/MongoDBMonitoringApi.cs b/src/DotNetCore.CAP.MongoDB/MongoDBMonitoringApi.cs new file mode 100644 index 0000000..7dfcff4 --- /dev/null +++ b/src/DotNetCore.CAP.MongoDB/MongoDBMonitoringApi.cs @@ -0,0 +1,176 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using DotNetCore.CAP.Dashboard; +using DotNetCore.CAP.Dashboard.Monitoring; +using DotNetCore.CAP.Infrastructure; +using DotNetCore.CAP.Models; +using MongoDB.Bson; +using MongoDB.Driver; + +namespace DotNetCore.CAP.MongoDB +{ + public class MongoDBMonitoringApi : IMonitoringApi + { + private IMongoClient _client; + private MongoDBOptions _options; + private IMongoDatabase _database; + + public MongoDBMonitoringApi(IMongoClient client, MongoDBOptions options) + { + _client = client ?? throw new ArgumentNullException(nameof(client)); + _options = options ?? throw new ArgumentNullException(nameof(options)); + + _database = _client.GetDatabase(_options.Database); + } + + public StatisticsDto GetStatistics() + { + var publishedCollection = _database.GetCollection(_options.PublishedCollection); + var receivedCollection = _database.GetCollection(_options.ReceivedCollection); + + var statistics = new StatisticsDto(); + + { + if (int.TryParse(publishedCollection.CountDocuments(x => x.StatusName == StatusName.Succeeded).ToString(), out var count)) + statistics.PublishedSucceeded = count; + } + { + if (int.TryParse(publishedCollection.CountDocuments(x => x.StatusName == StatusName.Failed).ToString(), out var count)) + statistics.PublishedFailed = count; + } + { + if (int.TryParse(receivedCollection.CountDocuments(x => x.StatusName == StatusName.Succeeded).ToString(), out var count)) + statistics.ReceivedSucceeded = count; + } + { + if (int.TryParse(receivedCollection.CountDocuments(x => x.StatusName == StatusName.Failed).ToString(), out var count)) + statistics.ReceivedFailed = count; + } + + return statistics; + } + + public IDictionary HourlyFailedJobs(MessageType type) + { + return GetHourlyTimelineStats(type, StatusName.Failed); + } + + public IDictionary HourlySucceededJobs(MessageType type) + { + return GetHourlyTimelineStats(type, StatusName.Succeeded); + } + + public IList Messages(MessageQueryDto queryDto) + { + queryDto.StatusName = StatusName.Standardized(queryDto.StatusName); + + var name = queryDto.MessageType == MessageType.Publish ? _options.PublishedCollection : _options.ReceivedCollection; + var collection = _database.GetCollection(name); + + var builder = Builders.Filter; + var filter = builder.Empty; + if (!string.IsNullOrEmpty(queryDto.StatusName)) + { + filter = filter & builder.Eq(x => x.StatusName, queryDto.StatusName); + } + if (!string.IsNullOrEmpty(queryDto.Name)) + { + filter = filter & builder.Eq(x => x.Name, queryDto.Name); + } + if (!string.IsNullOrEmpty(queryDto.Group)) + { + filter = filter & builder.Eq(x => x.Group, queryDto.Group); + } + if (!string.IsNullOrEmpty(queryDto.Content)) + { + filter = filter & builder.Regex(x => x.Content, ".*" + queryDto.Content + ".*"); + } + + var result = + collection.Find(filter) + .SortByDescending(x => x.Added) + .Skip(queryDto.PageSize * queryDto.CurrentPage) + .Limit(queryDto.PageSize) + .ToList(); + + return result; + } + + public int PublishedFailedCount() + { + return GetNumberOfMessage(_options.PublishedCollection, StatusName.Failed); + } + + public int PublishedSucceededCount() + { + return GetNumberOfMessage(_options.PublishedCollection, StatusName.Succeeded); + } + + public int ReceivedFailedCount() + { + return GetNumberOfMessage(_options.ReceivedCollection, StatusName.Failed); + } + + public int ReceivedSucceededCount() + { + return GetNumberOfMessage(_options.ReceivedCollection, StatusName.Succeeded); + } + + private int GetNumberOfMessage(string collectionName, string statusName) + { + var collection = _database.GetCollection(collectionName); + var count = collection.CountDocuments(new BsonDocument { { "StatusName", statusName } }); + return int.Parse(count.ToString()); + } + + private IDictionary GetHourlyTimelineStats(MessageType type, string statusName) + { + var collectionName = type == MessageType.Publish ? _options.PublishedCollection : _options.ReceivedCollection; + var endDate = DateTime.UtcNow; + + var groupby = new BsonDocument { + { "$group", new BsonDocument{ + { "_id", new BsonDocument { + { "Key", new BsonDocument { + { "$dateToString", new BsonDocument { + { "format", "%Y-%m-%d %H:00:00"}, + { "date", "$Added"} + }} + }} + } + }, + { "Count", new BsonDocument{ + { "$sum", 1} + }} + }} + }; + + var match = new BsonDocument { { "$match", new BsonDocument { + { "Added", new BsonDocument { { "$gt", endDate.AddHours(-24) } } }, + { "StatusName", new BsonDocument { { "$eq", statusName} } + } } } }; + var pipeline = new BsonDocument[] { match, groupby }; + + var collection = _database.GetCollection(collectionName); + var result = collection.Aggregate(pipeline: pipeline).ToList(); + + var dic = new Dictionary(); + for (var i = 0; i < 24; i++) + { + dic.Add(DateTime.Parse(endDate.ToLocalTime().ToString("yyyy-MM-dd HH:00:00")), 0); + endDate = endDate.AddHours(-1); + } + result.ForEach(d => + { + var key = d["_id"].AsBsonDocument["Key"].AsString; + if (DateTime.TryParse(key, out var dateTime)) + { + dic[dateTime.ToLocalTime()] = d["Count"].AsInt32; + } + }); + + return dic; + } + } +} \ No newline at end of file diff --git a/src/DotNetCore.CAP.MongoDB/MongoDBStorage.cs b/src/DotNetCore.CAP.MongoDB/MongoDBStorage.cs new file mode 100644 index 0000000..27084b8 --- /dev/null +++ b/src/DotNetCore.CAP.MongoDB/MongoDBStorage.cs @@ -0,0 +1,69 @@ +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using DotNetCore.CAP.Dashboard; +using Microsoft.Extensions.Logging; +using MongoDB.Bson; +using MongoDB.Driver; + +namespace DotNetCore.CAP.MongoDB +{ + public class MongoDBStorage : IStorage + { + private readonly CapOptions _capOptions; + private readonly MongoDBOptions _options; + private readonly IMongoClient _client; + private readonly ILogger _logger; + + public MongoDBStorage(CapOptions capOptions, + MongoDBOptions options, + IMongoClient client, + ILogger logger) + { + _capOptions = capOptions; + _options = options; + _client = client; + _logger = logger; + } + + public IStorageConnection GetConnection() + { + return new MongoDBStorageConnection(_capOptions, _options, _client); + } + + public IMonitoringApi GetMonitoringApi() + { + return new MongoDBMonitoringApi(_client, _options); + } + + public async Task InitializeAsync(CancellationToken cancellationToken) + { + if (cancellationToken.IsCancellationRequested) + { + return; + } + + var database = _client.GetDatabase(_options.Database); + var names = (await database.ListCollectionNamesAsync())?.ToList(); + + if (!names.Any(n => n == _options.ReceivedCollection)) + { + await database.CreateCollectionAsync(_options.ReceivedCollection); + } + if (!names.Any(n => n == _options.PublishedCollection)) + { + await database.CreateCollectionAsync(_options.PublishedCollection); + } + if (!names.Any(n => n == "Counter")) + { + await database.CreateCollectionAsync("Counter"); + var collection = database.GetCollection("Counter"); + await collection.InsertManyAsync(new BsonDocument[] + { + new BsonDocument{{"_id", _options.PublishedCollection}, {"sequence_value", 0}}, + new BsonDocument{{"_id", _options.ReceivedCollection}, {"sequence_value", 0}} + }); + } + } + } +} \ No newline at end of file diff --git a/src/DotNetCore.CAP.MongoDB/MongoDBStorageConnection.cs b/src/DotNetCore.CAP.MongoDB/MongoDBStorageConnection.cs new file mode 100644 index 0000000..11e8d8e --- /dev/null +++ b/src/DotNetCore.CAP.MongoDB/MongoDBStorageConnection.cs @@ -0,0 +1,118 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using DotNetCore.CAP.Infrastructure; +using DotNetCore.CAP.Models; +using MongoDB.Bson; +using MongoDB.Driver; + +namespace DotNetCore.CAP.MongoDB +{ + public class MongoDBStorageConnection : IStorageConnection + { + private CapOptions _capOptions; + private MongoDBOptions _options; + private readonly IMongoClient _client; + private readonly IMongoDatabase _database; + + public MongoDBStorageConnection(CapOptions capOptions, MongoDBOptions options, IMongoClient client) + { + _capOptions = capOptions; + _options = options; + _client = client; + _database = _client.GetDatabase(_options.Database); + } + + public bool ChangePublishedState(int messageId, string state) + { + var collection = _database.GetCollection(_options.PublishedCollection); + + var updateDef = Builders + .Update.Inc(x => x.Retries, 1) + .Set(x => x.ExpiresAt, null) + .Set(x => x.StatusName, state); + + var result = + collection.UpdateOne(x => x.Id == messageId, updateDef); + + return result.ModifiedCount > 0; + } + + public bool ChangeReceivedState(int messageId, string state) + { + var collection = _database.GetCollection(_options.ReceivedCollection); + + var updateDef = Builders + .Update.Inc(x => x.Retries, 1) + .Set(x => x.ExpiresAt, null) + .Set(x => x.StatusName, state); + + var result = + collection.UpdateOne(x => x.Id == messageId, updateDef); + + return result.ModifiedCount > 0; + } + + public IStorageTransaction CreateTransaction() + { + return new MongoDBStorageTransaction(_client, _options); + } + + public void Dispose() + { + } + + public async Task GetPublishedMessageAsync(int id) + { + var collection = _database.GetCollection(_options.PublishedCollection); + return await collection.Find(x => x.Id == id).FirstOrDefaultAsync(); + } + + public async Task> GetPublishedMessagesOfNeedRetry() + { + var fourMinsAgo = DateTime.Now.AddMinutes(-4); + var collection = _database.GetCollection(_options.PublishedCollection); + return await + collection.Find(x => + x.Retries < _capOptions.FailedRetryCount + && x.Added < fourMinsAgo + && (x.StatusName == StatusName.Failed || x.StatusName == StatusName.Scheduled)) + .Limit(200) + .ToListAsync(); + } + + public async Task GetReceivedMessageAsync(int id) + { + var collection = _database.GetCollection(_options.ReceivedCollection); + return await collection.Find(x => x.Id == id).FirstOrDefaultAsync(); + } + + public async Task> GetReceivedMessagesOfNeedRetry() + { + var fourMinsAgo = DateTime.Now.AddMinutes(-4); + var collection = _database.GetCollection(_options.ReceivedCollection); + + return await + collection.Find(x => + x.Retries < _capOptions.FailedRetryCount + && x.Added < fourMinsAgo + && (x.StatusName == StatusName.Failed || x.StatusName == StatusName.Scheduled) + ).Limit(200).ToListAsync(); + } + + public async Task StoreReceivedMessageAsync(CapReceivedMessage message) + { + if (message == null) + { + throw new ArgumentNullException(nameof(message)); + } + var collection = _database.GetCollection(_options.ReceivedCollection); + + message.Id = await new MongoDBUtil().GetNextSequenceValueAsync(_database, _options.ReceivedCollection); + + collection.InsertOne(message); + + return message.Id; + } + } +} \ No newline at end of file diff --git a/src/DotNetCore.CAP.MongoDB/MongoDBStorageTransaction.cs b/src/DotNetCore.CAP.MongoDB/MongoDBStorageTransaction.cs new file mode 100644 index 0000000..5ea0a56 --- /dev/null +++ b/src/DotNetCore.CAP.MongoDB/MongoDBStorageTransaction.cs @@ -0,0 +1,70 @@ +using System.Threading.Tasks; +using DotNetCore.CAP.Models; +using MongoDB.Driver; +using System; + +namespace DotNetCore.CAP.MongoDB +{ + internal class MongoDBStorageTransaction : IStorageTransaction + { + private IMongoClient _client; + private readonly MongoDBOptions _options; + private readonly IMongoDatabase _database; + private readonly IClientSessionHandle _session; + + public MongoDBStorageTransaction(IMongoClient client, MongoDBOptions options) + { + _client = client; + _options = options; + _database = client.GetDatabase(options.Database); + _session = _client.StartSession(); + _session.StartTransaction(); + } + + public async Task CommitAsync() + { + await _session.CommitTransactionAsync(); + } + + public void Dispose() + { + _session.Dispose(); + } + + public void UpdateMessage(CapPublishedMessage message) + { + if (message == null) + { + throw new ArgumentNullException(nameof(message)); + } + + var collection = _database.GetCollection(_options.PublishedCollection); + + var updateDef = Builders.Update + .Set(x => x.Retries, message.Retries) + .Set(x => x.Content, message.Content) + .Set(x => x.ExpiresAt, message.ExpiresAt) + .Set(x => x.StatusName, message.StatusName); + + collection.FindOneAndUpdate(_session, x => x.Id == message.Id, updateDef); + } + + public void UpdateMessage(CapReceivedMessage message) + { + if (message == null) + { + throw new ArgumentNullException(nameof(message)); + } + + var collection = _database.GetCollection(_options.ReceivedCollection); + + var updateDef = Builders.Update + .Set(x => x.Retries, message.Retries) + .Set(x => x.Content, message.Content) + .Set(x => x.ExpiresAt, message.ExpiresAt) + .Set(x => x.StatusName, message.StatusName); + + collection.FindOneAndUpdate(_session, x => x.Id == message.Id, updateDef); + } + } +} \ No newline at end of file diff --git a/src/DotNetCore.CAP.MongoDB/MongoDBUtil.cs b/src/DotNetCore.CAP.MongoDB/MongoDBUtil.cs new file mode 100644 index 0000000..8d862ad --- /dev/null +++ b/src/DotNetCore.CAP.MongoDB/MongoDBUtil.cs @@ -0,0 +1,63 @@ +using System; +using System.Threading.Tasks; +using MongoDB.Bson; +using MongoDB.Driver; + +namespace DotNetCore.CAP.MongoDB +{ + public class MongoDBUtil + { + FindOneAndUpdateOptions _options = new FindOneAndUpdateOptions() + { + ReturnDocument = ReturnDocument.After + }; + public async Task GetNextSequenceValueAsync(IMongoDatabase database, string collectionName, IClientSessionHandle session = null) + { + //https://www.tutorialspoint.com/mongodb/mongodb_autoincrement_sequence.htm + var collection = database.GetCollection("Counter"); + + var updateDef = Builders.Update.Inc("sequence_value", 1); + var filter = new BsonDocument { { "_id", collectionName } }; + + BsonDocument result; + if (session == null) + { + result = await collection.FindOneAndUpdateAsync(filter, updateDef, _options); + } + else + { + result = await collection.FindOneAndUpdateAsync(session, filter, updateDef, _options); + } + + if (result.TryGetValue("sequence_value", out var value)) + { + return value.ToInt32(); + } + throw new Exception("Unable to get next sequence value."); + } + + public int GetNextSequenceValue(IMongoDatabase database, string collectionName, IClientSessionHandle session = null) + { + var collection = database.GetCollection("Counter"); + + var filter = new BsonDocument { { "_id", collectionName } }; + var updateDef = Builders.Update.Inc("sequence_value", 1); + + BsonDocument result; + if (session == null) + { + result = collection.FindOneAndUpdate(filter, updateDef, _options); + } + else + { + result = collection.FindOneAndUpdate(session, filter, updateDef, _options); + } + + if (result.TryGetValue("sequence_value", out var value)) + { + return value.ToInt32(); + } + throw new Exception("Unable to get next sequence value."); + } + } +} \ No newline at end of file diff --git a/src/DotNetCore.CAP/Abstractions/CapPublisherBase.cs b/src/DotNetCore.CAP/Abstractions/CapPublisherBase.cs index 77a6c44..bc53ca8 100644 --- a/src/DotNetCore.CAP/Abstractions/CapPublisherBase.cs +++ b/src/DotNetCore.CAP/Abstractions/CapPublisherBase.cs @@ -15,11 +15,11 @@ namespace DotNetCore.CAP.Abstractions public abstract class CapPublisherBase : ICapPublisher, IDisposable { private readonly IDispatcher _dispatcher; - private readonly ILogger _logger; + protected readonly ILogger _logger; // diagnostics listener // ReSharper disable once InconsistentNaming - private static readonly DiagnosticListener s_diagnosticListener = + protected static readonly DiagnosticListener s_diagnosticListener = new DiagnosticListener(CapDiagnosticListenerExtensions.DiagnosticListenerName); protected CapPublisherBase(ILogger logger, IDispatcher dispatcher) @@ -67,6 +67,16 @@ namespace DotNetCore.CAP.Abstractions return PublishWithTransAsync(name, contentObj, callbackName); } + public virtual void PublishWithMongo(string name, T contentObj, object mongoSession = null, string callbackName = null) + { + throw new NotImplementedException("Work for MongoDB only."); + } + + public virtual Task PublishWithMongoAsync(string name, T contentObj, object mongoSession = null, string callbackName = null) + { + throw new NotImplementedException("Work for MongoDB only."); + } + protected void Enqueue(CapPublishedMessage message) { _dispatcher.EnqueueToPublish(message); @@ -205,7 +215,7 @@ namespace DotNetCore.CAP.Abstractions try { Console.WriteLine("================22222222222222====================="); - operationId = s_diagnosticListener.WritePublishMessageStoreBefore(message); + operationId = s_diagnosticListener.WritePublishMessageStoreBefore(message); var id = Execute(DbConnection, DbTransaction, message); Console.WriteLine("================777777777777777777777====================="); diff --git a/src/DotNetCore.CAP/ICapPublisher.cs b/src/DotNetCore.CAP/ICapPublisher.cs index 0688c02..4386926 100644 --- a/src/DotNetCore.CAP/ICapPublisher.cs +++ b/src/DotNetCore.CAP/ICapPublisher.cs @@ -12,7 +12,7 @@ namespace DotNetCore.CAP public interface ICapPublisher { /// - /// (EntityFramework) Asynchronous publish a object message. + /// (EntityFramework) Asynchronous publish an object message. /// /// If you are using the EntityFramework, you need to configure the DbContextType first. /// otherwise you need to use overloaded method with IDbTransaction. @@ -25,7 +25,7 @@ namespace DotNetCore.CAP Task PublishAsync(string name, T contentObj, string callbackName = null); /// - /// (EntityFramework) Publish a object message. + /// (EntityFramework) Publish an object message. /// /// If you are using the EntityFramework, you need to configure the DbContextType first. /// otherwise you need to use overloaded method with IDbTransaction. @@ -38,7 +38,7 @@ namespace DotNetCore.CAP void Publish(string name, T contentObj, string callbackName = null); /// - /// (ado.net) Asynchronous publish a object message. + /// (ado.net) Asynchronous publish an object message. /// /// the topic name or exchange router key. /// message body content, that will be serialized of json. @@ -47,12 +47,30 @@ namespace DotNetCore.CAP Task PublishAsync(string name, T contentObj, IDbTransaction dbTransaction, string callbackName = null); /// - /// (ado.net) Publish a object message. + /// (ado.net) Publish an object message. /// /// the topic name or exchange router key. /// message body content, that will be serialized of json. /// the transaction of /// callback subscriber name void Publish(string name, T contentObj, IDbTransaction dbTransaction, string callbackName = null); + + /// + /// Publish an object message with mongo. + /// + /// the topic name or exchange router key. + /// message body content, that will be serialized of json. + /// if seesion was set null, the message will be published directly. + /// callback subscriber name + void PublishWithMongo(string name, T contentObj, object mongoSession = null, string callbackName = null); + + /// + /// Asynchronous publish an object message with mongo. + /// + /// the topic name or exchange router key. + /// message body content, that will be serialized of json. + /// if seesion was set null, the message will be published directly. + /// callback subscriber name + Task PublishWithMongoAsync(string name, T contentObj, object mongoSession = null, string callbackName = null); } } \ No newline at end of file diff --git a/src/DotNetCore.CAP/Infrastructure/StatusName.cs b/src/DotNetCore.CAP/Infrastructure/StatusName.cs index 65d9427..f8c5142 100644 --- a/src/DotNetCore.CAP/Infrastructure/StatusName.cs +++ b/src/DotNetCore.CAP/Infrastructure/StatusName.cs @@ -11,5 +11,17 @@ namespace DotNetCore.CAP.Infrastructure public const string Scheduled = nameof(Scheduled); public const string Succeeded = nameof(Succeeded); public const string Failed = nameof(Failed); + + public static string Standardized(string input) + { + foreach (var item in typeof(StatusName).GetFields()) + { + if (item.Name.ToLower() == input.ToLower()) + { + return item.Name; + } + } + return string.Empty; + } } } \ No newline at end of file diff --git a/test/DotNetCore.CAP.MongoDB.Test/ConnectionUtil.cs b/test/DotNetCore.CAP.MongoDB.Test/ConnectionUtil.cs new file mode 100644 index 0000000..9c8aa29 --- /dev/null +++ b/test/DotNetCore.CAP.MongoDB.Test/ConnectionUtil.cs @@ -0,0 +1,7 @@ +namespace DotNetCore.CAP.MongoDB.Test +{ + public class ConnectionUtil + { + public static string ConnectionString = "mongodb://mongo1:27017,mongo2:27018,mongo3:27019/?replicaSet=my-mongo-set"; + } +} \ No newline at end of file diff --git a/test/DotNetCore.CAP.MongoDB.Test/DotNetCore.CAP.MongoDB.Test.csproj b/test/DotNetCore.CAP.MongoDB.Test/DotNetCore.CAP.MongoDB.Test.csproj new file mode 100644 index 0000000..5ec46fc --- /dev/null +++ b/test/DotNetCore.CAP.MongoDB.Test/DotNetCore.CAP.MongoDB.Test.csproj @@ -0,0 +1,23 @@ + + + + netcoreapp2.1 + + false + + + + + + + + + + + + + + + + + diff --git a/test/DotNetCore.CAP.MongoDB.Test/MongoDBMonitoringApiTest.cs b/test/DotNetCore.CAP.MongoDB.Test/MongoDBMonitoringApiTest.cs new file mode 100644 index 0000000..a4b6817 --- /dev/null +++ b/test/DotNetCore.CAP.MongoDB.Test/MongoDBMonitoringApiTest.cs @@ -0,0 +1,83 @@ +using MongoDB.Driver; +using DotNetCore.CAP.MongoDB; +using Xunit; +using System; +using DotNetCore.CAP.Models; +using FluentAssertions; +using DotNetCore.CAP.Dashboard.Monitoring; +using DotNetCore.CAP.Infrastructure; +using System.Linq; + +namespace DotNetCore.CAP.MongoDB.Test +{ + public class MongoDBMonitoringApiTest + { + private MongoClient _client; + private MongoDBOptions _options; + private MongoDBMonitoringApi _api; + + public MongoDBMonitoringApiTest() + { + _client = new MongoClient(ConnectionUtil.ConnectionString); + _options = new MongoDBOptions(); + _api = new MongoDBMonitoringApi(_client, _options); + + Init(); + } + + private void Init() + { + var helper = new MongoDBUtil(); + var database = _client.GetDatabase(_options.Database); + var collection = database.GetCollection(_options.PublishedCollection); + collection.InsertMany(new CapPublishedMessage[] + { + new CapPublishedMessage + { + Id = helper.GetNextSequenceValue(database,_options.PublishedCollection), + Added = DateTime.Now.AddHours(-1), + StatusName = "Failed", + Content = "abc" + }, + new CapPublishedMessage + { + Id = helper.GetNextSequenceValue(database,_options.PublishedCollection), + Added = DateTime.Now, + StatusName = "Failed", + Content = "bbc" + } + }); + } + + [Fact] + public void HourlyFailedJobs_Test() + { + var result = _api.HourlyFailedJobs(MessageType.Publish); + result.Should().HaveCount(24); + } + + [Fact] + public void Messages_Test() + { + var messages = + _api.Messages(new MessageQueryDto + { + MessageType = MessageType.Publish, + StatusName = StatusName.Failed, + Content = "b", + CurrentPage = 1, + PageSize = 1 + }); + + messages.Should().HaveCount(1); + messages.First().Content.Should().Contain("b"); + } + + [Fact] + public void PublishedFailedCount_Test() + { + var count = _api.PublishedFailedCount(); + count.Should().BeGreaterThan(1); + } + } +} \ No newline at end of file diff --git a/test/DotNetCore.CAP.MongoDB.Test/MongoDBStorageConnectionTest.cs b/test/DotNetCore.CAP.MongoDB.Test/MongoDBStorageConnectionTest.cs new file mode 100644 index 0000000..1c02736 --- /dev/null +++ b/test/DotNetCore.CAP.MongoDB.Test/MongoDBStorageConnectionTest.cs @@ -0,0 +1,67 @@ +using System.Threading; +using DotNetCore.CAP.Infrastructure; +using DotNetCore.CAP.Models; +using FluentAssertions; +using Microsoft.Extensions.Logging.Abstractions; +using MongoDB.Driver; +using Xunit; +using Xunit.Priority; + +namespace DotNetCore.CAP.MongoDB.Test +{ + [TestCaseOrderer(PriorityOrderer.Name, PriorityOrderer.Assembly)] + public class MongoDBStorageConnectionTest + { + private MongoClient _client; + private MongoDBOptions _options; + private MongoDBStorage _storage; + private IStorageConnection _connection; + + public MongoDBStorageConnectionTest() + { + _client = new MongoClient(ConnectionUtil.ConnectionString); + _options = new MongoDBOptions(); + _storage = new MongoDBStorage(new CapOptions(), _options, _client, NullLogger.Instance); + _connection = _storage.GetConnection(); + } + + [Fact, Priority(1)] + public async void StoreReceivedMessageAsync_TestAsync() + { + await _storage.InitializeAsync(default(CancellationToken)); + + var id = await + _connection.StoreReceivedMessageAsync(new CapReceivedMessage(new MessageContext + { + Group = "test", + Name = "test", + Content = "test-content" + })); + id.Should().BeGreaterThan(0); + } + + [Fact, Priority(2)] + public void ChangeReceivedState_Test() + { + var collection = _client.GetDatabase(_options.Database).GetCollection(_options.ReceivedCollection); + + var msg = collection.Find(x => true).FirstOrDefault(); + _connection.ChangeReceivedState(msg.Id, StatusName.Scheduled).Should().BeTrue(); + collection.Find(x => x.Id == msg.Id).FirstOrDefault()?.StatusName.Should().Be(StatusName.Scheduled); + } + + [Fact, Priority(3)] + public async void GetReceivedMessagesOfNeedRetry_TestAsync() + { + var msgs = await _connection.GetReceivedMessagesOfNeedRetry(); + msgs.Should().HaveCountGreaterThan(0); + } + + [Fact, Priority(4)] + public void GetReceivedMessageAsync_Test() + { + var msg = _connection.GetReceivedMessageAsync(1); + msg.Should().NotBeNull(); + } + } +} \ No newline at end of file diff --git a/test/DotNetCore.CAP.MongoDB.Test/MongoDBStorageTest.cs b/test/DotNetCore.CAP.MongoDB.Test/MongoDBStorageTest.cs new file mode 100644 index 0000000..630956a --- /dev/null +++ b/test/DotNetCore.CAP.MongoDB.Test/MongoDBStorageTest.cs @@ -0,0 +1,38 @@ +using System.Threading; +using FluentAssertions; +using Microsoft.Extensions.Logging.Abstractions; +using MongoDB.Bson; +using MongoDB.Driver; +using Xunit; + +namespace DotNetCore.CAP.MongoDB.Test +{ + public class MongoDBStorageTest + { + private MongoClient _client; + + public MongoDBStorageTest() + { + _client = new MongoClient(ConnectionUtil.ConnectionString); + } + + [Fact] + public async void InitializeAsync_Test() + { + var options = new MongoDBOptions(); + var storage = new MongoDBStorage(new CapOptions(), options, _client, NullLogger.Instance); + await storage.InitializeAsync(default(CancellationToken)); + var names = _client.ListDatabaseNames()?.ToList(); + names.Should().Contain(options.Database); + + var collections = _client.GetDatabase(options.Database).ListCollectionNames()?.ToList(); + collections.Should().Contain(options.PublishedCollection); + collections.Should().Contain(options.ReceivedCollection); + collections.Should().Contain("Counter"); + + var collection = _client.GetDatabase(options.Database).GetCollection("Counter"); + collection.CountDocuments(new BsonDocument { { "_id", options.PublishedCollection } }).Should().Be(1); + collection.CountDocuments(new BsonDocument { { "_id", options.ReceivedCollection } }).Should().Be(1); + } + } +} \ No newline at end of file diff --git a/test/DotNetCore.CAP.MongoDB.Test/MongoDBTest.cs b/test/DotNetCore.CAP.MongoDB.Test/MongoDBTest.cs new file mode 100644 index 0000000..1a91bba --- /dev/null +++ b/test/DotNetCore.CAP.MongoDB.Test/MongoDBTest.cs @@ -0,0 +1,82 @@ +using System; +using FluentAssertions; +using MongoDB.Bson; +using MongoDB.Driver; +using Xunit; + +namespace DotNetCore.CAP.MongoDB.Test +{ + public class MongoDBTest + { + private MongoClient _client; + + public MongoDBTest() + { + _client = new MongoClient(ConnectionUtil.ConnectionString); + } + + [Fact] + public void MongoDB_Connection_Test() + { + var names = _client.ListDatabaseNames(); + names.ToList().Should().NotBeNullOrEmpty(); + } + + [Fact] + public void Transaction_Test() + { + var document = new BsonDocument + { + { "name", "MongoDB" }, + { "type", "Database" }, + { "count", 1 }, + { "info", new BsonDocument + { + { "x", 203 }, + { "y", 102 } + }} + }; + var db = _client.GetDatabase("test"); + var collection1 = db.GetCollection("test1"); + var collection2 = db.GetCollection("test2"); + using (var sesstion = _client.StartSession()) + { + sesstion.StartTransaction(); + collection1.InsertOne(document); + collection2.InsertOne(document); + sesstion.CommitTransaction(); + } + var filter = new BsonDocument("name", "MongoDB"); + collection1.CountDocuments(filter).Should().BeGreaterThan(0); + collection2.CountDocuments(filter).Should().BeGreaterThan(0); + } + + [Fact] + public void Transaction_Rollback_Test() + { + var document = new BsonDocument + { + {"name", "MongoDB"}, + {"date", DateTimeOffset.Now.ToString()} + }; + var db = _client.GetDatabase("test"); + + var collection = db.GetCollection("test3"); + var collection4 = db.GetCollection("test4"); + + using (var session = _client.StartSession()) + { + session.IsInTransaction.Should().BeFalse(); + session.StartTransaction(); + session.IsInTransaction.Should().BeTrue(); + collection.InsertOne(session, document); + collection4.InsertOne(session, new BsonDocument { { "name", "MongoDB" } }); + + session.AbortTransaction(); + } + var filter = new BsonDocument("name", "MongoDB"); + collection.CountDocuments(filter).Should().Be(0); + collection4.CountDocuments(filter).Should().Be(0); + } + } +} diff --git a/test/DotNetCore.CAP.MongoDB.Test/MongoDBUtilTest.cs b/test/DotNetCore.CAP.MongoDB.Test/MongoDBUtilTest.cs new file mode 100644 index 0000000..49e221b --- /dev/null +++ b/test/DotNetCore.CAP.MongoDB.Test/MongoDBUtilTest.cs @@ -0,0 +1,50 @@ +using System.Collections.Concurrent; +using System.Linq; +using System.Threading.Tasks; +using DotNetCore.CAP.Models; +using FluentAssertions; +using MongoDB.Bson; +using MongoDB.Driver; +using Xunit; + +namespace DotNetCore.CAP.MongoDB.Test +{ + public class MongoDBUtilTest + { + private readonly MongoClient _client; + private readonly IMongoDatabase _database; + string _recieved = "ReceivedTest"; + + public MongoDBUtilTest() + { + _client = new MongoClient(ConnectionUtil.ConnectionString); + _database = _client.GetDatabase("CAP_Test"); + + //Initialize MongoDB + if (!_database.ListCollectionNames().ToList().Any(x => x == "Counter")) + { + var collection = _database.GetCollection("Counter"); + collection.InsertOne(new BsonDocument { { "_id", _recieved }, { "sequence_value", 0 } }); + } + } + + [Fact] + public async void GetNextSequenceValueAsync_Test() + { + var id = await new MongoDBUtil().GetNextSequenceValueAsync(_database, _recieved); + id.Should().BeGreaterThan(0); + } + + [Fact] + public void GetNextSequenceValue_Concurrency_Test() + { + var dic = new ConcurrentDictionary(); + Parallel.For(0, 30, (x) => + { + var id = new MongoDBUtil().GetNextSequenceValue(_database, _recieved); + id.Should().BeGreaterThan(0); + dic.TryAdd(id, x).Should().BeTrue(); //The id shouldn't be same. + }); + } + } +} \ No newline at end of file diff --git a/test/DotNetCore.CAP.Test/CAP.BuilderTest.cs b/test/DotNetCore.CAP.Test/CAP.BuilderTest.cs index bc7ad66..e32ff4a 100644 --- a/test/DotNetCore.CAP.Test/CAP.BuilderTest.cs +++ b/test/DotNetCore.CAP.Test/CAP.BuilderTest.cs @@ -15,13 +15,13 @@ namespace DotNetCore.CAP.Test public void CanCreateInstanceAndGetService() { var services = new ServiceCollection(); - + services.AddSingleton(); var builder = new CapBuilder(services); Assert.NotNull(builder); var count = builder.Services.Count; - Assert.Equal(1, count); + Assert.Equal(1, count); var provider = services.BuildServiceProvider(); var capPublisher = provider.GetService(); @@ -129,6 +129,11 @@ namespace DotNetCore.CAP.Test throw new NotImplementedException(); } + public void Publish(string name, T contentObj, object mongoSession, string callbackName = null) + { + throw new NotImplementedException(); + } + public Task PublishAsync(string topic, string content) { throw new NotImplementedException(); @@ -163,6 +168,16 @@ namespace DotNetCore.CAP.Test { throw new NotImplementedException(); } + + public void PublishWithMongo(string name, T contentObj, object mongoSession = null, string callbackName = null) + { + throw new NotImplementedException(); + } + + public Task PublishWithMongoAsync(string name, T contentObj, object mongoSession = null, string callbackName = null) + { + throw new NotImplementedException(); + } } } } \ No newline at end of file From 0cc7f5e5a09920f54929ee8f54da5c6552ec9d59 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Mon, 23 Jul 2018 13:17:26 +0800 Subject: [PATCH 120/223] Refactoring to specification --- .../CAP.MongoDBCapOptionsExtension.cs | 2 +- .../CAP.Options.Extensions.cs | 1 + src/DotNetCore.CAP.MongoDB/CapPublisher.cs | 14 ++-- .../DotNetCore.CAP.MongoDB.csproj | 17 +++-- .../MongoDBCollectProcessor.cs | 7 +- .../MongoDBMonitoringApi.cs | 74 +++++++++++-------- src/DotNetCore.CAP.MongoDB/MongoDBStorage.cs | 24 +++--- .../MongoDBStorageConnection.cs | 33 ++++----- .../MongoDBStorageTransaction.cs | 20 +++-- src/DotNetCore.CAP.MongoDB/MongoDBUtil.cs | 16 ++-- .../MongoDBMonitoringApiTest.cs | 6 +- .../MongoDBStorageConnectionTest.cs | 8 +- .../MongoDBStorageTest.cs | 2 +- .../MongoDBTest.cs | 2 +- .../MongoDBUtilTest.cs | 7 +- 15 files changed, 122 insertions(+), 111 deletions(-) diff --git a/src/DotNetCore.CAP.MongoDB/CAP.MongoDBCapOptionsExtension.cs b/src/DotNetCore.CAP.MongoDB/CAP.MongoDBCapOptionsExtension.cs index 3bbb891..101a57c 100644 --- a/src/DotNetCore.CAP.MongoDB/CAP.MongoDBCapOptionsExtension.cs +++ b/src/DotNetCore.CAP.MongoDB/CAP.MongoDBCapOptionsExtension.cs @@ -7,7 +7,7 @@ namespace DotNetCore.CAP.MongoDB { public class MongoDBCapOptionsExtension : ICapOptionsExtension { - private Action _configure; + private readonly Action _configure; public MongoDBCapOptionsExtension(Action configure) { diff --git a/src/DotNetCore.CAP.MongoDB/CAP.Options.Extensions.cs b/src/DotNetCore.CAP.MongoDB/CAP.Options.Extensions.cs index af69c02..18c846f 100644 --- a/src/DotNetCore.CAP.MongoDB/CAP.Options.Extensions.cs +++ b/src/DotNetCore.CAP.MongoDB/CAP.Options.Extensions.cs @@ -2,6 +2,7 @@ using System; using DotNetCore.CAP; using DotNetCore.CAP.MongoDB; +// ReSharper disable once CheckNamespace namespace Microsoft.Extensions.DependencyInjection { public static class CapOptionsExtensions diff --git a/src/DotNetCore.CAP.MongoDB/CapPublisher.cs b/src/DotNetCore.CAP.MongoDB/CapPublisher.cs index 1cda633..74cd8b9 100644 --- a/src/DotNetCore.CAP.MongoDB/CapPublisher.cs +++ b/src/DotNetCore.CAP.MongoDB/CapPublisher.cs @@ -12,16 +12,18 @@ namespace DotNetCore.CAP.MongoDB { public class CapPublisher : CapPublisherBase, ICallbackPublisher { - private readonly IMongoClient _client; private readonly MongoDBOptions _options; private readonly IMongoDatabase _database; private bool _isInTransaction = true; - public CapPublisher(ILogger logger, IDispatcher dispatcher, - IMongoClient client, MongoDBOptions options, IServiceProvider provider) - : base(logger, dispatcher) + public CapPublisher( + ILogger logger, + IDispatcher dispatcher, + IMongoClient client, + MongoDBOptions options, + IServiceProvider provider) + : base(logger, dispatcher) { - _client = client; _options = options; _database = client.GetDatabase(_options.Database); ServiceProvider = provider; @@ -122,7 +124,6 @@ namespace DotNetCore.CAP.MongoDB return message.Id; } - private async Task PublishWithSessionAsync(string name, T contentObj, IClientSessionHandle session, string callbackName) { Guid operationId = default(Guid); @@ -159,7 +160,6 @@ namespace DotNetCore.CAP.MongoDB throw; } } - private async Task ExecuteAsync(IClientSessionHandle session, CapPublishedMessage message) { message.Id = await new MongoDBUtil().GetNextSequenceValueAsync(_database, _options.PublishedCollection, session); diff --git a/src/DotNetCore.CAP.MongoDB/DotNetCore.CAP.MongoDB.csproj b/src/DotNetCore.CAP.MongoDB/DotNetCore.CAP.MongoDB.csproj index ef1b0c1..1ae2980 100644 --- a/src/DotNetCore.CAP.MongoDB/DotNetCore.CAP.MongoDB.csproj +++ b/src/DotNetCore.CAP.MongoDB/DotNetCore.CAP.MongoDB.csproj @@ -1,13 +1,20 @@ - - - - - + netstandard2.0 + DotNetCore.CAP.MongoDB + $(PackageTags);MongoDB + + + + bin\$(Configuration)\netstandard2.0\DotNetCore.CAP.MongoDB.xml + 1701;1702;1705;CS1591 + + + + diff --git a/src/DotNetCore.CAP.MongoDB/MongoDBCollectProcessor.cs b/src/DotNetCore.CAP.MongoDB/MongoDBCollectProcessor.cs index a0519bc..167c4c2 100644 --- a/src/DotNetCore.CAP.MongoDB/MongoDBCollectProcessor.cs +++ b/src/DotNetCore.CAP.MongoDB/MongoDBCollectProcessor.cs @@ -9,16 +9,15 @@ namespace DotNetCore.CAP.MongoDB { public class MongoDBCollectProcessor : ICollectProcessor { - private readonly IMongoClient _client; private readonly MongoDBOptions _options; private readonly ILogger _logger; private readonly IMongoDatabase _database; private readonly TimeSpan _waitingInterval = TimeSpan.FromMinutes(5); - public MongoDBCollectProcessor(IMongoClient client, MongoDBOptions options, - ILogger logger) + public MongoDBCollectProcessor(ILogger logger, + MongoDBOptions options, + IMongoClient client) { - _client = client; _options = options; _logger = logger; _database = client.GetDatabase(_options.Database); diff --git a/src/DotNetCore.CAP.MongoDB/MongoDBMonitoringApi.cs b/src/DotNetCore.CAP.MongoDB/MongoDBMonitoringApi.cs index 7dfcff4..b8845b9 100644 --- a/src/DotNetCore.CAP.MongoDB/MongoDBMonitoringApi.cs +++ b/src/DotNetCore.CAP.MongoDB/MongoDBMonitoringApi.cs @@ -12,16 +12,15 @@ namespace DotNetCore.CAP.MongoDB { public class MongoDBMonitoringApi : IMonitoringApi { - private IMongoClient _client; - private MongoDBOptions _options; - private IMongoDatabase _database; + private readonly MongoDBOptions _options; + private readonly IMongoDatabase _database; public MongoDBMonitoringApi(IMongoClient client, MongoDBOptions options) { - _client = client ?? throw new ArgumentNullException(nameof(client)); + var mongoClient = client ?? throw new ArgumentNullException(nameof(client)); _options = options ?? throw new ArgumentNullException(nameof(options)); - _database = _client.GetDatabase(_options.Database); + _database = mongoClient.GetDatabase(_options.Database); } public StatisticsDto GetStatistics() @@ -87,12 +86,12 @@ namespace DotNetCore.CAP.MongoDB filter = filter & builder.Regex(x => x.Content, ".*" + queryDto.Content + ".*"); } - var result = - collection.Find(filter) - .SortByDescending(x => x.Added) - .Skip(queryDto.PageSize * queryDto.CurrentPage) - .Limit(queryDto.PageSize) - .ToList(); + var result = collection + .Find(filter) + .SortByDescending(x => x.Added) + .Skip(queryDto.PageSize * queryDto.CurrentPage) + .Limit(queryDto.PageSize) + .ToList(); return result; } @@ -130,30 +129,45 @@ namespace DotNetCore.CAP.MongoDB var endDate = DateTime.UtcNow; var groupby = new BsonDocument { - { "$group", new BsonDocument{ - { "_id", new BsonDocument { - { "Key", new BsonDocument { - { "$dateToString", new BsonDocument { - { "format", "%Y-%m-%d %H:00:00"}, - { "date", "$Added"} - }} - }} + { + "$group", new BsonDocument { + { "_id", new BsonDocument { + { "Key", new BsonDocument{ + { "$dateToString", new BsonDocument { + { "format", "%Y-%m-%d %H:00:00"}, + { "date", "$Added"} + } + } + } + } + } + }, + { "Count", new BsonDocument{{ "$sum", 1}}} + } + } + }; + + var match = new BsonDocument { + { "$match", new BsonDocument { + { "Added", new BsonDocument + { + { "$gt", endDate.AddHours(-24) } + } + }, + { "StatusName", + new BsonDocument + { + { "$eq", statusName} + } } - }, - { "Count", new BsonDocument{ - { "$sum", 1} - }} - }} + } + } }; - var match = new BsonDocument { { "$match", new BsonDocument { - { "Added", new BsonDocument { { "$gt", endDate.AddHours(-24) } } }, - { "StatusName", new BsonDocument { { "$eq", statusName} } - } } } }; - var pipeline = new BsonDocument[] { match, groupby }; + var pipeline = new[] { match, groupby }; var collection = _database.GetCollection(collectionName); - var result = collection.Aggregate(pipeline: pipeline).ToList(); + var result = collection.Aggregate(pipeline).ToList(); var dic = new Dictionary(); for (var i = 0; i < 24; i++) diff --git a/src/DotNetCore.CAP.MongoDB/MongoDBStorage.cs b/src/DotNetCore.CAP.MongoDB/MongoDBStorage.cs index 27084b8..aa924a5 100644 --- a/src/DotNetCore.CAP.MongoDB/MongoDBStorage.cs +++ b/src/DotNetCore.CAP.MongoDB/MongoDBStorage.cs @@ -16,9 +16,9 @@ namespace DotNetCore.CAP.MongoDB private readonly ILogger _logger; public MongoDBStorage(CapOptions capOptions, - MongoDBOptions options, - IMongoClient client, - ILogger logger) + MongoDBOptions options, + IMongoClient client, + ILogger logger) { _capOptions = capOptions; _options = options; @@ -44,26 +44,30 @@ namespace DotNetCore.CAP.MongoDB } var database = _client.GetDatabase(_options.Database); - var names = (await database.ListCollectionNamesAsync())?.ToList(); + var names = (await database.ListCollectionNamesAsync(cancellationToken: cancellationToken))?.ToList(); if (!names.Any(n => n == _options.ReceivedCollection)) { - await database.CreateCollectionAsync(_options.ReceivedCollection); + await database.CreateCollectionAsync(_options.ReceivedCollection, cancellationToken: cancellationToken); } - if (!names.Any(n => n == _options.PublishedCollection)) + + if (names.All(n => n != _options.PublishedCollection)) { - await database.CreateCollectionAsync(_options.PublishedCollection); + await database.CreateCollectionAsync(_options.PublishedCollection, cancellationToken: cancellationToken); } - if (!names.Any(n => n == "Counter")) + + if (names.All(n => n != "Counter")) { - await database.CreateCollectionAsync("Counter"); + await database.CreateCollectionAsync("Counter", cancellationToken: cancellationToken); var collection = database.GetCollection("Counter"); await collection.InsertManyAsync(new BsonDocument[] { new BsonDocument{{"_id", _options.PublishedCollection}, {"sequence_value", 0}}, new BsonDocument{{"_id", _options.ReceivedCollection}, {"sequence_value", 0}} - }); + }, cancellationToken: cancellationToken); } + + _logger.LogDebug("Ensuring all create database tables script are applied."); } } } \ No newline at end of file diff --git a/src/DotNetCore.CAP.MongoDB/MongoDBStorageConnection.cs b/src/DotNetCore.CAP.MongoDB/MongoDBStorageConnection.cs index 11e8d8e..76efca9 100644 --- a/src/DotNetCore.CAP.MongoDB/MongoDBStorageConnection.cs +++ b/src/DotNetCore.CAP.MongoDB/MongoDBStorageConnection.cs @@ -10,8 +10,8 @@ namespace DotNetCore.CAP.MongoDB { public class MongoDBStorageConnection : IStorageConnection { - private CapOptions _capOptions; - private MongoDBOptions _options; + private readonly CapOptions _capOptions; + private readonly MongoDBOptions _options; private readonly IMongoClient _client; private readonly IMongoDatabase _database; @@ -58,10 +58,6 @@ namespace DotNetCore.CAP.MongoDB return new MongoDBStorageTransaction(_client, _options); } - public void Dispose() - { - } - public async Task GetPublishedMessageAsync(int id) { var collection = _database.GetCollection(_options.PublishedCollection); @@ -72,13 +68,10 @@ namespace DotNetCore.CAP.MongoDB { var fourMinsAgo = DateTime.Now.AddMinutes(-4); var collection = _database.GetCollection(_options.PublishedCollection); - return await - collection.Find(x => - x.Retries < _capOptions.FailedRetryCount - && x.Added < fourMinsAgo - && (x.StatusName == StatusName.Failed || x.StatusName == StatusName.Scheduled)) - .Limit(200) - .ToListAsync(); + return await collection + .Find(x => x.Retries < _capOptions.FailedRetryCount && x.Added < fourMinsAgo && (x.StatusName == StatusName.Failed || x.StatusName == StatusName.Scheduled)) + .Limit(200) + .ToListAsync(); } public async Task GetReceivedMessageAsync(int id) @@ -92,12 +85,10 @@ namespace DotNetCore.CAP.MongoDB var fourMinsAgo = DateTime.Now.AddMinutes(-4); var collection = _database.GetCollection(_options.ReceivedCollection); - return await - collection.Find(x => - x.Retries < _capOptions.FailedRetryCount - && x.Added < fourMinsAgo - && (x.StatusName == StatusName.Failed || x.StatusName == StatusName.Scheduled) - ).Limit(200).ToListAsync(); + return await collection + .Find(x => x.Retries < _capOptions.FailedRetryCount && x.Added < fourMinsAgo && (x.StatusName == StatusName.Failed || x.StatusName == StatusName.Scheduled)) + .Limit(200) + .ToListAsync(); } public async Task StoreReceivedMessageAsync(CapReceivedMessage message) @@ -114,5 +105,9 @@ namespace DotNetCore.CAP.MongoDB return message.Id; } + public void Dispose() + { + + } } } \ No newline at end of file diff --git a/src/DotNetCore.CAP.MongoDB/MongoDBStorageTransaction.cs b/src/DotNetCore.CAP.MongoDB/MongoDBStorageTransaction.cs index 5ea0a56..aff5c76 100644 --- a/src/DotNetCore.CAP.MongoDB/MongoDBStorageTransaction.cs +++ b/src/DotNetCore.CAP.MongoDB/MongoDBStorageTransaction.cs @@ -7,17 +7,15 @@ namespace DotNetCore.CAP.MongoDB { internal class MongoDBStorageTransaction : IStorageTransaction { - private IMongoClient _client; private readonly MongoDBOptions _options; private readonly IMongoDatabase _database; private readonly IClientSessionHandle _session; public MongoDBStorageTransaction(IMongoClient client, MongoDBOptions options) { - _client = client; _options = options; _database = client.GetDatabase(options.Database); - _session = _client.StartSession(); + _session = client.StartSession(); _session.StartTransaction(); } @@ -41,10 +39,10 @@ namespace DotNetCore.CAP.MongoDB var collection = _database.GetCollection(_options.PublishedCollection); var updateDef = Builders.Update - .Set(x => x.Retries, message.Retries) - .Set(x => x.Content, message.Content) - .Set(x => x.ExpiresAt, message.ExpiresAt) - .Set(x => x.StatusName, message.StatusName); + .Set(x => x.Retries, message.Retries) + .Set(x => x.Content, message.Content) + .Set(x => x.ExpiresAt, message.ExpiresAt) + .Set(x => x.StatusName, message.StatusName); collection.FindOneAndUpdate(_session, x => x.Id == message.Id, updateDef); } @@ -59,10 +57,10 @@ namespace DotNetCore.CAP.MongoDB var collection = _database.GetCollection(_options.ReceivedCollection); var updateDef = Builders.Update - .Set(x => x.Retries, message.Retries) - .Set(x => x.Content, message.Content) - .Set(x => x.ExpiresAt, message.ExpiresAt) - .Set(x => x.StatusName, message.StatusName); + .Set(x => x.Retries, message.Retries) + .Set(x => x.Content, message.Content) + .Set(x => x.ExpiresAt, message.ExpiresAt) + .Set(x => x.StatusName, message.StatusName); collection.FindOneAndUpdate(_session, x => x.Id == message.Id, updateDef); } diff --git a/src/DotNetCore.CAP.MongoDB/MongoDBUtil.cs b/src/DotNetCore.CAP.MongoDB/MongoDBUtil.cs index 8d862ad..be7b33f 100644 --- a/src/DotNetCore.CAP.MongoDB/MongoDBUtil.cs +++ b/src/DotNetCore.CAP.MongoDB/MongoDBUtil.cs @@ -5,9 +5,9 @@ using MongoDB.Driver; namespace DotNetCore.CAP.MongoDB { - public class MongoDBUtil + internal class MongoDBUtil { - FindOneAndUpdateOptions _options = new FindOneAndUpdateOptions() + readonly FindOneAndUpdateOptions _options = new FindOneAndUpdateOptions() { ReturnDocument = ReturnDocument.After }; @@ -43,15 +43,9 @@ namespace DotNetCore.CAP.MongoDB var filter = new BsonDocument { { "_id", collectionName } }; var updateDef = Builders.Update.Inc("sequence_value", 1); - BsonDocument result; - if (session == null) - { - result = collection.FindOneAndUpdate(filter, updateDef, _options); - } - else - { - result = collection.FindOneAndUpdate(session, filter, updateDef, _options); - } + var result = session == null + ? collection.FindOneAndUpdate(filter, updateDef, _options) + : collection.FindOneAndUpdate(session, filter, updateDef, _options); if (result.TryGetValue("sequence_value", out var value)) { diff --git a/test/DotNetCore.CAP.MongoDB.Test/MongoDBMonitoringApiTest.cs b/test/DotNetCore.CAP.MongoDB.Test/MongoDBMonitoringApiTest.cs index a4b6817..31c0f26 100644 --- a/test/DotNetCore.CAP.MongoDB.Test/MongoDBMonitoringApiTest.cs +++ b/test/DotNetCore.CAP.MongoDB.Test/MongoDBMonitoringApiTest.cs @@ -12,9 +12,9 @@ namespace DotNetCore.CAP.MongoDB.Test { public class MongoDBMonitoringApiTest { - private MongoClient _client; - private MongoDBOptions _options; - private MongoDBMonitoringApi _api; + private readonly MongoClient _client; + private readonly MongoDBOptions _options; + private readonly MongoDBMonitoringApi _api; public MongoDBMonitoringApiTest() { diff --git a/test/DotNetCore.CAP.MongoDB.Test/MongoDBStorageConnectionTest.cs b/test/DotNetCore.CAP.MongoDB.Test/MongoDBStorageConnectionTest.cs index 1c02736..a258a5d 100644 --- a/test/DotNetCore.CAP.MongoDB.Test/MongoDBStorageConnectionTest.cs +++ b/test/DotNetCore.CAP.MongoDB.Test/MongoDBStorageConnectionTest.cs @@ -12,10 +12,10 @@ namespace DotNetCore.CAP.MongoDB.Test [TestCaseOrderer(PriorityOrderer.Name, PriorityOrderer.Assembly)] public class MongoDBStorageConnectionTest { - private MongoClient _client; - private MongoDBOptions _options; - private MongoDBStorage _storage; - private IStorageConnection _connection; + private readonly MongoClient _client; + private readonly MongoDBOptions _options; + private readonly MongoDBStorage _storage; + private readonly IStorageConnection _connection; public MongoDBStorageConnectionTest() { diff --git a/test/DotNetCore.CAP.MongoDB.Test/MongoDBStorageTest.cs b/test/DotNetCore.CAP.MongoDB.Test/MongoDBStorageTest.cs index 630956a..dbbe95e 100644 --- a/test/DotNetCore.CAP.MongoDB.Test/MongoDBStorageTest.cs +++ b/test/DotNetCore.CAP.MongoDB.Test/MongoDBStorageTest.cs @@ -9,7 +9,7 @@ namespace DotNetCore.CAP.MongoDB.Test { public class MongoDBStorageTest { - private MongoClient _client; + private readonly MongoClient _client; public MongoDBStorageTest() { diff --git a/test/DotNetCore.CAP.MongoDB.Test/MongoDBTest.cs b/test/DotNetCore.CAP.MongoDB.Test/MongoDBTest.cs index 1a91bba..096e04c 100644 --- a/test/DotNetCore.CAP.MongoDB.Test/MongoDBTest.cs +++ b/test/DotNetCore.CAP.MongoDB.Test/MongoDBTest.cs @@ -8,7 +8,7 @@ namespace DotNetCore.CAP.MongoDB.Test { public class MongoDBTest { - private MongoClient _client; + private readonly MongoClient _client; public MongoDBTest() { diff --git a/test/DotNetCore.CAP.MongoDB.Test/MongoDBUtilTest.cs b/test/DotNetCore.CAP.MongoDB.Test/MongoDBUtilTest.cs index 49e221b..0944a21 100644 --- a/test/DotNetCore.CAP.MongoDB.Test/MongoDBUtilTest.cs +++ b/test/DotNetCore.CAP.MongoDB.Test/MongoDBUtilTest.cs @@ -11,17 +11,16 @@ namespace DotNetCore.CAP.MongoDB.Test { public class MongoDBUtilTest { - private readonly MongoClient _client; private readonly IMongoDatabase _database; string _recieved = "ReceivedTest"; public MongoDBUtilTest() { - _client = new MongoClient(ConnectionUtil.ConnectionString); - _database = _client.GetDatabase("CAP_Test"); + var client = new MongoClient(ConnectionUtil.ConnectionString); + _database = client.GetDatabase("CAP_Test"); //Initialize MongoDB - if (!_database.ListCollectionNames().ToList().Any(x => x == "Counter")) + if (_database.ListCollectionNames().ToList().All(x => x != "Counter")) { var collection = _database.GetCollection("Counter"); collection.InsertOne(new BsonDocument { { "_id", _recieved }, { "sequence_value", 0 } }); From b886cb3e9f80a8c5517e482cb7e110d6f2d1f964 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Mon, 23 Jul 2018 13:17:26 +0800 Subject: [PATCH 121/223] Refactoring to specification --- .../CAP.MongoDBCapOptionsExtension.cs | 2 +- .../CAP.Options.Extensions.cs | 1 + src/DotNetCore.CAP.MongoDB/CapPublisher.cs | 14 ++-- .../DotNetCore.CAP.MongoDB.csproj | 17 +++-- .../MongoDBCollectProcessor.cs | 7 +- .../MongoDBMonitoringApi.cs | 74 +++++++++++-------- src/DotNetCore.CAP.MongoDB/MongoDBStorage.cs | 24 +++--- .../MongoDBStorageConnection.cs | 33 ++++----- .../MongoDBStorageTransaction.cs | 20 +++-- src/DotNetCore.CAP.MongoDB/MongoDBUtil.cs | 16 ++-- .../MongoDBMonitoringApiTest.cs | 6 +- .../MongoDBStorageConnectionTest.cs | 8 +- .../MongoDBStorageTest.cs | 2 +- .../MongoDBTest.cs | 2 +- .../MongoDBUtilTest.cs | 7 +- 15 files changed, 122 insertions(+), 111 deletions(-) diff --git a/src/DotNetCore.CAP.MongoDB/CAP.MongoDBCapOptionsExtension.cs b/src/DotNetCore.CAP.MongoDB/CAP.MongoDBCapOptionsExtension.cs index 3bbb891..101a57c 100644 --- a/src/DotNetCore.CAP.MongoDB/CAP.MongoDBCapOptionsExtension.cs +++ b/src/DotNetCore.CAP.MongoDB/CAP.MongoDBCapOptionsExtension.cs @@ -7,7 +7,7 @@ namespace DotNetCore.CAP.MongoDB { public class MongoDBCapOptionsExtension : ICapOptionsExtension { - private Action _configure; + private readonly Action _configure; public MongoDBCapOptionsExtension(Action configure) { diff --git a/src/DotNetCore.CAP.MongoDB/CAP.Options.Extensions.cs b/src/DotNetCore.CAP.MongoDB/CAP.Options.Extensions.cs index af69c02..18c846f 100644 --- a/src/DotNetCore.CAP.MongoDB/CAP.Options.Extensions.cs +++ b/src/DotNetCore.CAP.MongoDB/CAP.Options.Extensions.cs @@ -2,6 +2,7 @@ using System; using DotNetCore.CAP; using DotNetCore.CAP.MongoDB; +// ReSharper disable once CheckNamespace namespace Microsoft.Extensions.DependencyInjection { public static class CapOptionsExtensions diff --git a/src/DotNetCore.CAP.MongoDB/CapPublisher.cs b/src/DotNetCore.CAP.MongoDB/CapPublisher.cs index 1cda633..74cd8b9 100644 --- a/src/DotNetCore.CAP.MongoDB/CapPublisher.cs +++ b/src/DotNetCore.CAP.MongoDB/CapPublisher.cs @@ -12,16 +12,18 @@ namespace DotNetCore.CAP.MongoDB { public class CapPublisher : CapPublisherBase, ICallbackPublisher { - private readonly IMongoClient _client; private readonly MongoDBOptions _options; private readonly IMongoDatabase _database; private bool _isInTransaction = true; - public CapPublisher(ILogger logger, IDispatcher dispatcher, - IMongoClient client, MongoDBOptions options, IServiceProvider provider) - : base(logger, dispatcher) + public CapPublisher( + ILogger logger, + IDispatcher dispatcher, + IMongoClient client, + MongoDBOptions options, + IServiceProvider provider) + : base(logger, dispatcher) { - _client = client; _options = options; _database = client.GetDatabase(_options.Database); ServiceProvider = provider; @@ -122,7 +124,6 @@ namespace DotNetCore.CAP.MongoDB return message.Id; } - private async Task PublishWithSessionAsync(string name, T contentObj, IClientSessionHandle session, string callbackName) { Guid operationId = default(Guid); @@ -159,7 +160,6 @@ namespace DotNetCore.CAP.MongoDB throw; } } - private async Task ExecuteAsync(IClientSessionHandle session, CapPublishedMessage message) { message.Id = await new MongoDBUtil().GetNextSequenceValueAsync(_database, _options.PublishedCollection, session); diff --git a/src/DotNetCore.CAP.MongoDB/DotNetCore.CAP.MongoDB.csproj b/src/DotNetCore.CAP.MongoDB/DotNetCore.CAP.MongoDB.csproj index ef1b0c1..1ae2980 100644 --- a/src/DotNetCore.CAP.MongoDB/DotNetCore.CAP.MongoDB.csproj +++ b/src/DotNetCore.CAP.MongoDB/DotNetCore.CAP.MongoDB.csproj @@ -1,13 +1,20 @@ - - - - - + netstandard2.0 + DotNetCore.CAP.MongoDB + $(PackageTags);MongoDB + + + + bin\$(Configuration)\netstandard2.0\DotNetCore.CAP.MongoDB.xml + 1701;1702;1705;CS1591 + + + + diff --git a/src/DotNetCore.CAP.MongoDB/MongoDBCollectProcessor.cs b/src/DotNetCore.CAP.MongoDB/MongoDBCollectProcessor.cs index a0519bc..167c4c2 100644 --- a/src/DotNetCore.CAP.MongoDB/MongoDBCollectProcessor.cs +++ b/src/DotNetCore.CAP.MongoDB/MongoDBCollectProcessor.cs @@ -9,16 +9,15 @@ namespace DotNetCore.CAP.MongoDB { public class MongoDBCollectProcessor : ICollectProcessor { - private readonly IMongoClient _client; private readonly MongoDBOptions _options; private readonly ILogger _logger; private readonly IMongoDatabase _database; private readonly TimeSpan _waitingInterval = TimeSpan.FromMinutes(5); - public MongoDBCollectProcessor(IMongoClient client, MongoDBOptions options, - ILogger logger) + public MongoDBCollectProcessor(ILogger logger, + MongoDBOptions options, + IMongoClient client) { - _client = client; _options = options; _logger = logger; _database = client.GetDatabase(_options.Database); diff --git a/src/DotNetCore.CAP.MongoDB/MongoDBMonitoringApi.cs b/src/DotNetCore.CAP.MongoDB/MongoDBMonitoringApi.cs index 7dfcff4..b8845b9 100644 --- a/src/DotNetCore.CAP.MongoDB/MongoDBMonitoringApi.cs +++ b/src/DotNetCore.CAP.MongoDB/MongoDBMonitoringApi.cs @@ -12,16 +12,15 @@ namespace DotNetCore.CAP.MongoDB { public class MongoDBMonitoringApi : IMonitoringApi { - private IMongoClient _client; - private MongoDBOptions _options; - private IMongoDatabase _database; + private readonly MongoDBOptions _options; + private readonly IMongoDatabase _database; public MongoDBMonitoringApi(IMongoClient client, MongoDBOptions options) { - _client = client ?? throw new ArgumentNullException(nameof(client)); + var mongoClient = client ?? throw new ArgumentNullException(nameof(client)); _options = options ?? throw new ArgumentNullException(nameof(options)); - _database = _client.GetDatabase(_options.Database); + _database = mongoClient.GetDatabase(_options.Database); } public StatisticsDto GetStatistics() @@ -87,12 +86,12 @@ namespace DotNetCore.CAP.MongoDB filter = filter & builder.Regex(x => x.Content, ".*" + queryDto.Content + ".*"); } - var result = - collection.Find(filter) - .SortByDescending(x => x.Added) - .Skip(queryDto.PageSize * queryDto.CurrentPage) - .Limit(queryDto.PageSize) - .ToList(); + var result = collection + .Find(filter) + .SortByDescending(x => x.Added) + .Skip(queryDto.PageSize * queryDto.CurrentPage) + .Limit(queryDto.PageSize) + .ToList(); return result; } @@ -130,30 +129,45 @@ namespace DotNetCore.CAP.MongoDB var endDate = DateTime.UtcNow; var groupby = new BsonDocument { - { "$group", new BsonDocument{ - { "_id", new BsonDocument { - { "Key", new BsonDocument { - { "$dateToString", new BsonDocument { - { "format", "%Y-%m-%d %H:00:00"}, - { "date", "$Added"} - }} - }} + { + "$group", new BsonDocument { + { "_id", new BsonDocument { + { "Key", new BsonDocument{ + { "$dateToString", new BsonDocument { + { "format", "%Y-%m-%d %H:00:00"}, + { "date", "$Added"} + } + } + } + } + } + }, + { "Count", new BsonDocument{{ "$sum", 1}}} + } + } + }; + + var match = new BsonDocument { + { "$match", new BsonDocument { + { "Added", new BsonDocument + { + { "$gt", endDate.AddHours(-24) } + } + }, + { "StatusName", + new BsonDocument + { + { "$eq", statusName} + } } - }, - { "Count", new BsonDocument{ - { "$sum", 1} - }} - }} + } + } }; - var match = new BsonDocument { { "$match", new BsonDocument { - { "Added", new BsonDocument { { "$gt", endDate.AddHours(-24) } } }, - { "StatusName", new BsonDocument { { "$eq", statusName} } - } } } }; - var pipeline = new BsonDocument[] { match, groupby }; + var pipeline = new[] { match, groupby }; var collection = _database.GetCollection(collectionName); - var result = collection.Aggregate(pipeline: pipeline).ToList(); + var result = collection.Aggregate(pipeline).ToList(); var dic = new Dictionary(); for (var i = 0; i < 24; i++) diff --git a/src/DotNetCore.CAP.MongoDB/MongoDBStorage.cs b/src/DotNetCore.CAP.MongoDB/MongoDBStorage.cs index 27084b8..aa924a5 100644 --- a/src/DotNetCore.CAP.MongoDB/MongoDBStorage.cs +++ b/src/DotNetCore.CAP.MongoDB/MongoDBStorage.cs @@ -16,9 +16,9 @@ namespace DotNetCore.CAP.MongoDB private readonly ILogger _logger; public MongoDBStorage(CapOptions capOptions, - MongoDBOptions options, - IMongoClient client, - ILogger logger) + MongoDBOptions options, + IMongoClient client, + ILogger logger) { _capOptions = capOptions; _options = options; @@ -44,26 +44,30 @@ namespace DotNetCore.CAP.MongoDB } var database = _client.GetDatabase(_options.Database); - var names = (await database.ListCollectionNamesAsync())?.ToList(); + var names = (await database.ListCollectionNamesAsync(cancellationToken: cancellationToken))?.ToList(); if (!names.Any(n => n == _options.ReceivedCollection)) { - await database.CreateCollectionAsync(_options.ReceivedCollection); + await database.CreateCollectionAsync(_options.ReceivedCollection, cancellationToken: cancellationToken); } - if (!names.Any(n => n == _options.PublishedCollection)) + + if (names.All(n => n != _options.PublishedCollection)) { - await database.CreateCollectionAsync(_options.PublishedCollection); + await database.CreateCollectionAsync(_options.PublishedCollection, cancellationToken: cancellationToken); } - if (!names.Any(n => n == "Counter")) + + if (names.All(n => n != "Counter")) { - await database.CreateCollectionAsync("Counter"); + await database.CreateCollectionAsync("Counter", cancellationToken: cancellationToken); var collection = database.GetCollection("Counter"); await collection.InsertManyAsync(new BsonDocument[] { new BsonDocument{{"_id", _options.PublishedCollection}, {"sequence_value", 0}}, new BsonDocument{{"_id", _options.ReceivedCollection}, {"sequence_value", 0}} - }); + }, cancellationToken: cancellationToken); } + + _logger.LogDebug("Ensuring all create database tables script are applied."); } } } \ No newline at end of file diff --git a/src/DotNetCore.CAP.MongoDB/MongoDBStorageConnection.cs b/src/DotNetCore.CAP.MongoDB/MongoDBStorageConnection.cs index 11e8d8e..76efca9 100644 --- a/src/DotNetCore.CAP.MongoDB/MongoDBStorageConnection.cs +++ b/src/DotNetCore.CAP.MongoDB/MongoDBStorageConnection.cs @@ -10,8 +10,8 @@ namespace DotNetCore.CAP.MongoDB { public class MongoDBStorageConnection : IStorageConnection { - private CapOptions _capOptions; - private MongoDBOptions _options; + private readonly CapOptions _capOptions; + private readonly MongoDBOptions _options; private readonly IMongoClient _client; private readonly IMongoDatabase _database; @@ -58,10 +58,6 @@ namespace DotNetCore.CAP.MongoDB return new MongoDBStorageTransaction(_client, _options); } - public void Dispose() - { - } - public async Task GetPublishedMessageAsync(int id) { var collection = _database.GetCollection(_options.PublishedCollection); @@ -72,13 +68,10 @@ namespace DotNetCore.CAP.MongoDB { var fourMinsAgo = DateTime.Now.AddMinutes(-4); var collection = _database.GetCollection(_options.PublishedCollection); - return await - collection.Find(x => - x.Retries < _capOptions.FailedRetryCount - && x.Added < fourMinsAgo - && (x.StatusName == StatusName.Failed || x.StatusName == StatusName.Scheduled)) - .Limit(200) - .ToListAsync(); + return await collection + .Find(x => x.Retries < _capOptions.FailedRetryCount && x.Added < fourMinsAgo && (x.StatusName == StatusName.Failed || x.StatusName == StatusName.Scheduled)) + .Limit(200) + .ToListAsync(); } public async Task GetReceivedMessageAsync(int id) @@ -92,12 +85,10 @@ namespace DotNetCore.CAP.MongoDB var fourMinsAgo = DateTime.Now.AddMinutes(-4); var collection = _database.GetCollection(_options.ReceivedCollection); - return await - collection.Find(x => - x.Retries < _capOptions.FailedRetryCount - && x.Added < fourMinsAgo - && (x.StatusName == StatusName.Failed || x.StatusName == StatusName.Scheduled) - ).Limit(200).ToListAsync(); + return await collection + .Find(x => x.Retries < _capOptions.FailedRetryCount && x.Added < fourMinsAgo && (x.StatusName == StatusName.Failed || x.StatusName == StatusName.Scheduled)) + .Limit(200) + .ToListAsync(); } public async Task StoreReceivedMessageAsync(CapReceivedMessage message) @@ -114,5 +105,9 @@ namespace DotNetCore.CAP.MongoDB return message.Id; } + public void Dispose() + { + + } } } \ No newline at end of file diff --git a/src/DotNetCore.CAP.MongoDB/MongoDBStorageTransaction.cs b/src/DotNetCore.CAP.MongoDB/MongoDBStorageTransaction.cs index 5ea0a56..aff5c76 100644 --- a/src/DotNetCore.CAP.MongoDB/MongoDBStorageTransaction.cs +++ b/src/DotNetCore.CAP.MongoDB/MongoDBStorageTransaction.cs @@ -7,17 +7,15 @@ namespace DotNetCore.CAP.MongoDB { internal class MongoDBStorageTransaction : IStorageTransaction { - private IMongoClient _client; private readonly MongoDBOptions _options; private readonly IMongoDatabase _database; private readonly IClientSessionHandle _session; public MongoDBStorageTransaction(IMongoClient client, MongoDBOptions options) { - _client = client; _options = options; _database = client.GetDatabase(options.Database); - _session = _client.StartSession(); + _session = client.StartSession(); _session.StartTransaction(); } @@ -41,10 +39,10 @@ namespace DotNetCore.CAP.MongoDB var collection = _database.GetCollection(_options.PublishedCollection); var updateDef = Builders.Update - .Set(x => x.Retries, message.Retries) - .Set(x => x.Content, message.Content) - .Set(x => x.ExpiresAt, message.ExpiresAt) - .Set(x => x.StatusName, message.StatusName); + .Set(x => x.Retries, message.Retries) + .Set(x => x.Content, message.Content) + .Set(x => x.ExpiresAt, message.ExpiresAt) + .Set(x => x.StatusName, message.StatusName); collection.FindOneAndUpdate(_session, x => x.Id == message.Id, updateDef); } @@ -59,10 +57,10 @@ namespace DotNetCore.CAP.MongoDB var collection = _database.GetCollection(_options.ReceivedCollection); var updateDef = Builders.Update - .Set(x => x.Retries, message.Retries) - .Set(x => x.Content, message.Content) - .Set(x => x.ExpiresAt, message.ExpiresAt) - .Set(x => x.StatusName, message.StatusName); + .Set(x => x.Retries, message.Retries) + .Set(x => x.Content, message.Content) + .Set(x => x.ExpiresAt, message.ExpiresAt) + .Set(x => x.StatusName, message.StatusName); collection.FindOneAndUpdate(_session, x => x.Id == message.Id, updateDef); } diff --git a/src/DotNetCore.CAP.MongoDB/MongoDBUtil.cs b/src/DotNetCore.CAP.MongoDB/MongoDBUtil.cs index 8d862ad..be7b33f 100644 --- a/src/DotNetCore.CAP.MongoDB/MongoDBUtil.cs +++ b/src/DotNetCore.CAP.MongoDB/MongoDBUtil.cs @@ -5,9 +5,9 @@ using MongoDB.Driver; namespace DotNetCore.CAP.MongoDB { - public class MongoDBUtil + internal class MongoDBUtil { - FindOneAndUpdateOptions _options = new FindOneAndUpdateOptions() + readonly FindOneAndUpdateOptions _options = new FindOneAndUpdateOptions() { ReturnDocument = ReturnDocument.After }; @@ -43,15 +43,9 @@ namespace DotNetCore.CAP.MongoDB var filter = new BsonDocument { { "_id", collectionName } }; var updateDef = Builders.Update.Inc("sequence_value", 1); - BsonDocument result; - if (session == null) - { - result = collection.FindOneAndUpdate(filter, updateDef, _options); - } - else - { - result = collection.FindOneAndUpdate(session, filter, updateDef, _options); - } + var result = session == null + ? collection.FindOneAndUpdate(filter, updateDef, _options) + : collection.FindOneAndUpdate(session, filter, updateDef, _options); if (result.TryGetValue("sequence_value", out var value)) { diff --git a/test/DotNetCore.CAP.MongoDB.Test/MongoDBMonitoringApiTest.cs b/test/DotNetCore.CAP.MongoDB.Test/MongoDBMonitoringApiTest.cs index a4b6817..31c0f26 100644 --- a/test/DotNetCore.CAP.MongoDB.Test/MongoDBMonitoringApiTest.cs +++ b/test/DotNetCore.CAP.MongoDB.Test/MongoDBMonitoringApiTest.cs @@ -12,9 +12,9 @@ namespace DotNetCore.CAP.MongoDB.Test { public class MongoDBMonitoringApiTest { - private MongoClient _client; - private MongoDBOptions _options; - private MongoDBMonitoringApi _api; + private readonly MongoClient _client; + private readonly MongoDBOptions _options; + private readonly MongoDBMonitoringApi _api; public MongoDBMonitoringApiTest() { diff --git a/test/DotNetCore.CAP.MongoDB.Test/MongoDBStorageConnectionTest.cs b/test/DotNetCore.CAP.MongoDB.Test/MongoDBStorageConnectionTest.cs index 1c02736..a258a5d 100644 --- a/test/DotNetCore.CAP.MongoDB.Test/MongoDBStorageConnectionTest.cs +++ b/test/DotNetCore.CAP.MongoDB.Test/MongoDBStorageConnectionTest.cs @@ -12,10 +12,10 @@ namespace DotNetCore.CAP.MongoDB.Test [TestCaseOrderer(PriorityOrderer.Name, PriorityOrderer.Assembly)] public class MongoDBStorageConnectionTest { - private MongoClient _client; - private MongoDBOptions _options; - private MongoDBStorage _storage; - private IStorageConnection _connection; + private readonly MongoClient _client; + private readonly MongoDBOptions _options; + private readonly MongoDBStorage _storage; + private readonly IStorageConnection _connection; public MongoDBStorageConnectionTest() { diff --git a/test/DotNetCore.CAP.MongoDB.Test/MongoDBStorageTest.cs b/test/DotNetCore.CAP.MongoDB.Test/MongoDBStorageTest.cs index 630956a..dbbe95e 100644 --- a/test/DotNetCore.CAP.MongoDB.Test/MongoDBStorageTest.cs +++ b/test/DotNetCore.CAP.MongoDB.Test/MongoDBStorageTest.cs @@ -9,7 +9,7 @@ namespace DotNetCore.CAP.MongoDB.Test { public class MongoDBStorageTest { - private MongoClient _client; + private readonly MongoClient _client; public MongoDBStorageTest() { diff --git a/test/DotNetCore.CAP.MongoDB.Test/MongoDBTest.cs b/test/DotNetCore.CAP.MongoDB.Test/MongoDBTest.cs index 1a91bba..096e04c 100644 --- a/test/DotNetCore.CAP.MongoDB.Test/MongoDBTest.cs +++ b/test/DotNetCore.CAP.MongoDB.Test/MongoDBTest.cs @@ -8,7 +8,7 @@ namespace DotNetCore.CAP.MongoDB.Test { public class MongoDBTest { - private MongoClient _client; + private readonly MongoClient _client; public MongoDBTest() { diff --git a/test/DotNetCore.CAP.MongoDB.Test/MongoDBUtilTest.cs b/test/DotNetCore.CAP.MongoDB.Test/MongoDBUtilTest.cs index 49e221b..0944a21 100644 --- a/test/DotNetCore.CAP.MongoDB.Test/MongoDBUtilTest.cs +++ b/test/DotNetCore.CAP.MongoDB.Test/MongoDBUtilTest.cs @@ -11,17 +11,16 @@ namespace DotNetCore.CAP.MongoDB.Test { public class MongoDBUtilTest { - private readonly MongoClient _client; private readonly IMongoDatabase _database; string _recieved = "ReceivedTest"; public MongoDBUtilTest() { - _client = new MongoClient(ConnectionUtil.ConnectionString); - _database = _client.GetDatabase("CAP_Test"); + var client = new MongoClient(ConnectionUtil.ConnectionString); + _database = client.GetDatabase("CAP_Test"); //Initialize MongoDB - if (!_database.ListCollectionNames().ToList().Any(x => x == "Counter")) + if (_database.ListCollectionNames().ToList().All(x => x != "Counter")) { var collection = _database.GetCollection("Counter"); collection.InsertOne(new BsonDocument { { "_id", _recieved }, { "sequence_value", 0 } }); From 60c12d41a1f4b62b330db7f38461ba40255c3bf0 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Mon, 23 Jul 2018 13:49:11 +0800 Subject: [PATCH 122/223] add copyright license header to code file. --- .../CAP.MongoDBCapOptionsExtension.cs | 4 +- .../CAP.MongoDBOptions.cs | 3 +- .../CAP.Options.Extensions.cs | 3 + src/DotNetCore.CAP.MongoDB/CapPublisher.cs | 44 ++++++---- .../MongoDBCollectProcessor.cs | 16 ++-- .../MongoDBMonitoringApi.cs | 84 +++++++++++++------ src/DotNetCore.CAP.MongoDB/MongoDBStorage.cs | 14 ++-- .../MongoDBStorageConnection.cs | 31 ++++--- .../MongoDBStorageTransaction.cs | 7 +- src/DotNetCore.CAP.MongoDB/MongoDBUtil.cs | 18 ++-- 10 files changed, 152 insertions(+), 72 deletions(-) diff --git a/src/DotNetCore.CAP.MongoDB/CAP.MongoDBCapOptionsExtension.cs b/src/DotNetCore.CAP.MongoDB/CAP.MongoDBCapOptionsExtension.cs index 101a57c..e07a55a 100644 --- a/src/DotNetCore.CAP.MongoDB/CAP.MongoDBCapOptionsExtension.cs +++ b/src/DotNetCore.CAP.MongoDB/CAP.MongoDBCapOptionsExtension.cs @@ -1,5 +1,7 @@ +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + using System; -using DotNetCore.CAP; using DotNetCore.CAP.Processor; using Microsoft.Extensions.DependencyInjection; diff --git a/src/DotNetCore.CAP.MongoDB/CAP.MongoDBOptions.cs b/src/DotNetCore.CAP.MongoDB/CAP.MongoDBOptions.cs index 4e6ef3a..93184aa 100644 --- a/src/DotNetCore.CAP.MongoDB/CAP.MongoDBOptions.cs +++ b/src/DotNetCore.CAP.MongoDB/CAP.MongoDBOptions.cs @@ -1,4 +1,5 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. namespace DotNetCore.CAP.MongoDB { diff --git a/src/DotNetCore.CAP.MongoDB/CAP.Options.Extensions.cs b/src/DotNetCore.CAP.MongoDB/CAP.Options.Extensions.cs index 18c846f..3a64171 100644 --- a/src/DotNetCore.CAP.MongoDB/CAP.Options.Extensions.cs +++ b/src/DotNetCore.CAP.MongoDB/CAP.Options.Extensions.cs @@ -1,3 +1,6 @@ +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + using System; using DotNetCore.CAP; using DotNetCore.CAP.MongoDB; diff --git a/src/DotNetCore.CAP.MongoDB/CapPublisher.cs b/src/DotNetCore.CAP.MongoDB/CapPublisher.cs index 74cd8b9..01f8746 100644 --- a/src/DotNetCore.CAP.MongoDB/CapPublisher.cs +++ b/src/DotNetCore.CAP.MongoDB/CapPublisher.cs @@ -1,3 +1,6 @@ +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + using System; using System.Data; using System.Threading.Tasks; @@ -12,8 +15,8 @@ namespace DotNetCore.CAP.MongoDB { public class CapPublisher : CapPublisherBase, ICallbackPublisher { - private readonly MongoDBOptions _options; private readonly IMongoDatabase _database; + private readonly MongoDBOptions _options; private bool _isInTransaction = true; public CapPublisher( @@ -37,22 +40,25 @@ namespace DotNetCore.CAP.MongoDB Enqueue(message); } - protected override int Execute(IDbConnection dbConnection, IDbTransaction dbTransaction, CapPublishedMessage message) + protected override int Execute(IDbConnection dbConnection, IDbTransaction dbTransaction, + CapPublishedMessage message) { - throw new System.NotImplementedException("Not work for MongoDB"); + throw new NotImplementedException("Not work for MongoDB"); } - protected override Task ExecuteAsync(IDbConnection dbConnection, IDbTransaction dbTransaction, CapPublishedMessage message) + protected override Task ExecuteAsync(IDbConnection dbConnection, IDbTransaction dbTransaction, + CapPublishedMessage message) { - throw new System.NotImplementedException("Not work for MongoDB"); + throw new NotImplementedException("Not work for MongoDB"); } protected override void PrepareConnectionForEF() { - throw new System.NotImplementedException("Not work for MongoDB"); + throw new NotImplementedException("Not work for MongoDB"); } - public override void PublishWithMongo(string name, T contentObj, object mongoSession = null, string callbackName = null) + public override void PublishWithMongo(string name, T contentObj, object mongoSession = null, + string callbackName = null) { var session = mongoSession as IClientSessionHandle; if (session == null) @@ -60,10 +66,11 @@ namespace DotNetCore.CAP.MongoDB _isInTransaction = false; } - PublishWithSession(name, contentObj, session, callbackName); + PublishWithSession(name, contentObj, session, callbackName); } - public override async Task PublishWithMongoAsync(string name, T contentObj, object mongoSession = null, string callbackName = null) + public override async Task PublishWithMongoAsync(string name, T contentObj, object mongoSession = null, + string callbackName = null) { var session = mongoSession as IClientSessionHandle; if (session == null) @@ -71,12 +78,12 @@ namespace DotNetCore.CAP.MongoDB _isInTransaction = false; } - await PublishWithSessionAsync(name, contentObj, session, callbackName); + await PublishWithSessionAsync(name, contentObj, session, callbackName); } private void PublishWithSession(string name, T contentObj, IClientSessionHandle session, string callbackName) { - Guid operationId = default(Guid); + var operationId = default(Guid); var content = Serialize(contentObj, callbackName); @@ -100,7 +107,7 @@ namespace DotNetCore.CAP.MongoDB Enqueue(message); } } - catch (System.Exception e) + catch (Exception e) { _logger.LogError(e, "An exception was occurred when publish message. message:" + name); s_diagnosticListener.WritePublishMessageStoreError(operationId, message, e); @@ -121,12 +128,14 @@ namespace DotNetCore.CAP.MongoDB { collection.InsertOne(message); } + return message.Id; } - private async Task PublishWithSessionAsync(string name, T contentObj, IClientSessionHandle session, string callbackName) + private async Task PublishWithSessionAsync(string name, T contentObj, IClientSessionHandle session, + string callbackName) { - Guid operationId = default(Guid); + var operationId = default(Guid); var content = Serialize(contentObj, callbackName); var message = new CapPublishedMessage @@ -152,7 +161,7 @@ namespace DotNetCore.CAP.MongoDB Enqueue(message); } } - catch (System.Exception e) + catch (Exception e) { _logger.LogError(e, "An exception was occurred when publish message async. exception message:" + name); s_diagnosticListener.WritePublishMessageStoreError(operationId, message, e); @@ -160,9 +169,11 @@ namespace DotNetCore.CAP.MongoDB throw; } } + private async Task ExecuteAsync(IClientSessionHandle session, CapPublishedMessage message) { - message.Id = await new MongoDBUtil().GetNextSequenceValueAsync(_database, _options.PublishedCollection, session); + message.Id = + await new MongoDBUtil().GetNextSequenceValueAsync(_database, _options.PublishedCollection, session); var collection = _database.GetCollection(_options.PublishedCollection); if (_isInTransaction) { @@ -172,6 +183,7 @@ namespace DotNetCore.CAP.MongoDB { await collection.InsertOneAsync(message); } + return message.Id; } } diff --git a/src/DotNetCore.CAP.MongoDB/MongoDBCollectProcessor.cs b/src/DotNetCore.CAP.MongoDB/MongoDBCollectProcessor.cs index 167c4c2..eaca4ce 100644 --- a/src/DotNetCore.CAP.MongoDB/MongoDBCollectProcessor.cs +++ b/src/DotNetCore.CAP.MongoDB/MongoDBCollectProcessor.cs @@ -1,3 +1,6 @@ +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + using System; using System.Threading.Tasks; using DotNetCore.CAP.Models; @@ -9,9 +12,9 @@ namespace DotNetCore.CAP.MongoDB { public class MongoDBCollectProcessor : ICollectProcessor { - private readonly MongoDBOptions _options; - private readonly ILogger _logger; private readonly IMongoDatabase _database; + private readonly ILogger _logger; + private readonly MongoDBOptions _options; private readonly TimeSpan _waitingInterval = TimeSpan.FromMinutes(5); public MongoDBCollectProcessor(ILogger logger, @@ -25,18 +28,21 @@ namespace DotNetCore.CAP.MongoDB public async Task ProcessAsync(ProcessingContext context) { - _logger.LogDebug($"Collecting expired data from collection [{_options.Database}].[{_options.PublishedCollection}]."); + _logger.LogDebug( + $"Collecting expired data from collection [{_options.Database}].[{_options.PublishedCollection}]."); var publishedCollection = _database.GetCollection(_options.PublishedCollection); var receivedCollection = _database.GetCollection(_options.ReceivedCollection); await publishedCollection.BulkWriteAsync(new[] { - new DeleteManyModel(Builders.Filter.Lt(x => x.ExpiresAt, DateTime.Now)) + new DeleteManyModel( + Builders.Filter.Lt(x => x.ExpiresAt, DateTime.Now)) }); await receivedCollection.BulkWriteAsync(new[] { - new DeleteManyModel(Builders.Filter.Lt(x => x.ExpiresAt, DateTime.Now)) + new DeleteManyModel( + Builders.Filter.Lt(x => x.ExpiresAt, DateTime.Now)) }); await context.WaitAsync(_waitingInterval); diff --git a/src/DotNetCore.CAP.MongoDB/MongoDBMonitoringApi.cs b/src/DotNetCore.CAP.MongoDB/MongoDBMonitoringApi.cs index b8845b9..6146ff4 100644 --- a/src/DotNetCore.CAP.MongoDB/MongoDBMonitoringApi.cs +++ b/src/DotNetCore.CAP.MongoDB/MongoDBMonitoringApi.cs @@ -1,6 +1,8 @@ +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + using System; using System.Collections.Generic; -using System.Linq; using DotNetCore.CAP.Dashboard; using DotNetCore.CAP.Dashboard.Monitoring; using DotNetCore.CAP.Infrastructure; @@ -12,8 +14,8 @@ namespace DotNetCore.CAP.MongoDB { public class MongoDBMonitoringApi : IMonitoringApi { - private readonly MongoDBOptions _options; private readonly IMongoDatabase _database; + private readonly MongoDBOptions _options; public MongoDBMonitoringApi(IMongoClient client, MongoDBOptions options) { @@ -31,20 +33,34 @@ namespace DotNetCore.CAP.MongoDB var statistics = new StatisticsDto(); { - if (int.TryParse(publishedCollection.CountDocuments(x => x.StatusName == StatusName.Succeeded).ToString(), out var count)) + if (int.TryParse( + publishedCollection.CountDocuments(x => x.StatusName == StatusName.Succeeded).ToString(), + out var count)) + { statistics.PublishedSucceeded = count; + } } { - if (int.TryParse(publishedCollection.CountDocuments(x => x.StatusName == StatusName.Failed).ToString(), out var count)) + if (int.TryParse(publishedCollection.CountDocuments(x => x.StatusName == StatusName.Failed).ToString(), + out var count)) + { statistics.PublishedFailed = count; + } } { - if (int.TryParse(receivedCollection.CountDocuments(x => x.StatusName == StatusName.Succeeded).ToString(), out var count)) + if (int.TryParse( + receivedCollection.CountDocuments(x => x.StatusName == StatusName.Succeeded).ToString(), + out var count)) + { statistics.ReceivedSucceeded = count; + } } { - if (int.TryParse(receivedCollection.CountDocuments(x => x.StatusName == StatusName.Failed).ToString(), out var count)) + if (int.TryParse(receivedCollection.CountDocuments(x => x.StatusName == StatusName.Failed).ToString(), + out var count)) + { statistics.ReceivedFailed = count; + } } return statistics; @@ -64,7 +80,9 @@ namespace DotNetCore.CAP.MongoDB { queryDto.StatusName = StatusName.Standardized(queryDto.StatusName); - var name = queryDto.MessageType == MessageType.Publish ? _options.PublishedCollection : _options.ReceivedCollection; + var name = queryDto.MessageType == MessageType.Publish + ? _options.PublishedCollection + : _options.ReceivedCollection; var collection = _database.GetCollection(name); var builder = Builders.Filter; @@ -73,14 +91,17 @@ namespace DotNetCore.CAP.MongoDB { filter = filter & builder.Eq(x => x.StatusName, queryDto.StatusName); } + if (!string.IsNullOrEmpty(queryDto.Name)) { filter = filter & builder.Eq(x => x.Name, queryDto.Name); } + if (!string.IsNullOrEmpty(queryDto.Group)) { filter = filter & builder.Eq(x => x.Group, queryDto.Group); } + if (!string.IsNullOrEmpty(queryDto.Content)) { filter = filter & builder.Regex(x => x.Content, ".*" + queryDto.Content + ".*"); @@ -119,52 +140,66 @@ namespace DotNetCore.CAP.MongoDB private int GetNumberOfMessage(string collectionName, string statusName) { var collection = _database.GetCollection(collectionName); - var count = collection.CountDocuments(new BsonDocument { { "StatusName", statusName } }); + var count = collection.CountDocuments(new BsonDocument {{"StatusName", statusName}}); return int.Parse(count.ToString()); } private IDictionary GetHourlyTimelineStats(MessageType type, string statusName) { - var collectionName = type == MessageType.Publish ? _options.PublishedCollection : _options.ReceivedCollection; + var collectionName = + type == MessageType.Publish ? _options.PublishedCollection : _options.ReceivedCollection; var endDate = DateTime.UtcNow; - var groupby = new BsonDocument { + var groupby = new BsonDocument + { { - "$group", new BsonDocument { - { "_id", new BsonDocument { - { "Key", new BsonDocument{ - { "$dateToString", new BsonDocument { - { "format", "%Y-%m-%d %H:00:00"}, - { "date", "$Added"} + "$group", new BsonDocument + { + { + "_id", new BsonDocument + { + { + "Key", new BsonDocument + { + { + "$dateToString", new BsonDocument + { + {"format", "%Y-%m-%d %H:00:00"}, + {"date", "$Added"} } } } } } }, - { "Count", new BsonDocument{{ "$sum", 1}}} + {"Count", new BsonDocument {{"$sum", 1}}} } } }; - var match = new BsonDocument { - { "$match", new BsonDocument { - { "Added", new BsonDocument + var match = new BsonDocument + { + { + "$match", new BsonDocument + { + { + "Added", new BsonDocument { - { "$gt", endDate.AddHours(-24) } + {"$gt", endDate.AddHours(-24)} } }, - { "StatusName", + { + "StatusName", new BsonDocument { - { "$eq", statusName} + {"$eq", statusName} } } } } }; - var pipeline = new[] { match, groupby }; + var pipeline = new[] {match, groupby}; var collection = _database.GetCollection(collectionName); var result = collection.Aggregate(pipeline).ToList(); @@ -175,6 +210,7 @@ namespace DotNetCore.CAP.MongoDB dic.Add(DateTime.Parse(endDate.ToLocalTime().ToString("yyyy-MM-dd HH:00:00")), 0); endDate = endDate.AddHours(-1); } + result.ForEach(d => { var key = d["_id"].AsBsonDocument["Key"].AsString; diff --git a/src/DotNetCore.CAP.MongoDB/MongoDBStorage.cs b/src/DotNetCore.CAP.MongoDB/MongoDBStorage.cs index aa924a5..3f1aaaf 100644 --- a/src/DotNetCore.CAP.MongoDB/MongoDBStorage.cs +++ b/src/DotNetCore.CAP.MongoDB/MongoDBStorage.cs @@ -1,3 +1,6 @@ +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -11,9 +14,9 @@ namespace DotNetCore.CAP.MongoDB public class MongoDBStorage : IStorage { private readonly CapOptions _capOptions; - private readonly MongoDBOptions _options; private readonly IMongoClient _client; private readonly ILogger _logger; + private readonly MongoDBOptions _options; public MongoDBStorage(CapOptions capOptions, MongoDBOptions options, @@ -53,17 +56,18 @@ namespace DotNetCore.CAP.MongoDB if (names.All(n => n != _options.PublishedCollection)) { - await database.CreateCollectionAsync(_options.PublishedCollection, cancellationToken: cancellationToken); + await database.CreateCollectionAsync(_options.PublishedCollection, + cancellationToken: cancellationToken); } if (names.All(n => n != "Counter")) { await database.CreateCollectionAsync("Counter", cancellationToken: cancellationToken); var collection = database.GetCollection("Counter"); - await collection.InsertManyAsync(new BsonDocument[] + await collection.InsertManyAsync(new[] { - new BsonDocument{{"_id", _options.PublishedCollection}, {"sequence_value", 0}}, - new BsonDocument{{"_id", _options.ReceivedCollection}, {"sequence_value", 0}} + new BsonDocument {{"_id", _options.PublishedCollection}, {"sequence_value", 0}}, + new BsonDocument {{"_id", _options.ReceivedCollection}, {"sequence_value", 0}} }, cancellationToken: cancellationToken); } diff --git a/src/DotNetCore.CAP.MongoDB/MongoDBStorageConnection.cs b/src/DotNetCore.CAP.MongoDB/MongoDBStorageConnection.cs index 76efca9..08bf04c 100644 --- a/src/DotNetCore.CAP.MongoDB/MongoDBStorageConnection.cs +++ b/src/DotNetCore.CAP.MongoDB/MongoDBStorageConnection.cs @@ -1,9 +1,11 @@ +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + using System; using System.Collections.Generic; using System.Threading.Tasks; using DotNetCore.CAP.Infrastructure; using DotNetCore.CAP.Models; -using MongoDB.Bson; using MongoDB.Driver; namespace DotNetCore.CAP.MongoDB @@ -11,9 +13,9 @@ namespace DotNetCore.CAP.MongoDB public class MongoDBStorageConnection : IStorageConnection { private readonly CapOptions _capOptions; - private readonly MongoDBOptions _options; private readonly IMongoClient _client; private readonly IMongoDatabase _database; + private readonly MongoDBOptions _options; public MongoDBStorageConnection(CapOptions capOptions, MongoDBOptions options, IMongoClient client) { @@ -28,12 +30,12 @@ namespace DotNetCore.CAP.MongoDB var collection = _database.GetCollection(_options.PublishedCollection); var updateDef = Builders - .Update.Inc(x => x.Retries, 1) - .Set(x => x.ExpiresAt, null) - .Set(x => x.StatusName, state); + .Update.Inc(x => x.Retries, 1) + .Set(x => x.ExpiresAt, null) + .Set(x => x.StatusName, state); var result = - collection.UpdateOne(x => x.Id == messageId, updateDef); + collection.UpdateOne(x => x.Id == messageId, updateDef); return result.ModifiedCount > 0; } @@ -43,12 +45,12 @@ namespace DotNetCore.CAP.MongoDB var collection = _database.GetCollection(_options.ReceivedCollection); var updateDef = Builders - .Update.Inc(x => x.Retries, 1) - .Set(x => x.ExpiresAt, null) - .Set(x => x.StatusName, state); + .Update.Inc(x => x.Retries, 1) + .Set(x => x.ExpiresAt, null) + .Set(x => x.StatusName, state); var result = - collection.UpdateOne(x => x.Id == messageId, updateDef); + collection.UpdateOne(x => x.Id == messageId, updateDef); return result.ModifiedCount > 0; } @@ -69,7 +71,8 @@ namespace DotNetCore.CAP.MongoDB var fourMinsAgo = DateTime.Now.AddMinutes(-4); var collection = _database.GetCollection(_options.PublishedCollection); return await collection - .Find(x => x.Retries < _capOptions.FailedRetryCount && x.Added < fourMinsAgo && (x.StatusName == StatusName.Failed || x.StatusName == StatusName.Scheduled)) + .Find(x => x.Retries < _capOptions.FailedRetryCount && x.Added < fourMinsAgo && + (x.StatusName == StatusName.Failed || x.StatusName == StatusName.Scheduled)) .Limit(200) .ToListAsync(); } @@ -86,7 +89,8 @@ namespace DotNetCore.CAP.MongoDB var collection = _database.GetCollection(_options.ReceivedCollection); return await collection - .Find(x => x.Retries < _capOptions.FailedRetryCount && x.Added < fourMinsAgo && (x.StatusName == StatusName.Failed || x.StatusName == StatusName.Scheduled)) + .Find(x => x.Retries < _capOptions.FailedRetryCount && x.Added < fourMinsAgo && + (x.StatusName == StatusName.Failed || x.StatusName == StatusName.Scheduled)) .Limit(200) .ToListAsync(); } @@ -97,6 +101,7 @@ namespace DotNetCore.CAP.MongoDB { throw new ArgumentNullException(nameof(message)); } + var collection = _database.GetCollection(_options.ReceivedCollection); message.Id = await new MongoDBUtil().GetNextSequenceValueAsync(_database, _options.ReceivedCollection); @@ -105,9 +110,9 @@ namespace DotNetCore.CAP.MongoDB return message.Id; } + public void Dispose() { - } } } \ No newline at end of file diff --git a/src/DotNetCore.CAP.MongoDB/MongoDBStorageTransaction.cs b/src/DotNetCore.CAP.MongoDB/MongoDBStorageTransaction.cs index aff5c76..b648e0c 100644 --- a/src/DotNetCore.CAP.MongoDB/MongoDBStorageTransaction.cs +++ b/src/DotNetCore.CAP.MongoDB/MongoDBStorageTransaction.cs @@ -1,14 +1,17 @@ +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using System.Threading.Tasks; using DotNetCore.CAP.Models; using MongoDB.Driver; -using System; namespace DotNetCore.CAP.MongoDB { internal class MongoDBStorageTransaction : IStorageTransaction { - private readonly MongoDBOptions _options; private readonly IMongoDatabase _database; + private readonly MongoDBOptions _options; private readonly IClientSessionHandle _session; public MongoDBStorageTransaction(IMongoClient client, MongoDBOptions options) diff --git a/src/DotNetCore.CAP.MongoDB/MongoDBUtil.cs b/src/DotNetCore.CAP.MongoDB/MongoDBUtil.cs index be7b33f..c645dfd 100644 --- a/src/DotNetCore.CAP.MongoDB/MongoDBUtil.cs +++ b/src/DotNetCore.CAP.MongoDB/MongoDBUtil.cs @@ -1,3 +1,6 @@ +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + using System; using System.Threading.Tasks; using MongoDB.Bson; @@ -7,17 +10,19 @@ namespace DotNetCore.CAP.MongoDB { internal class MongoDBUtil { - readonly FindOneAndUpdateOptions _options = new FindOneAndUpdateOptions() + private readonly FindOneAndUpdateOptions _options = new FindOneAndUpdateOptions { ReturnDocument = ReturnDocument.After }; - public async Task GetNextSequenceValueAsync(IMongoDatabase database, string collectionName, IClientSessionHandle session = null) + + public async Task GetNextSequenceValueAsync(IMongoDatabase database, string collectionName, + IClientSessionHandle session = null) { //https://www.tutorialspoint.com/mongodb/mongodb_autoincrement_sequence.htm var collection = database.GetCollection("Counter"); var updateDef = Builders.Update.Inc("sequence_value", 1); - var filter = new BsonDocument { { "_id", collectionName } }; + var filter = new BsonDocument {{"_id", collectionName}}; BsonDocument result; if (session == null) @@ -33,14 +38,16 @@ namespace DotNetCore.CAP.MongoDB { return value.ToInt32(); } + throw new Exception("Unable to get next sequence value."); } - public int GetNextSequenceValue(IMongoDatabase database, string collectionName, IClientSessionHandle session = null) + public int GetNextSequenceValue(IMongoDatabase database, string collectionName, + IClientSessionHandle session = null) { var collection = database.GetCollection("Counter"); - var filter = new BsonDocument { { "_id", collectionName } }; + var filter = new BsonDocument {{"_id", collectionName}}; var updateDef = Builders.Update.Inc("sequence_value", 1); var result = session == null @@ -51,6 +58,7 @@ namespace DotNetCore.CAP.MongoDB { return value.ToInt32(); } + throw new Exception("Unable to get next sequence value."); } } From b67e8ebc4a3b7f00bad765c991cfcaa75baaf659 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Mon, 23 Jul 2018 13:49:11 +0800 Subject: [PATCH 123/223] add copyright license header to code file. --- .../CAP.MongoDBCapOptionsExtension.cs | 4 +- .../CAP.MongoDBOptions.cs | 3 +- .../CAP.Options.Extensions.cs | 3 + src/DotNetCore.CAP.MongoDB/CapPublisher.cs | 44 ++++++---- .../MongoDBCollectProcessor.cs | 16 ++-- .../MongoDBMonitoringApi.cs | 84 +++++++++++++------ src/DotNetCore.CAP.MongoDB/MongoDBStorage.cs | 14 ++-- .../MongoDBStorageConnection.cs | 31 ++++--- .../MongoDBStorageTransaction.cs | 7 +- src/DotNetCore.CAP.MongoDB/MongoDBUtil.cs | 18 ++-- 10 files changed, 152 insertions(+), 72 deletions(-) diff --git a/src/DotNetCore.CAP.MongoDB/CAP.MongoDBCapOptionsExtension.cs b/src/DotNetCore.CAP.MongoDB/CAP.MongoDBCapOptionsExtension.cs index 101a57c..e07a55a 100644 --- a/src/DotNetCore.CAP.MongoDB/CAP.MongoDBCapOptionsExtension.cs +++ b/src/DotNetCore.CAP.MongoDB/CAP.MongoDBCapOptionsExtension.cs @@ -1,5 +1,7 @@ +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + using System; -using DotNetCore.CAP; using DotNetCore.CAP.Processor; using Microsoft.Extensions.DependencyInjection; diff --git a/src/DotNetCore.CAP.MongoDB/CAP.MongoDBOptions.cs b/src/DotNetCore.CAP.MongoDB/CAP.MongoDBOptions.cs index 4e6ef3a..93184aa 100644 --- a/src/DotNetCore.CAP.MongoDB/CAP.MongoDBOptions.cs +++ b/src/DotNetCore.CAP.MongoDB/CAP.MongoDBOptions.cs @@ -1,4 +1,5 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. namespace DotNetCore.CAP.MongoDB { diff --git a/src/DotNetCore.CAP.MongoDB/CAP.Options.Extensions.cs b/src/DotNetCore.CAP.MongoDB/CAP.Options.Extensions.cs index 18c846f..3a64171 100644 --- a/src/DotNetCore.CAP.MongoDB/CAP.Options.Extensions.cs +++ b/src/DotNetCore.CAP.MongoDB/CAP.Options.Extensions.cs @@ -1,3 +1,6 @@ +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + using System; using DotNetCore.CAP; using DotNetCore.CAP.MongoDB; diff --git a/src/DotNetCore.CAP.MongoDB/CapPublisher.cs b/src/DotNetCore.CAP.MongoDB/CapPublisher.cs index 74cd8b9..01f8746 100644 --- a/src/DotNetCore.CAP.MongoDB/CapPublisher.cs +++ b/src/DotNetCore.CAP.MongoDB/CapPublisher.cs @@ -1,3 +1,6 @@ +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + using System; using System.Data; using System.Threading.Tasks; @@ -12,8 +15,8 @@ namespace DotNetCore.CAP.MongoDB { public class CapPublisher : CapPublisherBase, ICallbackPublisher { - private readonly MongoDBOptions _options; private readonly IMongoDatabase _database; + private readonly MongoDBOptions _options; private bool _isInTransaction = true; public CapPublisher( @@ -37,22 +40,25 @@ namespace DotNetCore.CAP.MongoDB Enqueue(message); } - protected override int Execute(IDbConnection dbConnection, IDbTransaction dbTransaction, CapPublishedMessage message) + protected override int Execute(IDbConnection dbConnection, IDbTransaction dbTransaction, + CapPublishedMessage message) { - throw new System.NotImplementedException("Not work for MongoDB"); + throw new NotImplementedException("Not work for MongoDB"); } - protected override Task ExecuteAsync(IDbConnection dbConnection, IDbTransaction dbTransaction, CapPublishedMessage message) + protected override Task ExecuteAsync(IDbConnection dbConnection, IDbTransaction dbTransaction, + CapPublishedMessage message) { - throw new System.NotImplementedException("Not work for MongoDB"); + throw new NotImplementedException("Not work for MongoDB"); } protected override void PrepareConnectionForEF() { - throw new System.NotImplementedException("Not work for MongoDB"); + throw new NotImplementedException("Not work for MongoDB"); } - public override void PublishWithMongo(string name, T contentObj, object mongoSession = null, string callbackName = null) + public override void PublishWithMongo(string name, T contentObj, object mongoSession = null, + string callbackName = null) { var session = mongoSession as IClientSessionHandle; if (session == null) @@ -60,10 +66,11 @@ namespace DotNetCore.CAP.MongoDB _isInTransaction = false; } - PublishWithSession(name, contentObj, session, callbackName); + PublishWithSession(name, contentObj, session, callbackName); } - public override async Task PublishWithMongoAsync(string name, T contentObj, object mongoSession = null, string callbackName = null) + public override async Task PublishWithMongoAsync(string name, T contentObj, object mongoSession = null, + string callbackName = null) { var session = mongoSession as IClientSessionHandle; if (session == null) @@ -71,12 +78,12 @@ namespace DotNetCore.CAP.MongoDB _isInTransaction = false; } - await PublishWithSessionAsync(name, contentObj, session, callbackName); + await PublishWithSessionAsync(name, contentObj, session, callbackName); } private void PublishWithSession(string name, T contentObj, IClientSessionHandle session, string callbackName) { - Guid operationId = default(Guid); + var operationId = default(Guid); var content = Serialize(contentObj, callbackName); @@ -100,7 +107,7 @@ namespace DotNetCore.CAP.MongoDB Enqueue(message); } } - catch (System.Exception e) + catch (Exception e) { _logger.LogError(e, "An exception was occurred when publish message. message:" + name); s_diagnosticListener.WritePublishMessageStoreError(operationId, message, e); @@ -121,12 +128,14 @@ namespace DotNetCore.CAP.MongoDB { collection.InsertOne(message); } + return message.Id; } - private async Task PublishWithSessionAsync(string name, T contentObj, IClientSessionHandle session, string callbackName) + private async Task PublishWithSessionAsync(string name, T contentObj, IClientSessionHandle session, + string callbackName) { - Guid operationId = default(Guid); + var operationId = default(Guid); var content = Serialize(contentObj, callbackName); var message = new CapPublishedMessage @@ -152,7 +161,7 @@ namespace DotNetCore.CAP.MongoDB Enqueue(message); } } - catch (System.Exception e) + catch (Exception e) { _logger.LogError(e, "An exception was occurred when publish message async. exception message:" + name); s_diagnosticListener.WritePublishMessageStoreError(operationId, message, e); @@ -160,9 +169,11 @@ namespace DotNetCore.CAP.MongoDB throw; } } + private async Task ExecuteAsync(IClientSessionHandle session, CapPublishedMessage message) { - message.Id = await new MongoDBUtil().GetNextSequenceValueAsync(_database, _options.PublishedCollection, session); + message.Id = + await new MongoDBUtil().GetNextSequenceValueAsync(_database, _options.PublishedCollection, session); var collection = _database.GetCollection(_options.PublishedCollection); if (_isInTransaction) { @@ -172,6 +183,7 @@ namespace DotNetCore.CAP.MongoDB { await collection.InsertOneAsync(message); } + return message.Id; } } diff --git a/src/DotNetCore.CAP.MongoDB/MongoDBCollectProcessor.cs b/src/DotNetCore.CAP.MongoDB/MongoDBCollectProcessor.cs index 167c4c2..eaca4ce 100644 --- a/src/DotNetCore.CAP.MongoDB/MongoDBCollectProcessor.cs +++ b/src/DotNetCore.CAP.MongoDB/MongoDBCollectProcessor.cs @@ -1,3 +1,6 @@ +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + using System; using System.Threading.Tasks; using DotNetCore.CAP.Models; @@ -9,9 +12,9 @@ namespace DotNetCore.CAP.MongoDB { public class MongoDBCollectProcessor : ICollectProcessor { - private readonly MongoDBOptions _options; - private readonly ILogger _logger; private readonly IMongoDatabase _database; + private readonly ILogger _logger; + private readonly MongoDBOptions _options; private readonly TimeSpan _waitingInterval = TimeSpan.FromMinutes(5); public MongoDBCollectProcessor(ILogger logger, @@ -25,18 +28,21 @@ namespace DotNetCore.CAP.MongoDB public async Task ProcessAsync(ProcessingContext context) { - _logger.LogDebug($"Collecting expired data from collection [{_options.Database}].[{_options.PublishedCollection}]."); + _logger.LogDebug( + $"Collecting expired data from collection [{_options.Database}].[{_options.PublishedCollection}]."); var publishedCollection = _database.GetCollection(_options.PublishedCollection); var receivedCollection = _database.GetCollection(_options.ReceivedCollection); await publishedCollection.BulkWriteAsync(new[] { - new DeleteManyModel(Builders.Filter.Lt(x => x.ExpiresAt, DateTime.Now)) + new DeleteManyModel( + Builders.Filter.Lt(x => x.ExpiresAt, DateTime.Now)) }); await receivedCollection.BulkWriteAsync(new[] { - new DeleteManyModel(Builders.Filter.Lt(x => x.ExpiresAt, DateTime.Now)) + new DeleteManyModel( + Builders.Filter.Lt(x => x.ExpiresAt, DateTime.Now)) }); await context.WaitAsync(_waitingInterval); diff --git a/src/DotNetCore.CAP.MongoDB/MongoDBMonitoringApi.cs b/src/DotNetCore.CAP.MongoDB/MongoDBMonitoringApi.cs index b8845b9..6146ff4 100644 --- a/src/DotNetCore.CAP.MongoDB/MongoDBMonitoringApi.cs +++ b/src/DotNetCore.CAP.MongoDB/MongoDBMonitoringApi.cs @@ -1,6 +1,8 @@ +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + using System; using System.Collections.Generic; -using System.Linq; using DotNetCore.CAP.Dashboard; using DotNetCore.CAP.Dashboard.Monitoring; using DotNetCore.CAP.Infrastructure; @@ -12,8 +14,8 @@ namespace DotNetCore.CAP.MongoDB { public class MongoDBMonitoringApi : IMonitoringApi { - private readonly MongoDBOptions _options; private readonly IMongoDatabase _database; + private readonly MongoDBOptions _options; public MongoDBMonitoringApi(IMongoClient client, MongoDBOptions options) { @@ -31,20 +33,34 @@ namespace DotNetCore.CAP.MongoDB var statistics = new StatisticsDto(); { - if (int.TryParse(publishedCollection.CountDocuments(x => x.StatusName == StatusName.Succeeded).ToString(), out var count)) + if (int.TryParse( + publishedCollection.CountDocuments(x => x.StatusName == StatusName.Succeeded).ToString(), + out var count)) + { statistics.PublishedSucceeded = count; + } } { - if (int.TryParse(publishedCollection.CountDocuments(x => x.StatusName == StatusName.Failed).ToString(), out var count)) + if (int.TryParse(publishedCollection.CountDocuments(x => x.StatusName == StatusName.Failed).ToString(), + out var count)) + { statistics.PublishedFailed = count; + } } { - if (int.TryParse(receivedCollection.CountDocuments(x => x.StatusName == StatusName.Succeeded).ToString(), out var count)) + if (int.TryParse( + receivedCollection.CountDocuments(x => x.StatusName == StatusName.Succeeded).ToString(), + out var count)) + { statistics.ReceivedSucceeded = count; + } } { - if (int.TryParse(receivedCollection.CountDocuments(x => x.StatusName == StatusName.Failed).ToString(), out var count)) + if (int.TryParse(receivedCollection.CountDocuments(x => x.StatusName == StatusName.Failed).ToString(), + out var count)) + { statistics.ReceivedFailed = count; + } } return statistics; @@ -64,7 +80,9 @@ namespace DotNetCore.CAP.MongoDB { queryDto.StatusName = StatusName.Standardized(queryDto.StatusName); - var name = queryDto.MessageType == MessageType.Publish ? _options.PublishedCollection : _options.ReceivedCollection; + var name = queryDto.MessageType == MessageType.Publish + ? _options.PublishedCollection + : _options.ReceivedCollection; var collection = _database.GetCollection(name); var builder = Builders.Filter; @@ -73,14 +91,17 @@ namespace DotNetCore.CAP.MongoDB { filter = filter & builder.Eq(x => x.StatusName, queryDto.StatusName); } + if (!string.IsNullOrEmpty(queryDto.Name)) { filter = filter & builder.Eq(x => x.Name, queryDto.Name); } + if (!string.IsNullOrEmpty(queryDto.Group)) { filter = filter & builder.Eq(x => x.Group, queryDto.Group); } + if (!string.IsNullOrEmpty(queryDto.Content)) { filter = filter & builder.Regex(x => x.Content, ".*" + queryDto.Content + ".*"); @@ -119,52 +140,66 @@ namespace DotNetCore.CAP.MongoDB private int GetNumberOfMessage(string collectionName, string statusName) { var collection = _database.GetCollection(collectionName); - var count = collection.CountDocuments(new BsonDocument { { "StatusName", statusName } }); + var count = collection.CountDocuments(new BsonDocument {{"StatusName", statusName}}); return int.Parse(count.ToString()); } private IDictionary GetHourlyTimelineStats(MessageType type, string statusName) { - var collectionName = type == MessageType.Publish ? _options.PublishedCollection : _options.ReceivedCollection; + var collectionName = + type == MessageType.Publish ? _options.PublishedCollection : _options.ReceivedCollection; var endDate = DateTime.UtcNow; - var groupby = new BsonDocument { + var groupby = new BsonDocument + { { - "$group", new BsonDocument { - { "_id", new BsonDocument { - { "Key", new BsonDocument{ - { "$dateToString", new BsonDocument { - { "format", "%Y-%m-%d %H:00:00"}, - { "date", "$Added"} + "$group", new BsonDocument + { + { + "_id", new BsonDocument + { + { + "Key", new BsonDocument + { + { + "$dateToString", new BsonDocument + { + {"format", "%Y-%m-%d %H:00:00"}, + {"date", "$Added"} } } } } } }, - { "Count", new BsonDocument{{ "$sum", 1}}} + {"Count", new BsonDocument {{"$sum", 1}}} } } }; - var match = new BsonDocument { - { "$match", new BsonDocument { - { "Added", new BsonDocument + var match = new BsonDocument + { + { + "$match", new BsonDocument + { + { + "Added", new BsonDocument { - { "$gt", endDate.AddHours(-24) } + {"$gt", endDate.AddHours(-24)} } }, - { "StatusName", + { + "StatusName", new BsonDocument { - { "$eq", statusName} + {"$eq", statusName} } } } } }; - var pipeline = new[] { match, groupby }; + var pipeline = new[] {match, groupby}; var collection = _database.GetCollection(collectionName); var result = collection.Aggregate(pipeline).ToList(); @@ -175,6 +210,7 @@ namespace DotNetCore.CAP.MongoDB dic.Add(DateTime.Parse(endDate.ToLocalTime().ToString("yyyy-MM-dd HH:00:00")), 0); endDate = endDate.AddHours(-1); } + result.ForEach(d => { var key = d["_id"].AsBsonDocument["Key"].AsString; diff --git a/src/DotNetCore.CAP.MongoDB/MongoDBStorage.cs b/src/DotNetCore.CAP.MongoDB/MongoDBStorage.cs index aa924a5..3f1aaaf 100644 --- a/src/DotNetCore.CAP.MongoDB/MongoDBStorage.cs +++ b/src/DotNetCore.CAP.MongoDB/MongoDBStorage.cs @@ -1,3 +1,6 @@ +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -11,9 +14,9 @@ namespace DotNetCore.CAP.MongoDB public class MongoDBStorage : IStorage { private readonly CapOptions _capOptions; - private readonly MongoDBOptions _options; private readonly IMongoClient _client; private readonly ILogger _logger; + private readonly MongoDBOptions _options; public MongoDBStorage(CapOptions capOptions, MongoDBOptions options, @@ -53,17 +56,18 @@ namespace DotNetCore.CAP.MongoDB if (names.All(n => n != _options.PublishedCollection)) { - await database.CreateCollectionAsync(_options.PublishedCollection, cancellationToken: cancellationToken); + await database.CreateCollectionAsync(_options.PublishedCollection, + cancellationToken: cancellationToken); } if (names.All(n => n != "Counter")) { await database.CreateCollectionAsync("Counter", cancellationToken: cancellationToken); var collection = database.GetCollection("Counter"); - await collection.InsertManyAsync(new BsonDocument[] + await collection.InsertManyAsync(new[] { - new BsonDocument{{"_id", _options.PublishedCollection}, {"sequence_value", 0}}, - new BsonDocument{{"_id", _options.ReceivedCollection}, {"sequence_value", 0}} + new BsonDocument {{"_id", _options.PublishedCollection}, {"sequence_value", 0}}, + new BsonDocument {{"_id", _options.ReceivedCollection}, {"sequence_value", 0}} }, cancellationToken: cancellationToken); } diff --git a/src/DotNetCore.CAP.MongoDB/MongoDBStorageConnection.cs b/src/DotNetCore.CAP.MongoDB/MongoDBStorageConnection.cs index 76efca9..08bf04c 100644 --- a/src/DotNetCore.CAP.MongoDB/MongoDBStorageConnection.cs +++ b/src/DotNetCore.CAP.MongoDB/MongoDBStorageConnection.cs @@ -1,9 +1,11 @@ +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + using System; using System.Collections.Generic; using System.Threading.Tasks; using DotNetCore.CAP.Infrastructure; using DotNetCore.CAP.Models; -using MongoDB.Bson; using MongoDB.Driver; namespace DotNetCore.CAP.MongoDB @@ -11,9 +13,9 @@ namespace DotNetCore.CAP.MongoDB public class MongoDBStorageConnection : IStorageConnection { private readonly CapOptions _capOptions; - private readonly MongoDBOptions _options; private readonly IMongoClient _client; private readonly IMongoDatabase _database; + private readonly MongoDBOptions _options; public MongoDBStorageConnection(CapOptions capOptions, MongoDBOptions options, IMongoClient client) { @@ -28,12 +30,12 @@ namespace DotNetCore.CAP.MongoDB var collection = _database.GetCollection(_options.PublishedCollection); var updateDef = Builders - .Update.Inc(x => x.Retries, 1) - .Set(x => x.ExpiresAt, null) - .Set(x => x.StatusName, state); + .Update.Inc(x => x.Retries, 1) + .Set(x => x.ExpiresAt, null) + .Set(x => x.StatusName, state); var result = - collection.UpdateOne(x => x.Id == messageId, updateDef); + collection.UpdateOne(x => x.Id == messageId, updateDef); return result.ModifiedCount > 0; } @@ -43,12 +45,12 @@ namespace DotNetCore.CAP.MongoDB var collection = _database.GetCollection(_options.ReceivedCollection); var updateDef = Builders - .Update.Inc(x => x.Retries, 1) - .Set(x => x.ExpiresAt, null) - .Set(x => x.StatusName, state); + .Update.Inc(x => x.Retries, 1) + .Set(x => x.ExpiresAt, null) + .Set(x => x.StatusName, state); var result = - collection.UpdateOne(x => x.Id == messageId, updateDef); + collection.UpdateOne(x => x.Id == messageId, updateDef); return result.ModifiedCount > 0; } @@ -69,7 +71,8 @@ namespace DotNetCore.CAP.MongoDB var fourMinsAgo = DateTime.Now.AddMinutes(-4); var collection = _database.GetCollection(_options.PublishedCollection); return await collection - .Find(x => x.Retries < _capOptions.FailedRetryCount && x.Added < fourMinsAgo && (x.StatusName == StatusName.Failed || x.StatusName == StatusName.Scheduled)) + .Find(x => x.Retries < _capOptions.FailedRetryCount && x.Added < fourMinsAgo && + (x.StatusName == StatusName.Failed || x.StatusName == StatusName.Scheduled)) .Limit(200) .ToListAsync(); } @@ -86,7 +89,8 @@ namespace DotNetCore.CAP.MongoDB var collection = _database.GetCollection(_options.ReceivedCollection); return await collection - .Find(x => x.Retries < _capOptions.FailedRetryCount && x.Added < fourMinsAgo && (x.StatusName == StatusName.Failed || x.StatusName == StatusName.Scheduled)) + .Find(x => x.Retries < _capOptions.FailedRetryCount && x.Added < fourMinsAgo && + (x.StatusName == StatusName.Failed || x.StatusName == StatusName.Scheduled)) .Limit(200) .ToListAsync(); } @@ -97,6 +101,7 @@ namespace DotNetCore.CAP.MongoDB { throw new ArgumentNullException(nameof(message)); } + var collection = _database.GetCollection(_options.ReceivedCollection); message.Id = await new MongoDBUtil().GetNextSequenceValueAsync(_database, _options.ReceivedCollection); @@ -105,9 +110,9 @@ namespace DotNetCore.CAP.MongoDB return message.Id; } + public void Dispose() { - } } } \ No newline at end of file diff --git a/src/DotNetCore.CAP.MongoDB/MongoDBStorageTransaction.cs b/src/DotNetCore.CAP.MongoDB/MongoDBStorageTransaction.cs index aff5c76..b648e0c 100644 --- a/src/DotNetCore.CAP.MongoDB/MongoDBStorageTransaction.cs +++ b/src/DotNetCore.CAP.MongoDB/MongoDBStorageTransaction.cs @@ -1,14 +1,17 @@ +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using System.Threading.Tasks; using DotNetCore.CAP.Models; using MongoDB.Driver; -using System; namespace DotNetCore.CAP.MongoDB { internal class MongoDBStorageTransaction : IStorageTransaction { - private readonly MongoDBOptions _options; private readonly IMongoDatabase _database; + private readonly MongoDBOptions _options; private readonly IClientSessionHandle _session; public MongoDBStorageTransaction(IMongoClient client, MongoDBOptions options) diff --git a/src/DotNetCore.CAP.MongoDB/MongoDBUtil.cs b/src/DotNetCore.CAP.MongoDB/MongoDBUtil.cs index be7b33f..c645dfd 100644 --- a/src/DotNetCore.CAP.MongoDB/MongoDBUtil.cs +++ b/src/DotNetCore.CAP.MongoDB/MongoDBUtil.cs @@ -1,3 +1,6 @@ +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + using System; using System.Threading.Tasks; using MongoDB.Bson; @@ -7,17 +10,19 @@ namespace DotNetCore.CAP.MongoDB { internal class MongoDBUtil { - readonly FindOneAndUpdateOptions _options = new FindOneAndUpdateOptions() + private readonly FindOneAndUpdateOptions _options = new FindOneAndUpdateOptions { ReturnDocument = ReturnDocument.After }; - public async Task GetNextSequenceValueAsync(IMongoDatabase database, string collectionName, IClientSessionHandle session = null) + + public async Task GetNextSequenceValueAsync(IMongoDatabase database, string collectionName, + IClientSessionHandle session = null) { //https://www.tutorialspoint.com/mongodb/mongodb_autoincrement_sequence.htm var collection = database.GetCollection("Counter"); var updateDef = Builders.Update.Inc("sequence_value", 1); - var filter = new BsonDocument { { "_id", collectionName } }; + var filter = new BsonDocument {{"_id", collectionName}}; BsonDocument result; if (session == null) @@ -33,14 +38,16 @@ namespace DotNetCore.CAP.MongoDB { return value.ToInt32(); } + throw new Exception("Unable to get next sequence value."); } - public int GetNextSequenceValue(IMongoDatabase database, string collectionName, IClientSessionHandle session = null) + public int GetNextSequenceValue(IMongoDatabase database, string collectionName, + IClientSessionHandle session = null) { var collection = database.GetCollection("Counter"); - var filter = new BsonDocument { { "_id", collectionName } }; + var filter = new BsonDocument {{"_id", collectionName}}; var updateDef = Builders.Update.Inc("sequence_value", 1); var result = session == null @@ -51,6 +58,7 @@ namespace DotNetCore.CAP.MongoDB { return value.ToInt32(); } + throw new Exception("Unable to get next sequence value."); } } From 5c076aa56f0c018855fe291994dcb4c22c5f503a Mon Sep 17 00:00:00 2001 From: Savorboard Date: Mon, 23 Jul 2018 16:22:58 +0800 Subject: [PATCH 124/223] =?UTF-8?q?tweak=20database=20configuration=20name?= =?UTF-8?q?=20=20to=20=E2=80=98DatabaseName=E2=80=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CAP.MongoDBOptions.cs | 26 ++++++++++++++----- src/DotNetCore.CAP.MongoDB/CapPublisher.cs | 2 +- .../MongoDBCollectProcessor.cs | 4 +-- .../MongoDBMonitoringApi.cs | 2 +- src/DotNetCore.CAP.MongoDB/MongoDBStorage.cs | 8 +++--- .../MongoDBStorageConnection.cs | 2 +- .../MongoDBStorageTransaction.cs | 2 +- src/DotNetCore.CAP.MongoDB/MongoDBUtil.cs | 8 +++--- 8 files changed, 34 insertions(+), 20 deletions(-) diff --git a/src/DotNetCore.CAP.MongoDB/CAP.MongoDBOptions.cs b/src/DotNetCore.CAP.MongoDB/CAP.MongoDBOptions.cs index 93184aa..98ef69b 100644 --- a/src/DotNetCore.CAP.MongoDB/CAP.MongoDBOptions.cs +++ b/src/DotNetCore.CAP.MongoDB/CAP.MongoDBOptions.cs @@ -5,16 +5,30 @@ namespace DotNetCore.CAP.MongoDB { public class MongoDBOptions { - public const string DefaultDatabase = "Cap"; + /// + /// Gets or sets the database name to use when creating database objects. + /// Default value: "cap" + /// + public string DatabaseName { get; set; } = "cap"; /// - /// Gets or sets the database to use when creating database objects. - /// Default is . + /// MongoDB database connection string. + /// Default value: "mongodb://localhost:27017" /// - public string Database { get; set; } = DefaultDatabase; + public string DatabaseConnection { get; set; } = "mongodb://localhost:27017"; - public string ReceivedCollection { get; } = "Received"; + /// + /// MongoDB received message collection name. + /// Default value: "received" + /// + public string ReceivedCollection { get; set; } = "cap.received"; + + /// + /// MongoDB published message collection name. + /// Default value: "published" + /// + public string PublishedCollection { get; set; } = "cap.published"; - public string PublishedCollection { get; } = "Published"; + internal const string CounterCollection = "cap.counter"; } } \ No newline at end of file diff --git a/src/DotNetCore.CAP.MongoDB/CapPublisher.cs b/src/DotNetCore.CAP.MongoDB/CapPublisher.cs index 01f8746..475d947 100644 --- a/src/DotNetCore.CAP.MongoDB/CapPublisher.cs +++ b/src/DotNetCore.CAP.MongoDB/CapPublisher.cs @@ -28,7 +28,7 @@ namespace DotNetCore.CAP.MongoDB : base(logger, dispatcher) { _options = options; - _database = client.GetDatabase(_options.Database); + _database = client.GetDatabase(_options.DatabaseName); ServiceProvider = provider; } diff --git a/src/DotNetCore.CAP.MongoDB/MongoDBCollectProcessor.cs b/src/DotNetCore.CAP.MongoDB/MongoDBCollectProcessor.cs index eaca4ce..6b19878 100644 --- a/src/DotNetCore.CAP.MongoDB/MongoDBCollectProcessor.cs +++ b/src/DotNetCore.CAP.MongoDB/MongoDBCollectProcessor.cs @@ -23,13 +23,13 @@ namespace DotNetCore.CAP.MongoDB { _options = options; _logger = logger; - _database = client.GetDatabase(_options.Database); + _database = client.GetDatabase(_options.DatabaseName); } public async Task ProcessAsync(ProcessingContext context) { _logger.LogDebug( - $"Collecting expired data from collection [{_options.Database}].[{_options.PublishedCollection}]."); + $"Collecting expired data from collection [{_options.PublishedCollection}]."); var publishedCollection = _database.GetCollection(_options.PublishedCollection); var receivedCollection = _database.GetCollection(_options.ReceivedCollection); diff --git a/src/DotNetCore.CAP.MongoDB/MongoDBMonitoringApi.cs b/src/DotNetCore.CAP.MongoDB/MongoDBMonitoringApi.cs index 6146ff4..0553b1b 100644 --- a/src/DotNetCore.CAP.MongoDB/MongoDBMonitoringApi.cs +++ b/src/DotNetCore.CAP.MongoDB/MongoDBMonitoringApi.cs @@ -22,7 +22,7 @@ namespace DotNetCore.CAP.MongoDB var mongoClient = client ?? throw new ArgumentNullException(nameof(client)); _options = options ?? throw new ArgumentNullException(nameof(options)); - _database = mongoClient.GetDatabase(_options.Database); + _database = mongoClient.GetDatabase(_options.DatabaseName); } public StatisticsDto GetStatistics() diff --git a/src/DotNetCore.CAP.MongoDB/MongoDBStorage.cs b/src/DotNetCore.CAP.MongoDB/MongoDBStorage.cs index 3f1aaaf..24fa082 100644 --- a/src/DotNetCore.CAP.MongoDB/MongoDBStorage.cs +++ b/src/DotNetCore.CAP.MongoDB/MongoDBStorage.cs @@ -46,7 +46,7 @@ namespace DotNetCore.CAP.MongoDB return; } - var database = _client.GetDatabase(_options.Database); + var database = _client.GetDatabase(_options.DatabaseName); var names = (await database.ListCollectionNamesAsync(cancellationToken: cancellationToken))?.ToList(); if (!names.Any(n => n == _options.ReceivedCollection)) @@ -60,10 +60,10 @@ namespace DotNetCore.CAP.MongoDB cancellationToken: cancellationToken); } - if (names.All(n => n != "Counter")) + if (names.All(n => n != MongoDBOptions.CounterCollection)) { - await database.CreateCollectionAsync("Counter", cancellationToken: cancellationToken); - var collection = database.GetCollection("Counter"); + await database.CreateCollectionAsync(MongoDBOptions.CounterCollection, cancellationToken: cancellationToken); + var collection = database.GetCollection(MongoDBOptions.CounterCollection); await collection.InsertManyAsync(new[] { new BsonDocument {{"_id", _options.PublishedCollection}, {"sequence_value", 0}}, diff --git a/src/DotNetCore.CAP.MongoDB/MongoDBStorageConnection.cs b/src/DotNetCore.CAP.MongoDB/MongoDBStorageConnection.cs index 08bf04c..098ddf9 100644 --- a/src/DotNetCore.CAP.MongoDB/MongoDBStorageConnection.cs +++ b/src/DotNetCore.CAP.MongoDB/MongoDBStorageConnection.cs @@ -22,7 +22,7 @@ namespace DotNetCore.CAP.MongoDB _capOptions = capOptions; _options = options; _client = client; - _database = _client.GetDatabase(_options.Database); + _database = _client.GetDatabase(_options.DatabaseName); } public bool ChangePublishedState(int messageId, string state) diff --git a/src/DotNetCore.CAP.MongoDB/MongoDBStorageTransaction.cs b/src/DotNetCore.CAP.MongoDB/MongoDBStorageTransaction.cs index b648e0c..a2b2390 100644 --- a/src/DotNetCore.CAP.MongoDB/MongoDBStorageTransaction.cs +++ b/src/DotNetCore.CAP.MongoDB/MongoDBStorageTransaction.cs @@ -17,7 +17,7 @@ namespace DotNetCore.CAP.MongoDB public MongoDBStorageTransaction(IMongoClient client, MongoDBOptions options) { _options = options; - _database = client.GetDatabase(options.Database); + _database = client.GetDatabase(options.DatabaseName); _session = client.StartSession(); _session.StartTransaction(); } diff --git a/src/DotNetCore.CAP.MongoDB/MongoDBUtil.cs b/src/DotNetCore.CAP.MongoDB/MongoDBUtil.cs index c645dfd..3d3c1f7 100644 --- a/src/DotNetCore.CAP.MongoDB/MongoDBUtil.cs +++ b/src/DotNetCore.CAP.MongoDB/MongoDBUtil.cs @@ -19,10 +19,10 @@ namespace DotNetCore.CAP.MongoDB IClientSessionHandle session = null) { //https://www.tutorialspoint.com/mongodb/mongodb_autoincrement_sequence.htm - var collection = database.GetCollection("Counter"); + var collection = database.GetCollection(MongoDBOptions.CounterCollection); var updateDef = Builders.Update.Inc("sequence_value", 1); - var filter = new BsonDocument {{"_id", collectionName}}; + var filter = new BsonDocument { { "_id", collectionName } }; BsonDocument result; if (session == null) @@ -45,9 +45,9 @@ namespace DotNetCore.CAP.MongoDB public int GetNextSequenceValue(IMongoDatabase database, string collectionName, IClientSessionHandle session = null) { - var collection = database.GetCollection("Counter"); + var collection = database.GetCollection(MongoDBOptions.CounterCollection); - var filter = new BsonDocument {{"_id", collectionName}}; + var filter = new BsonDocument { { "_id", collectionName } }; var updateDef = Builders.Update.Inc("sequence_value", 1); var result = session == null From 308539032b6a4feee1d9d37a1d49de03be1cd984 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Mon, 23 Jul 2018 16:22:58 +0800 Subject: [PATCH 125/223] =?UTF-8?q?tweak=20database=20configuration=20name?= =?UTF-8?q?=20=20to=20=E2=80=98DatabaseName=E2=80=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CAP.MongoDBOptions.cs | 26 ++++++++++++++----- src/DotNetCore.CAP.MongoDB/CapPublisher.cs | 2 +- .../MongoDBCollectProcessor.cs | 4 +-- .../MongoDBMonitoringApi.cs | 2 +- src/DotNetCore.CAP.MongoDB/MongoDBStorage.cs | 8 +++--- .../MongoDBStorageConnection.cs | 2 +- .../MongoDBStorageTransaction.cs | 2 +- src/DotNetCore.CAP.MongoDB/MongoDBUtil.cs | 8 +++--- 8 files changed, 34 insertions(+), 20 deletions(-) diff --git a/src/DotNetCore.CAP.MongoDB/CAP.MongoDBOptions.cs b/src/DotNetCore.CAP.MongoDB/CAP.MongoDBOptions.cs index 93184aa..98ef69b 100644 --- a/src/DotNetCore.CAP.MongoDB/CAP.MongoDBOptions.cs +++ b/src/DotNetCore.CAP.MongoDB/CAP.MongoDBOptions.cs @@ -5,16 +5,30 @@ namespace DotNetCore.CAP.MongoDB { public class MongoDBOptions { - public const string DefaultDatabase = "Cap"; + /// + /// Gets or sets the database name to use when creating database objects. + /// Default value: "cap" + /// + public string DatabaseName { get; set; } = "cap"; /// - /// Gets or sets the database to use when creating database objects. - /// Default is . + /// MongoDB database connection string. + /// Default value: "mongodb://localhost:27017" /// - public string Database { get; set; } = DefaultDatabase; + public string DatabaseConnection { get; set; } = "mongodb://localhost:27017"; - public string ReceivedCollection { get; } = "Received"; + /// + /// MongoDB received message collection name. + /// Default value: "received" + /// + public string ReceivedCollection { get; set; } = "cap.received"; + + /// + /// MongoDB published message collection name. + /// Default value: "published" + /// + public string PublishedCollection { get; set; } = "cap.published"; - public string PublishedCollection { get; } = "Published"; + internal const string CounterCollection = "cap.counter"; } } \ No newline at end of file diff --git a/src/DotNetCore.CAP.MongoDB/CapPublisher.cs b/src/DotNetCore.CAP.MongoDB/CapPublisher.cs index 01f8746..475d947 100644 --- a/src/DotNetCore.CAP.MongoDB/CapPublisher.cs +++ b/src/DotNetCore.CAP.MongoDB/CapPublisher.cs @@ -28,7 +28,7 @@ namespace DotNetCore.CAP.MongoDB : base(logger, dispatcher) { _options = options; - _database = client.GetDatabase(_options.Database); + _database = client.GetDatabase(_options.DatabaseName); ServiceProvider = provider; } diff --git a/src/DotNetCore.CAP.MongoDB/MongoDBCollectProcessor.cs b/src/DotNetCore.CAP.MongoDB/MongoDBCollectProcessor.cs index eaca4ce..6b19878 100644 --- a/src/DotNetCore.CAP.MongoDB/MongoDBCollectProcessor.cs +++ b/src/DotNetCore.CAP.MongoDB/MongoDBCollectProcessor.cs @@ -23,13 +23,13 @@ namespace DotNetCore.CAP.MongoDB { _options = options; _logger = logger; - _database = client.GetDatabase(_options.Database); + _database = client.GetDatabase(_options.DatabaseName); } public async Task ProcessAsync(ProcessingContext context) { _logger.LogDebug( - $"Collecting expired data from collection [{_options.Database}].[{_options.PublishedCollection}]."); + $"Collecting expired data from collection [{_options.PublishedCollection}]."); var publishedCollection = _database.GetCollection(_options.PublishedCollection); var receivedCollection = _database.GetCollection(_options.ReceivedCollection); diff --git a/src/DotNetCore.CAP.MongoDB/MongoDBMonitoringApi.cs b/src/DotNetCore.CAP.MongoDB/MongoDBMonitoringApi.cs index 6146ff4..0553b1b 100644 --- a/src/DotNetCore.CAP.MongoDB/MongoDBMonitoringApi.cs +++ b/src/DotNetCore.CAP.MongoDB/MongoDBMonitoringApi.cs @@ -22,7 +22,7 @@ namespace DotNetCore.CAP.MongoDB var mongoClient = client ?? throw new ArgumentNullException(nameof(client)); _options = options ?? throw new ArgumentNullException(nameof(options)); - _database = mongoClient.GetDatabase(_options.Database); + _database = mongoClient.GetDatabase(_options.DatabaseName); } public StatisticsDto GetStatistics() diff --git a/src/DotNetCore.CAP.MongoDB/MongoDBStorage.cs b/src/DotNetCore.CAP.MongoDB/MongoDBStorage.cs index 3f1aaaf..24fa082 100644 --- a/src/DotNetCore.CAP.MongoDB/MongoDBStorage.cs +++ b/src/DotNetCore.CAP.MongoDB/MongoDBStorage.cs @@ -46,7 +46,7 @@ namespace DotNetCore.CAP.MongoDB return; } - var database = _client.GetDatabase(_options.Database); + var database = _client.GetDatabase(_options.DatabaseName); var names = (await database.ListCollectionNamesAsync(cancellationToken: cancellationToken))?.ToList(); if (!names.Any(n => n == _options.ReceivedCollection)) @@ -60,10 +60,10 @@ namespace DotNetCore.CAP.MongoDB cancellationToken: cancellationToken); } - if (names.All(n => n != "Counter")) + if (names.All(n => n != MongoDBOptions.CounterCollection)) { - await database.CreateCollectionAsync("Counter", cancellationToken: cancellationToken); - var collection = database.GetCollection("Counter"); + await database.CreateCollectionAsync(MongoDBOptions.CounterCollection, cancellationToken: cancellationToken); + var collection = database.GetCollection(MongoDBOptions.CounterCollection); await collection.InsertManyAsync(new[] { new BsonDocument {{"_id", _options.PublishedCollection}, {"sequence_value", 0}}, diff --git a/src/DotNetCore.CAP.MongoDB/MongoDBStorageConnection.cs b/src/DotNetCore.CAP.MongoDB/MongoDBStorageConnection.cs index 08bf04c..098ddf9 100644 --- a/src/DotNetCore.CAP.MongoDB/MongoDBStorageConnection.cs +++ b/src/DotNetCore.CAP.MongoDB/MongoDBStorageConnection.cs @@ -22,7 +22,7 @@ namespace DotNetCore.CAP.MongoDB _capOptions = capOptions; _options = options; _client = client; - _database = _client.GetDatabase(_options.Database); + _database = _client.GetDatabase(_options.DatabaseName); } public bool ChangePublishedState(int messageId, string state) diff --git a/src/DotNetCore.CAP.MongoDB/MongoDBStorageTransaction.cs b/src/DotNetCore.CAP.MongoDB/MongoDBStorageTransaction.cs index b648e0c..a2b2390 100644 --- a/src/DotNetCore.CAP.MongoDB/MongoDBStorageTransaction.cs +++ b/src/DotNetCore.CAP.MongoDB/MongoDBStorageTransaction.cs @@ -17,7 +17,7 @@ namespace DotNetCore.CAP.MongoDB public MongoDBStorageTransaction(IMongoClient client, MongoDBOptions options) { _options = options; - _database = client.GetDatabase(options.Database); + _database = client.GetDatabase(options.DatabaseName); _session = client.StartSession(); _session.StartTransaction(); } diff --git a/src/DotNetCore.CAP.MongoDB/MongoDBUtil.cs b/src/DotNetCore.CAP.MongoDB/MongoDBUtil.cs index c645dfd..3d3c1f7 100644 --- a/src/DotNetCore.CAP.MongoDB/MongoDBUtil.cs +++ b/src/DotNetCore.CAP.MongoDB/MongoDBUtil.cs @@ -19,10 +19,10 @@ namespace DotNetCore.CAP.MongoDB IClientSessionHandle session = null) { //https://www.tutorialspoint.com/mongodb/mongodb_autoincrement_sequence.htm - var collection = database.GetCollection("Counter"); + var collection = database.GetCollection(MongoDBOptions.CounterCollection); var updateDef = Builders.Update.Inc("sequence_value", 1); - var filter = new BsonDocument {{"_id", collectionName}}; + var filter = new BsonDocument { { "_id", collectionName } }; BsonDocument result; if (session == null) @@ -45,9 +45,9 @@ namespace DotNetCore.CAP.MongoDB public int GetNextSequenceValue(IMongoDatabase database, string collectionName, IClientSessionHandle session = null) { - var collection = database.GetCollection("Counter"); + var collection = database.GetCollection(MongoDBOptions.CounterCollection); - var filter = new BsonDocument {{"_id", collectionName}}; + var filter = new BsonDocument { { "_id", collectionName } }; var updateDef = Builders.Update.Inc("sequence_value", 1); var result = session == null From 2b4cd5576707c3b0fa00ab1f187c196b196b5661 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Mon, 23 Jul 2018 16:24:17 +0800 Subject: [PATCH 126/223] Refactoring unit tests have been adapted to apperyor --- .../ConnectionUtil.cs | 2 +- .../DatabaseTestHost.cs | 55 +++++++++++++++++ .../DotNetCore.CAP.MongoDB.Test.csproj | 11 ++-- .../MongoDBMonitoringApiTest.cs | 33 ++++------ .../MongoDBStorageConnectionTest.cs | 60 +++++++++++-------- .../MongoDBStorageTest.cs | 37 +++++------- ...ngoDBTest.cs => MongoDBTransactionTest.cs} | 24 +++----- .../MongoDBUtilTest.cs | 27 ++------- 8 files changed, 133 insertions(+), 116 deletions(-) create mode 100644 test/DotNetCore.CAP.MongoDB.Test/DatabaseTestHost.cs rename test/DotNetCore.CAP.MongoDB.Test/{MongoDBTest.cs => MongoDBTransactionTest.cs} (79%) diff --git a/test/DotNetCore.CAP.MongoDB.Test/ConnectionUtil.cs b/test/DotNetCore.CAP.MongoDB.Test/ConnectionUtil.cs index 9c8aa29..da8de03 100644 --- a/test/DotNetCore.CAP.MongoDB.Test/ConnectionUtil.cs +++ b/test/DotNetCore.CAP.MongoDB.Test/ConnectionUtil.cs @@ -2,6 +2,6 @@ namespace DotNetCore.CAP.MongoDB.Test { public class ConnectionUtil { - public static string ConnectionString = "mongodb://mongo1:27017,mongo2:27018,mongo3:27019/?replicaSet=my-mongo-set"; + public static string ConnectionString = "mongodb://localhost:27017"; } } \ No newline at end of file diff --git a/test/DotNetCore.CAP.MongoDB.Test/DatabaseTestHost.cs b/test/DotNetCore.CAP.MongoDB.Test/DatabaseTestHost.cs new file mode 100644 index 0000000..cd837fb --- /dev/null +++ b/test/DotNetCore.CAP.MongoDB.Test/DatabaseTestHost.cs @@ -0,0 +1,55 @@ +using System; +using System.Threading; +using Microsoft.Extensions.DependencyInjection; +using MongoDB.Driver; + +namespace DotNetCore.CAP.MongoDB.Test +{ + public abstract class DatabaseTestHost : IDisposable + { + private string _connectionString; + + protected IServiceProvider Provider { get; private set; } + protected IMongoClient MongoClient => Provider.GetService(); + protected IMongoDatabase Database => MongoClient.GetDatabase(MongoDBOptions.DatabaseName); + protected CapOptions CapOptions => Provider.GetService(); + protected MongoDBOptions MongoDBOptions => Provider.GetService(); + + protected DatabaseTestHost() + { + CreateServiceCollection(); + CreateDatabase(); + } + + private void CreateDatabase() + { + Provider.GetService().InitializeAsync(CancellationToken.None).GetAwaiter().GetResult(); + } + + protected virtual void AddService(ServiceCollection serviceCollection) + { + + } + + private void CreateServiceCollection() + { + var services = new ServiceCollection(); + services.AddOptions(); + services.AddLogging(); + _connectionString = ConnectionUtil.ConnectionString; + + services.AddSingleton(new MongoDBOptions() { DatabaseConnection = _connectionString }); + services.AddSingleton(new CapOptions()); + services.AddSingleton(x => new MongoClient(_connectionString)); + services.AddSingleton(); + + AddService(services); + Provider = services.BuildServiceProvider(); + } + + public void Dispose() + { + MongoClient.DropDatabase(MongoDBOptions.DatabaseName); + } + } +} \ No newline at end of file diff --git a/test/DotNetCore.CAP.MongoDB.Test/DotNetCore.CAP.MongoDB.Test.csproj b/test/DotNetCore.CAP.MongoDB.Test/DotNetCore.CAP.MongoDB.Test.csproj index 5ec46fc..9fb481e 100644 --- a/test/DotNetCore.CAP.MongoDB.Test/DotNetCore.CAP.MongoDB.Test.csproj +++ b/test/DotNetCore.CAP.MongoDB.Test/DotNetCore.CAP.MongoDB.Test.csproj @@ -1,19 +1,18 @@ - + netcoreapp2.1 - false + + - + - - - + diff --git a/test/DotNetCore.CAP.MongoDB.Test/MongoDBMonitoringApiTest.cs b/test/DotNetCore.CAP.MongoDB.Test/MongoDBMonitoringApiTest.cs index 31c0f26..2592b43 100644 --- a/test/DotNetCore.CAP.MongoDB.Test/MongoDBMonitoringApiTest.cs +++ b/test/DotNetCore.CAP.MongoDB.Test/MongoDBMonitoringApiTest.cs @@ -1,47 +1,36 @@ -using MongoDB.Driver; -using DotNetCore.CAP.MongoDB; -using Xunit; using System; -using DotNetCore.CAP.Models; -using FluentAssertions; +using System.Linq; using DotNetCore.CAP.Dashboard.Monitoring; using DotNetCore.CAP.Infrastructure; -using System.Linq; +using DotNetCore.CAP.Models; +using FluentAssertions; +using Xunit; namespace DotNetCore.CAP.MongoDB.Test { - public class MongoDBMonitoringApiTest + [Collection("MongoDB")] + public class MongoDBMonitoringApiTest : DatabaseTestHost { - private readonly MongoClient _client; - private readonly MongoDBOptions _options; private readonly MongoDBMonitoringApi _api; public MongoDBMonitoringApiTest() { - _client = new MongoClient(ConnectionUtil.ConnectionString); - _options = new MongoDBOptions(); - _api = new MongoDBMonitoringApi(_client, _options); + _api = new MongoDBMonitoringApi(MongoClient, MongoDBOptions); - Init(); - } - - private void Init() - { var helper = new MongoDBUtil(); - var database = _client.GetDatabase(_options.Database); - var collection = database.GetCollection(_options.PublishedCollection); - collection.InsertMany(new CapPublishedMessage[] + var collection = Database.GetCollection(MongoDBOptions.PublishedCollection); + collection.InsertMany(new[] { new CapPublishedMessage { - Id = helper.GetNextSequenceValue(database,_options.PublishedCollection), + Id = helper.GetNextSequenceValue(Database,MongoDBOptions.PublishedCollection), Added = DateTime.Now.AddHours(-1), StatusName = "Failed", Content = "abc" }, new CapPublishedMessage { - Id = helper.GetNextSequenceValue(database,_options.PublishedCollection), + Id = helper.GetNextSequenceValue(Database,MongoDBOptions.PublishedCollection), Added = DateTime.Now, StatusName = "Failed", Content = "bbc" diff --git a/test/DotNetCore.CAP.MongoDB.Test/MongoDBStorageConnectionTest.cs b/test/DotNetCore.CAP.MongoDB.Test/MongoDBStorageConnectionTest.cs index a258a5d..2b326af 100644 --- a/test/DotNetCore.CAP.MongoDB.Test/MongoDBStorageConnectionTest.cs +++ b/test/DotNetCore.CAP.MongoDB.Test/MongoDBStorageConnectionTest.cs @@ -1,63 +1,71 @@ -using System.Threading; +using System; using DotNetCore.CAP.Infrastructure; using DotNetCore.CAP.Models; using FluentAssertions; -using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.Extensions.DependencyInjection; using MongoDB.Driver; using Xunit; -using Xunit.Priority; namespace DotNetCore.CAP.MongoDB.Test { - [TestCaseOrderer(PriorityOrderer.Name, PriorityOrderer.Assembly)] - public class MongoDBStorageConnectionTest + [Collection("MongoDB")] + public class MongoDBStorageConnectionTest : DatabaseTestHost { - private readonly MongoClient _client; - private readonly MongoDBOptions _options; - private readonly MongoDBStorage _storage; - private readonly IStorageConnection _connection; + private IStorageConnection _connection => + Provider.GetService().GetConnection(); - public MongoDBStorageConnectionTest() - { - _client = new MongoClient(ConnectionUtil.ConnectionString); - _options = new MongoDBOptions(); - _storage = new MongoDBStorage(new CapOptions(), _options, _client, NullLogger.Instance); - _connection = _storage.GetConnection(); - } - - [Fact, Priority(1)] + [Fact] public async void StoreReceivedMessageAsync_TestAsync() { - await _storage.InitializeAsync(default(CancellationToken)); - - var id = await - _connection.StoreReceivedMessageAsync(new CapReceivedMessage(new MessageContext + var id = await _connection.StoreReceivedMessageAsync(new CapReceivedMessage(new MessageContext { Group = "test", Name = "test", Content = "test-content" })); + id.Should().BeGreaterThan(0); } - [Fact, Priority(2)] + [Fact] public void ChangeReceivedState_Test() { - var collection = _client.GetDatabase(_options.Database).GetCollection(_options.ReceivedCollection); + StoreReceivedMessageAsync_TestAsync(); + var collection = Database.GetCollection(MongoDBOptions.ReceivedCollection); var msg = collection.Find(x => true).FirstOrDefault(); _connection.ChangeReceivedState(msg.Id, StatusName.Scheduled).Should().BeTrue(); collection.Find(x => x.Id == msg.Id).FirstOrDefault()?.StatusName.Should().Be(StatusName.Scheduled); } - [Fact, Priority(3)] + [Fact] public async void GetReceivedMessagesOfNeedRetry_TestAsync() { var msgs = await _connection.GetReceivedMessagesOfNeedRetry(); + + msgs.Should().BeEmpty(); + + var msg = new CapReceivedMessage + { + Group = "test", + Name = "test", + Content = "test-content", + StatusName = StatusName.Failed + }; + var id = await _connection.StoreReceivedMessageAsync(msg); + + var collection = Database.GetCollection(MongoDBOptions.ReceivedCollection); + + var updateDef = Builders + .Update.Set(x => x.Added, DateTime.Now.AddMinutes(-5)); + + await collection.UpdateOneAsync(x => x.Id == id, updateDef); + + msgs = await _connection.GetReceivedMessagesOfNeedRetry(); msgs.Should().HaveCountGreaterThan(0); } - [Fact, Priority(4)] + [Fact] public void GetReceivedMessageAsync_Test() { var msg = _connection.GetReceivedMessageAsync(1); diff --git a/test/DotNetCore.CAP.MongoDB.Test/MongoDBStorageTest.cs b/test/DotNetCore.CAP.MongoDB.Test/MongoDBStorageTest.cs index dbbe95e..1a5f4de 100644 --- a/test/DotNetCore.CAP.MongoDB.Test/MongoDBStorageTest.cs +++ b/test/DotNetCore.CAP.MongoDB.Test/MongoDBStorageTest.cs @@ -1,38 +1,29 @@ -using System.Threading; using FluentAssertions; -using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.Extensions.DependencyInjection; using MongoDB.Bson; using MongoDB.Driver; using Xunit; namespace DotNetCore.CAP.MongoDB.Test { - public class MongoDBStorageTest + [Collection("MongoDB")] + public class MongoDBStorageTest : DatabaseTestHost { - private readonly MongoClient _client; - - public MongoDBStorageTest() - { - _client = new MongoClient(ConnectionUtil.ConnectionString); - } - [Fact] - public async void InitializeAsync_Test() + public void InitializeAsync_Test() { - var options = new MongoDBOptions(); - var storage = new MongoDBStorage(new CapOptions(), options, _client, NullLogger.Instance); - await storage.InitializeAsync(default(CancellationToken)); - var names = _client.ListDatabaseNames()?.ToList(); - names.Should().Contain(options.Database); + var storage = Provider.GetService(); + var names = MongoClient.ListDatabaseNames()?.ToList(); + names.Should().Contain(MongoDBOptions.DatabaseName); - var collections = _client.GetDatabase(options.Database).ListCollectionNames()?.ToList(); - collections.Should().Contain(options.PublishedCollection); - collections.Should().Contain(options.ReceivedCollection); - collections.Should().Contain("Counter"); + var collections = Database.ListCollectionNames()?.ToList(); + collections.Should().Contain(MongoDBOptions.PublishedCollection); + collections.Should().Contain(MongoDBOptions.ReceivedCollection); + collections.Should().Contain(MongoDBOptions.CounterCollection); - var collection = _client.GetDatabase(options.Database).GetCollection("Counter"); - collection.CountDocuments(new BsonDocument { { "_id", options.PublishedCollection } }).Should().Be(1); - collection.CountDocuments(new BsonDocument { { "_id", options.ReceivedCollection } }).Should().Be(1); + var collection = Database.GetCollection(MongoDBOptions.CounterCollection); + collection.CountDocuments(new BsonDocument { { "_id", MongoDBOptions.PublishedCollection } }).Should().Be(1); + collection.CountDocuments(new BsonDocument { { "_id", MongoDBOptions.ReceivedCollection } }).Should().Be(1); } } } \ No newline at end of file diff --git a/test/DotNetCore.CAP.MongoDB.Test/MongoDBTest.cs b/test/DotNetCore.CAP.MongoDB.Test/MongoDBTransactionTest.cs similarity index 79% rename from test/DotNetCore.CAP.MongoDB.Test/MongoDBTest.cs rename to test/DotNetCore.CAP.MongoDB.Test/MongoDBTransactionTest.cs index 096e04c..dd7351a 100644 --- a/test/DotNetCore.CAP.MongoDB.Test/MongoDBTest.cs +++ b/test/DotNetCore.CAP.MongoDB.Test/MongoDBTransactionTest.cs @@ -6,23 +6,17 @@ using Xunit; namespace DotNetCore.CAP.MongoDB.Test { - public class MongoDBTest + [Collection("MongoDB")] + public class MongoDBTransactionTest : DatabaseTestHost { - private readonly MongoClient _client; - - public MongoDBTest() - { - _client = new MongoClient(ConnectionUtil.ConnectionString); - } - [Fact] public void MongoDB_Connection_Test() { - var names = _client.ListDatabaseNames(); + var names = MongoClient.ListDatabaseNames(); names.ToList().Should().NotBeNullOrEmpty(); } - [Fact] + [Fact(Skip = "Because of Appveyor dose not support MongoDB 4.0, so we skip this test for now.")] public void Transaction_Test() { var document = new BsonDocument @@ -36,10 +30,10 @@ namespace DotNetCore.CAP.MongoDB.Test { "y", 102 } }} }; - var db = _client.GetDatabase("test"); + var db = MongoClient.GetDatabase("test"); var collection1 = db.GetCollection("test1"); var collection2 = db.GetCollection("test2"); - using (var sesstion = _client.StartSession()) + using (var sesstion = MongoClient.StartSession()) { sesstion.StartTransaction(); collection1.InsertOne(document); @@ -51,7 +45,7 @@ namespace DotNetCore.CAP.MongoDB.Test collection2.CountDocuments(filter).Should().BeGreaterThan(0); } - [Fact] + [Fact(Skip = "Because of Appveyor dose not support MongoDB 4.0, so we skip this test for now.")] public void Transaction_Rollback_Test() { var document = new BsonDocument @@ -59,12 +53,12 @@ namespace DotNetCore.CAP.MongoDB.Test {"name", "MongoDB"}, {"date", DateTimeOffset.Now.ToString()} }; - var db = _client.GetDatabase("test"); + var db = MongoClient.GetDatabase("test"); var collection = db.GetCollection("test3"); var collection4 = db.GetCollection("test4"); - using (var session = _client.StartSession()) + using (var session = MongoClient.StartSession()) { session.IsInTransaction.Should().BeFalse(); session.StartTransaction(); diff --git a/test/DotNetCore.CAP.MongoDB.Test/MongoDBUtilTest.cs b/test/DotNetCore.CAP.MongoDB.Test/MongoDBUtilTest.cs index 0944a21..45695f9 100644 --- a/test/DotNetCore.CAP.MongoDB.Test/MongoDBUtilTest.cs +++ b/test/DotNetCore.CAP.MongoDB.Test/MongoDBUtilTest.cs @@ -1,36 +1,17 @@ using System.Collections.Concurrent; -using System.Linq; using System.Threading.Tasks; -using DotNetCore.CAP.Models; using FluentAssertions; -using MongoDB.Bson; -using MongoDB.Driver; using Xunit; namespace DotNetCore.CAP.MongoDB.Test { - public class MongoDBUtilTest + [Collection("MongoDB")] + public class MongoDBUtilTest : DatabaseTestHost { - private readonly IMongoDatabase _database; - string _recieved = "ReceivedTest"; - - public MongoDBUtilTest() - { - var client = new MongoClient(ConnectionUtil.ConnectionString); - _database = client.GetDatabase("CAP_Test"); - - //Initialize MongoDB - if (_database.ListCollectionNames().ToList().All(x => x != "Counter")) - { - var collection = _database.GetCollection("Counter"); - collection.InsertOne(new BsonDocument { { "_id", _recieved }, { "sequence_value", 0 } }); - } - } - [Fact] public async void GetNextSequenceValueAsync_Test() { - var id = await new MongoDBUtil().GetNextSequenceValueAsync(_database, _recieved); + var id = await new MongoDBUtil().GetNextSequenceValueAsync(Database, MongoDBOptions.ReceivedCollection); id.Should().BeGreaterThan(0); } @@ -40,7 +21,7 @@ namespace DotNetCore.CAP.MongoDB.Test var dic = new ConcurrentDictionary(); Parallel.For(0, 30, (x) => { - var id = new MongoDBUtil().GetNextSequenceValue(_database, _recieved); + var id = new MongoDBUtil().GetNextSequenceValue(Database, MongoDBOptions.ReceivedCollection); id.Should().BeGreaterThan(0); dic.TryAdd(id, x).Should().BeTrue(); //The id shouldn't be same. }); From 9bc0909c23790d965bf39abca7f8a2c95d690528 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Mon, 23 Jul 2018 16:24:17 +0800 Subject: [PATCH 127/223] Refactoring unit tests have been adapted to apperyor --- .../ConnectionUtil.cs | 2 +- .../DatabaseTestHost.cs | 55 +++++++++++++++++ .../DotNetCore.CAP.MongoDB.Test.csproj | 11 ++-- .../MongoDBMonitoringApiTest.cs | 33 ++++------ .../MongoDBStorageConnectionTest.cs | 60 +++++++++++-------- .../MongoDBStorageTest.cs | 37 +++++------- ...ngoDBTest.cs => MongoDBTransactionTest.cs} | 24 +++----- .../MongoDBUtilTest.cs | 27 ++------- 8 files changed, 133 insertions(+), 116 deletions(-) create mode 100644 test/DotNetCore.CAP.MongoDB.Test/DatabaseTestHost.cs rename test/DotNetCore.CAP.MongoDB.Test/{MongoDBTest.cs => MongoDBTransactionTest.cs} (79%) diff --git a/test/DotNetCore.CAP.MongoDB.Test/ConnectionUtil.cs b/test/DotNetCore.CAP.MongoDB.Test/ConnectionUtil.cs index 9c8aa29..da8de03 100644 --- a/test/DotNetCore.CAP.MongoDB.Test/ConnectionUtil.cs +++ b/test/DotNetCore.CAP.MongoDB.Test/ConnectionUtil.cs @@ -2,6 +2,6 @@ namespace DotNetCore.CAP.MongoDB.Test { public class ConnectionUtil { - public static string ConnectionString = "mongodb://mongo1:27017,mongo2:27018,mongo3:27019/?replicaSet=my-mongo-set"; + public static string ConnectionString = "mongodb://localhost:27017"; } } \ No newline at end of file diff --git a/test/DotNetCore.CAP.MongoDB.Test/DatabaseTestHost.cs b/test/DotNetCore.CAP.MongoDB.Test/DatabaseTestHost.cs new file mode 100644 index 0000000..cd837fb --- /dev/null +++ b/test/DotNetCore.CAP.MongoDB.Test/DatabaseTestHost.cs @@ -0,0 +1,55 @@ +using System; +using System.Threading; +using Microsoft.Extensions.DependencyInjection; +using MongoDB.Driver; + +namespace DotNetCore.CAP.MongoDB.Test +{ + public abstract class DatabaseTestHost : IDisposable + { + private string _connectionString; + + protected IServiceProvider Provider { get; private set; } + protected IMongoClient MongoClient => Provider.GetService(); + protected IMongoDatabase Database => MongoClient.GetDatabase(MongoDBOptions.DatabaseName); + protected CapOptions CapOptions => Provider.GetService(); + protected MongoDBOptions MongoDBOptions => Provider.GetService(); + + protected DatabaseTestHost() + { + CreateServiceCollection(); + CreateDatabase(); + } + + private void CreateDatabase() + { + Provider.GetService().InitializeAsync(CancellationToken.None).GetAwaiter().GetResult(); + } + + protected virtual void AddService(ServiceCollection serviceCollection) + { + + } + + private void CreateServiceCollection() + { + var services = new ServiceCollection(); + services.AddOptions(); + services.AddLogging(); + _connectionString = ConnectionUtil.ConnectionString; + + services.AddSingleton(new MongoDBOptions() { DatabaseConnection = _connectionString }); + services.AddSingleton(new CapOptions()); + services.AddSingleton(x => new MongoClient(_connectionString)); + services.AddSingleton(); + + AddService(services); + Provider = services.BuildServiceProvider(); + } + + public void Dispose() + { + MongoClient.DropDatabase(MongoDBOptions.DatabaseName); + } + } +} \ No newline at end of file diff --git a/test/DotNetCore.CAP.MongoDB.Test/DotNetCore.CAP.MongoDB.Test.csproj b/test/DotNetCore.CAP.MongoDB.Test/DotNetCore.CAP.MongoDB.Test.csproj index 5ec46fc..9fb481e 100644 --- a/test/DotNetCore.CAP.MongoDB.Test/DotNetCore.CAP.MongoDB.Test.csproj +++ b/test/DotNetCore.CAP.MongoDB.Test/DotNetCore.CAP.MongoDB.Test.csproj @@ -1,19 +1,18 @@ - + netcoreapp2.1 - false + + - + - - - + diff --git a/test/DotNetCore.CAP.MongoDB.Test/MongoDBMonitoringApiTest.cs b/test/DotNetCore.CAP.MongoDB.Test/MongoDBMonitoringApiTest.cs index 31c0f26..2592b43 100644 --- a/test/DotNetCore.CAP.MongoDB.Test/MongoDBMonitoringApiTest.cs +++ b/test/DotNetCore.CAP.MongoDB.Test/MongoDBMonitoringApiTest.cs @@ -1,47 +1,36 @@ -using MongoDB.Driver; -using DotNetCore.CAP.MongoDB; -using Xunit; using System; -using DotNetCore.CAP.Models; -using FluentAssertions; +using System.Linq; using DotNetCore.CAP.Dashboard.Monitoring; using DotNetCore.CAP.Infrastructure; -using System.Linq; +using DotNetCore.CAP.Models; +using FluentAssertions; +using Xunit; namespace DotNetCore.CAP.MongoDB.Test { - public class MongoDBMonitoringApiTest + [Collection("MongoDB")] + public class MongoDBMonitoringApiTest : DatabaseTestHost { - private readonly MongoClient _client; - private readonly MongoDBOptions _options; private readonly MongoDBMonitoringApi _api; public MongoDBMonitoringApiTest() { - _client = new MongoClient(ConnectionUtil.ConnectionString); - _options = new MongoDBOptions(); - _api = new MongoDBMonitoringApi(_client, _options); + _api = new MongoDBMonitoringApi(MongoClient, MongoDBOptions); - Init(); - } - - private void Init() - { var helper = new MongoDBUtil(); - var database = _client.GetDatabase(_options.Database); - var collection = database.GetCollection(_options.PublishedCollection); - collection.InsertMany(new CapPublishedMessage[] + var collection = Database.GetCollection(MongoDBOptions.PublishedCollection); + collection.InsertMany(new[] { new CapPublishedMessage { - Id = helper.GetNextSequenceValue(database,_options.PublishedCollection), + Id = helper.GetNextSequenceValue(Database,MongoDBOptions.PublishedCollection), Added = DateTime.Now.AddHours(-1), StatusName = "Failed", Content = "abc" }, new CapPublishedMessage { - Id = helper.GetNextSequenceValue(database,_options.PublishedCollection), + Id = helper.GetNextSequenceValue(Database,MongoDBOptions.PublishedCollection), Added = DateTime.Now, StatusName = "Failed", Content = "bbc" diff --git a/test/DotNetCore.CAP.MongoDB.Test/MongoDBStorageConnectionTest.cs b/test/DotNetCore.CAP.MongoDB.Test/MongoDBStorageConnectionTest.cs index a258a5d..2b326af 100644 --- a/test/DotNetCore.CAP.MongoDB.Test/MongoDBStorageConnectionTest.cs +++ b/test/DotNetCore.CAP.MongoDB.Test/MongoDBStorageConnectionTest.cs @@ -1,63 +1,71 @@ -using System.Threading; +using System; using DotNetCore.CAP.Infrastructure; using DotNetCore.CAP.Models; using FluentAssertions; -using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.Extensions.DependencyInjection; using MongoDB.Driver; using Xunit; -using Xunit.Priority; namespace DotNetCore.CAP.MongoDB.Test { - [TestCaseOrderer(PriorityOrderer.Name, PriorityOrderer.Assembly)] - public class MongoDBStorageConnectionTest + [Collection("MongoDB")] + public class MongoDBStorageConnectionTest : DatabaseTestHost { - private readonly MongoClient _client; - private readonly MongoDBOptions _options; - private readonly MongoDBStorage _storage; - private readonly IStorageConnection _connection; + private IStorageConnection _connection => + Provider.GetService().GetConnection(); - public MongoDBStorageConnectionTest() - { - _client = new MongoClient(ConnectionUtil.ConnectionString); - _options = new MongoDBOptions(); - _storage = new MongoDBStorage(new CapOptions(), _options, _client, NullLogger.Instance); - _connection = _storage.GetConnection(); - } - - [Fact, Priority(1)] + [Fact] public async void StoreReceivedMessageAsync_TestAsync() { - await _storage.InitializeAsync(default(CancellationToken)); - - var id = await - _connection.StoreReceivedMessageAsync(new CapReceivedMessage(new MessageContext + var id = await _connection.StoreReceivedMessageAsync(new CapReceivedMessage(new MessageContext { Group = "test", Name = "test", Content = "test-content" })); + id.Should().BeGreaterThan(0); } - [Fact, Priority(2)] + [Fact] public void ChangeReceivedState_Test() { - var collection = _client.GetDatabase(_options.Database).GetCollection(_options.ReceivedCollection); + StoreReceivedMessageAsync_TestAsync(); + var collection = Database.GetCollection(MongoDBOptions.ReceivedCollection); var msg = collection.Find(x => true).FirstOrDefault(); _connection.ChangeReceivedState(msg.Id, StatusName.Scheduled).Should().BeTrue(); collection.Find(x => x.Id == msg.Id).FirstOrDefault()?.StatusName.Should().Be(StatusName.Scheduled); } - [Fact, Priority(3)] + [Fact] public async void GetReceivedMessagesOfNeedRetry_TestAsync() { var msgs = await _connection.GetReceivedMessagesOfNeedRetry(); + + msgs.Should().BeEmpty(); + + var msg = new CapReceivedMessage + { + Group = "test", + Name = "test", + Content = "test-content", + StatusName = StatusName.Failed + }; + var id = await _connection.StoreReceivedMessageAsync(msg); + + var collection = Database.GetCollection(MongoDBOptions.ReceivedCollection); + + var updateDef = Builders + .Update.Set(x => x.Added, DateTime.Now.AddMinutes(-5)); + + await collection.UpdateOneAsync(x => x.Id == id, updateDef); + + msgs = await _connection.GetReceivedMessagesOfNeedRetry(); msgs.Should().HaveCountGreaterThan(0); } - [Fact, Priority(4)] + [Fact] public void GetReceivedMessageAsync_Test() { var msg = _connection.GetReceivedMessageAsync(1); diff --git a/test/DotNetCore.CAP.MongoDB.Test/MongoDBStorageTest.cs b/test/DotNetCore.CAP.MongoDB.Test/MongoDBStorageTest.cs index dbbe95e..1a5f4de 100644 --- a/test/DotNetCore.CAP.MongoDB.Test/MongoDBStorageTest.cs +++ b/test/DotNetCore.CAP.MongoDB.Test/MongoDBStorageTest.cs @@ -1,38 +1,29 @@ -using System.Threading; using FluentAssertions; -using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.Extensions.DependencyInjection; using MongoDB.Bson; using MongoDB.Driver; using Xunit; namespace DotNetCore.CAP.MongoDB.Test { - public class MongoDBStorageTest + [Collection("MongoDB")] + public class MongoDBStorageTest : DatabaseTestHost { - private readonly MongoClient _client; - - public MongoDBStorageTest() - { - _client = new MongoClient(ConnectionUtil.ConnectionString); - } - [Fact] - public async void InitializeAsync_Test() + public void InitializeAsync_Test() { - var options = new MongoDBOptions(); - var storage = new MongoDBStorage(new CapOptions(), options, _client, NullLogger.Instance); - await storage.InitializeAsync(default(CancellationToken)); - var names = _client.ListDatabaseNames()?.ToList(); - names.Should().Contain(options.Database); + var storage = Provider.GetService(); + var names = MongoClient.ListDatabaseNames()?.ToList(); + names.Should().Contain(MongoDBOptions.DatabaseName); - var collections = _client.GetDatabase(options.Database).ListCollectionNames()?.ToList(); - collections.Should().Contain(options.PublishedCollection); - collections.Should().Contain(options.ReceivedCollection); - collections.Should().Contain("Counter"); + var collections = Database.ListCollectionNames()?.ToList(); + collections.Should().Contain(MongoDBOptions.PublishedCollection); + collections.Should().Contain(MongoDBOptions.ReceivedCollection); + collections.Should().Contain(MongoDBOptions.CounterCollection); - var collection = _client.GetDatabase(options.Database).GetCollection("Counter"); - collection.CountDocuments(new BsonDocument { { "_id", options.PublishedCollection } }).Should().Be(1); - collection.CountDocuments(new BsonDocument { { "_id", options.ReceivedCollection } }).Should().Be(1); + var collection = Database.GetCollection(MongoDBOptions.CounterCollection); + collection.CountDocuments(new BsonDocument { { "_id", MongoDBOptions.PublishedCollection } }).Should().Be(1); + collection.CountDocuments(new BsonDocument { { "_id", MongoDBOptions.ReceivedCollection } }).Should().Be(1); } } } \ No newline at end of file diff --git a/test/DotNetCore.CAP.MongoDB.Test/MongoDBTest.cs b/test/DotNetCore.CAP.MongoDB.Test/MongoDBTransactionTest.cs similarity index 79% rename from test/DotNetCore.CAP.MongoDB.Test/MongoDBTest.cs rename to test/DotNetCore.CAP.MongoDB.Test/MongoDBTransactionTest.cs index 096e04c..dd7351a 100644 --- a/test/DotNetCore.CAP.MongoDB.Test/MongoDBTest.cs +++ b/test/DotNetCore.CAP.MongoDB.Test/MongoDBTransactionTest.cs @@ -6,23 +6,17 @@ using Xunit; namespace DotNetCore.CAP.MongoDB.Test { - public class MongoDBTest + [Collection("MongoDB")] + public class MongoDBTransactionTest : DatabaseTestHost { - private readonly MongoClient _client; - - public MongoDBTest() - { - _client = new MongoClient(ConnectionUtil.ConnectionString); - } - [Fact] public void MongoDB_Connection_Test() { - var names = _client.ListDatabaseNames(); + var names = MongoClient.ListDatabaseNames(); names.ToList().Should().NotBeNullOrEmpty(); } - [Fact] + [Fact(Skip = "Because of Appveyor dose not support MongoDB 4.0, so we skip this test for now.")] public void Transaction_Test() { var document = new BsonDocument @@ -36,10 +30,10 @@ namespace DotNetCore.CAP.MongoDB.Test { "y", 102 } }} }; - var db = _client.GetDatabase("test"); + var db = MongoClient.GetDatabase("test"); var collection1 = db.GetCollection("test1"); var collection2 = db.GetCollection("test2"); - using (var sesstion = _client.StartSession()) + using (var sesstion = MongoClient.StartSession()) { sesstion.StartTransaction(); collection1.InsertOne(document); @@ -51,7 +45,7 @@ namespace DotNetCore.CAP.MongoDB.Test collection2.CountDocuments(filter).Should().BeGreaterThan(0); } - [Fact] + [Fact(Skip = "Because of Appveyor dose not support MongoDB 4.0, so we skip this test for now.")] public void Transaction_Rollback_Test() { var document = new BsonDocument @@ -59,12 +53,12 @@ namespace DotNetCore.CAP.MongoDB.Test {"name", "MongoDB"}, {"date", DateTimeOffset.Now.ToString()} }; - var db = _client.GetDatabase("test"); + var db = MongoClient.GetDatabase("test"); var collection = db.GetCollection("test3"); var collection4 = db.GetCollection("test4"); - using (var session = _client.StartSession()) + using (var session = MongoClient.StartSession()) { session.IsInTransaction.Should().BeFalse(); session.StartTransaction(); diff --git a/test/DotNetCore.CAP.MongoDB.Test/MongoDBUtilTest.cs b/test/DotNetCore.CAP.MongoDB.Test/MongoDBUtilTest.cs index 0944a21..45695f9 100644 --- a/test/DotNetCore.CAP.MongoDB.Test/MongoDBUtilTest.cs +++ b/test/DotNetCore.CAP.MongoDB.Test/MongoDBUtilTest.cs @@ -1,36 +1,17 @@ using System.Collections.Concurrent; -using System.Linq; using System.Threading.Tasks; -using DotNetCore.CAP.Models; using FluentAssertions; -using MongoDB.Bson; -using MongoDB.Driver; using Xunit; namespace DotNetCore.CAP.MongoDB.Test { - public class MongoDBUtilTest + [Collection("MongoDB")] + public class MongoDBUtilTest : DatabaseTestHost { - private readonly IMongoDatabase _database; - string _recieved = "ReceivedTest"; - - public MongoDBUtilTest() - { - var client = new MongoClient(ConnectionUtil.ConnectionString); - _database = client.GetDatabase("CAP_Test"); - - //Initialize MongoDB - if (_database.ListCollectionNames().ToList().All(x => x != "Counter")) - { - var collection = _database.GetCollection("Counter"); - collection.InsertOne(new BsonDocument { { "_id", _recieved }, { "sequence_value", 0 } }); - } - } - [Fact] public async void GetNextSequenceValueAsync_Test() { - var id = await new MongoDBUtil().GetNextSequenceValueAsync(_database, _recieved); + var id = await new MongoDBUtil().GetNextSequenceValueAsync(Database, MongoDBOptions.ReceivedCollection); id.Should().BeGreaterThan(0); } @@ -40,7 +21,7 @@ namespace DotNetCore.CAP.MongoDB.Test var dic = new ConcurrentDictionary(); Parallel.For(0, 30, (x) => { - var id = new MongoDBUtil().GetNextSequenceValue(_database, _recieved); + var id = new MongoDBUtil().GetNextSequenceValue(Database, MongoDBOptions.ReceivedCollection); id.Should().BeGreaterThan(0); dic.TryAdd(id, x).Should().BeTrue(); //The id shouldn't be same. }); From dc338fd6346bca512817c707f41f78a3e5aff341 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Mon, 23 Jul 2018 16:27:16 +0800 Subject: [PATCH 128/223] update version num to 2.2.6 --- build/version.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/version.props b/build/version.props index 449a52f..106dba1 100644 --- a/build/version.props +++ b/build/version.props @@ -2,7 +2,7 @@ 2 2 - 5 + 6 $(VersionMajor).$(VersionMinor).$(VersionPatch) From 1aea9de76f55bf897010f5e26feaa8166bf0e73c Mon Sep 17 00:00:00 2001 From: Savorboard Date: Mon, 23 Jul 2018 16:27:16 +0800 Subject: [PATCH 129/223] update version num to 2.2.6 --- build/version.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/version.props b/build/version.props index 449a52f..106dba1 100644 --- a/build/version.props +++ b/build/version.props @@ -2,7 +2,7 @@ 2 2 - 5 + 6 $(VersionMajor).$(VersionMinor).$(VersionPatch) From d31d6108211305c014c017794efdd7a0fa4b6e7f Mon Sep 17 00:00:00 2001 From: Savorboard Date: Mon, 23 Jul 2018 16:31:33 +0800 Subject: [PATCH 130/223] add mongodb service --- appveyor.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/appveyor.yml b/appveyor.yml index 2bdfaba..981d513 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -10,6 +10,7 @@ services: - mssql2014 - mysql - postgresql + - mongodb build_script: - ps: ./build.ps1 test: off From 542d966d6bf124f420068d9d4ea7e228a3993cb8 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Mon, 23 Jul 2018 16:31:33 +0800 Subject: [PATCH 131/223] add mongodb service --- appveyor.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/appveyor.yml b/appveyor.yml index 2bdfaba..981d513 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -10,6 +10,7 @@ services: - mssql2014 - mysql - postgresql + - mongodb build_script: - ps: ./build.ps1 test: off From 77e5547e33928fd591192d039ec9982079e2cb9f Mon Sep 17 00:00:00 2001 From: Savorboard Date: Mon, 23 Jul 2018 16:38:13 +0800 Subject: [PATCH 132/223] add AssemblyInfo.cs --- src/DotNetCore.CAP.MongoDB/Properties/AssemblyInfo.cs | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 src/DotNetCore.CAP.MongoDB/Properties/AssemblyInfo.cs diff --git a/src/DotNetCore.CAP.MongoDB/Properties/AssemblyInfo.cs b/src/DotNetCore.CAP.MongoDB/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..27f2e75 --- /dev/null +++ b/src/DotNetCore.CAP.MongoDB/Properties/AssemblyInfo.cs @@ -0,0 +1,6 @@ +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("DotNetCore.CAP.MongoDB.Test")] \ No newline at end of file From 8542a5f3fe9656d83b59d58179ae9a9141dda9d2 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Mon, 23 Jul 2018 16:38:13 +0800 Subject: [PATCH 133/223] add AssemblyInfo.cs --- src/DotNetCore.CAP.MongoDB/Properties/AssemblyInfo.cs | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 src/DotNetCore.CAP.MongoDB/Properties/AssemblyInfo.cs diff --git a/src/DotNetCore.CAP.MongoDB/Properties/AssemblyInfo.cs b/src/DotNetCore.CAP.MongoDB/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..27f2e75 --- /dev/null +++ b/src/DotNetCore.CAP.MongoDB/Properties/AssemblyInfo.cs @@ -0,0 +1,6 @@ +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("DotNetCore.CAP.MongoDB.Test")] \ No newline at end of file From b5afb488cfd6c5732c6fb3a595e76f95ab7724e5 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Mon, 23 Jul 2018 16:46:49 +0800 Subject: [PATCH 134/223] update nuget api key --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 981d513..0c787da 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -21,6 +21,6 @@ deploy: on: appveyor_repo_tag: true api_key: - secure: /gak8VxtAbZvOTqON513KwsK5BtDUmoPjjzCMu+tn2i+vkupZbjnIKq/XnP4GGgv + secure: jzEiNdfiHYkpKBFoxtV+Tu48pJxBpPpu4CnQRsGnhu0Sx59sXgKVgfcg759sSWAO skip_symbols: true artifact: /artifacts\/packages\/.+\.nupkg/ From 35dcb5e205f65d36940dc3559724d8ddd8b29d6a Mon Sep 17 00:00:00 2001 From: Savorboard Date: Mon, 23 Jul 2018 16:46:49 +0800 Subject: [PATCH 135/223] update nuget api key --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 981d513..0c787da 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -21,6 +21,6 @@ deploy: on: appveyor_repo_tag: true api_key: - secure: /gak8VxtAbZvOTqON513KwsK5BtDUmoPjjzCMu+tn2i+vkupZbjnIKq/XnP4GGgv + secure: jzEiNdfiHYkpKBFoxtV+Tu48pJxBpPpu4CnQRsGnhu0Sx59sXgKVgfcg759sSWAO skip_symbols: true artifact: /artifacts\/packages\/.+\.nupkg/ From fa8562cd71f64a4d0a2260dd2ef69a07e42e9807 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Mon, 23 Jul 2018 17:29:49 +0800 Subject: [PATCH 136/223] update api key --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 0c787da..dd081a6 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -21,6 +21,6 @@ deploy: on: appveyor_repo_tag: true api_key: - secure: jzEiNdfiHYkpKBFoxtV+Tu48pJxBpPpu4CnQRsGnhu0Sx59sXgKVgfcg759sSWAO + secure: Zczt30A2GYhE6rOqeRFIvuSMgK/6HCWaI+RB0vQ+snmfwK3oyd/zBpGqMqfDh8Wz skip_symbols: true artifact: /artifacts\/packages\/.+\.nupkg/ From fe46e872743c4d68d03181c983609be7bbc9d0b6 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Mon, 23 Jul 2018 17:29:49 +0800 Subject: [PATCH 137/223] update api key --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 0c787da..dd081a6 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -21,6 +21,6 @@ deploy: on: appveyor_repo_tag: true api_key: - secure: jzEiNdfiHYkpKBFoxtV+Tu48pJxBpPpu4CnQRsGnhu0Sx59sXgKVgfcg759sSWAO + secure: Zczt30A2GYhE6rOqeRFIvuSMgK/6HCWaI+RB0vQ+snmfwK3oyd/zBpGqMqfDh8Wz skip_symbols: true artifact: /artifacts\/packages\/.+\.nupkg/ From 4027fe83d7564052c0d37678e62faa30e18117ea Mon Sep 17 00:00:00 2001 From: Savorboard Date: Mon, 23 Jul 2018 17:48:46 +0800 Subject: [PATCH 138/223] update nuget api key --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index dd081a6..c762a41 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -21,6 +21,6 @@ deploy: on: appveyor_repo_tag: true api_key: - secure: Zczt30A2GYhE6rOqeRFIvuSMgK/6HCWaI+RB0vQ+snmfwK3oyd/zBpGqMqfDh8Wz + secure:62g+D0FPQQgVHQE+VaLPri7EAoQgu8WQbtXaawwa0c8o6fLxTc2oJSx2MgcLvpzN skip_symbols: true artifact: /artifacts\/packages\/.+\.nupkg/ From ca8aee6af81e625f68b7ce08261f219f984968fb Mon Sep 17 00:00:00 2001 From: Savorboard Date: Mon, 23 Jul 2018 17:48:46 +0800 Subject: [PATCH 139/223] update nuget api key --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index dd081a6..c762a41 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -21,6 +21,6 @@ deploy: on: appveyor_repo_tag: true api_key: - secure: Zczt30A2GYhE6rOqeRFIvuSMgK/6HCWaI+RB0vQ+snmfwK3oyd/zBpGqMqfDh8Wz + secure:62g+D0FPQQgVHQE+VaLPri7EAoQgu8WQbtXaawwa0c8o6fLxTc2oJSx2MgcLvpzN skip_symbols: true artifact: /artifacts\/packages\/.+\.nupkg/ From 04a5554c56500bf7228614c8db5e552ed0ad11c7 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Mon, 23 Jul 2018 18:19:28 +0800 Subject: [PATCH 140/223] Update appveyor.yml --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index c762a41..e80f7ce 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -21,6 +21,6 @@ deploy: on: appveyor_repo_tag: true api_key: - secure:62g+D0FPQQgVHQE+VaLPri7EAoQgu8WQbtXaawwa0c8o6fLxTc2oJSx2MgcLvpzN + secure: 62g+D0FPQQgVHQE+VaLPri7EAoQgu8WQbtXaawwa0c8o6fLxTc2oJSx2MgcLvpzN skip_symbols: true artifact: /artifacts\/packages\/.+\.nupkg/ From 987096aa2f80c30c7095f628859bed9317314b80 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Mon, 23 Jul 2018 18:19:28 +0800 Subject: [PATCH 141/223] Update appveyor.yml --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index c762a41..e80f7ce 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -21,6 +21,6 @@ deploy: on: appveyor_repo_tag: true api_key: - secure:62g+D0FPQQgVHQE+VaLPri7EAoQgu8WQbtXaawwa0c8o6fLxTc2oJSx2MgcLvpzN + secure: 62g+D0FPQQgVHQE+VaLPri7EAoQgu8WQbtXaawwa0c8o6fLxTc2oJSx2MgcLvpzN skip_symbols: true artifact: /artifacts\/packages\/.+\.nupkg/ From 3ecc92f21f17f05d28dae23626dc228c9d1d9168 Mon Sep 17 00:00:00 2001 From: keke Date: Tue, 24 Jul 2018 09:35:54 +0800 Subject: [PATCH 142/223] Abstract MongoTransaction to make the usage more comfortable (#167) * Abstract MongoTransaction to make the usage more comfortable * Tweak the method name --- .../Controllers/ValuesController.cs | 45 +++++++++++---- .../CAP.MongoDBCapOptionsExtension.cs | 4 ++ src/DotNetCore.CAP.MongoDB/CapPublisher.cs | 54 +++++++++++------- .../MongoTransaction.cs | 57 +++++++++++++++++++ .../Abstractions/CapPublisherBase.cs | 4 +- .../Abstractions/IMongoTransaction.cs | 18 ++++++ src/DotNetCore.CAP/ICapPublisher.cs | 9 +-- test/DotNetCore.CAP.Test/CAP.BuilderTest.cs | 4 +- 8 files changed, 154 insertions(+), 41 deletions(-) create mode 100644 src/DotNetCore.CAP.MongoDB/MongoTransaction.cs create mode 100644 src/DotNetCore.CAP/Abstractions/IMongoTransaction.cs diff --git a/samples/Sample.RabbitMQ.MongoDB/Controllers/ValuesController.cs b/samples/Sample.RabbitMQ.MongoDB/Controllers/ValuesController.cs index 374100d..a22af60 100644 --- a/samples/Sample.RabbitMQ.MongoDB/Controllers/ValuesController.cs +++ b/samples/Sample.RabbitMQ.MongoDB/Controllers/ValuesController.cs @@ -3,6 +3,8 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using DotNetCore.CAP; +using DotNetCore.CAP.Abstractions; +using DotNetCore.CAP.MongoDB; using Microsoft.AspNetCore.Mvc; using MongoDB.Bson; using MongoDB.Driver; @@ -15,39 +17,58 @@ namespace Sample.RabbitMQ.MongoDB.Controllers { private readonly IMongoClient _client; private readonly ICapPublisher _capPublisher; + private readonly IMongoTransaction _mongoTransaction; - public ValuesController(IMongoClient client, ICapPublisher capPublisher) + public ValuesController(IMongoClient client, ICapPublisher capPublisher, IMongoTransaction mongoTransaction) { _client = client; _capPublisher = capPublisher; + _mongoTransaction = mongoTransaction; } [Route("~/publish")] - public IActionResult PublishWithSession() + public async Task PublishWithTrans() { - using (var session = _client.StartSession()) + using (var trans = await _mongoTransaction.BegeinAsync()) { - session.StartTransaction(); var collection = _client.GetDatabase("TEST").GetCollection("test"); - collection.InsertOne(session, new BsonDocument { { "hello", "world" } }); + collection.InsertOne(trans.GetSession(), new BsonDocument { { "hello", "world" } }); - _capPublisher.PublishWithMongo("sample.rabbitmq.mongodb", DateTime.Now, session); + await _capPublisher.PublishWithMongoAsync("sample.rabbitmq.mongodb", DateTime.Now, trans); + } + return Ok(); + } + + [Route("~/publish/not/autocommit")] + public IActionResult PublishNotAutoCommit() + { + using (var trans = _mongoTransaction.Begein(autoCommit: false)) + { + var session = trans.GetSession(); + + var collection = _client.GetDatabase("TEST").GetCollection("test"); + collection.InsertOne(session, new BsonDocument { { "Hello", "World" } }); + + _capPublisher.PublishWithMongo("sample.rabbitmq.mongodb", DateTime.Now, trans); + //Do something, and commit by yourself. session.CommitTransaction(); } return Ok(); } - [Route("~/publish_rollback")] + [Route("~/publish/rollback")] public IActionResult PublishRollback() { - using (var session = _client.StartSession()) + using (var trans = _mongoTransaction.Begein(autoCommit: false)) { + var session = trans.GetSession(); try { - session.StartTransaction(); - _capPublisher.PublishWithMongo("sample.rabbitmq.mongodb", DateTime.Now, session); + _capPublisher.PublishWithMongo("sample.rabbitmq.mongodb", DateTime.Now, trans); + //Do something, but throw new Exception("Foo"); + session.CommitTransaction(); } catch (System.Exception ex) { @@ -57,8 +78,8 @@ namespace Sample.RabbitMQ.MongoDB.Controllers } } - [Route("~/publish_without_session")] - public IActionResult PublishWithoutSession() + [Route("~/publish/without/trans")] + public IActionResult PublishWithoutTrans() { _capPublisher.PublishWithMongo("sample.rabbitmq.mongodb", DateTime.Now); return Ok(); diff --git a/src/DotNetCore.CAP.MongoDB/CAP.MongoDBCapOptionsExtension.cs b/src/DotNetCore.CAP.MongoDB/CAP.MongoDBCapOptionsExtension.cs index e07a55a..9b00686 100644 --- a/src/DotNetCore.CAP.MongoDB/CAP.MongoDBCapOptionsExtension.cs +++ b/src/DotNetCore.CAP.MongoDB/CAP.MongoDBCapOptionsExtension.cs @@ -2,6 +2,8 @@ // Licensed under the MIT License. See License.txt in the project root for license information. using System; +using DotNetCore.CAP; +using DotNetCore.CAP.Abstractions; using DotNetCore.CAP.Processor; using Microsoft.Extensions.DependencyInjection; @@ -25,6 +27,8 @@ namespace DotNetCore.CAP.MongoDB services.AddScoped(); services.AddTransient(); + services.AddTransient(); + var options = new MongoDBOptions(); _configure?.Invoke(options); services.AddSingleton(options); diff --git a/src/DotNetCore.CAP.MongoDB/CapPublisher.cs b/src/DotNetCore.CAP.MongoDB/CapPublisher.cs index 475d947..df19c48 100644 --- a/src/DotNetCore.CAP.MongoDB/CapPublisher.cs +++ b/src/DotNetCore.CAP.MongoDB/CapPublisher.cs @@ -15,9 +15,9 @@ namespace DotNetCore.CAP.MongoDB { public class CapPublisher : CapPublisherBase, ICallbackPublisher { - private readonly IMongoDatabase _database; private readonly MongoDBOptions _options; - private bool _isInTransaction = true; + private readonly IMongoDatabase _database; + private bool _usingTransaction = true; public CapPublisher( ILogger logger, @@ -57,31 +57,29 @@ namespace DotNetCore.CAP.MongoDB throw new NotImplementedException("Not work for MongoDB"); } - public override void PublishWithMongo(string name, T contentObj, object mongoSession = null, - string callbackName = null) + public override void PublishWithMongo(string name, T contentObj, IMongoTransaction mongoTransaction = null, string callbackName = null) { - var session = mongoSession as IClientSessionHandle; - if (session == null) + if (mongoTransaction == null) { - _isInTransaction = false; + _usingTransaction = false; + mongoTransaction = new NullMongoTransaction(); } - PublishWithSession(name, contentObj, session, callbackName); + PublishWithTransaction(name, contentObj, mongoTransaction, callbackName); } - public override async Task PublishWithMongoAsync(string name, T contentObj, object mongoSession = null, - string callbackName = null) + public override async Task PublishWithMongoAsync(string name, T contentObj, IMongoTransaction mongoTransaction = null, string callbackName = null) { - var session = mongoSession as IClientSessionHandle; - if (session == null) + if (mongoTransaction == null) { - _isInTransaction = false; + _usingTransaction = false; + mongoTransaction = new NullMongoTransaction(); } - await PublishWithSessionAsync(name, contentObj, session, callbackName); + await PublishWithTransactionAsync(name, contentObj, mongoTransaction, callbackName); } - private void PublishWithSession(string name, T contentObj, IClientSessionHandle session, string callbackName) + private void PublishWithTransaction(string name, T contentObj, IMongoTransaction transaction, string callbackName) { var operationId = default(Guid); @@ -94,12 +92,19 @@ namespace DotNetCore.CAP.MongoDB StatusName = StatusName.Scheduled }; + var session = transaction.GetSession(); + try { operationId = s_diagnosticListener.WritePublishMessageStoreBefore(message); var id = Execute(session, message); - if (!_isInTransaction && id > 0) + if (transaction.AutoCommit) + { + session.CommitTransaction(); + } + + if (!_usingTransaction || (transaction.AutoCommit && id > 0)) { _logger.LogInformation($"message [{message}] has been persisted in the database."); s_diagnosticListener.WritePublishMessageStoreAfter(operationId, message); @@ -120,7 +125,7 @@ namespace DotNetCore.CAP.MongoDB message.Id = new MongoDBUtil().GetNextSequenceValue(_database, _options.PublishedCollection, session); var collection = _database.GetCollection(_options.PublishedCollection); - if (_isInTransaction) + if (_usingTransaction) { collection.InsertOne(session, message); } @@ -132,8 +137,8 @@ namespace DotNetCore.CAP.MongoDB return message.Id; } - private async Task PublishWithSessionAsync(string name, T contentObj, IClientSessionHandle session, - string callbackName) + + private async Task PublishWithTransactionAsync(string name, T contentObj, IMongoTransaction transaction, string callbackName) { var operationId = default(Guid); var content = Serialize(contentObj, callbackName); @@ -145,13 +150,20 @@ namespace DotNetCore.CAP.MongoDB StatusName = StatusName.Scheduled }; + var session = transaction.GetSession(); + try { operationId = s_diagnosticListener.WritePublishMessageStoreBefore(message); var id = await ExecuteAsync(session, message); - if (!_isInTransaction && id > 0) + if (transaction.AutoCommit) + { + await session.CommitTransactionAsync(); + } + + if (!_usingTransaction || (transaction.AutoCommit && id > 0)) { _logger.LogInformation($"message [{message}] has been persisted in the database."); s_diagnosticListener.WritePublishMessageStoreAfter(operationId, message); @@ -175,7 +187,7 @@ namespace DotNetCore.CAP.MongoDB message.Id = await new MongoDBUtil().GetNextSequenceValueAsync(_database, _options.PublishedCollection, session); var collection = _database.GetCollection(_options.PublishedCollection); - if (_isInTransaction) + if (_usingTransaction) { await collection.InsertOneAsync(session, message); } diff --git a/src/DotNetCore.CAP.MongoDB/MongoTransaction.cs b/src/DotNetCore.CAP.MongoDB/MongoTransaction.cs new file mode 100644 index 0000000..367cac5 --- /dev/null +++ b/src/DotNetCore.CAP.MongoDB/MongoTransaction.cs @@ -0,0 +1,57 @@ +using System; +using System.Threading.Tasks; +using DotNetCore.CAP.Abstractions; +using MongoDB.Driver; + +namespace DotNetCore.CAP.MongoDB +{ + public class MongoTransaction : IMongoTransaction + { + private readonly IMongoClient _client; + public MongoTransaction(IMongoClient client) + { + _client = client; + } + + public IClientSessionHandle Session { get; set; } + public bool AutoCommit { get; set; } + + public async Task BegeinAsync(bool autoCommit = true) + { + AutoCommit = autoCommit; + Session = await _client.StartSessionAsync(); + Session.StartTransaction(); + return this; + } + + public IMongoTransaction Begein(bool autoCommit = true) + { + AutoCommit = autoCommit; + Session = _client.StartSession(); + Session.StartTransaction(); + return this; + } + + public void Dispose() + { + Session?.Dispose(); + } + } + + public class NullMongoTransaction : MongoTransaction + { + public NullMongoTransaction(IMongoClient client = null) : base(client) + { + AutoCommit = false; + } + } + + public static class MongoTransactionExtensions + { + public static IClientSessionHandle GetSession(this IMongoTransaction mongoTransaction) + { + var trans = mongoTransaction as MongoTransaction; + return trans?.Session; + } + } +} \ No newline at end of file diff --git a/src/DotNetCore.CAP/Abstractions/CapPublisherBase.cs b/src/DotNetCore.CAP/Abstractions/CapPublisherBase.cs index bc53ca8..d585406 100644 --- a/src/DotNetCore.CAP/Abstractions/CapPublisherBase.cs +++ b/src/DotNetCore.CAP/Abstractions/CapPublisherBase.cs @@ -67,12 +67,12 @@ namespace DotNetCore.CAP.Abstractions return PublishWithTransAsync(name, contentObj, callbackName); } - public virtual void PublishWithMongo(string name, T contentObj, object mongoSession = null, string callbackName = null) + public virtual void PublishWithMongo(string name, T contentObj, IMongoTransaction mongoTransaction = null, string callbackName = null) { throw new NotImplementedException("Work for MongoDB only."); } - public virtual Task PublishWithMongoAsync(string name, T contentObj, object mongoSession = null, string callbackName = null) + public virtual Task PublishWithMongoAsync(string name, T contentObj, IMongoTransaction mongoTransaction = null, string callbackName = null) { throw new NotImplementedException("Work for MongoDB only."); } diff --git a/src/DotNetCore.CAP/Abstractions/IMongoTransaction.cs b/src/DotNetCore.CAP/Abstractions/IMongoTransaction.cs new file mode 100644 index 0000000..e8c16d0 --- /dev/null +++ b/src/DotNetCore.CAP/Abstractions/IMongoTransaction.cs @@ -0,0 +1,18 @@ +using System; +using System.Threading.Tasks; + +namespace DotNetCore.CAP.Abstractions +{ + public interface IMongoTransaction : IDisposable + { + /// + /// If set true, the session.CommitTransaction() will be called automatically. + /// + /// + bool AutoCommit { get; set; } + + Task BegeinAsync(bool autoCommit = true); + + IMongoTransaction Begein(bool autoCommit = true); + } +} \ No newline at end of file diff --git a/src/DotNetCore.CAP/ICapPublisher.cs b/src/DotNetCore.CAP/ICapPublisher.cs index 4386926..017b620 100644 --- a/src/DotNetCore.CAP/ICapPublisher.cs +++ b/src/DotNetCore.CAP/ICapPublisher.cs @@ -3,6 +3,7 @@ using System.Data; using System.Threading.Tasks; +using DotNetCore.CAP.Abstractions; namespace DotNetCore.CAP { @@ -60,17 +61,17 @@ namespace DotNetCore.CAP /// /// the topic name or exchange router key. /// message body content, that will be serialized of json. - /// if seesion was set null, the message will be published directly. + /// if transaction was set null, the message will be published directly. /// callback subscriber name - void PublishWithMongo(string name, T contentObj, object mongoSession = null, string callbackName = null); + void PublishWithMongo(string name, T contentObj, IMongoTransaction mongoTransaction = null, string callbackName = null); /// /// Asynchronous publish an object message with mongo. /// /// the topic name or exchange router key. /// message body content, that will be serialized of json. - /// if seesion was set null, the message will be published directly. + /// if transaction was set null, the message will be published directly. /// callback subscriber name - Task PublishWithMongoAsync(string name, T contentObj, object mongoSession = null, string callbackName = null); + Task PublishWithMongoAsync(string name, T contentObj, IMongoTransaction mongoTransaction = null, string callbackName = null); } } \ No newline at end of file diff --git a/test/DotNetCore.CAP.Test/CAP.BuilderTest.cs b/test/DotNetCore.CAP.Test/CAP.BuilderTest.cs index e32ff4a..402e72f 100644 --- a/test/DotNetCore.CAP.Test/CAP.BuilderTest.cs +++ b/test/DotNetCore.CAP.Test/CAP.BuilderTest.cs @@ -169,12 +169,12 @@ namespace DotNetCore.CAP.Test throw new NotImplementedException(); } - public void PublishWithMongo(string name, T contentObj, object mongoSession = null, string callbackName = null) + public void PublishWithMongo(string name, T contentObj, IMongoTransaction mongoTransaction = null, string callbackName = null) { throw new NotImplementedException(); } - public Task PublishWithMongoAsync(string name, T contentObj, object mongoSession = null, string callbackName = null) + public Task PublishWithMongoAsync(string name, T contentObj, IMongoTransaction mongoTransaction = null, string callbackName = null) { throw new NotImplementedException(); } From 885b37ba72da8d3ab29712ad2a70c857ffaf3317 Mon Sep 17 00:00:00 2001 From: keke Date: Tue, 24 Jul 2018 09:35:54 +0800 Subject: [PATCH 143/223] Abstract MongoTransaction to make the usage more comfortable (#167) * Abstract MongoTransaction to make the usage more comfortable * Tweak the method name --- .../Controllers/ValuesController.cs | 45 +++++++++++---- .../CAP.MongoDBCapOptionsExtension.cs | 4 ++ src/DotNetCore.CAP.MongoDB/CapPublisher.cs | 54 +++++++++++------- .../MongoTransaction.cs | 57 +++++++++++++++++++ .../Abstractions/CapPublisherBase.cs | 4 +- .../Abstractions/IMongoTransaction.cs | 18 ++++++ src/DotNetCore.CAP/ICapPublisher.cs | 9 +-- test/DotNetCore.CAP.Test/CAP.BuilderTest.cs | 4 +- 8 files changed, 154 insertions(+), 41 deletions(-) create mode 100644 src/DotNetCore.CAP.MongoDB/MongoTransaction.cs create mode 100644 src/DotNetCore.CAP/Abstractions/IMongoTransaction.cs diff --git a/samples/Sample.RabbitMQ.MongoDB/Controllers/ValuesController.cs b/samples/Sample.RabbitMQ.MongoDB/Controllers/ValuesController.cs index 374100d..a22af60 100644 --- a/samples/Sample.RabbitMQ.MongoDB/Controllers/ValuesController.cs +++ b/samples/Sample.RabbitMQ.MongoDB/Controllers/ValuesController.cs @@ -3,6 +3,8 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using DotNetCore.CAP; +using DotNetCore.CAP.Abstractions; +using DotNetCore.CAP.MongoDB; using Microsoft.AspNetCore.Mvc; using MongoDB.Bson; using MongoDB.Driver; @@ -15,39 +17,58 @@ namespace Sample.RabbitMQ.MongoDB.Controllers { private readonly IMongoClient _client; private readonly ICapPublisher _capPublisher; + private readonly IMongoTransaction _mongoTransaction; - public ValuesController(IMongoClient client, ICapPublisher capPublisher) + public ValuesController(IMongoClient client, ICapPublisher capPublisher, IMongoTransaction mongoTransaction) { _client = client; _capPublisher = capPublisher; + _mongoTransaction = mongoTransaction; } [Route("~/publish")] - public IActionResult PublishWithSession() + public async Task PublishWithTrans() { - using (var session = _client.StartSession()) + using (var trans = await _mongoTransaction.BegeinAsync()) { - session.StartTransaction(); var collection = _client.GetDatabase("TEST").GetCollection("test"); - collection.InsertOne(session, new BsonDocument { { "hello", "world" } }); + collection.InsertOne(trans.GetSession(), new BsonDocument { { "hello", "world" } }); - _capPublisher.PublishWithMongo("sample.rabbitmq.mongodb", DateTime.Now, session); + await _capPublisher.PublishWithMongoAsync("sample.rabbitmq.mongodb", DateTime.Now, trans); + } + return Ok(); + } + + [Route("~/publish/not/autocommit")] + public IActionResult PublishNotAutoCommit() + { + using (var trans = _mongoTransaction.Begein(autoCommit: false)) + { + var session = trans.GetSession(); + + var collection = _client.GetDatabase("TEST").GetCollection("test"); + collection.InsertOne(session, new BsonDocument { { "Hello", "World" } }); + + _capPublisher.PublishWithMongo("sample.rabbitmq.mongodb", DateTime.Now, trans); + //Do something, and commit by yourself. session.CommitTransaction(); } return Ok(); } - [Route("~/publish_rollback")] + [Route("~/publish/rollback")] public IActionResult PublishRollback() { - using (var session = _client.StartSession()) + using (var trans = _mongoTransaction.Begein(autoCommit: false)) { + var session = trans.GetSession(); try { - session.StartTransaction(); - _capPublisher.PublishWithMongo("sample.rabbitmq.mongodb", DateTime.Now, session); + _capPublisher.PublishWithMongo("sample.rabbitmq.mongodb", DateTime.Now, trans); + //Do something, but throw new Exception("Foo"); + session.CommitTransaction(); } catch (System.Exception ex) { @@ -57,8 +78,8 @@ namespace Sample.RabbitMQ.MongoDB.Controllers } } - [Route("~/publish_without_session")] - public IActionResult PublishWithoutSession() + [Route("~/publish/without/trans")] + public IActionResult PublishWithoutTrans() { _capPublisher.PublishWithMongo("sample.rabbitmq.mongodb", DateTime.Now); return Ok(); diff --git a/src/DotNetCore.CAP.MongoDB/CAP.MongoDBCapOptionsExtension.cs b/src/DotNetCore.CAP.MongoDB/CAP.MongoDBCapOptionsExtension.cs index e07a55a..9b00686 100644 --- a/src/DotNetCore.CAP.MongoDB/CAP.MongoDBCapOptionsExtension.cs +++ b/src/DotNetCore.CAP.MongoDB/CAP.MongoDBCapOptionsExtension.cs @@ -2,6 +2,8 @@ // Licensed under the MIT License. See License.txt in the project root for license information. using System; +using DotNetCore.CAP; +using DotNetCore.CAP.Abstractions; using DotNetCore.CAP.Processor; using Microsoft.Extensions.DependencyInjection; @@ -25,6 +27,8 @@ namespace DotNetCore.CAP.MongoDB services.AddScoped(); services.AddTransient(); + services.AddTransient(); + var options = new MongoDBOptions(); _configure?.Invoke(options); services.AddSingleton(options); diff --git a/src/DotNetCore.CAP.MongoDB/CapPublisher.cs b/src/DotNetCore.CAP.MongoDB/CapPublisher.cs index 475d947..df19c48 100644 --- a/src/DotNetCore.CAP.MongoDB/CapPublisher.cs +++ b/src/DotNetCore.CAP.MongoDB/CapPublisher.cs @@ -15,9 +15,9 @@ namespace DotNetCore.CAP.MongoDB { public class CapPublisher : CapPublisherBase, ICallbackPublisher { - private readonly IMongoDatabase _database; private readonly MongoDBOptions _options; - private bool _isInTransaction = true; + private readonly IMongoDatabase _database; + private bool _usingTransaction = true; public CapPublisher( ILogger logger, @@ -57,31 +57,29 @@ namespace DotNetCore.CAP.MongoDB throw new NotImplementedException("Not work for MongoDB"); } - public override void PublishWithMongo(string name, T contentObj, object mongoSession = null, - string callbackName = null) + public override void PublishWithMongo(string name, T contentObj, IMongoTransaction mongoTransaction = null, string callbackName = null) { - var session = mongoSession as IClientSessionHandle; - if (session == null) + if (mongoTransaction == null) { - _isInTransaction = false; + _usingTransaction = false; + mongoTransaction = new NullMongoTransaction(); } - PublishWithSession(name, contentObj, session, callbackName); + PublishWithTransaction(name, contentObj, mongoTransaction, callbackName); } - public override async Task PublishWithMongoAsync(string name, T contentObj, object mongoSession = null, - string callbackName = null) + public override async Task PublishWithMongoAsync(string name, T contentObj, IMongoTransaction mongoTransaction = null, string callbackName = null) { - var session = mongoSession as IClientSessionHandle; - if (session == null) + if (mongoTransaction == null) { - _isInTransaction = false; + _usingTransaction = false; + mongoTransaction = new NullMongoTransaction(); } - await PublishWithSessionAsync(name, contentObj, session, callbackName); + await PublishWithTransactionAsync(name, contentObj, mongoTransaction, callbackName); } - private void PublishWithSession(string name, T contentObj, IClientSessionHandle session, string callbackName) + private void PublishWithTransaction(string name, T contentObj, IMongoTransaction transaction, string callbackName) { var operationId = default(Guid); @@ -94,12 +92,19 @@ namespace DotNetCore.CAP.MongoDB StatusName = StatusName.Scheduled }; + var session = transaction.GetSession(); + try { operationId = s_diagnosticListener.WritePublishMessageStoreBefore(message); var id = Execute(session, message); - if (!_isInTransaction && id > 0) + if (transaction.AutoCommit) + { + session.CommitTransaction(); + } + + if (!_usingTransaction || (transaction.AutoCommit && id > 0)) { _logger.LogInformation($"message [{message}] has been persisted in the database."); s_diagnosticListener.WritePublishMessageStoreAfter(operationId, message); @@ -120,7 +125,7 @@ namespace DotNetCore.CAP.MongoDB message.Id = new MongoDBUtil().GetNextSequenceValue(_database, _options.PublishedCollection, session); var collection = _database.GetCollection(_options.PublishedCollection); - if (_isInTransaction) + if (_usingTransaction) { collection.InsertOne(session, message); } @@ -132,8 +137,8 @@ namespace DotNetCore.CAP.MongoDB return message.Id; } - private async Task PublishWithSessionAsync(string name, T contentObj, IClientSessionHandle session, - string callbackName) + + private async Task PublishWithTransactionAsync(string name, T contentObj, IMongoTransaction transaction, string callbackName) { var operationId = default(Guid); var content = Serialize(contentObj, callbackName); @@ -145,13 +150,20 @@ namespace DotNetCore.CAP.MongoDB StatusName = StatusName.Scheduled }; + var session = transaction.GetSession(); + try { operationId = s_diagnosticListener.WritePublishMessageStoreBefore(message); var id = await ExecuteAsync(session, message); - if (!_isInTransaction && id > 0) + if (transaction.AutoCommit) + { + await session.CommitTransactionAsync(); + } + + if (!_usingTransaction || (transaction.AutoCommit && id > 0)) { _logger.LogInformation($"message [{message}] has been persisted in the database."); s_diagnosticListener.WritePublishMessageStoreAfter(operationId, message); @@ -175,7 +187,7 @@ namespace DotNetCore.CAP.MongoDB message.Id = await new MongoDBUtil().GetNextSequenceValueAsync(_database, _options.PublishedCollection, session); var collection = _database.GetCollection(_options.PublishedCollection); - if (_isInTransaction) + if (_usingTransaction) { await collection.InsertOneAsync(session, message); } diff --git a/src/DotNetCore.CAP.MongoDB/MongoTransaction.cs b/src/DotNetCore.CAP.MongoDB/MongoTransaction.cs new file mode 100644 index 0000000..367cac5 --- /dev/null +++ b/src/DotNetCore.CAP.MongoDB/MongoTransaction.cs @@ -0,0 +1,57 @@ +using System; +using System.Threading.Tasks; +using DotNetCore.CAP.Abstractions; +using MongoDB.Driver; + +namespace DotNetCore.CAP.MongoDB +{ + public class MongoTransaction : IMongoTransaction + { + private readonly IMongoClient _client; + public MongoTransaction(IMongoClient client) + { + _client = client; + } + + public IClientSessionHandle Session { get; set; } + public bool AutoCommit { get; set; } + + public async Task BegeinAsync(bool autoCommit = true) + { + AutoCommit = autoCommit; + Session = await _client.StartSessionAsync(); + Session.StartTransaction(); + return this; + } + + public IMongoTransaction Begein(bool autoCommit = true) + { + AutoCommit = autoCommit; + Session = _client.StartSession(); + Session.StartTransaction(); + return this; + } + + public void Dispose() + { + Session?.Dispose(); + } + } + + public class NullMongoTransaction : MongoTransaction + { + public NullMongoTransaction(IMongoClient client = null) : base(client) + { + AutoCommit = false; + } + } + + public static class MongoTransactionExtensions + { + public static IClientSessionHandle GetSession(this IMongoTransaction mongoTransaction) + { + var trans = mongoTransaction as MongoTransaction; + return trans?.Session; + } + } +} \ No newline at end of file diff --git a/src/DotNetCore.CAP/Abstractions/CapPublisherBase.cs b/src/DotNetCore.CAP/Abstractions/CapPublisherBase.cs index bc53ca8..d585406 100644 --- a/src/DotNetCore.CAP/Abstractions/CapPublisherBase.cs +++ b/src/DotNetCore.CAP/Abstractions/CapPublisherBase.cs @@ -67,12 +67,12 @@ namespace DotNetCore.CAP.Abstractions return PublishWithTransAsync(name, contentObj, callbackName); } - public virtual void PublishWithMongo(string name, T contentObj, object mongoSession = null, string callbackName = null) + public virtual void PublishWithMongo(string name, T contentObj, IMongoTransaction mongoTransaction = null, string callbackName = null) { throw new NotImplementedException("Work for MongoDB only."); } - public virtual Task PublishWithMongoAsync(string name, T contentObj, object mongoSession = null, string callbackName = null) + public virtual Task PublishWithMongoAsync(string name, T contentObj, IMongoTransaction mongoTransaction = null, string callbackName = null) { throw new NotImplementedException("Work for MongoDB only."); } diff --git a/src/DotNetCore.CAP/Abstractions/IMongoTransaction.cs b/src/DotNetCore.CAP/Abstractions/IMongoTransaction.cs new file mode 100644 index 0000000..e8c16d0 --- /dev/null +++ b/src/DotNetCore.CAP/Abstractions/IMongoTransaction.cs @@ -0,0 +1,18 @@ +using System; +using System.Threading.Tasks; + +namespace DotNetCore.CAP.Abstractions +{ + public interface IMongoTransaction : IDisposable + { + /// + /// If set true, the session.CommitTransaction() will be called automatically. + /// + /// + bool AutoCommit { get; set; } + + Task BegeinAsync(bool autoCommit = true); + + IMongoTransaction Begein(bool autoCommit = true); + } +} \ No newline at end of file diff --git a/src/DotNetCore.CAP/ICapPublisher.cs b/src/DotNetCore.CAP/ICapPublisher.cs index 4386926..017b620 100644 --- a/src/DotNetCore.CAP/ICapPublisher.cs +++ b/src/DotNetCore.CAP/ICapPublisher.cs @@ -3,6 +3,7 @@ using System.Data; using System.Threading.Tasks; +using DotNetCore.CAP.Abstractions; namespace DotNetCore.CAP { @@ -60,17 +61,17 @@ namespace DotNetCore.CAP /// /// the topic name or exchange router key. /// message body content, that will be serialized of json. - /// if seesion was set null, the message will be published directly. + /// if transaction was set null, the message will be published directly. /// callback subscriber name - void PublishWithMongo(string name, T contentObj, object mongoSession = null, string callbackName = null); + void PublishWithMongo(string name, T contentObj, IMongoTransaction mongoTransaction = null, string callbackName = null); /// /// Asynchronous publish an object message with mongo. /// /// the topic name or exchange router key. /// message body content, that will be serialized of json. - /// if seesion was set null, the message will be published directly. + /// if transaction was set null, the message will be published directly. /// callback subscriber name - Task PublishWithMongoAsync(string name, T contentObj, object mongoSession = null, string callbackName = null); + Task PublishWithMongoAsync(string name, T contentObj, IMongoTransaction mongoTransaction = null, string callbackName = null); } } \ No newline at end of file diff --git a/test/DotNetCore.CAP.Test/CAP.BuilderTest.cs b/test/DotNetCore.CAP.Test/CAP.BuilderTest.cs index e32ff4a..402e72f 100644 --- a/test/DotNetCore.CAP.Test/CAP.BuilderTest.cs +++ b/test/DotNetCore.CAP.Test/CAP.BuilderTest.cs @@ -169,12 +169,12 @@ namespace DotNetCore.CAP.Test throw new NotImplementedException(); } - public void PublishWithMongo(string name, T contentObj, object mongoSession = null, string callbackName = null) + public void PublishWithMongo(string name, T contentObj, IMongoTransaction mongoTransaction = null, string callbackName = null) { throw new NotImplementedException(); } - public Task PublishWithMongoAsync(string name, T contentObj, object mongoSession = null, string callbackName = null) + public Task PublishWithMongoAsync(string name, T contentObj, IMongoTransaction mongoTransaction = null, string callbackName = null) { throw new NotImplementedException(); } From 8b3c30cd93a806868a26a8ecd07338b3e48b158d Mon Sep 17 00:00:00 2001 From: Savorboard Date: Tue, 24 Jul 2018 10:12:15 +0800 Subject: [PATCH 144/223] cleanup samples. --- .../Controllers/ValuesController.cs | 2 -- samples/Sample.RabbitMQ.MongoDB/Program.cs | 9 +-------- samples/Sample.RabbitMQ.MongoDB/Startup.cs | 10 ++-------- 3 files changed, 3 insertions(+), 18 deletions(-) diff --git a/samples/Sample.RabbitMQ.MongoDB/Controllers/ValuesController.cs b/samples/Sample.RabbitMQ.MongoDB/Controllers/ValuesController.cs index a22af60..5e0a07d 100644 --- a/samples/Sample.RabbitMQ.MongoDB/Controllers/ValuesController.cs +++ b/samples/Sample.RabbitMQ.MongoDB/Controllers/ValuesController.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; using System.Threading.Tasks; using DotNetCore.CAP; using DotNetCore.CAP.Abstractions; diff --git a/samples/Sample.RabbitMQ.MongoDB/Program.cs b/samples/Sample.RabbitMQ.MongoDB/Program.cs index 7f32066..9271a46 100644 --- a/samples/Sample.RabbitMQ.MongoDB/Program.cs +++ b/samples/Sample.RabbitMQ.MongoDB/Program.cs @@ -1,12 +1,5 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore; +using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Logging; namespace Sample.RabbitMQ.MongoDB { diff --git a/samples/Sample.RabbitMQ.MongoDB/Startup.cs b/samples/Sample.RabbitMQ.MongoDB/Startup.cs index 4f4bd69..637285d 100644 --- a/samples/Sample.RabbitMQ.MongoDB/Startup.cs +++ b/samples/Sample.RabbitMQ.MongoDB/Startup.cs @@ -1,16 +1,9 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using DotNetCore.CAP; +using DotNetCore.CAP; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.HttpsPolicy; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; using MongoDB.Driver; namespace Sample.RabbitMQ.MongoDB @@ -52,6 +45,7 @@ namespace Sample.RabbitMQ.MongoDB { app.UseDeveloperExceptionPage(); } + app.UseMvc(); app.UseCap(); From aa6840f07ec27a5d7d20edd79051ba1bd4875c9f Mon Sep 17 00:00:00 2001 From: Savorboard Date: Tue, 24 Jul 2018 10:12:15 +0800 Subject: [PATCH 145/223] cleanup samples. --- .../Controllers/ValuesController.cs | 2 -- samples/Sample.RabbitMQ.MongoDB/Program.cs | 9 +-------- samples/Sample.RabbitMQ.MongoDB/Startup.cs | 10 ++-------- 3 files changed, 3 insertions(+), 18 deletions(-) diff --git a/samples/Sample.RabbitMQ.MongoDB/Controllers/ValuesController.cs b/samples/Sample.RabbitMQ.MongoDB/Controllers/ValuesController.cs index a22af60..5e0a07d 100644 --- a/samples/Sample.RabbitMQ.MongoDB/Controllers/ValuesController.cs +++ b/samples/Sample.RabbitMQ.MongoDB/Controllers/ValuesController.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; using System.Threading.Tasks; using DotNetCore.CAP; using DotNetCore.CAP.Abstractions; diff --git a/samples/Sample.RabbitMQ.MongoDB/Program.cs b/samples/Sample.RabbitMQ.MongoDB/Program.cs index 7f32066..9271a46 100644 --- a/samples/Sample.RabbitMQ.MongoDB/Program.cs +++ b/samples/Sample.RabbitMQ.MongoDB/Program.cs @@ -1,12 +1,5 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore; +using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Logging; namespace Sample.RabbitMQ.MongoDB { diff --git a/samples/Sample.RabbitMQ.MongoDB/Startup.cs b/samples/Sample.RabbitMQ.MongoDB/Startup.cs index 4f4bd69..637285d 100644 --- a/samples/Sample.RabbitMQ.MongoDB/Startup.cs +++ b/samples/Sample.RabbitMQ.MongoDB/Startup.cs @@ -1,16 +1,9 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using DotNetCore.CAP; +using DotNetCore.CAP; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.HttpsPolicy; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; using MongoDB.Driver; namespace Sample.RabbitMQ.MongoDB @@ -52,6 +45,7 @@ namespace Sample.RabbitMQ.MongoDB { app.UseDeveloperExceptionPage(); } + app.UseMvc(); app.UseCap(); From c7010473ee461eef483e5b2588da627216825153 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Tue, 24 Jul 2018 10:13:02 +0800 Subject: [PATCH 146/223] add file license header. --- src/DotNetCore.CAP.MongoDB/MongoTransaction.cs | 5 ++++- src/DotNetCore.CAP/Abstractions/IMongoTransaction.cs | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/DotNetCore.CAP.MongoDB/MongoTransaction.cs b/src/DotNetCore.CAP.MongoDB/MongoTransaction.cs index 367cac5..96aed37 100644 --- a/src/DotNetCore.CAP.MongoDB/MongoTransaction.cs +++ b/src/DotNetCore.CAP.MongoDB/MongoTransaction.cs @@ -1,4 +1,6 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + using System.Threading.Tasks; using DotNetCore.CAP.Abstractions; using MongoDB.Driver; @@ -8,6 +10,7 @@ namespace DotNetCore.CAP.MongoDB public class MongoTransaction : IMongoTransaction { private readonly IMongoClient _client; + public MongoTransaction(IMongoClient client) { _client = client; diff --git a/src/DotNetCore.CAP/Abstractions/IMongoTransaction.cs b/src/DotNetCore.CAP/Abstractions/IMongoTransaction.cs index e8c16d0..b98caf8 100644 --- a/src/DotNetCore.CAP/Abstractions/IMongoTransaction.cs +++ b/src/DotNetCore.CAP/Abstractions/IMongoTransaction.cs @@ -1,3 +1,6 @@ +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + using System; using System.Threading.Tasks; @@ -12,7 +15,7 @@ namespace DotNetCore.CAP.Abstractions bool AutoCommit { get; set; } Task BegeinAsync(bool autoCommit = true); - + IMongoTransaction Begein(bool autoCommit = true); } } \ No newline at end of file From 6d35ef96abfbd6a8b5597a034231f12789c9cbd6 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Tue, 24 Jul 2018 10:13:02 +0800 Subject: [PATCH 147/223] add file license header. --- src/DotNetCore.CAP.MongoDB/MongoTransaction.cs | 5 ++++- src/DotNetCore.CAP/Abstractions/IMongoTransaction.cs | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/DotNetCore.CAP.MongoDB/MongoTransaction.cs b/src/DotNetCore.CAP.MongoDB/MongoTransaction.cs index 367cac5..96aed37 100644 --- a/src/DotNetCore.CAP.MongoDB/MongoTransaction.cs +++ b/src/DotNetCore.CAP.MongoDB/MongoTransaction.cs @@ -1,4 +1,6 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + using System.Threading.Tasks; using DotNetCore.CAP.Abstractions; using MongoDB.Driver; @@ -8,6 +10,7 @@ namespace DotNetCore.CAP.MongoDB public class MongoTransaction : IMongoTransaction { private readonly IMongoClient _client; + public MongoTransaction(IMongoClient client) { _client = client; diff --git a/src/DotNetCore.CAP/Abstractions/IMongoTransaction.cs b/src/DotNetCore.CAP/Abstractions/IMongoTransaction.cs index e8c16d0..b98caf8 100644 --- a/src/DotNetCore.CAP/Abstractions/IMongoTransaction.cs +++ b/src/DotNetCore.CAP/Abstractions/IMongoTransaction.cs @@ -1,3 +1,6 @@ +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + using System; using System.Threading.Tasks; @@ -12,7 +15,7 @@ namespace DotNetCore.CAP.Abstractions bool AutoCommit { get; set; } Task BegeinAsync(bool autoCommit = true); - + IMongoTransaction Begein(bool autoCommit = true); } } \ No newline at end of file From f23f53babf948504065279017cea316eb88bebd1 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Fri, 27 Jul 2018 18:01:53 +0800 Subject: [PATCH 148/223] add captransaction interface. --- src/DotNetCore.CAP/ICapTransaction.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 src/DotNetCore.CAP/ICapTransaction.cs diff --git a/src/DotNetCore.CAP/ICapTransaction.cs b/src/DotNetCore.CAP/ICapTransaction.cs new file mode 100644 index 0000000..7134438 --- /dev/null +++ b/src/DotNetCore.CAP/ICapTransaction.cs @@ -0,0 +1,15 @@ +using System; + +namespace DotNetCore.CAP +{ + public interface ICapTransaction : IDisposable + { + bool AutoCommit { get; set; } + + object DbTransaction { get; set; } + + void Commit(); + + void Rollback(); + } +} From 764e23fa32e348e281938b6d9301a28cfe26eb77 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Sat, 28 Jul 2018 18:02:33 +0800 Subject: [PATCH 149/223] add impl for ICapTransaction --- src/DotNetCore.CAP/ICapTransaction.Base.cs | 41 ++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 src/DotNetCore.CAP/ICapTransaction.Base.cs diff --git a/src/DotNetCore.CAP/ICapTransaction.Base.cs b/src/DotNetCore.CAP/ICapTransaction.Base.cs new file mode 100644 index 0000000..3ab5fb2 --- /dev/null +++ b/src/DotNetCore.CAP/ICapTransaction.Base.cs @@ -0,0 +1,41 @@ +using System.Collections.Generic; +using DotNetCore.CAP.Models; + +namespace DotNetCore.CAP +{ + public abstract class CapTransactionBase : ICapTransaction + { + private readonly IDispatcher _dispatcher; + + private readonly IList _bufferList; + + protected CapTransactionBase(IDispatcher dispatcher) + { + _dispatcher = dispatcher; + _bufferList = new List(1); + } + + public bool AutoCommit { get; set; } + + public object DbTransaction { get; set; } + + protected internal void AddToSent(CapPublishedMessage msg) + { + _bufferList.Add(msg); + } + + protected void Flush() + { + foreach (var message in _bufferList) + { + _dispatcher.EnqueueToPublish(message); + } + } + + public abstract void Commit(); + + public abstract void Rollback(); + + public abstract void Dispose(); + } +} From d907215b15b417cf99442216c6ef9936da46e0d9 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Sun, 29 Jul 2018 18:03:20 +0800 Subject: [PATCH 150/223] add mysql impl for ICapTransaction --- .../ICapTransaction.MySql.cs | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 src/DotNetCore.CAP.MySql/ICapTransaction.MySql.cs diff --git a/src/DotNetCore.CAP.MySql/ICapTransaction.MySql.cs b/src/DotNetCore.CAP.MySql/ICapTransaction.MySql.cs new file mode 100644 index 0000000..7d228e5 --- /dev/null +++ b/src/DotNetCore.CAP.MySql/ICapTransaction.MySql.cs @@ -0,0 +1,73 @@ +using System.Data; +using System.Diagnostics; +using Microsoft.EntityFrameworkCore.Storage; + +// ReSharper disable once CheckNamespace +namespace DotNetCore.CAP +{ + public class MySqlCapTransaction : CapTransactionBase + { + public MySqlCapTransaction(IDispatcher dispatcher) : base(dispatcher) { } + + public override void Commit() + { + Debug.Assert(DbTransaction != null); + + switch (DbTransaction) + { + case IDbTransaction dbTransaction: + dbTransaction.Commit(); + break; + case IDbContextTransaction dbContextTransaction: + dbContextTransaction.Commit(); + break; + } + + Flush(); + } + + public override void Rollback() + { + Debug.Assert(DbTransaction != null); + + switch (DbTransaction) + { + case IDbTransaction dbTransaction: + dbTransaction.Rollback(); + break; + case IDbContextTransaction dbContextTransaction: + dbContextTransaction.Rollback(); + break; + } + } + + public override void Dispose() + { + (DbTransaction as IDbTransaction)?.Dispose(); + } + } + + public static class CapTransactionExtensions + { + public static ICapTransaction Begin(this ICapTransaction transaction, + IDbTransaction dbTransaction, bool autoCommit = false) + { + + transaction.DbTransaction = dbTransaction; + transaction.AutoCommit = autoCommit; + + return transaction; + } + + public static ICapTransaction Begin(this ICapTransaction transaction, + IDbContextTransaction dbTransaction, bool autoCommit = false) + { + + transaction.DbTransaction = dbTransaction; + transaction.AutoCommit = autoCommit; + + return transaction; + } + } + +} From 362b6f5219d37ffa703d897ef446d1594595b08a Mon Sep 17 00:00:00 2001 From: Savorboard Date: Mon, 30 Jul 2018 18:03:31 +0800 Subject: [PATCH 151/223] refactor --- .../Abstractions/CapPublisherBase.cs | 257 +++++------------- 1 file changed, 63 insertions(+), 194 deletions(-) diff --git a/src/DotNetCore.CAP/Abstractions/CapPublisherBase.cs b/src/DotNetCore.CAP/Abstractions/CapPublisherBase.cs index d585406..b029f69 100644 --- a/src/DotNetCore.CAP/Abstractions/CapPublisherBase.cs +++ b/src/DotNetCore.CAP/Abstractions/CapPublisherBase.cs @@ -2,262 +2,131 @@ // Licensed under the MIT License. See License.txt in the project root for license information. using System; -using System.Data; using System.Diagnostics; +using System.Threading; using System.Threading.Tasks; using DotNetCore.CAP.Diagnostics; using DotNetCore.CAP.Infrastructure; using DotNetCore.CAP.Models; -using Microsoft.Extensions.Logging; +using Microsoft.Extensions.DependencyInjection; namespace DotNetCore.CAP.Abstractions { - public abstract class CapPublisherBase : ICapPublisher, IDisposable + public abstract class CapPublisherBase : ICapPublisher { - private readonly IDispatcher _dispatcher; - protected readonly ILogger _logger; + private readonly CapTransactionBase _capTransaction; + private readonly IMessagePacker _msgPacker; + private readonly IContentSerializer _serializer; + + protected bool NotUseTransaction; - // diagnostics listener // ReSharper disable once InconsistentNaming protected static readonly DiagnosticListener s_diagnosticListener = new DiagnosticListener(CapDiagnosticListenerExtensions.DiagnosticListenerName); - protected CapPublisherBase(ILogger logger, IDispatcher dispatcher) - { - _logger = logger; - _dispatcher = dispatcher; - } - - protected IDbConnection DbConnection { get; set; } - protected IDbTransaction DbTransaction { get; set; } - protected bool IsCapOpenedTrans { get; set; } - protected bool IsCapOpenedConn { get; set; } - protected bool IsUsingEF { get; set; } - protected IServiceProvider ServiceProvider { get; set; } - - public void Publish(string name, T contentObj, string callbackName = null) + protected CapPublisherBase(IServiceProvider service) { - CheckIsUsingEF(name); - PrepareConnectionForEF(); - - PublishWithTrans(name, contentObj, callbackName); - } - - public Task PublishAsync(string name, T contentObj, string callbackName = null) - { - CheckIsUsingEF(name); - PrepareConnectionForEF(); - - return PublishWithTransAsync(name, contentObj, callbackName); - } - - public void Publish(string name, T contentObj, IDbTransaction dbTransaction, string callbackName = null) - { - CheckIsAdoNet(name); - PrepareConnectionForAdo(dbTransaction); - - PublishWithTrans(name, contentObj, callbackName); + ServiceProvider = service; + _capTransaction = service.GetRequiredService(); + _msgPacker = service.GetRequiredService(); + _serializer = service.GetRequiredService(); } - public Task PublishAsync(string name, T contentObj, IDbTransaction dbTransaction, string callbackName = null) - { - CheckIsAdoNet(name); - PrepareConnectionForAdo(dbTransaction); - - return PublishWithTransAsync(name, contentObj, callbackName); - } - - public virtual void PublishWithMongo(string name, T contentObj, IMongoTransaction mongoTransaction = null, string callbackName = null) - { - throw new NotImplementedException("Work for MongoDB only."); - } - - public virtual Task PublishWithMongoAsync(string name, T contentObj, IMongoTransaction mongoTransaction = null, string callbackName = null) - { - throw new NotImplementedException("Work for MongoDB only."); - } - - protected void Enqueue(CapPublishedMessage message) - { - _dispatcher.EnqueueToPublish(message); - } - - protected abstract void PrepareConnectionForEF(); - - protected abstract int Execute(IDbConnection dbConnection, IDbTransaction dbTransaction, - CapPublishedMessage message); + protected IServiceProvider ServiceProvider { get; } - protected abstract Task ExecuteAsync(IDbConnection dbConnection, IDbTransaction dbTransaction, - CapPublishedMessage message); + public ICapTransaction CapTransaction => _capTransaction; - protected virtual string Serialize(T obj, string callbackName = null) + public void Publish(string name, T contentObj, string callbackName = null) { - var packer = (IMessagePacker)ServiceProvider.GetService(typeof(IMessagePacker)); - string content; - if (obj != null) - { - if (Helper.IsComplexType(obj.GetType())) - { - var serializer = (IContentSerializer)ServiceProvider.GetService(typeof(IContentSerializer)); - content = serializer.Serialize(obj); - } - else - { - content = obj.ToString(); - } - } - else - { - content = string.Empty; - } - - var message = new CapMessageDto(content) + var message = new CapPublishedMessage { - CallbackName = callbackName + Name = name, + Content = Serialize(contentObj, callbackName), + StatusName = StatusName.Scheduled }; - return packer.Pack(message); - } - - #region private methods - private void PrepareConnectionForAdo(IDbTransaction dbTransaction) - { - DbTransaction = dbTransaction ?? throw new ArgumentNullException(nameof(dbTransaction)); - DbConnection = DbTransaction.Connection; - if (DbConnection.State != ConnectionState.Open) - { - IsCapOpenedConn = true; - DbConnection.Open(); - } + PublishAsyncInternal(message).GetAwaiter().GetResult(); } - private void CheckIsUsingEF(string name) + public async Task PublishAsync(string name, T contentObj, string callbackName = null, + CancellationToken cancellationToken = default(CancellationToken)) { - if (name == null) + var message = new CapPublishedMessage { - throw new ArgumentNullException(nameof(name)); - } + Name = name, + Content = Serialize(contentObj, callbackName), + StatusName = StatusName.Scheduled + }; - if (!IsUsingEF) - { - throw new InvalidOperationException( - "If you are using the EntityFramework, you need to configure the DbContextType first." + - " otherwise you need to use overloaded method with IDbTransaction."); - } + await PublishAsyncInternal(message); } - private void CheckIsAdoNet(string name) + protected async Task PublishAsyncInternal(CapPublishedMessage message) { - if (name == null) - { - throw new ArgumentNullException(nameof(name)); - } - - if (IsUsingEF) + if (CapTransaction.DbTransaction == null) { - throw new InvalidOperationException( - "If you are using the EntityFramework, you do not need to use this overloaded."); + NotUseTransaction = true; + CapTransaction.DbTransaction = GetDbTransaction(); } - } - private async Task PublishWithTransAsync(string name, T contentObj, string callbackName = null) - { Guid operationId = default(Guid); - var content = Serialize(contentObj, callbackName); - - var message = new CapPublishedMessage - { - Name = name, - Content = content, - StatusName = StatusName.Scheduled - }; try { operationId = s_diagnosticListener.WritePublishMessageStoreBefore(message); - var id = await ExecuteAsync(DbConnection, DbTransaction, message); - - ClosedCap(); + message.Id = await ExecuteAsync(message, CapTransaction); - if (id > 0) + if (message.Id > 0) { - _logger.LogInformation($"message [{message}] has been persisted in the database."); + _capTransaction.AddToSent(message); s_diagnosticListener.WritePublishMessageStoreAfter(operationId, message); + } - message.Id = id; - - Enqueue(message); + if (NotUseTransaction || CapTransaction.AutoCommit) + { + _capTransaction.Commit(); } } catch (Exception e) { - _logger.LogError(e, "An exception was occurred when publish message async. exception message:" + name); s_diagnosticListener.WritePublishMessageStoreError(operationId, message, e); - Console.WriteLine(e); + throw; } - } - - private void PublishWithTrans(string name, T contentObj, string callbackName = null) - { - Guid operationId = default(Guid); - - var content = Serialize(contentObj, callbackName); - - var message = new CapPublishedMessage + finally { - Name = name, - Content = content, - StatusName = StatusName.Scheduled - }; - - try - { - Console.WriteLine("================22222222222222====================="); - operationId = s_diagnosticListener.WritePublishMessageStoreBefore(message); - - var id = Execute(DbConnection, DbTransaction, message); - Console.WriteLine("================777777777777777777777====================="); - ClosedCap(); - - if (id > 0) + if (NotUseTransaction || CapTransaction.AutoCommit) { - _logger.LogInformation($"message [{message}] has been persisted in the database."); - s_diagnosticListener.WritePublishMessageStoreAfter(operationId, message); - message.Id = id; - Enqueue(message); + _capTransaction.Dispose(); } } - catch (Exception e) - { - _logger.LogError(e, "An exception was occurred when publish message. message:" + name); - s_diagnosticListener.WritePublishMessageStoreError(operationId, message, e); - Console.WriteLine(e); - throw; - } } - private void ClosedCap() + protected abstract object GetDbTransaction(); + + protected abstract Task ExecuteAsync(CapPublishedMessage message, + ICapTransaction transaction, + CancellationToken cancel = default(CancellationToken)); + + protected virtual string Serialize(T obj, string callbackName = null) { - if (IsCapOpenedTrans) + string content; + if (obj != null) { - DbTransaction.Commit(); - DbTransaction.Dispose(); + content = Helper.IsComplexType(obj.GetType()) + ? _serializer.Serialize(obj) + : obj.ToString(); } - - if (IsCapOpenedConn) + else { - DbConnection.Dispose(); + content = string.Empty; } + var message = new CapMessageDto(content) + { + CallbackName = callbackName + }; + return _msgPacker.Pack(message); } - - public void Dispose() - { - DbTransaction?.Dispose(); - DbConnection?.Dispose(); - } - - #endregion private methods } } \ No newline at end of file From 1184cd0e7d77ec9bd89df8f7dee59ff0f046af04 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Wed, 1 Aug 2018 18:06:40 +0800 Subject: [PATCH 152/223] [break changes] modify api for fix #141 --- .../CAP.MySqlCapOptionsExtension.cs | 1 + src/DotNetCore.CAP.MySql/CapPublisher.cs | 75 ++++++++++--------- src/DotNetCore.CAP/DotNetCore.CAP.csproj | 1 - src/DotNetCore.CAP/ICapPublisher.cs | 58 ++------------ .../IPublishMessageSender.Base.cs | 2 - 5 files changed, 49 insertions(+), 88 deletions(-) diff --git a/src/DotNetCore.CAP.MySql/CAP.MySqlCapOptionsExtension.cs b/src/DotNetCore.CAP.MySql/CAP.MySqlCapOptionsExtension.cs index 0c58540..8cae0be 100644 --- a/src/DotNetCore.CAP.MySql/CAP.MySqlCapOptionsExtension.cs +++ b/src/DotNetCore.CAP.MySql/CAP.MySqlCapOptionsExtension.cs @@ -27,6 +27,7 @@ namespace DotNetCore.CAP services.AddScoped(); services.AddScoped(); services.AddTransient(); + services.AddTransient(); AddSingletionMySqlOptions(services); } diff --git a/src/DotNetCore.CAP.MySql/CapPublisher.cs b/src/DotNetCore.CAP.MySql/CapPublisher.cs index 662e257..644e743 100644 --- a/src/DotNetCore.CAP.MySql/CapPublisher.cs +++ b/src/DotNetCore.CAP.MySql/CapPublisher.cs @@ -3,27 +3,27 @@ using System; using System.Data; +using System.Threading; using System.Threading.Tasks; using Dapper; using DotNetCore.CAP.Abstractions; using DotNetCore.CAP.Models; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Storage; -using Microsoft.Extensions.Logging; using MySql.Data.MySqlClient; namespace DotNetCore.CAP.MySql { - public class CapPublisher : CapPublisherBase, ICallbackPublisher + public class CapPublisher : CapPublisherBase, ICallbackPublisher, IDisposable { private readonly DbContext _dbContext; private readonly MySqlOptions _options; + private readonly bool _isUsingEF; - public CapPublisher(ILogger logger, IDispatcher dispatcher, IServiceProvider provider, - MySqlOptions options) - : base(logger, dispatcher) + private MySqlConnection _connection; + + public CapPublisher(IServiceProvider provider, MySqlOptions options) : base(provider) { - ServiceProvider = provider; _options = options; if (_options.DbContextType == null) @@ -31,47 +31,41 @@ namespace DotNetCore.CAP.MySql return; } - IsUsingEF = true; - _dbContext = (DbContext) ServiceProvider.GetService(_options.DbContextType); + _isUsingEF = true; + _dbContext = (DbContext)ServiceProvider.GetService(_options.DbContextType); } public async Task PublishCallbackAsync(CapPublishedMessage message) { - using (var conn = new MySqlConnection(_options.ConnectionString)) - { - var id = await conn.ExecuteScalarAsync(PrepareSql(), message); - message.Id = id; - Enqueue(message); - } + await PublishAsyncInternal(message); } - protected override void PrepareConnectionForEF() + protected override Task ExecuteAsync(CapPublishedMessage message, ICapTransaction transaction, + CancellationToken cancel = default(CancellationToken)) { - DbConnection = _dbContext.Database.GetDbConnection(); - var dbContextTransaction = _dbContext.Database.CurrentTransaction; - var dbTrans = dbContextTransaction?.GetDbTransaction(); - //DbTransaction is dispose in original - if (dbTrans?.Connection == null) + var dbTrans = transaction.DbTransaction as IDbTransaction; + if (dbTrans == null && transaction.DbTransaction is IDbContextTransaction dbContextTrans) { - IsCapOpenedTrans = true; - dbContextTransaction?.Dispose(); - dbContextTransaction = _dbContext.Database.BeginTransaction(IsolationLevel.ReadCommitted); - dbTrans = dbContextTransaction.GetDbTransaction(); + dbTrans = dbContextTrans.GetDbTransaction(); } - - DbTransaction = dbTrans; + var conn = dbTrans?.Connection; + return conn.ExecuteScalarAsync(PrepareSql(), message, dbTrans); } - protected override int Execute(IDbConnection dbConnection, IDbTransaction dbTransaction, - CapPublishedMessage message) + protected override object GetDbTransaction() { - return dbConnection.ExecuteScalar(PrepareSql(), message, dbTransaction); - } + if (_isUsingEF) + { + var dbContextTransaction = _dbContext.Database.CurrentTransaction; + if (dbContextTransaction == null) + { + return InitDbConnection(); + } - protected override async Task ExecuteAsync(IDbConnection dbConnection, IDbTransaction dbTransaction, - CapPublishedMessage message) - { - return await dbConnection.ExecuteScalarAsync(PrepareSql(), message, dbTransaction); + return dbContextTransaction; + } + + return InitDbConnection(); } #region private methods @@ -82,6 +76,19 @@ namespace DotNetCore.CAP.MySql $"INSERT INTO `{_options.TableNamePrefix}.published` (`Name`,`Content`,`Retries`,`Added`,`ExpiresAt`,`StatusName`)VALUES(@Name,@Content,@Retries,@Added,@ExpiresAt,@StatusName);SELECT LAST_INSERT_ID()"; } + private IDbTransaction InitDbConnection() + { + _connection = new MySqlConnection(_options.ConnectionString); + _connection.Open(); + return _connection.BeginTransaction(IsolationLevel.ReadCommitted); + } + #endregion private methods + + public void Dispose() + { + _dbContext?.Dispose(); + _connection?.Dispose(); + } } } \ No newline at end of file diff --git a/src/DotNetCore.CAP/DotNetCore.CAP.csproj b/src/DotNetCore.CAP/DotNetCore.CAP.csproj index cc1a94d..f2e0982 100644 --- a/src/DotNetCore.CAP/DotNetCore.CAP.csproj +++ b/src/DotNetCore.CAP/DotNetCore.CAP.csproj @@ -37,7 +37,6 @@ - diff --git a/src/DotNetCore.CAP/ICapPublisher.cs b/src/DotNetCore.CAP/ICapPublisher.cs index 017b620..a8fe3b2 100644 --- a/src/DotNetCore.CAP/ICapPublisher.cs +++ b/src/DotNetCore.CAP/ICapPublisher.cs @@ -1,9 +1,8 @@ // Copyright (c) .NET Core Community. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. -using System.Data; +using System.Threading; using System.Threading.Tasks; -using DotNetCore.CAP.Abstractions; namespace DotNetCore.CAP { @@ -12,66 +11,23 @@ namespace DotNetCore.CAP /// public interface ICapPublisher { - /// - /// (EntityFramework) Asynchronous publish an object message. - /// - /// If you are using the EntityFramework, you need to configure the DbContextType first. - /// otherwise you need to use overloaded method with IDbTransaction. - /// - /// - /// The type of content object. - /// the topic name or exchange router key. - /// message body content, that will be serialized of json. - /// callback subscriber name - Task PublishAsync(string name, T contentObj, string callbackName = null); + ICapTransaction CapTransaction { get; } /// - /// (EntityFramework) Publish an object message. - /// - /// If you are using the EntityFramework, you need to configure the DbContextType first. - /// otherwise you need to use overloaded method with IDbTransaction. - /// + /// Asynchronous publish an object message. /// - /// The type of content object. /// the topic name or exchange router key. /// message body content, that will be serialized of json. /// callback subscriber name - void Publish(string name, T contentObj, string callbackName = null); + /// + Task PublishAsync(string name, T contentObj, string callbackName = null, CancellationToken cancellationToken = default(CancellationToken)); /// - /// (ado.net) Asynchronous publish an object message. + /// Publish an object message. /// /// the topic name or exchange router key. /// message body content, that will be serialized of json. - /// the transaction of /// callback subscriber name - Task PublishAsync(string name, T contentObj, IDbTransaction dbTransaction, string callbackName = null); - - /// - /// (ado.net) Publish an object message. - /// - /// the topic name or exchange router key. - /// message body content, that will be serialized of json. - /// the transaction of - /// callback subscriber name - void Publish(string name, T contentObj, IDbTransaction dbTransaction, string callbackName = null); - - /// - /// Publish an object message with mongo. - /// - /// the topic name or exchange router key. - /// message body content, that will be serialized of json. - /// if transaction was set null, the message will be published directly. - /// callback subscriber name - void PublishWithMongo(string name, T contentObj, IMongoTransaction mongoTransaction = null, string callbackName = null); - - /// - /// Asynchronous publish an object message with mongo. - /// - /// the topic name or exchange router key. - /// message body content, that will be serialized of json. - /// if transaction was set null, the message will be published directly. - /// callback subscriber name - Task PublishWithMongoAsync(string name, T contentObj, IMongoTransaction mongoTransaction = null, string callbackName = null); + void Publish(string name, T contentObj, string callbackName = null); } } \ No newline at end of file diff --git a/src/DotNetCore.CAP/IPublishMessageSender.Base.cs b/src/DotNetCore.CAP/IPublishMessageSender.Base.cs index cbbc780..45292ab 100644 --- a/src/DotNetCore.CAP/IPublishMessageSender.Base.cs +++ b/src/DotNetCore.CAP/IPublishMessageSender.Base.cs @@ -173,8 +173,6 @@ namespace DotNetCore.CAP du); s_diagnosticListener.WritePublishAfter(eventData); - - _logger.MessageHasBeenSent(du.TotalSeconds); } private void TracingError(Guid operationId, CapPublishedMessage message, OperateResult result, DateTimeOffset startTime, TimeSpan du) From 7a005826dd4c9968d433fd4d6be83bb88c07f1ed Mon Sep 17 00:00:00 2001 From: Savorboard Date: Wed, 1 Aug 2018 18:07:55 +0800 Subject: [PATCH 153/223] update version to 2.3.0 --- build/version.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/version.props b/build/version.props index 106dba1..5f50de5 100644 --- a/build/version.props +++ b/build/version.props @@ -1,8 +1,8 @@ 2 - 2 - 6 + 3 + 0 $(VersionMajor).$(VersionMinor).$(VersionPatch) From d57c88e76b5ebf1ee477286345cdda5cbcab83ee Mon Sep 17 00:00:00 2001 From: Savorboard Date: Fri, 3 Aug 2018 15:46:50 +0800 Subject: [PATCH 154/223] upgrade nuget packages --- samples/Sample.Kafka.MySql/Sample.Kafka.MySql.csproj | 2 +- .../Sample.RabbitMQ.MongoDB/Sample.RabbitMQ.MongoDB.csproj | 7 +------ samples/Sample.RabbitMQ.MySql/Sample.RabbitMQ.MySql.csproj | 2 +- src/DotNetCore.CAP.Kafka/DotNetCore.CAP.Kafka.csproj | 2 +- src/DotNetCore.CAP.MySql/DotNetCore.CAP.MySql.csproj | 2 +- .../DotNetCore.CAP.PostgreSql.csproj | 2 +- src/DotNetCore.CAP.RabbitMQ/DotNetCore.CAP.RabbitMQ.csproj | 2 +- src/DotNetCore.CAP/DotNetCore.CAP.csproj | 2 +- .../DotNetCore.CAP.MongoDB.Test.csproj | 6 +++--- .../DotNetCore.CAP.MySql.Test.csproj | 4 ++-- .../DotNetCore.CAP.PostgreSql.Test.csproj | 2 +- .../DotNetCore.CAP.SqlServer.Test.csproj | 2 +- test/DotNetCore.CAP.Test/DotNetCore.CAP.Test.csproj | 2 +- 13 files changed, 16 insertions(+), 21 deletions(-) diff --git a/samples/Sample.Kafka.MySql/Sample.Kafka.MySql.csproj b/samples/Sample.Kafka.MySql/Sample.Kafka.MySql.csproj index 27a8c6e..191ad86 100644 --- a/samples/Sample.Kafka.MySql/Sample.Kafka.MySql.csproj +++ b/samples/Sample.Kafka.MySql/Sample.Kafka.MySql.csproj @@ -9,7 +9,7 @@ - + diff --git a/samples/Sample.RabbitMQ.MongoDB/Sample.RabbitMQ.MongoDB.csproj b/samples/Sample.RabbitMQ.MongoDB/Sample.RabbitMQ.MongoDB.csproj index fdaa849..b693e95 100644 --- a/samples/Sample.RabbitMQ.MongoDB/Sample.RabbitMQ.MongoDB.csproj +++ b/samples/Sample.RabbitMQ.MongoDB/Sample.RabbitMQ.MongoDB.csproj @@ -1,13 +1,8 @@ - + netcoreapp2.1 - - - - - diff --git a/samples/Sample.RabbitMQ.MySql/Sample.RabbitMQ.MySql.csproj b/samples/Sample.RabbitMQ.MySql/Sample.RabbitMQ.MySql.csproj index 5faf15f..4ebfbda 100644 --- a/samples/Sample.RabbitMQ.MySql/Sample.RabbitMQ.MySql.csproj +++ b/samples/Sample.RabbitMQ.MySql/Sample.RabbitMQ.MySql.csproj @@ -6,7 +6,7 @@ - + diff --git a/src/DotNetCore.CAP.Kafka/DotNetCore.CAP.Kafka.csproj b/src/DotNetCore.CAP.Kafka/DotNetCore.CAP.Kafka.csproj index 8c8d5e7..13388ea 100644 --- a/src/DotNetCore.CAP.Kafka/DotNetCore.CAP.Kafka.csproj +++ b/src/DotNetCore.CAP.Kafka/DotNetCore.CAP.Kafka.csproj @@ -13,7 +13,7 @@ - + diff --git a/src/DotNetCore.CAP.MySql/DotNetCore.CAP.MySql.csproj b/src/DotNetCore.CAP.MySql/DotNetCore.CAP.MySql.csproj index 14734ad..002ebeb 100644 --- a/src/DotNetCore.CAP.MySql/DotNetCore.CAP.MySql.csproj +++ b/src/DotNetCore.CAP.MySql/DotNetCore.CAP.MySql.csproj @@ -15,7 +15,7 @@ - + diff --git a/src/DotNetCore.CAP.PostgreSql/DotNetCore.CAP.PostgreSql.csproj b/src/DotNetCore.CAP.PostgreSql/DotNetCore.CAP.PostgreSql.csproj index 0e3698f..fc76e6c 100644 --- a/src/DotNetCore.CAP.PostgreSql/DotNetCore.CAP.PostgreSql.csproj +++ b/src/DotNetCore.CAP.PostgreSql/DotNetCore.CAP.PostgreSql.csproj @@ -15,7 +15,7 @@ - + diff --git a/src/DotNetCore.CAP.RabbitMQ/DotNetCore.CAP.RabbitMQ.csproj b/src/DotNetCore.CAP.RabbitMQ/DotNetCore.CAP.RabbitMQ.csproj index deccfb7..f35dd38 100644 --- a/src/DotNetCore.CAP.RabbitMQ/DotNetCore.CAP.RabbitMQ.csproj +++ b/src/DotNetCore.CAP.RabbitMQ/DotNetCore.CAP.RabbitMQ.csproj @@ -12,7 +12,7 @@ - + diff --git a/src/DotNetCore.CAP/DotNetCore.CAP.csproj b/src/DotNetCore.CAP/DotNetCore.CAP.csproj index cc1a94d..2f571df 100644 --- a/src/DotNetCore.CAP/DotNetCore.CAP.csproj +++ b/src/DotNetCore.CAP/DotNetCore.CAP.csproj @@ -31,7 +31,7 @@ - + diff --git a/test/DotNetCore.CAP.MongoDB.Test/DotNetCore.CAP.MongoDB.Test.csproj b/test/DotNetCore.CAP.MongoDB.Test/DotNetCore.CAP.MongoDB.Test.csproj index 9fb481e..b04ca28 100644 --- a/test/DotNetCore.CAP.MongoDB.Test/DotNetCore.CAP.MongoDB.Test.csproj +++ b/test/DotNetCore.CAP.MongoDB.Test/DotNetCore.CAP.MongoDB.Test.csproj @@ -6,10 +6,10 @@ - - + + - + diff --git a/test/DotNetCore.CAP.MySql.Test/DotNetCore.CAP.MySql.Test.csproj b/test/DotNetCore.CAP.MySql.Test/DotNetCore.CAP.MySql.Test.csproj index 8678e45..99f246c 100644 --- a/test/DotNetCore.CAP.MySql.Test/DotNetCore.CAP.MySql.Test.csproj +++ b/test/DotNetCore.CAP.MySql.Test/DotNetCore.CAP.MySql.Test.csproj @@ -13,12 +13,12 @@ - + - + diff --git a/test/DotNetCore.CAP.PostgreSql.Test/DotNetCore.CAP.PostgreSql.Test.csproj b/test/DotNetCore.CAP.PostgreSql.Test/DotNetCore.CAP.PostgreSql.Test.csproj index 335f851..3177749 100644 --- a/test/DotNetCore.CAP.PostgreSql.Test/DotNetCore.CAP.PostgreSql.Test.csproj +++ b/test/DotNetCore.CAP.PostgreSql.Test/DotNetCore.CAP.PostgreSql.Test.csproj @@ -8,7 +8,7 @@ - + diff --git a/test/DotNetCore.CAP.SqlServer.Test/DotNetCore.CAP.SqlServer.Test.csproj b/test/DotNetCore.CAP.SqlServer.Test/DotNetCore.CAP.SqlServer.Test.csproj index 9375d31..7df0282 100644 --- a/test/DotNetCore.CAP.SqlServer.Test/DotNetCore.CAP.SqlServer.Test.csproj +++ b/test/DotNetCore.CAP.SqlServer.Test/DotNetCore.CAP.SqlServer.Test.csproj @@ -18,7 +18,7 @@ - + diff --git a/test/DotNetCore.CAP.Test/DotNetCore.CAP.Test.csproj b/test/DotNetCore.CAP.Test/DotNetCore.CAP.Test.csproj index 7216569..bdab81c 100644 --- a/test/DotNetCore.CAP.Test/DotNetCore.CAP.Test.csproj +++ b/test/DotNetCore.CAP.Test/DotNetCore.CAP.Test.csproj @@ -12,7 +12,7 @@ - + From 09beaeecf767212551883b1b68e87a641add059b Mon Sep 17 00:00:00 2001 From: Savorboard Date: Fri, 3 Aug 2018 15:46:50 +0800 Subject: [PATCH 155/223] upgrade nuget packages --- samples/Sample.Kafka.MySql/Sample.Kafka.MySql.csproj | 2 +- .../Sample.RabbitMQ.MongoDB/Sample.RabbitMQ.MongoDB.csproj | 7 +------ samples/Sample.RabbitMQ.MySql/Sample.RabbitMQ.MySql.csproj | 2 +- src/DotNetCore.CAP.Kafka/DotNetCore.CAP.Kafka.csproj | 2 +- src/DotNetCore.CAP.MySql/DotNetCore.CAP.MySql.csproj | 2 +- .../DotNetCore.CAP.PostgreSql.csproj | 2 +- src/DotNetCore.CAP.RabbitMQ/DotNetCore.CAP.RabbitMQ.csproj | 2 +- src/DotNetCore.CAP/DotNetCore.CAP.csproj | 2 +- .../DotNetCore.CAP.MongoDB.Test.csproj | 6 +++--- .../DotNetCore.CAP.MySql.Test.csproj | 4 ++-- .../DotNetCore.CAP.PostgreSql.Test.csproj | 2 +- .../DotNetCore.CAP.SqlServer.Test.csproj | 2 +- test/DotNetCore.CAP.Test/DotNetCore.CAP.Test.csproj | 2 +- 13 files changed, 16 insertions(+), 21 deletions(-) diff --git a/samples/Sample.Kafka.MySql/Sample.Kafka.MySql.csproj b/samples/Sample.Kafka.MySql/Sample.Kafka.MySql.csproj index 27a8c6e..191ad86 100644 --- a/samples/Sample.Kafka.MySql/Sample.Kafka.MySql.csproj +++ b/samples/Sample.Kafka.MySql/Sample.Kafka.MySql.csproj @@ -9,7 +9,7 @@ - + diff --git a/samples/Sample.RabbitMQ.MongoDB/Sample.RabbitMQ.MongoDB.csproj b/samples/Sample.RabbitMQ.MongoDB/Sample.RabbitMQ.MongoDB.csproj index fdaa849..b693e95 100644 --- a/samples/Sample.RabbitMQ.MongoDB/Sample.RabbitMQ.MongoDB.csproj +++ b/samples/Sample.RabbitMQ.MongoDB/Sample.RabbitMQ.MongoDB.csproj @@ -1,13 +1,8 @@ - + netcoreapp2.1 - - - - - diff --git a/samples/Sample.RabbitMQ.MySql/Sample.RabbitMQ.MySql.csproj b/samples/Sample.RabbitMQ.MySql/Sample.RabbitMQ.MySql.csproj index 5faf15f..4ebfbda 100644 --- a/samples/Sample.RabbitMQ.MySql/Sample.RabbitMQ.MySql.csproj +++ b/samples/Sample.RabbitMQ.MySql/Sample.RabbitMQ.MySql.csproj @@ -6,7 +6,7 @@ - + diff --git a/src/DotNetCore.CAP.Kafka/DotNetCore.CAP.Kafka.csproj b/src/DotNetCore.CAP.Kafka/DotNetCore.CAP.Kafka.csproj index 8c8d5e7..13388ea 100644 --- a/src/DotNetCore.CAP.Kafka/DotNetCore.CAP.Kafka.csproj +++ b/src/DotNetCore.CAP.Kafka/DotNetCore.CAP.Kafka.csproj @@ -13,7 +13,7 @@ - + diff --git a/src/DotNetCore.CAP.MySql/DotNetCore.CAP.MySql.csproj b/src/DotNetCore.CAP.MySql/DotNetCore.CAP.MySql.csproj index 14734ad..002ebeb 100644 --- a/src/DotNetCore.CAP.MySql/DotNetCore.CAP.MySql.csproj +++ b/src/DotNetCore.CAP.MySql/DotNetCore.CAP.MySql.csproj @@ -15,7 +15,7 @@ - + diff --git a/src/DotNetCore.CAP.PostgreSql/DotNetCore.CAP.PostgreSql.csproj b/src/DotNetCore.CAP.PostgreSql/DotNetCore.CAP.PostgreSql.csproj index 0e3698f..fc76e6c 100644 --- a/src/DotNetCore.CAP.PostgreSql/DotNetCore.CAP.PostgreSql.csproj +++ b/src/DotNetCore.CAP.PostgreSql/DotNetCore.CAP.PostgreSql.csproj @@ -15,7 +15,7 @@ - + diff --git a/src/DotNetCore.CAP.RabbitMQ/DotNetCore.CAP.RabbitMQ.csproj b/src/DotNetCore.CAP.RabbitMQ/DotNetCore.CAP.RabbitMQ.csproj index deccfb7..f35dd38 100644 --- a/src/DotNetCore.CAP.RabbitMQ/DotNetCore.CAP.RabbitMQ.csproj +++ b/src/DotNetCore.CAP.RabbitMQ/DotNetCore.CAP.RabbitMQ.csproj @@ -12,7 +12,7 @@ - + diff --git a/src/DotNetCore.CAP/DotNetCore.CAP.csproj b/src/DotNetCore.CAP/DotNetCore.CAP.csproj index cc1a94d..2f571df 100644 --- a/src/DotNetCore.CAP/DotNetCore.CAP.csproj +++ b/src/DotNetCore.CAP/DotNetCore.CAP.csproj @@ -31,7 +31,7 @@ - + diff --git a/test/DotNetCore.CAP.MongoDB.Test/DotNetCore.CAP.MongoDB.Test.csproj b/test/DotNetCore.CAP.MongoDB.Test/DotNetCore.CAP.MongoDB.Test.csproj index 9fb481e..b04ca28 100644 --- a/test/DotNetCore.CAP.MongoDB.Test/DotNetCore.CAP.MongoDB.Test.csproj +++ b/test/DotNetCore.CAP.MongoDB.Test/DotNetCore.CAP.MongoDB.Test.csproj @@ -6,10 +6,10 @@ - - + + - + diff --git a/test/DotNetCore.CAP.MySql.Test/DotNetCore.CAP.MySql.Test.csproj b/test/DotNetCore.CAP.MySql.Test/DotNetCore.CAP.MySql.Test.csproj index 8678e45..99f246c 100644 --- a/test/DotNetCore.CAP.MySql.Test/DotNetCore.CAP.MySql.Test.csproj +++ b/test/DotNetCore.CAP.MySql.Test/DotNetCore.CAP.MySql.Test.csproj @@ -13,12 +13,12 @@ - + - + diff --git a/test/DotNetCore.CAP.PostgreSql.Test/DotNetCore.CAP.PostgreSql.Test.csproj b/test/DotNetCore.CAP.PostgreSql.Test/DotNetCore.CAP.PostgreSql.Test.csproj index 335f851..3177749 100644 --- a/test/DotNetCore.CAP.PostgreSql.Test/DotNetCore.CAP.PostgreSql.Test.csproj +++ b/test/DotNetCore.CAP.PostgreSql.Test/DotNetCore.CAP.PostgreSql.Test.csproj @@ -8,7 +8,7 @@ - + diff --git a/test/DotNetCore.CAP.SqlServer.Test/DotNetCore.CAP.SqlServer.Test.csproj b/test/DotNetCore.CAP.SqlServer.Test/DotNetCore.CAP.SqlServer.Test.csproj index 9375d31..7df0282 100644 --- a/test/DotNetCore.CAP.SqlServer.Test/DotNetCore.CAP.SqlServer.Test.csproj +++ b/test/DotNetCore.CAP.SqlServer.Test/DotNetCore.CAP.SqlServer.Test.csproj @@ -18,7 +18,7 @@ - + diff --git a/test/DotNetCore.CAP.Test/DotNetCore.CAP.Test.csproj b/test/DotNetCore.CAP.Test/DotNetCore.CAP.Test.csproj index 7216569..bdab81c 100644 --- a/test/DotNetCore.CAP.Test/DotNetCore.CAP.Test.csproj +++ b/test/DotNetCore.CAP.Test/DotNetCore.CAP.Test.csproj @@ -12,7 +12,7 @@ - + From c1bbb9fa742663af633ac287bcbd51351146255f Mon Sep 17 00:00:00 2001 From: Savorboard Date: Fri, 3 Aug 2018 15:54:57 +0800 Subject: [PATCH 156/223] remove unused code. --- src/DotNetCore.CAP/Abstractions/CapPublisherBase.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/DotNetCore.CAP/Abstractions/CapPublisherBase.cs b/src/DotNetCore.CAP/Abstractions/CapPublisherBase.cs index d585406..7589aad 100644 --- a/src/DotNetCore.CAP/Abstractions/CapPublisherBase.cs +++ b/src/DotNetCore.CAP/Abstractions/CapPublisherBase.cs @@ -214,11 +214,10 @@ namespace DotNetCore.CAP.Abstractions try { - Console.WriteLine("================22222222222222====================="); operationId = s_diagnosticListener.WritePublishMessageStoreBefore(message); var id = Execute(DbConnection, DbTransaction, message); - Console.WriteLine("================777777777777777777777====================="); + ClosedCap(); if (id > 0) From 214293cf5e24f648310296072203736853c8ec53 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Fri, 3 Aug 2018 15:54:57 +0800 Subject: [PATCH 157/223] remove unused code. --- src/DotNetCore.CAP/Abstractions/CapPublisherBase.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/DotNetCore.CAP/Abstractions/CapPublisherBase.cs b/src/DotNetCore.CAP/Abstractions/CapPublisherBase.cs index d585406..7589aad 100644 --- a/src/DotNetCore.CAP/Abstractions/CapPublisherBase.cs +++ b/src/DotNetCore.CAP/Abstractions/CapPublisherBase.cs @@ -214,11 +214,10 @@ namespace DotNetCore.CAP.Abstractions try { - Console.WriteLine("================22222222222222====================="); operationId = s_diagnosticListener.WritePublishMessageStoreBefore(message); var id = Execute(DbConnection, DbTransaction, message); - Console.WriteLine("================777777777777777777777====================="); + ClosedCap(); if (id > 0) From d20605d8d54c9ebcccf3926528b4438cc4bc5380 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Fri, 3 Aug 2018 16:40:00 +0800 Subject: [PATCH 158/223] switch mysql driver to mysql.data --- samples/Sample.Kafka.MySql/Sample.Kafka.MySql.csproj | 1 - src/DotNetCore.CAP.MySql/DotNetCore.CAP.MySql.csproj | 2 +- test/DotNetCore.CAP.MySql.Test/DotNetCore.CAP.MySql.Test.csproj | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/samples/Sample.Kafka.MySql/Sample.Kafka.MySql.csproj b/samples/Sample.Kafka.MySql/Sample.Kafka.MySql.csproj index 191ad86..e64e18b 100644 --- a/samples/Sample.Kafka.MySql/Sample.Kafka.MySql.csproj +++ b/samples/Sample.Kafka.MySql/Sample.Kafka.MySql.csproj @@ -9,7 +9,6 @@ - diff --git a/src/DotNetCore.CAP.MySql/DotNetCore.CAP.MySql.csproj b/src/DotNetCore.CAP.MySql/DotNetCore.CAP.MySql.csproj index 002ebeb..2d71cf8 100644 --- a/src/DotNetCore.CAP.MySql/DotNetCore.CAP.MySql.csproj +++ b/src/DotNetCore.CAP.MySql/DotNetCore.CAP.MySql.csproj @@ -15,7 +15,7 @@ - + diff --git a/test/DotNetCore.CAP.MySql.Test/DotNetCore.CAP.MySql.Test.csproj b/test/DotNetCore.CAP.MySql.Test/DotNetCore.CAP.MySql.Test.csproj index 99f246c..1b98a58 100644 --- a/test/DotNetCore.CAP.MySql.Test/DotNetCore.CAP.MySql.Test.csproj +++ b/test/DotNetCore.CAP.MySql.Test/DotNetCore.CAP.MySql.Test.csproj @@ -13,7 +13,6 @@ - From ae63286896b822370b02dde12d024214d6b5b732 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Fri, 3 Aug 2018 16:40:00 +0800 Subject: [PATCH 159/223] switch mysql driver to mysql.data --- samples/Sample.Kafka.MySql/Sample.Kafka.MySql.csproj | 1 - src/DotNetCore.CAP.MySql/DotNetCore.CAP.MySql.csproj | 2 +- test/DotNetCore.CAP.MySql.Test/DotNetCore.CAP.MySql.Test.csproj | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/samples/Sample.Kafka.MySql/Sample.Kafka.MySql.csproj b/samples/Sample.Kafka.MySql/Sample.Kafka.MySql.csproj index 191ad86..e64e18b 100644 --- a/samples/Sample.Kafka.MySql/Sample.Kafka.MySql.csproj +++ b/samples/Sample.Kafka.MySql/Sample.Kafka.MySql.csproj @@ -9,7 +9,6 @@ - diff --git a/src/DotNetCore.CAP.MySql/DotNetCore.CAP.MySql.csproj b/src/DotNetCore.CAP.MySql/DotNetCore.CAP.MySql.csproj index 002ebeb..2d71cf8 100644 --- a/src/DotNetCore.CAP.MySql/DotNetCore.CAP.MySql.csproj +++ b/src/DotNetCore.CAP.MySql/DotNetCore.CAP.MySql.csproj @@ -15,7 +15,7 @@ - + diff --git a/test/DotNetCore.CAP.MySql.Test/DotNetCore.CAP.MySql.Test.csproj b/test/DotNetCore.CAP.MySql.Test/DotNetCore.CAP.MySql.Test.csproj index 99f246c..1b98a58 100644 --- a/test/DotNetCore.CAP.MySql.Test/DotNetCore.CAP.MySql.Test.csproj +++ b/test/DotNetCore.CAP.MySql.Test/DotNetCore.CAP.MySql.Test.csproj @@ -13,7 +13,6 @@ - From 6abf92c2b80f241a5827e294f3e4665e763f3863 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Fri, 3 Aug 2018 16:45:06 +0800 Subject: [PATCH 160/223] add SslMode=none to mysql connection string --- appveyor.yml | 2 +- test/DotNetCore.CAP.MySql.Test/ConnectionUtil.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index e80f7ce..8a571f4 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -4,7 +4,7 @@ environment: BUILDING_ON_PLATFORM: win BuildEnvironment: appveyor Cap_SqlServer_ConnectionStringTemplate: Server=(local)\SQL2014;Database={0};User ID=sa;Password=Password12! - Cap_MySql_ConnectionStringTemplate: Server=localhost;Database={0};Uid=root;Pwd=Password12!;Allow User Variables=True + Cap_MySql_ConnectionStringTemplate: Server=localhost;Database={0};Uid=root;Pwd=Password12!;Allow User Variables=True;SslMode=none Cap_PostgreSql_ConnectionStringTemplate: Server=localhost;Database={0};UserId=postgres;Password=Password12! services: - mssql2014 diff --git a/test/DotNetCore.CAP.MySql.Test/ConnectionUtil.cs b/test/DotNetCore.CAP.MySql.Test/ConnectionUtil.cs index 9d79882..fec2706 100644 --- a/test/DotNetCore.CAP.MySql.Test/ConnectionUtil.cs +++ b/test/DotNetCore.CAP.MySql.Test/ConnectionUtil.cs @@ -12,7 +12,7 @@ namespace DotNetCore.CAP.MySql.Test private const string DefaultDatabaseName = @"DotNetCore.CAP.MySql.Test"; private const string DefaultConnectionStringTemplate = - @"Server=localhost;Database={0};Uid=root;Pwd=123123;Allow User Variables=True;"; + @"Server=localhost;Database={0};Uid=root;Pwd=123123;Allow User Variables=True;SslMode=none;"; public static string GetDatabaseName() { From 111a200643a76e6795ec9d2215d59bc1851b55b8 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Fri, 3 Aug 2018 16:45:06 +0800 Subject: [PATCH 161/223] add SslMode=none to mysql connection string --- appveyor.yml | 2 +- test/DotNetCore.CAP.MySql.Test/ConnectionUtil.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index e80f7ce..8a571f4 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -4,7 +4,7 @@ environment: BUILDING_ON_PLATFORM: win BuildEnvironment: appveyor Cap_SqlServer_ConnectionStringTemplate: Server=(local)\SQL2014;Database={0};User ID=sa;Password=Password12! - Cap_MySql_ConnectionStringTemplate: Server=localhost;Database={0};Uid=root;Pwd=Password12!;Allow User Variables=True + Cap_MySql_ConnectionStringTemplate: Server=localhost;Database={0};Uid=root;Pwd=Password12!;Allow User Variables=True;SslMode=none Cap_PostgreSql_ConnectionStringTemplate: Server=localhost;Database={0};UserId=postgres;Password=Password12! services: - mssql2014 diff --git a/test/DotNetCore.CAP.MySql.Test/ConnectionUtil.cs b/test/DotNetCore.CAP.MySql.Test/ConnectionUtil.cs index 9d79882..fec2706 100644 --- a/test/DotNetCore.CAP.MySql.Test/ConnectionUtil.cs +++ b/test/DotNetCore.CAP.MySql.Test/ConnectionUtil.cs @@ -12,7 +12,7 @@ namespace DotNetCore.CAP.MySql.Test private const string DefaultDatabaseName = @"DotNetCore.CAP.MySql.Test"; private const string DefaultConnectionStringTemplate = - @"Server=localhost;Database={0};Uid=root;Pwd=123123;Allow User Variables=True;"; + @"Server=localhost;Database={0};Uid=root;Pwd=123123;Allow User Variables=True;SslMode=none;"; public static string GetDatabaseName() { From a51bd4ebc77a537944534b209de02a6d5dae11cd Mon Sep 17 00:00:00 2001 From: Savorboard Date: Sat, 4 Aug 2018 10:57:12 +0800 Subject: [PATCH 162/223] reduction driven to mysqlconnector --- src/DotNetCore.CAP.MySql/DotNetCore.CAP.MySql.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/DotNetCore.CAP.MySql/DotNetCore.CAP.MySql.csproj b/src/DotNetCore.CAP.MySql/DotNetCore.CAP.MySql.csproj index 2d71cf8..002ebeb 100644 --- a/src/DotNetCore.CAP.MySql/DotNetCore.CAP.MySql.csproj +++ b/src/DotNetCore.CAP.MySql/DotNetCore.CAP.MySql.csproj @@ -15,7 +15,7 @@ - + From 393c37b0f6781b7ce1b73874ea1a62c12b80be79 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Sat, 4 Aug 2018 10:57:12 +0800 Subject: [PATCH 163/223] reduction driven to mysqlconnector --- src/DotNetCore.CAP.MySql/DotNetCore.CAP.MySql.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/DotNetCore.CAP.MySql/DotNetCore.CAP.MySql.csproj b/src/DotNetCore.CAP.MySql/DotNetCore.CAP.MySql.csproj index 2d71cf8..002ebeb 100644 --- a/src/DotNetCore.CAP.MySql/DotNetCore.CAP.MySql.csproj +++ b/src/DotNetCore.CAP.MySql/DotNetCore.CAP.MySql.csproj @@ -15,7 +15,7 @@ - + From c84bccffd09b32ff236f2ac226424b42bb119ea8 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Fri, 10 Aug 2018 20:43:02 +0800 Subject: [PATCH 164/223] tweak ObjectId --- src/DotNetCore.CAP/Infrastructure/ObjectId.cs | 280 ++---------------- 1 file changed, 27 insertions(+), 253 deletions(-) diff --git a/src/DotNetCore.CAP/Infrastructure/ObjectId.cs b/src/DotNetCore.CAP/Infrastructure/ObjectId.cs index 4b93ce6..8d84a6f 100644 --- a/src/DotNetCore.CAP/Infrastructure/ObjectId.cs +++ b/src/DotNetCore.CAP/Infrastructure/ObjectId.cs @@ -18,15 +18,13 @@ namespace DotNetCore.CAP.Infrastructure public struct ObjectId : IComparable, IEquatable { // private static fields - private static readonly DateTime __unixEpoch; + private static readonly DateTime UnixEpoch; - private static readonly long __dateTimeMaxValueMillisecondsSinceEpoch; - private static readonly long __dateTimeMinValueMillisecondsSinceEpoch; - private static readonly int __staticMachine; - private static readonly short __staticPid; - private static int __staticIncrement; // high byte will be masked out when generating new ObjectId + private static readonly int StaticMachine; + private static readonly short StaticPid; + private static int _staticIncrement; // high byte will be masked out when generating new ObjectId - private static readonly uint[] _lookup32 = Enumerable.Range(0, 256).Select(i => + private static readonly uint[] Lookup32 = Enumerable.Range(0, 256).Select(i => { var s = i.ToString("x2"); return (uint) s[0] + ((uint) s[1] << 16); @@ -44,40 +42,13 @@ namespace DotNetCore.CAP.Infrastructure // static constructor static ObjectId() { - __unixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); - __dateTimeMaxValueMillisecondsSinceEpoch = (DateTime.MaxValue - __unixEpoch).Ticks / 10000; - __dateTimeMinValueMillisecondsSinceEpoch = (DateTime.MinValue - __unixEpoch).Ticks / 10000; - __staticMachine = GetMachineHash(); - __staticIncrement = new Random().Next(); - __staticPid = (short) GetCurrentProcessId(); + UnixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); + StaticMachine = GetMachineHash(); + _staticIncrement = new Random().Next(); + StaticPid = (short) GetCurrentProcessId(); } // constructors - /// - /// Initializes a new instance of the ObjectId class. - /// - /// The bytes. - public ObjectId(byte[] bytes) - { - if (bytes == null) - { - throw new ArgumentNullException("bytes"); - } - - Unpack(bytes, out _timestamp, out _machine, out _pid, out _increment); - } - - /// - /// Initializes a new instance of the ObjectId class. - /// - /// The timestamp (expressed as a DateTime). - /// The machine hash. - /// The PID. - /// The increment. - public ObjectId(DateTime timestamp, int machine, short pid, int increment) - : this(GetTimestampFromDateTime(timestamp), machine, pid, increment) - { - } /// /// Initializes a new instance of the ObjectId class. @@ -90,14 +61,14 @@ namespace DotNetCore.CAP.Infrastructure { if ((machine & 0xff000000) != 0) { - throw new ArgumentOutOfRangeException("machine", - "The machine value must be between 0 and 16777215 (it must fit in 3 bytes)."); + throw new ArgumentOutOfRangeException(nameof(machine), + @"The machine value must be between 0 and 16777215 (it must fit in 3 bytes)."); } if ((increment & 0xff000000) != 0) { - throw new ArgumentOutOfRangeException("increment", - "The increment value must be between 0 and 16777215 (it must fit in 3 bytes)."); + throw new ArgumentOutOfRangeException(nameof(increment), + @"The increment value must be between 0 and 16777215 (it must fit in 3 bytes)."); } _timestamp = timestamp; @@ -105,76 +76,7 @@ namespace DotNetCore.CAP.Infrastructure _pid = pid; _increment = increment; } - - /// - /// Initializes a new instance of the ObjectId class. - /// - /// The value. - public ObjectId(string value) - { - if (value == null) - { - throw new ArgumentNullException("value"); - } - - Unpack(ParseHexString(value), out _timestamp, out _machine, out _pid, out _increment); - } - - // public static properties - /// - /// Gets an instance of ObjectId where the value is empty. - /// - public static ObjectId Empty { get; } = default(ObjectId); - - // public properties - /// - /// Gets the timestamp. - /// - public int Timestamp => _timestamp; - - /// - /// Gets the machine. - /// - public int Machine => _machine; - - /// - /// Gets the PID. - /// - public short Pid => _pid; - - /// - /// Gets the increment. - /// - public int Increment => _increment; - - /// - /// Gets the creation time (derived from the timestamp). - /// - public DateTime CreationTime => __unixEpoch.AddSeconds(_timestamp); - - // public operators - /// - /// Compares two ObjectIds. - /// - /// The first ObjectId. - /// The other ObjectId - /// True if the first ObjectId is less than the second ObjectId. - public static bool operator <(ObjectId lhs, ObjectId rhs) - { - return lhs.CompareTo(rhs) < 0; - } - - /// - /// Compares two ObjectIds. - /// - /// The first ObjectId. - /// The other ObjectId - /// True if the first ObjectId is less than or equal to the second ObjectId. - public static bool operator <=(ObjectId lhs, ObjectId rhs) - { - return lhs.CompareTo(rhs) <= 0; - } - + /// /// Compares two ObjectIds. /// @@ -196,29 +98,7 @@ namespace DotNetCore.CAP.Infrastructure { return !(lhs == rhs); } - - /// - /// Compares two ObjectIds. - /// - /// The first ObjectId. - /// The other ObjectId - /// True if the first ObjectId is greather than or equal to the second ObjectId. - public static bool operator >=(ObjectId lhs, ObjectId rhs) - { - return lhs.CompareTo(rhs) >= 0; - } - - /// - /// Compares two ObjectIds. - /// - /// The first ObjectId. - /// The other ObjectId - /// True if the first ObjectId is greather than the second ObjectId. - public static bool operator >(ObjectId lhs, ObjectId rhs) - { - return lhs.CompareTo(rhs) > 0; - } - + // public static methods /// /// Generates a new ObjectId with a unique value. @@ -228,17 +108,7 @@ namespace DotNetCore.CAP.Infrastructure { return GenerateNewId(GetTimestampFromDateTime(DateTime.UtcNow)); } - - /// - /// Generates a new ObjectId with a unique value (with the timestamp component based on a given DateTime). - /// - /// The timestamp component (expressed as a DateTime). - /// An ObjectId. - public static ObjectId GenerateNewId(DateTime timestamp) - { - return GenerateNewId(GetTimestampFromDateTime(timestamp)); - } - + /// /// Generates a new ObjectId with a unique value (with the given timestamp). /// @@ -246,8 +116,8 @@ namespace DotNetCore.CAP.Infrastructure /// An ObjectId. public static ObjectId GenerateNewId(int timestamp) { - var increment = Interlocked.Increment(ref __staticIncrement) & 0x00ffffff; // only use low order 3 bytes - return new ObjectId(timestamp, __staticMachine, __staticPid, increment); + var increment = Interlocked.Increment(ref _staticIncrement) & 0x00ffffff; // only use low order 3 bytes + return new ObjectId(timestamp, StaticMachine, StaticPid, increment); } /// @@ -271,14 +141,14 @@ namespace DotNetCore.CAP.Infrastructure { if ((machine & 0xff000000) != 0) { - throw new ArgumentOutOfRangeException("machine", - "The machine value must be between 0 and 16777215 (it must fit in 3 bytes)."); + throw new ArgumentOutOfRangeException(nameof(machine), + @"The machine value must be between 0 and 16777215 (it must fit in 3 bytes)."); } if ((increment & 0xff000000) != 0) { - throw new ArgumentOutOfRangeException("increment", - "The increment value must be between 0 and 16777215 (it must fit in 3 bytes)."); + throw new ArgumentOutOfRangeException(nameof(increment), + @"The increment value must be between 0 and 16777215 (it must fit in 3 bytes)."); } var bytes = new byte[12]; @@ -297,53 +167,6 @@ namespace DotNetCore.CAP.Infrastructure return bytes; } - /// - /// Parses a string and creates a new ObjectId. - /// - /// The string value. - /// A ObjectId. - public static ObjectId Parse(string s) - { - if (s == null) - { - throw new ArgumentNullException("s"); - } - - if (s.Length != 24) - { - throw new ArgumentOutOfRangeException("s", "ObjectId string value must be 24 characters."); - } - - return new ObjectId(ParseHexString(s)); - } - - /// - /// Unpacks a byte array into the components of an ObjectId. - /// - /// A byte array. - /// The timestamp. - /// The machine hash. - /// The PID. - /// The increment. - public static void Unpack(byte[] bytes, out int timestamp, out int machine, out short pid, out int increment) - { - if (bytes == null) - { - throw new ArgumentNullException("bytes"); - } - - if (bytes.Length != 12) - { - throw new ArgumentOutOfRangeException("bytes", "Byte array must be 12 bytes long."); - } - - timestamp = (bytes[0] << 24) + (bytes[1] << 16) + (bytes[2] << 8) + bytes[3]; - machine = (bytes[4] << 16) + (bytes[5] << 8) + bytes[6]; - pid = (short) ((bytes[7] << 8) + bytes[8]); - increment = (bytes[9] << 16) + (bytes[10] << 8) + bytes[11]; - } - - // private static methods /// /// Gets the current process id. This method exists because of how CAS operates on the call stack, checking /// for permissions before executing the method. Hence, if we inlined this call, the calling method would not execute @@ -365,7 +188,7 @@ namespace DotNetCore.CAP.Infrastructure private static int GetTimestampFromDateTime(DateTime timestamp) { - return (int) Math.Floor((ToUniversalTime(timestamp) - __unixEpoch).TotalSeconds); + return (int) Math.Floor((ToUniversalTime(timestamp) - UnixEpoch).TotalSeconds); } // public methods @@ -421,9 +244,9 @@ namespace DotNetCore.CAP.Infrastructure /// True if the other object is an ObjectId and equal to this one. public override bool Equals(object obj) { - if (obj is ObjectId) + if (obj is ObjectId id) { - return Equals((ObjectId) obj); + return Equals(id); } return false; @@ -461,33 +284,6 @@ namespace DotNetCore.CAP.Infrastructure return ToHexString(ToByteArray()); } - /// - /// Parses a hex string into its equivalent byte array. - /// - /// The hex string to parse. - /// The byte equivalent of the hex string. - public static byte[] ParseHexString(string s) - { - if (s == null) - { - throw new ArgumentNullException("s"); - } - - if (s.Length % 2 == 1) - { - throw new Exception("The binary key cannot have an odd number of digits"); - } - - var arr = new byte[s.Length >> 1]; - - for (var i = 0; i < s.Length >> 1; ++i) - { - arr[i] = (byte) ((GetHexVal(s[i << 1]) << 4) + GetHexVal(s[(i << 1) + 1])); - } - - return arr; - } - /// /// Converts a byte array to a hex string. /// @@ -497,13 +293,13 @@ namespace DotNetCore.CAP.Infrastructure { if (bytes == null) { - throw new ArgumentNullException("bytes"); + throw new ArgumentNullException(nameof(bytes)); } var result = new char[bytes.Length * 2]; for (var i = 0; i < bytes.Length; i++) { - var val = _lookup32[bytes[i]]; + var val = Lookup32[bytes[i]]; result[2 * i] = (char) val; result[2 * i + 1] = (char) (val >> 16); } @@ -511,17 +307,6 @@ namespace DotNetCore.CAP.Infrastructure return new string(result); } - /// - /// Converts a DateTime to number of milliseconds since Unix epoch. - /// - /// A DateTime. - /// Number of seconds since Unix epoch. - public static long ToMillisecondsSinceEpoch(DateTime dateTime) - { - var utcDateTime = ToUniversalTime(dateTime); - return (utcDateTime - __unixEpoch).Ticks / 10000; - } - /// /// Converts a DateTime to UTC (with special handling for MinValue and MaxValue). /// @@ -541,16 +326,5 @@ namespace DotNetCore.CAP.Infrastructure return dateTime.ToUniversalTime(); } - - private static int GetHexVal(char hex) - { - int val = hex; - //For uppercase A-F letters: - //return val - (val < 58 ? 48 : 55); - //For lowercase a-f letters: - //return val - (val < 58 ? 48 : 87); - //Or the two combined, but a bit slower: - return val - (val < 58 ? 48 : (val < 97 ? 55 : 87)); - } } } \ No newline at end of file From 86ae8bfbdc0fa2d687570297fc3fcbd17440291f Mon Sep 17 00:00:00 2001 From: Savorboard Date: Fri, 10 Aug 2018 20:43:22 +0800 Subject: [PATCH 165/223] tweak unittests --- test/DotNetCore.CAP.Test/CAP.BuilderTest.cs | 57 ++------------------- 1 file changed, 5 insertions(+), 52 deletions(-) diff --git a/test/DotNetCore.CAP.Test/CAP.BuilderTest.cs b/test/DotNetCore.CAP.Test/CAP.BuilderTest.cs index 402e72f..cbdb3fe 100644 --- a/test/DotNetCore.CAP.Test/CAP.BuilderTest.cs +++ b/test/DotNetCore.CAP.Test/CAP.BuilderTest.cs @@ -1,5 +1,5 @@ using System; -using System.Data; +using System.Threading; using System.Threading.Tasks; using DotNetCore.CAP.Abstractions; using DotNetCore.CAP.Models; @@ -119,62 +119,15 @@ namespace DotNetCore.CAP.Test private class MyProducerService : ICapPublisher { - public void Publish(string name, T contentObj, string callbackName = null) - { - throw new NotImplementedException(); - } + public ICapTransaction CapTransaction { get; } - public void Publish(string name, T contentObj, IDbTransaction dbTransaction, string callbackName = null) + public Task PublishAsync(string name, T contentObj, string callbackName = null, + CancellationToken cancellationToken = default(CancellationToken)) { throw new NotImplementedException(); } - public void Publish(string name, T contentObj, object mongoSession, string callbackName = null) - { - throw new NotImplementedException(); - } - - public Task PublishAsync(string topic, string content) - { - throw new NotImplementedException(); - } - - public Task PublishAsync(string topic, T contentObj) - { - throw new NotImplementedException(); - } - - public Task PublishAsync(string topic, string content, IDbConnection dbConnection) - { - throw new NotImplementedException(); - } - - public Task PublishAsync(string topic, string content, IDbConnection dbConnection, IDbTransaction dbTransaction) - { - throw new NotImplementedException(); - } - - public Task PublishAsync(string name, T contentObj, IDbConnection dbConnection, IDbTransaction dbTransaction = null) - { - throw new NotImplementedException(); - } - - public Task PublishAsync(string name, T contentObj, string callbackName = null) - { - throw new NotImplementedException(); - } - - public Task PublishAsync(string name, T contentObj, IDbTransaction dbTransaction, string callbackName = null) - { - throw new NotImplementedException(); - } - - public void PublishWithMongo(string name, T contentObj, IMongoTransaction mongoTransaction = null, string callbackName = null) - { - throw new NotImplementedException(); - } - - public Task PublishWithMongoAsync(string name, T contentObj, IMongoTransaction mongoTransaction = null, string callbackName = null) + public void Publish(string name, T contentObj, string callbackName = null) { throw new NotImplementedException(); } From c5b0b67811a5dfe7251357a02cce60c24189bd73 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Fri, 10 Aug 2018 23:44:28 +0800 Subject: [PATCH 166/223] Add SnowflakeId generator class. --- .../Infrastructure/SnowflakeId.cs | 93 +++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 src/DotNetCore.CAP/Infrastructure/SnowflakeId.cs diff --git a/src/DotNetCore.CAP/Infrastructure/SnowflakeId.cs b/src/DotNetCore.CAP/Infrastructure/SnowflakeId.cs new file mode 100644 index 0000000..59e4db4 --- /dev/null +++ b/src/DotNetCore.CAP/Infrastructure/SnowflakeId.cs @@ -0,0 +1,93 @@ +// Copyright 2010-2012 Twitter, Inc. +// An object that generates IDs. This is broken into a separate class in case we ever want to support multiple worker threads per process + +using System; + +namespace DotNetCore.CAP.Infrastructure +{ + public class SnowflakeId + { + public const long Twepoch = 1288834974657L; + + private const int WorkerIdBits = 5; + private const int DatacenterIdBits = 5; + private const int SequenceBits = 12; + private const long MaxWorkerId = -1L ^ (-1L << WorkerIdBits); + private const long MaxDatacenterId = -1L ^ (-1L << DatacenterIdBits); + + private const int WorkerIdShift = SequenceBits; + private const int DatacenterIdShift = SequenceBits + WorkerIdBits; + public const int TimestampLeftShift = SequenceBits + WorkerIdBits + DatacenterIdBits; + private const long SequenceMask = -1L ^ (-1L << SequenceBits); + + private static SnowflakeId _snowflakeId; + + private readonly object _lock = new object(); + private long _lastTimestamp = -1L; + + private SnowflakeId(long workerId, long datacenterId, long sequence = 0L) + { + WorkerId = workerId; + DatacenterId = datacenterId; + Sequence = sequence; + + // sanity check for workerId + if (workerId > MaxWorkerId || workerId < 0) + throw new ArgumentException($"worker Id can't be greater than {MaxWorkerId} or less than 0"); + + if (datacenterId > MaxDatacenterId || datacenterId < 0) + throw new ArgumentException($"datacenter Id can't be greater than {MaxDatacenterId} or less than 0"); + } + + public long WorkerId { get; protected set; } + public long DatacenterId { get; protected set; } + + public long Sequence { get; internal set; } + + public static SnowflakeId Default(long datacenterId = 0) + { + return _snowflakeId ?? (_snowflakeId = new SnowflakeId(AppDomain.CurrentDomain.Id, datacenterId)); + } + + public virtual long NextId() + { + lock (_lock) + { + var timestamp = TimeGen(); + + if (timestamp < _lastTimestamp) + throw new Exception( + $"InvalidSystemClock: Clock moved backwards, Refusing to generate id for {_lastTimestamp - timestamp} milliseconds"); + + if (_lastTimestamp == timestamp) + { + Sequence = (Sequence + 1) & SequenceMask; + if (Sequence == 0) timestamp = TilNextMillis(_lastTimestamp); + } + else + { + Sequence = 0; + } + + _lastTimestamp = timestamp; + var id = ((timestamp - Twepoch) << TimestampLeftShift) | + (DatacenterId << DatacenterIdShift) | + (WorkerId << WorkerIdShift) | Sequence; + + return id; + } + } + + protected virtual long TilNextMillis(long lastTimestamp) + { + var timestamp = TimeGen(); + while (timestamp <= lastTimestamp) timestamp = TimeGen(); + return timestamp; + } + + protected virtual long TimeGen() + { + return DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); + } + } +} \ No newline at end of file From 3aab6b2344c33037def4f3f83d5c754e2ea508f5 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Fri, 10 Aug 2018 23:50:38 +0800 Subject: [PATCH 167/223] refactor --- src/DotNetCore.CAP.MongoDB/CAP.MongoDBCapOptionsExtension.cs | 5 +++-- src/DotNetCore.CAP.MongoDB/CAP.MongoDBOptions.cs | 1 + src/DotNetCore.CAP.MongoDB/MongoDBUtil.cs | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/DotNetCore.CAP.MongoDB/CAP.MongoDBCapOptionsExtension.cs b/src/DotNetCore.CAP.MongoDB/CAP.MongoDBCapOptionsExtension.cs index 9b00686..ab1158d 100644 --- a/src/DotNetCore.CAP.MongoDB/CAP.MongoDBCapOptionsExtension.cs +++ b/src/DotNetCore.CAP.MongoDB/CAP.MongoDBCapOptionsExtension.cs @@ -2,13 +2,13 @@ // Licensed under the MIT License. See License.txt in the project root for license information. using System; -using DotNetCore.CAP; using DotNetCore.CAP.Abstractions; using DotNetCore.CAP.Processor; using Microsoft.Extensions.DependencyInjection; namespace DotNetCore.CAP.MongoDB { + // ReSharper disable once InconsistentNaming public class MongoDBCapOptionsExtension : ICapOptionsExtension { private readonly Action _configure; @@ -23,10 +23,11 @@ namespace DotNetCore.CAP.MongoDB services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); + services.AddScoped(); services.AddScoped(); - services.AddTransient(); + services.AddTransient(); services.AddTransient(); var options = new MongoDBOptions(); diff --git a/src/DotNetCore.CAP.MongoDB/CAP.MongoDBOptions.cs b/src/DotNetCore.CAP.MongoDB/CAP.MongoDBOptions.cs index 98ef69b..fe00791 100644 --- a/src/DotNetCore.CAP.MongoDB/CAP.MongoDBOptions.cs +++ b/src/DotNetCore.CAP.MongoDB/CAP.MongoDBOptions.cs @@ -3,6 +3,7 @@ namespace DotNetCore.CAP.MongoDB { + // ReSharper disable once InconsistentNaming public class MongoDBOptions { /// diff --git a/src/DotNetCore.CAP.MongoDB/MongoDBUtil.cs b/src/DotNetCore.CAP.MongoDB/MongoDBUtil.cs index 3d3c1f7..4af9989 100644 --- a/src/DotNetCore.CAP.MongoDB/MongoDBUtil.cs +++ b/src/DotNetCore.CAP.MongoDB/MongoDBUtil.cs @@ -28,6 +28,7 @@ namespace DotNetCore.CAP.MongoDB if (session == null) { result = await collection.FindOneAndUpdateAsync(filter, updateDef, _options); + } else { From c3b7d6b61f35b1ba0ae88622d13f1df208184e8f Mon Sep 17 00:00:00 2001 From: Savorboard Date: Sat, 11 Aug 2018 00:14:19 +0800 Subject: [PATCH 168/223] Message table in database changes the primary key to non auto-Increment. (#180) --- src/DotNetCore.CAP.MySql/CapPublisher.cs | 2 +- src/DotNetCore.CAP.MySql/MySqlStorageConnection.cs | 6 +++--- .../Abstractions/CapPublisherBase.cs | 14 +++++++------- src/DotNetCore.CAP/IConsumerHandler.Default.cs | 6 ++---- src/DotNetCore.CAP/IStorageConnection.cs | 2 +- .../Internal/ICallbackMessageSender.Default.cs | 1 + src/DotNetCore.CAP/LoggerExtensions.cs | 10 +++++----- src/DotNetCore.CAP/Models/CapPublishedMessage.cs | 2 +- src/DotNetCore.CAP/Models/CapReceivedMessage.cs | 3 ++- .../MySqlStorageConnectionTest.cs | 3 +++ .../Processor/StateChangerTest.cs | 2 ++ 11 files changed, 28 insertions(+), 23 deletions(-) diff --git a/src/DotNetCore.CAP.MySql/CapPublisher.cs b/src/DotNetCore.CAP.MySql/CapPublisher.cs index 644e743..b6d1d13 100644 --- a/src/DotNetCore.CAP.MySql/CapPublisher.cs +++ b/src/DotNetCore.CAP.MySql/CapPublisher.cs @@ -40,7 +40,7 @@ namespace DotNetCore.CAP.MySql await PublishAsyncInternal(message); } - protected override Task ExecuteAsync(CapPublishedMessage message, ICapTransaction transaction, + protected override Task ExecuteAsync(CapPublishedMessage message, ICapTransaction transaction, CancellationToken cancel = default(CancellationToken)) { var dbTrans = transaction.DbTransaction as IDbTransaction; diff --git a/src/DotNetCore.CAP.MySql/MySqlStorageConnection.cs b/src/DotNetCore.CAP.MySql/MySqlStorageConnection.cs index 77f9fbb..b212178 100644 --- a/src/DotNetCore.CAP.MySql/MySqlStorageConnection.cs +++ b/src/DotNetCore.CAP.MySql/MySqlStorageConnection.cs @@ -52,7 +52,7 @@ namespace DotNetCore.CAP.MySql } } - public async Task StoreReceivedMessageAsync(CapReceivedMessage message) + public Task StoreReceivedMessageAsync(CapReceivedMessage message) { if (message == null) { @@ -61,11 +61,11 @@ namespace DotNetCore.CAP.MySql var sql = $@" INSERT INTO `{_prefix}.received`(`Name`,`Group`,`Content`,`Retries`,`Added`,`ExpiresAt`,`StatusName`) -VALUES(@Name,@Group,@Content,@Retries,@Added,@ExpiresAt,@StatusName);SELECT LAST_INSERT_ID();"; +VALUES(@Name,@Group,@Content,@Retries,@Added,@ExpiresAt,@StatusName);"; using (var connection = new MySqlConnection(Options.ConnectionString)) { - return await connection.ExecuteScalarAsync(sql, message); + return connection.ExecuteScalarAsync(sql, message); } } diff --git a/src/DotNetCore.CAP/Abstractions/CapPublisherBase.cs b/src/DotNetCore.CAP/Abstractions/CapPublisherBase.cs index b029f69..18700c9 100644 --- a/src/DotNetCore.CAP/Abstractions/CapPublisherBase.cs +++ b/src/DotNetCore.CAP/Abstractions/CapPublisherBase.cs @@ -40,6 +40,7 @@ namespace DotNetCore.CAP.Abstractions { var message = new CapPublishedMessage { + Id = SnowflakeId.Default().NextId(), Name = name, Content = Serialize(contentObj, callbackName), StatusName = StatusName.Scheduled @@ -53,6 +54,7 @@ namespace DotNetCore.CAP.Abstractions { var message = new CapPublishedMessage { + Id = SnowflakeId.Default().NextId(), Name = name, Content = Serialize(contentObj, callbackName), StatusName = StatusName.Scheduled @@ -75,13 +77,11 @@ namespace DotNetCore.CAP.Abstractions { operationId = s_diagnosticListener.WritePublishMessageStoreBefore(message); - message.Id = await ExecuteAsync(message, CapTransaction); + await ExecuteAsync(message, CapTransaction); - if (message.Id > 0) - { - _capTransaction.AddToSent(message); - s_diagnosticListener.WritePublishMessageStoreAfter(operationId, message); - } + _capTransaction.AddToSent(message); + + s_diagnosticListener.WritePublishMessageStoreAfter(operationId, message); if (NotUseTransaction || CapTransaction.AutoCommit) { @@ -105,7 +105,7 @@ namespace DotNetCore.CAP.Abstractions protected abstract object GetDbTransaction(); - protected abstract Task ExecuteAsync(CapPublishedMessage message, + protected abstract Task ExecuteAsync(CapPublishedMessage message, ICapTransaction transaction, CancellationToken cancel = default(CancellationToken)); diff --git a/src/DotNetCore.CAP/IConsumerHandler.Default.cs b/src/DotNetCore.CAP/IConsumerHandler.Default.cs index e2179ff..995668c 100644 --- a/src/DotNetCore.CAP/IConsumerHandler.Default.cs +++ b/src/DotNetCore.CAP/IConsumerHandler.Default.cs @@ -112,6 +112,7 @@ namespace DotNetCore.CAP var receivedMessage = new CapReceivedMessage(messageContext) { + Id = SnowflakeId.Default().NextId(), StatusName = StatusName.Scheduled, Content = messageBody }; @@ -170,10 +171,7 @@ namespace DotNetCore.CAP private void StoreMessage(CapReceivedMessage receivedMessage) { - var id = _connection.StoreReceivedMessageAsync(receivedMessage) - .GetAwaiter().GetResult(); - - receivedMessage.Id = id; + _connection.StoreReceivedMessageAsync(receivedMessage).GetAwaiter().GetResult(); } private (Guid, string) TracingBefore(string topic, string values) diff --git a/src/DotNetCore.CAP/IStorageConnection.cs b/src/DotNetCore.CAP/IStorageConnection.cs index 18d5ff8..b694737 100644 --- a/src/DotNetCore.CAP/IStorageConnection.cs +++ b/src/DotNetCore.CAP/IStorageConnection.cs @@ -32,7 +32,7 @@ namespace DotNetCore.CAP /// Stores the message. /// /// The message to store. - Task StoreReceivedMessageAsync(CapReceivedMessage message); + Task StoreReceivedMessageAsync(CapReceivedMessage message); /// /// Returns the message with the given id. diff --git a/src/DotNetCore.CAP/Internal/ICallbackMessageSender.Default.cs b/src/DotNetCore.CAP/Internal/ICallbackMessageSender.Default.cs index 1dc7fc8..2114eed 100644 --- a/src/DotNetCore.CAP/Internal/ICallbackMessageSender.Default.cs +++ b/src/DotNetCore.CAP/Internal/ICallbackMessageSender.Default.cs @@ -54,6 +54,7 @@ namespace DotNetCore.CAP.Internal var publishedMessage = new CapPublishedMessage { + Id = SnowflakeId.Default().NextId(), Name = topicName, Content = content, StatusName = StatusName.Scheduled diff --git a/src/DotNetCore.CAP/LoggerExtensions.cs b/src/DotNetCore.CAP/LoggerExtensions.cs index ab80367..c3497c2 100644 --- a/src/DotNetCore.CAP/LoggerExtensions.cs +++ b/src/DotNetCore.CAP/LoggerExtensions.cs @@ -10,12 +10,12 @@ namespace DotNetCore.CAP [SuppressMessage("ReSharper", "InconsistentNaming")] internal static class LoggerExtensions { - public static void ConsumerExecutedAfterThreshold(this ILogger logger, int messageId, int retries) + public static void ConsumerExecutedAfterThreshold(this ILogger logger, long messageId, int retries) { logger.LogWarning($"The Subscriber of the message({messageId}) still fails after {retries}th executions and we will stop retrying."); } - public static void SenderAfterThreshold(this ILogger logger, int messageId, int retries) + public static void SenderAfterThreshold(this ILogger logger, long messageId, int retries) { logger.LogWarning($"The Publisher of the message({messageId}) still fails after {retries}th sends and we will stop retrying."); } @@ -25,12 +25,12 @@ namespace DotNetCore.CAP logger.LogWarning(ex, "FailedThresholdCallback action raised an exception:" + ex.Message); } - public static void ConsumerExecutionRetrying(this ILogger logger, int messageId, int retries) + public static void ConsumerExecutionRetrying(this ILogger logger, long messageId, int retries) { logger.LogWarning($"The {retries}th retrying consume a message failed. message id: {messageId}"); } - public static void SenderRetrying(this ILogger logger, int messageId, int retries) + public static void SenderRetrying(this ILogger logger, long messageId, int retries) { logger.LogWarning($"The {retries}th retrying send a message failed. message id: {messageId} "); } @@ -40,7 +40,7 @@ namespace DotNetCore.CAP logger.LogDebug($"Message published. name: {name}, content:{content}."); } - public static void MessagePublishException(this ILogger logger, int messageId, string reason, Exception ex) + public static void MessagePublishException(this ILogger logger, long messageId, string reason, Exception ex) { logger.LogError(ex, $"An exception occured while publishing a message, reason:{reason}. message id:{messageId}"); } diff --git a/src/DotNetCore.CAP/Models/CapPublishedMessage.cs b/src/DotNetCore.CAP/Models/CapPublishedMessage.cs index d95e72f..955063d 100644 --- a/src/DotNetCore.CAP/Models/CapPublishedMessage.cs +++ b/src/DotNetCore.CAP/Models/CapPublishedMessage.cs @@ -15,7 +15,7 @@ namespace DotNetCore.CAP.Models Added = DateTime.Now; } - public int Id { get; set; } + public long Id { get; set; } public string Name { get; set; } diff --git a/src/DotNetCore.CAP/Models/CapReceivedMessage.cs b/src/DotNetCore.CAP/Models/CapReceivedMessage.cs index 790aca2..b14728e 100644 --- a/src/DotNetCore.CAP/Models/CapReceivedMessage.cs +++ b/src/DotNetCore.CAP/Models/CapReceivedMessage.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. See License.txt in the project root for license information. using System; +using DotNetCore.CAP.Infrastructure; namespace DotNetCore.CAP.Models { @@ -22,7 +23,7 @@ namespace DotNetCore.CAP.Models Content = message.Content; } - public int Id { get; set; } + public long Id { get; set; } public string Group { get; set; } diff --git a/test/DotNetCore.CAP.MySql.Test/MySqlStorageConnectionTest.cs b/test/DotNetCore.CAP.MySql.Test/MySqlStorageConnectionTest.cs index aefa831..f9fae9d 100644 --- a/test/DotNetCore.CAP.MySql.Test/MySqlStorageConnectionTest.cs +++ b/test/DotNetCore.CAP.MySql.Test/MySqlStorageConnectionTest.cs @@ -25,6 +25,7 @@ namespace DotNetCore.CAP.MySql.Test var sql = "INSERT INTO `cap.published`(`Name`,`Content`,`Retries`,`Added`,`ExpiresAt`,`StatusName`) VALUES(@Name,@Content,@Retries,@Added,@ExpiresAt,@StatusName);SELECT @@IDENTITY;"; var publishMessage = new CapPublishedMessage { + Id = SnowflakeId.Default().NextId(), Name = "MySqlStorageConnectionTest", Content = "", StatusName = StatusName.Scheduled @@ -45,6 +46,7 @@ namespace DotNetCore.CAP.MySql.Test { var receivedMessage = new CapReceivedMessage { + Id = SnowflakeId.Default().NextId(), Name = "MySqlStorageConnectionTest", Content = "", Group = "mygroup", @@ -71,6 +73,7 @@ namespace DotNetCore.CAP.MySql.Test VALUES(@Name,@Group,@Content,@Retries,@Added,@ExpiresAt,@StatusName);SELECT @@IDENTITY;"; var receivedMessage = new CapReceivedMessage { + Id = SnowflakeId.Default().NextId(), Name = "MySqlStorageConnectionTest", Content = "", Group = "mygroup", diff --git a/test/DotNetCore.CAP.Test/Processor/StateChangerTest.cs b/test/DotNetCore.CAP.Test/Processor/StateChangerTest.cs index 5a72ffa..26cb8ee 100644 --- a/test/DotNetCore.CAP.Test/Processor/StateChangerTest.cs +++ b/test/DotNetCore.CAP.Test/Processor/StateChangerTest.cs @@ -16,6 +16,7 @@ namespace DotNetCore.CAP.Test var fixture = Create(); var message = new CapPublishedMessage { + Id = SnowflakeId.Default().NextId(), StatusName = StatusName.Scheduled }; var state = Mock.Of(s => s.Name == "s" && s.ExpiresAfter == null); @@ -39,6 +40,7 @@ namespace DotNetCore.CAP.Test var fixture = Create(); var message = new CapPublishedMessage { + Id = SnowflakeId.Default().NextId(), StatusName = StatusName.Scheduled }; var state = Mock.Of(s => s.Name == "s" && s.ExpiresAfter == TimeSpan.FromHours(1)); From 1c0225e847b252c2ae88b9a69777c2ccfda228cf Mon Sep 17 00:00:00 2001 From: Savorboard Date: Mon, 13 Aug 2018 15:56:51 +0800 Subject: [PATCH 169/223] chang message id data type from int to long --- src/DotNetCore.CAP.MySql/MySqlStorage.cs | 6 +++++- .../MySqlStorageConnection.cs | 16 ++++++++-------- src/DotNetCore.CAP/Dashboard/DashboardRoutes.cs | 4 ++-- .../Dashboard/Monitoring/MessageDto.cs | 2 +- src/DotNetCore.CAP/IStorageConnection.cs | 10 +++++----- 5 files changed, 21 insertions(+), 17 deletions(-) diff --git a/src/DotNetCore.CAP.MySql/MySqlStorage.cs b/src/DotNetCore.CAP.MySql/MySqlStorage.cs index 98fa0de..e0170de 100644 --- a/src/DotNetCore.CAP.MySql/MySqlStorage.cs +++ b/src/DotNetCore.CAP.MySql/MySqlStorage.cs @@ -81,7 +81,11 @@ CREATE TABLE IF NOT EXISTS `{prefix}.published` ( `ExpiresAt` datetime DEFAULT NULL, `StatusName` varchar(40) NOT NULL, PRIMARY KEY (`Id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8;"; +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +ALTER TABLE `{prefix}.published` MODIFY Id BIGINT NOT NULL; +ALTER TABLE `{prefix}.received` MODIFY Id BIGINT NOT NULL; +"; return batchSql; } diff --git a/src/DotNetCore.CAP.MySql/MySqlStorageConnection.cs b/src/DotNetCore.CAP.MySql/MySqlStorageConnection.cs index b212178..97aadf6 100644 --- a/src/DotNetCore.CAP.MySql/MySqlStorageConnection.cs +++ b/src/DotNetCore.CAP.MySql/MySqlStorageConnection.cs @@ -30,7 +30,7 @@ namespace DotNetCore.CAP.MySql return new MySqlStorageTransaction(this); } - public async Task GetPublishedMessageAsync(int id) + public async Task GetPublishedMessageAsync(long id) { var sql = $@"SELECT * FROM `{_prefix}.published` WHERE `Id`={id};"; @@ -52,7 +52,7 @@ namespace DotNetCore.CAP.MySql } } - public Task StoreReceivedMessageAsync(CapReceivedMessage message) + public void StoreReceivedMessage(CapReceivedMessage message) { if (message == null) { @@ -60,16 +60,16 @@ namespace DotNetCore.CAP.MySql } var sql = $@" -INSERT INTO `{_prefix}.received`(`Name`,`Group`,`Content`,`Retries`,`Added`,`ExpiresAt`,`StatusName`) -VALUES(@Name,@Group,@Content,@Retries,@Added,@ExpiresAt,@StatusName);"; +INSERT INTO `{_prefix}.received`(`Id`,`Name`,`Group`,`Content`,`Retries`,`Added`,`ExpiresAt`,`StatusName`) +VALUES(@Id,@Name,@Group,@Content,@Retries,@Added,@ExpiresAt,@StatusName);"; using (var connection = new MySqlConnection(Options.ConnectionString)) { - return connection.ExecuteScalarAsync(sql, message); + connection.Execute(sql, message); } } - public async Task GetReceivedMessageAsync(int id) + public async Task GetReceivedMessageAsync(long id) { var sql = $@"SELECT * FROM `{_prefix}.received` WHERE Id={id};"; using (var connection = new MySqlConnection(Options.ConnectionString)) @@ -89,7 +89,7 @@ VALUES(@Name,@Group,@Content,@Retries,@Added,@ExpiresAt,@StatusName);"; } } - public bool ChangePublishedState(int messageId, string state) + public bool ChangePublishedState(long messageId, string state) { var sql = $"UPDATE `{_prefix}.published` SET `Retries`=`Retries`+1,`ExpiresAt`=NULL,`StatusName` = '{state}' WHERE `Id`={messageId}"; @@ -100,7 +100,7 @@ VALUES(@Name,@Group,@Content,@Retries,@Added,@ExpiresAt,@StatusName);"; } } - public bool ChangeReceivedState(int messageId, string state) + public bool ChangeReceivedState(long messageId, string state) { var sql = $"UPDATE `{_prefix}.received` SET `Retries`=`Retries`+1,`ExpiresAt`=NULL,`StatusName` = '{state}' WHERE `Id`={messageId}"; diff --git a/src/DotNetCore.CAP/Dashboard/DashboardRoutes.cs b/src/DotNetCore.CAP/Dashboard/DashboardRoutes.cs index 9a51faa..34f3aee 100644 --- a/src/DotNetCore.CAP/Dashboard/DashboardRoutes.cs +++ b/src/DotNetCore.CAP/Dashboard/DashboardRoutes.cs @@ -82,14 +82,14 @@ namespace DotNetCore.CAP.Dashboard Routes.AddJsonResult("/published/message/(?.+)", x => { - var id = int.Parse(x.UriMatch.Groups["Id"].Value); + var id = long.Parse(x.UriMatch.Groups["Id"].Value); var message = x.Storage.GetConnection().GetPublishedMessageAsync(id) .GetAwaiter().GetResult(); return message.Content; }); Routes.AddJsonResult("/received/message/(?.+)", x => { - var id = int.Parse(x.UriMatch.Groups["Id"].Value); + var id = long.Parse(x.UriMatch.Groups["Id"].Value); var message = x.Storage.GetConnection().GetReceivedMessageAsync(id) .GetAwaiter().GetResult(); return message.Content; diff --git a/src/DotNetCore.CAP/Dashboard/Monitoring/MessageDto.cs b/src/DotNetCore.CAP/Dashboard/Monitoring/MessageDto.cs index 1a256ba..0baaec4 100644 --- a/src/DotNetCore.CAP/Dashboard/Monitoring/MessageDto.cs +++ b/src/DotNetCore.CAP/Dashboard/Monitoring/MessageDto.cs @@ -7,7 +7,7 @@ namespace DotNetCore.CAP.Dashboard.Monitoring { public class MessageDto { - public int Id { get; set; } + public long Id { get; set; } public string Group { get; set; } diff --git a/src/DotNetCore.CAP/IStorageConnection.cs b/src/DotNetCore.CAP/IStorageConnection.cs index b694737..e305f58 100644 --- a/src/DotNetCore.CAP/IStorageConnection.cs +++ b/src/DotNetCore.CAP/IStorageConnection.cs @@ -19,7 +19,7 @@ namespace DotNetCore.CAP /// Returns the message with the given id. /// /// The message's id. - Task GetPublishedMessageAsync(int id); + Task GetPublishedMessageAsync(long id); /// /// Returns executed failed messages. @@ -32,13 +32,13 @@ namespace DotNetCore.CAP /// Stores the message. /// /// The message to store. - Task StoreReceivedMessageAsync(CapReceivedMessage message); + void StoreReceivedMessage(CapReceivedMessage message); /// /// Returns the message with the given id. /// /// The message's id. - Task GetReceivedMessageAsync(int id); + Task GetReceivedMessageAsync(long id); /// /// Returns executed failed message. @@ -55,13 +55,13 @@ namespace DotNetCore.CAP /// /// Message id /// State name - bool ChangePublishedState(int messageId, string state); + bool ChangePublishedState(long messageId, string state); /// /// Change specified message's state of received message /// /// Message id /// State name - bool ChangeReceivedState(int messageId, string state); + bool ChangeReceivedState(long messageId, string state); } } \ No newline at end of file From 82e43964d347c21220bf891ac03b57ae78d511e9 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Mon, 13 Aug 2018 15:57:41 +0800 Subject: [PATCH 170/223] refactor code --- src/DotNetCore.CAP.MySql/CapPublisher.cs | 2 +- src/DotNetCore.CAP.MySql/MySqlStorageTransaction.cs | 5 ----- src/DotNetCore.CAP/IConsumerHandler.Default.cs | 2 +- src/DotNetCore.CAP/Models/CapReceivedMessage.cs | 1 - 4 files changed, 2 insertions(+), 8 deletions(-) diff --git a/src/DotNetCore.CAP.MySql/CapPublisher.cs b/src/DotNetCore.CAP.MySql/CapPublisher.cs index b6d1d13..8ce9b8d 100644 --- a/src/DotNetCore.CAP.MySql/CapPublisher.cs +++ b/src/DotNetCore.CAP.MySql/CapPublisher.cs @@ -73,7 +73,7 @@ namespace DotNetCore.CAP.MySql private string PrepareSql() { return - $"INSERT INTO `{_options.TableNamePrefix}.published` (`Name`,`Content`,`Retries`,`Added`,`ExpiresAt`,`StatusName`)VALUES(@Name,@Content,@Retries,@Added,@ExpiresAt,@StatusName);SELECT LAST_INSERT_ID()"; + $"INSERT INTO `{_options.TableNamePrefix}.published` (`Id`,`Name`,`Content`,`Retries`,`Added`,`ExpiresAt`,`StatusName`)VALUES(@Id,@Name,@Content,@Retries,@Added,@ExpiresAt,@StatusName);"; } private IDbTransaction InitDbConnection() diff --git a/src/DotNetCore.CAP.MySql/MySqlStorageTransaction.cs b/src/DotNetCore.CAP.MySql/MySqlStorageTransaction.cs index 7121b09..44b77ab 100644 --- a/src/DotNetCore.CAP.MySql/MySqlStorageTransaction.cs +++ b/src/DotNetCore.CAP.MySql/MySqlStorageTransaction.cs @@ -14,7 +14,6 @@ namespace DotNetCore.CAP.MySql { private readonly IDbConnection _dbConnection; - //private readonly IDbTransaction _dbTransaction; private readonly string _prefix; public MySqlStorageTransaction(MySqlStorageConnection connection) @@ -23,8 +22,6 @@ namespace DotNetCore.CAP.MySql _prefix = options.TableNamePrefix; _dbConnection = new MySqlConnection(options.ConnectionString); - // _dbConnection.Open(); for performance - // _dbTransaction = _dbConnection.BeginTransaction(IsolationLevel.ReadCommitted); } public void UpdateMessage(CapPublishedMessage message) @@ -55,13 +52,11 @@ namespace DotNetCore.CAP.MySql { _dbConnection.Close(); _dbConnection.Dispose(); - //_dbTransaction.Commit(); return Task.CompletedTask; } public void Dispose() { - //_dbTransaction.Dispose(); _dbConnection.Dispose(); } } diff --git a/src/DotNetCore.CAP/IConsumerHandler.Default.cs b/src/DotNetCore.CAP/IConsumerHandler.Default.cs index 995668c..a977703 100644 --- a/src/DotNetCore.CAP/IConsumerHandler.Default.cs +++ b/src/DotNetCore.CAP/IConsumerHandler.Default.cs @@ -171,7 +171,7 @@ namespace DotNetCore.CAP private void StoreMessage(CapReceivedMessage receivedMessage) { - _connection.StoreReceivedMessageAsync(receivedMessage).GetAwaiter().GetResult(); + _connection.StoreReceivedMessage(receivedMessage); } private (Guid, string) TracingBefore(string topic, string values) diff --git a/src/DotNetCore.CAP/Models/CapReceivedMessage.cs b/src/DotNetCore.CAP/Models/CapReceivedMessage.cs index b14728e..3e2dbf6 100644 --- a/src/DotNetCore.CAP/Models/CapReceivedMessage.cs +++ b/src/DotNetCore.CAP/Models/CapReceivedMessage.cs @@ -2,7 +2,6 @@ // Licensed under the MIT License. See License.txt in the project root for license information. using System; -using DotNetCore.CAP.Infrastructure; namespace DotNetCore.CAP.Models { From 20717b5817858447c921b182038071cae152b2d0 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Mon, 13 Aug 2018 18:12:00 +0800 Subject: [PATCH 171/223] Refactor MongoDB for new transaction --- .../Controllers/ValuesController.cs | 58 ++---- .../CAP.MongoDBCapOptionsExtension.cs | 2 - .../CAP.MongoDBOptions.cs | 2 - src/DotNetCore.CAP.MongoDB/CapPublisher.cs | 188 ++---------------- .../ICapTransaction.MongoDB.cs | 54 +++++ ...cessor.cs => ICollectProcessor.MongoDB.cs} | 0 ...toringApi.cs => IMonitoringApi.MongoDB.cs} | 0 src/DotNetCore.CAP.MongoDB/MongoDBStorage.cs | 17 +- .../MongoDBStorageConnection.cs | 14 +- src/DotNetCore.CAP.MongoDB/MongoDBUtil.cs | 66 ------ .../MongoTransaction.cs | 60 ------ .../CAP.MySqlCapOptionsExtension.cs | 2 + src/DotNetCore.CAP.MySql/CAP.MySqlOptions.cs | 1 + ...nitoringApi.cs => IMonitoringApi.MySql.cs} | 0 .../Abstractions/IMongoTransaction.cs | 21 -- 15 files changed, 98 insertions(+), 387 deletions(-) create mode 100644 src/DotNetCore.CAP.MongoDB/ICapTransaction.MongoDB.cs rename src/DotNetCore.CAP.MongoDB/{MongoDBCollectProcessor.cs => ICollectProcessor.MongoDB.cs} (100%) rename src/DotNetCore.CAP.MongoDB/{MongoDBMonitoringApi.cs => IMonitoringApi.MongoDB.cs} (100%) delete mode 100644 src/DotNetCore.CAP.MongoDB/MongoDBUtil.cs delete mode 100644 src/DotNetCore.CAP.MongoDB/MongoTransaction.cs rename src/DotNetCore.CAP.MySql/{MySqlMonitoringApi.cs => IMonitoringApi.MySql.cs} (100%) delete mode 100644 src/DotNetCore.CAP/Abstractions/IMongoTransaction.cs diff --git a/samples/Sample.RabbitMQ.MongoDB/Controllers/ValuesController.cs b/samples/Sample.RabbitMQ.MongoDB/Controllers/ValuesController.cs index 5e0a07d..bf202ad 100644 --- a/samples/Sample.RabbitMQ.MongoDB/Controllers/ValuesController.cs +++ b/samples/Sample.RabbitMQ.MongoDB/Controllers/ValuesController.cs @@ -1,8 +1,5 @@ using System; -using System.Threading.Tasks; using DotNetCore.CAP; -using DotNetCore.CAP.Abstractions; -using DotNetCore.CAP.MongoDB; using Microsoft.AspNetCore.Mvc; using MongoDB.Bson; using MongoDB.Driver; @@ -15,71 +12,48 @@ namespace Sample.RabbitMQ.MongoDB.Controllers { private readonly IMongoClient _client; private readonly ICapPublisher _capPublisher; - private readonly IMongoTransaction _mongoTransaction; - public ValuesController(IMongoClient client, ICapPublisher capPublisher, IMongoTransaction mongoTransaction) + public ValuesController(IMongoClient client, ICapPublisher capPublisher) { _client = client; _capPublisher = capPublisher; - _mongoTransaction = mongoTransaction; } [Route("~/publish")] - public async Task PublishWithTrans() + public IActionResult PublishWithTrans() { - using (var trans = await _mongoTransaction.BegeinAsync()) + using (var session = _client.StartSession()) + using (var trans = _capPublisher.CapTransaction.Begin(session)) { var collection = _client.GetDatabase("TEST").GetCollection("test"); - collection.InsertOne(trans.GetSession(), new BsonDocument { { "hello", "world" } }); + collection.InsertOne(session, new BsonDocument { { "hello", "world" } }); - await _capPublisher.PublishWithMongoAsync("sample.rabbitmq.mongodb", DateTime.Now, trans); + _capPublisher.Publish("sample.rabbitmq.mongodb", DateTime.Now); + + trans.Commit(); } return Ok(); } - [Route("~/publish/not/autocommit")] + [Route("~/publish/autocommit")] public IActionResult PublishNotAutoCommit() { - using (var trans = _mongoTransaction.Begein(autoCommit: false)) + using (var session = _client.StartSession()) + using (_capPublisher.CapTransaction.Begin(session,true)) { - var session = trans.GetSession(); - var collection = _client.GetDatabase("TEST").GetCollection("test"); - collection.InsertOne(session, new BsonDocument { { "Hello", "World" } }); - - _capPublisher.PublishWithMongo("sample.rabbitmq.mongodb", DateTime.Now, trans); + collection.InsertOne(session, new BsonDocument { { "hello", "world" } }); - //Do something, and commit by yourself. - session.CommitTransaction(); + _capPublisher.Publish("sample.rabbitmq.mongodb", DateTime.Now); } - return Ok(); - } - [Route("~/publish/rollback")] - public IActionResult PublishRollback() - { - using (var trans = _mongoTransaction.Begein(autoCommit: false)) - { - var session = trans.GetSession(); - try - { - _capPublisher.PublishWithMongo("sample.rabbitmq.mongodb", DateTime.Now, trans); - //Do something, but - throw new Exception("Foo"); - session.CommitTransaction(); - } - catch (System.Exception ex) - { - session.AbortTransaction(); - return StatusCode(500, ex.Message); - } - } + return Ok(); } - + [Route("~/publish/without/trans")] public IActionResult PublishWithoutTrans() { - _capPublisher.PublishWithMongo("sample.rabbitmq.mongodb", DateTime.Now); + _capPublisher.Publish("sample.rabbitmq.mongodb", DateTime.Now); return Ok(); } diff --git a/src/DotNetCore.CAP.MongoDB/CAP.MongoDBCapOptionsExtension.cs b/src/DotNetCore.CAP.MongoDB/CAP.MongoDBCapOptionsExtension.cs index ab1158d..e53752d 100644 --- a/src/DotNetCore.CAP.MongoDB/CAP.MongoDBCapOptionsExtension.cs +++ b/src/DotNetCore.CAP.MongoDB/CAP.MongoDBCapOptionsExtension.cs @@ -2,7 +2,6 @@ // Licensed under the MIT License. See License.txt in the project root for license information. using System; -using DotNetCore.CAP.Abstractions; using DotNetCore.CAP.Processor; using Microsoft.Extensions.DependencyInjection; @@ -28,7 +27,6 @@ namespace DotNetCore.CAP.MongoDB services.AddScoped(); services.AddTransient(); - services.AddTransient(); var options = new MongoDBOptions(); _configure?.Invoke(options); diff --git a/src/DotNetCore.CAP.MongoDB/CAP.MongoDBOptions.cs b/src/DotNetCore.CAP.MongoDB/CAP.MongoDBOptions.cs index fe00791..0cb350e 100644 --- a/src/DotNetCore.CAP.MongoDB/CAP.MongoDBOptions.cs +++ b/src/DotNetCore.CAP.MongoDB/CAP.MongoDBOptions.cs @@ -29,7 +29,5 @@ namespace DotNetCore.CAP.MongoDB /// Default value: "published" /// public string PublishedCollection { get; set; } = "cap.published"; - - internal const string CounterCollection = "cap.counter"; } } \ No newline at end of file diff --git a/src/DotNetCore.CAP.MongoDB/CapPublisher.cs b/src/DotNetCore.CAP.MongoDB/CapPublisher.cs index df19c48..a02d3af 100644 --- a/src/DotNetCore.CAP.MongoDB/CapPublisher.cs +++ b/src/DotNetCore.CAP.MongoDB/CapPublisher.cs @@ -2,13 +2,11 @@ // Licensed under the MIT License. See License.txt in the project root for license information. using System; -using System.Data; +using System.Threading; using System.Threading.Tasks; using DotNetCore.CAP.Abstractions; -using DotNetCore.CAP.Diagnostics; -using DotNetCore.CAP.Infrastructure; using DotNetCore.CAP.Models; -using Microsoft.Extensions.Logging; +using Microsoft.Extensions.DependencyInjection; using MongoDB.Driver; namespace DotNetCore.CAP.MongoDB @@ -16,187 +14,37 @@ namespace DotNetCore.CAP.MongoDB public class CapPublisher : CapPublisherBase, ICallbackPublisher { private readonly MongoDBOptions _options; - private readonly IMongoDatabase _database; - private bool _usingTransaction = true; - public CapPublisher( - ILogger logger, - IDispatcher dispatcher, - IMongoClient client, - MongoDBOptions options, - IServiceProvider provider) - : base(logger, dispatcher) + public CapPublisher(IServiceProvider provider, MongoDBOptions options) + : base(provider) { _options = options; - _database = client.GetDatabase(_options.DatabaseName); - ServiceProvider = provider; } public async Task PublishCallbackAsync(CapPublishedMessage message) { - var collection = _database.GetCollection(_options.PublishedCollection); - message.Id = await new MongoDBUtil().GetNextSequenceValueAsync(_database, _options.PublishedCollection); - collection.InsertOne(message); - Enqueue(message); + await PublishAsyncInternal(message); } - protected override int Execute(IDbConnection dbConnection, IDbTransaction dbTransaction, - CapPublishedMessage message) + protected override Task ExecuteAsync(CapPublishedMessage message, ICapTransaction transaction, + CancellationToken cancel = default(CancellationToken)) { - throw new NotImplementedException("Not work for MongoDB"); - } - - protected override Task ExecuteAsync(IDbConnection dbConnection, IDbTransaction dbTransaction, - CapPublishedMessage message) - { - throw new NotImplementedException("Not work for MongoDB"); - } - - protected override void PrepareConnectionForEF() - { - throw new NotImplementedException("Not work for MongoDB"); - } - - public override void PublishWithMongo(string name, T contentObj, IMongoTransaction mongoTransaction = null, string callbackName = null) - { - if (mongoTransaction == null) - { - _usingTransaction = false; - mongoTransaction = new NullMongoTransaction(); - } - - PublishWithTransaction(name, contentObj, mongoTransaction, callbackName); - } + var dbTrans = (IClientSessionHandle)transaction.DbTransaction; - public override async Task PublishWithMongoAsync(string name, T contentObj, IMongoTransaction mongoTransaction = null, string callbackName = null) - { - if (mongoTransaction == null) - { - _usingTransaction = false; - mongoTransaction = new NullMongoTransaction(); - } + var collection = dbTrans.Client + .GetDatabase(_options.DatabaseName) + .GetCollection(_options.PublishedCollection); - await PublishWithTransactionAsync(name, contentObj, mongoTransaction, callbackName); + var insertOptions = new InsertOneOptions { BypassDocumentValidation = false }; + return collection.InsertOneAsync(dbTrans, message, insertOptions, cancel); } - private void PublishWithTransaction(string name, T contentObj, IMongoTransaction transaction, string callbackName) + protected override object GetDbTransaction() { - var operationId = default(Guid); - - var content = Serialize(contentObj, callbackName); - - var message = new CapPublishedMessage - { - Name = name, - Content = content, - StatusName = StatusName.Scheduled - }; - - var session = transaction.GetSession(); - - try - { - operationId = s_diagnosticListener.WritePublishMessageStoreBefore(message); - var id = Execute(session, message); - - if (transaction.AutoCommit) - { - session.CommitTransaction(); - } - - if (!_usingTransaction || (transaction.AutoCommit && id > 0)) - { - _logger.LogInformation($"message [{message}] has been persisted in the database."); - s_diagnosticListener.WritePublishMessageStoreAfter(operationId, message); - message.Id = id; - Enqueue(message); - } - } - catch (Exception e) - { - _logger.LogError(e, "An exception was occurred when publish message. message:" + name); - s_diagnosticListener.WritePublishMessageStoreError(operationId, message, e); - throw; - } - } - - private int Execute(IClientSessionHandle session, CapPublishedMessage message) - { - message.Id = new MongoDBUtil().GetNextSequenceValue(_database, _options.PublishedCollection, session); - - var collection = _database.GetCollection(_options.PublishedCollection); - if (_usingTransaction) - { - collection.InsertOne(session, message); - } - else - { - collection.InsertOne(message); - } - - return message.Id; - } - - - private async Task PublishWithTransactionAsync(string name, T contentObj, IMongoTransaction transaction, string callbackName) - { - var operationId = default(Guid); - var content = Serialize(contentObj, callbackName); - - var message = new CapPublishedMessage - { - Name = name, - Content = content, - StatusName = StatusName.Scheduled - }; - - var session = transaction.GetSession(); - - try - { - operationId = s_diagnosticListener.WritePublishMessageStoreBefore(message); - - var id = await ExecuteAsync(session, message); - - if (transaction.AutoCommit) - { - await session.CommitTransactionAsync(); - } - - if (!_usingTransaction || (transaction.AutoCommit && id > 0)) - { - _logger.LogInformation($"message [{message}] has been persisted in the database."); - s_diagnosticListener.WritePublishMessageStoreAfter(operationId, message); - - message.Id = id; - - Enqueue(message); - } - } - catch (Exception e) - { - _logger.LogError(e, "An exception was occurred when publish message async. exception message:" + name); - s_diagnosticListener.WritePublishMessageStoreError(operationId, message, e); - Console.WriteLine(e); - throw; - } - } - - private async Task ExecuteAsync(IClientSessionHandle session, CapPublishedMessage message) - { - message.Id = - await new MongoDBUtil().GetNextSequenceValueAsync(_database, _options.PublishedCollection, session); - var collection = _database.GetCollection(_options.PublishedCollection); - if (_usingTransaction) - { - await collection.InsertOneAsync(session, message); - } - else - { - await collection.InsertOneAsync(message); - } - - return message.Id; + var client = ServiceProvider.GetRequiredService(); + var session = client.StartSession(new ClientSessionOptions()); + session.StartTransaction(); + return session; } } } \ No newline at end of file diff --git a/src/DotNetCore.CAP.MongoDB/ICapTransaction.MongoDB.cs b/src/DotNetCore.CAP.MongoDB/ICapTransaction.MongoDB.cs new file mode 100644 index 0000000..e0a0054 --- /dev/null +++ b/src/DotNetCore.CAP.MongoDB/ICapTransaction.MongoDB.cs @@ -0,0 +1,54 @@ +using System.Data; +using System.Diagnostics; +using MongoDB.Driver; + +// ReSharper disable once CheckNamespace +namespace DotNetCore.CAP +{ + public class MongoDBCapTransaction : CapTransactionBase + { + public MongoDBCapTransaction(IDispatcher dispatcher) + : base(dispatcher) + { + } + + public override void Commit() + { + Debug.Assert(DbTransaction != null); + + if (DbTransaction is IClientSessionHandle session) + { + session.CommitTransaction(); + } + + Flush(); + } + + public override void Rollback() + { + Debug.Assert(DbTransaction != null); + + if (DbTransaction is IClientSessionHandle session) + { + session.AbortTransaction(); + } + } + + public override void Dispose() + { + (DbTransaction as IDbTransaction)?.Dispose(); + } + } + + public static class CapTransactionExtensions + { + public static ICapTransaction Begin(this ICapTransaction transaction, + IClientSessionHandle dbTransaction, bool autoCommit = false) + { + transaction.DbTransaction = dbTransaction; + transaction.AutoCommit = autoCommit; + + return transaction; + } + } +} diff --git a/src/DotNetCore.CAP.MongoDB/MongoDBCollectProcessor.cs b/src/DotNetCore.CAP.MongoDB/ICollectProcessor.MongoDB.cs similarity index 100% rename from src/DotNetCore.CAP.MongoDB/MongoDBCollectProcessor.cs rename to src/DotNetCore.CAP.MongoDB/ICollectProcessor.MongoDB.cs diff --git a/src/DotNetCore.CAP.MongoDB/MongoDBMonitoringApi.cs b/src/DotNetCore.CAP.MongoDB/IMonitoringApi.MongoDB.cs similarity index 100% rename from src/DotNetCore.CAP.MongoDB/MongoDBMonitoringApi.cs rename to src/DotNetCore.CAP.MongoDB/IMonitoringApi.MongoDB.cs diff --git a/src/DotNetCore.CAP.MongoDB/MongoDBStorage.cs b/src/DotNetCore.CAP.MongoDB/MongoDBStorage.cs index 24fa082..805240c 100644 --- a/src/DotNetCore.CAP.MongoDB/MongoDBStorage.cs +++ b/src/DotNetCore.CAP.MongoDB/MongoDBStorage.cs @@ -6,7 +6,6 @@ using System.Threading; using System.Threading.Tasks; using DotNetCore.CAP.Dashboard; using Microsoft.Extensions.Logging; -using MongoDB.Bson; using MongoDB.Driver; namespace DotNetCore.CAP.MongoDB @@ -49,26 +48,14 @@ namespace DotNetCore.CAP.MongoDB var database = _client.GetDatabase(_options.DatabaseName); var names = (await database.ListCollectionNamesAsync(cancellationToken: cancellationToken))?.ToList(); - if (!names.Any(n => n == _options.ReceivedCollection)) + if (names.All(n => n != _options.ReceivedCollection)) { await database.CreateCollectionAsync(_options.ReceivedCollection, cancellationToken: cancellationToken); } if (names.All(n => n != _options.PublishedCollection)) { - await database.CreateCollectionAsync(_options.PublishedCollection, - cancellationToken: cancellationToken); - } - - if (names.All(n => n != MongoDBOptions.CounterCollection)) - { - await database.CreateCollectionAsync(MongoDBOptions.CounterCollection, cancellationToken: cancellationToken); - var collection = database.GetCollection(MongoDBOptions.CounterCollection); - await collection.InsertManyAsync(new[] - { - new BsonDocument {{"_id", _options.PublishedCollection}, {"sequence_value", 0}}, - new BsonDocument {{"_id", _options.ReceivedCollection}, {"sequence_value", 0}} - }, cancellationToken: cancellationToken); + await database.CreateCollectionAsync(_options.PublishedCollection, cancellationToken: cancellationToken); } _logger.LogDebug("Ensuring all create database tables script are applied."); diff --git a/src/DotNetCore.CAP.MongoDB/MongoDBStorageConnection.cs b/src/DotNetCore.CAP.MongoDB/MongoDBStorageConnection.cs index 098ddf9..7b91026 100644 --- a/src/DotNetCore.CAP.MongoDB/MongoDBStorageConnection.cs +++ b/src/DotNetCore.CAP.MongoDB/MongoDBStorageConnection.cs @@ -25,7 +25,7 @@ namespace DotNetCore.CAP.MongoDB _database = _client.GetDatabase(_options.DatabaseName); } - public bool ChangePublishedState(int messageId, string state) + public bool ChangePublishedState(long messageId, string state) { var collection = _database.GetCollection(_options.PublishedCollection); @@ -40,7 +40,7 @@ namespace DotNetCore.CAP.MongoDB return result.ModifiedCount > 0; } - public bool ChangeReceivedState(int messageId, string state) + public bool ChangeReceivedState(long messageId, string state) { var collection = _database.GetCollection(_options.ReceivedCollection); @@ -60,7 +60,7 @@ namespace DotNetCore.CAP.MongoDB return new MongoDBStorageTransaction(_client, _options); } - public async Task GetPublishedMessageAsync(int id) + public async Task GetPublishedMessageAsync(long id) { var collection = _database.GetCollection(_options.PublishedCollection); return await collection.Find(x => x.Id == id).FirstOrDefaultAsync(); @@ -77,7 +77,7 @@ namespace DotNetCore.CAP.MongoDB .ToListAsync(); } - public async Task GetReceivedMessageAsync(int id) + public async Task GetReceivedMessageAsync(long id) { var collection = _database.GetCollection(_options.ReceivedCollection); return await collection.Find(x => x.Id == id).FirstOrDefaultAsync(); @@ -95,7 +95,7 @@ namespace DotNetCore.CAP.MongoDB .ToListAsync(); } - public async Task StoreReceivedMessageAsync(CapReceivedMessage message) + public void StoreReceivedMessage(CapReceivedMessage message) { if (message == null) { @@ -104,11 +104,7 @@ namespace DotNetCore.CAP.MongoDB var collection = _database.GetCollection(_options.ReceivedCollection); - message.Id = await new MongoDBUtil().GetNextSequenceValueAsync(_database, _options.ReceivedCollection); - collection.InsertOne(message); - - return message.Id; } public void Dispose() diff --git a/src/DotNetCore.CAP.MongoDB/MongoDBUtil.cs b/src/DotNetCore.CAP.MongoDB/MongoDBUtil.cs deleted file mode 100644 index 4af9989..0000000 --- a/src/DotNetCore.CAP.MongoDB/MongoDBUtil.cs +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright (c) .NET Core Community. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. - -using System; -using System.Threading.Tasks; -using MongoDB.Bson; -using MongoDB.Driver; - -namespace DotNetCore.CAP.MongoDB -{ - internal class MongoDBUtil - { - private readonly FindOneAndUpdateOptions _options = new FindOneAndUpdateOptions - { - ReturnDocument = ReturnDocument.After - }; - - public async Task GetNextSequenceValueAsync(IMongoDatabase database, string collectionName, - IClientSessionHandle session = null) - { - //https://www.tutorialspoint.com/mongodb/mongodb_autoincrement_sequence.htm - var collection = database.GetCollection(MongoDBOptions.CounterCollection); - - var updateDef = Builders.Update.Inc("sequence_value", 1); - var filter = new BsonDocument { { "_id", collectionName } }; - - BsonDocument result; - if (session == null) - { - result = await collection.FindOneAndUpdateAsync(filter, updateDef, _options); - - } - else - { - result = await collection.FindOneAndUpdateAsync(session, filter, updateDef, _options); - } - - if (result.TryGetValue("sequence_value", out var value)) - { - return value.ToInt32(); - } - - throw new Exception("Unable to get next sequence value."); - } - - public int GetNextSequenceValue(IMongoDatabase database, string collectionName, - IClientSessionHandle session = null) - { - var collection = database.GetCollection(MongoDBOptions.CounterCollection); - - var filter = new BsonDocument { { "_id", collectionName } }; - var updateDef = Builders.Update.Inc("sequence_value", 1); - - var result = session == null - ? collection.FindOneAndUpdate(filter, updateDef, _options) - : collection.FindOneAndUpdate(session, filter, updateDef, _options); - - if (result.TryGetValue("sequence_value", out var value)) - { - return value.ToInt32(); - } - - throw new Exception("Unable to get next sequence value."); - } - } -} \ No newline at end of file diff --git a/src/DotNetCore.CAP.MongoDB/MongoTransaction.cs b/src/DotNetCore.CAP.MongoDB/MongoTransaction.cs deleted file mode 100644 index 96aed37..0000000 --- a/src/DotNetCore.CAP.MongoDB/MongoTransaction.cs +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright (c) .NET Core Community. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. - -using System.Threading.Tasks; -using DotNetCore.CAP.Abstractions; -using MongoDB.Driver; - -namespace DotNetCore.CAP.MongoDB -{ - public class MongoTransaction : IMongoTransaction - { - private readonly IMongoClient _client; - - public MongoTransaction(IMongoClient client) - { - _client = client; - } - - public IClientSessionHandle Session { get; set; } - public bool AutoCommit { get; set; } - - public async Task BegeinAsync(bool autoCommit = true) - { - AutoCommit = autoCommit; - Session = await _client.StartSessionAsync(); - Session.StartTransaction(); - return this; - } - - public IMongoTransaction Begein(bool autoCommit = true) - { - AutoCommit = autoCommit; - Session = _client.StartSession(); - Session.StartTransaction(); - return this; - } - - public void Dispose() - { - Session?.Dispose(); - } - } - - public class NullMongoTransaction : MongoTransaction - { - public NullMongoTransaction(IMongoClient client = null) : base(client) - { - AutoCommit = false; - } - } - - public static class MongoTransactionExtensions - { - public static IClientSessionHandle GetSession(this IMongoTransaction mongoTransaction) - { - var trans = mongoTransaction as MongoTransaction; - return trans?.Session; - } - } -} \ No newline at end of file diff --git a/src/DotNetCore.CAP.MySql/CAP.MySqlCapOptionsExtension.cs b/src/DotNetCore.CAP.MySql/CAP.MySqlCapOptionsExtension.cs index 8cae0be..7ba01c5 100644 --- a/src/DotNetCore.CAP.MySql/CAP.MySqlCapOptionsExtension.cs +++ b/src/DotNetCore.CAP.MySql/CAP.MySqlCapOptionsExtension.cs @@ -24,8 +24,10 @@ namespace DotNetCore.CAP services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); + services.AddScoped(); services.AddScoped(); + services.AddTransient(); services.AddTransient(); diff --git a/src/DotNetCore.CAP.MySql/CAP.MySqlOptions.cs b/src/DotNetCore.CAP.MySql/CAP.MySqlOptions.cs index bcac400..e27650d 100644 --- a/src/DotNetCore.CAP.MySql/CAP.MySqlOptions.cs +++ b/src/DotNetCore.CAP.MySql/CAP.MySqlOptions.cs @@ -1,6 +1,7 @@ // Copyright (c) .NET Core Community. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. +// ReSharper disable once CheckNamespace namespace DotNetCore.CAP { public class MySqlOptions : EFOptions diff --git a/src/DotNetCore.CAP.MySql/MySqlMonitoringApi.cs b/src/DotNetCore.CAP.MySql/IMonitoringApi.MySql.cs similarity index 100% rename from src/DotNetCore.CAP.MySql/MySqlMonitoringApi.cs rename to src/DotNetCore.CAP.MySql/IMonitoringApi.MySql.cs diff --git a/src/DotNetCore.CAP/Abstractions/IMongoTransaction.cs b/src/DotNetCore.CAP/Abstractions/IMongoTransaction.cs deleted file mode 100644 index b98caf8..0000000 --- a/src/DotNetCore.CAP/Abstractions/IMongoTransaction.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) .NET Core Community. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. - -using System; -using System.Threading.Tasks; - -namespace DotNetCore.CAP.Abstractions -{ - public interface IMongoTransaction : IDisposable - { - /// - /// If set true, the session.CommitTransaction() will be called automatically. - /// - /// - bool AutoCommit { get; set; } - - Task BegeinAsync(bool autoCommit = true); - - IMongoTransaction Begein(bool autoCommit = true); - } -} \ No newline at end of file From 8bee1d4369582155fb6ae01d42f8d0e470542399 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Mon, 13 Aug 2018 18:12:11 +0800 Subject: [PATCH 172/223] update samples --- .../Sample.RabbitMQ.MySql/Controllers/ValuesController.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/samples/Sample.RabbitMQ.MySql/Controllers/ValuesController.cs b/samples/Sample.RabbitMQ.MySql/Controllers/ValuesController.cs index 03f33c0..12c4c42 100644 --- a/samples/Sample.RabbitMQ.MySql/Controllers/ValuesController.cs +++ b/samples/Sample.RabbitMQ.MySql/Controllers/ValuesController.cs @@ -37,10 +37,14 @@ namespace Sample.RabbitMQ.MySql.Controllers public async Task PublishMessageWithTransaction() { using (var trans = await _dbContext.Database.BeginTransactionAsync()) + using (var capTrans = _capBus.CapTransaction.Begin(trans)) { - await _capBus.PublishAsync("sample.kafka.sqlserver", ""); + for (int i = 0; i < 10; i++) + { + await _capBus.PublishAsync("sample.rabbitmq.mysql", DateTime.Now); + } - trans.Commit(); + capTrans.Commit(); } return Ok(); } From 925d28acfbc0f5423fc09f87e370fdff44792d60 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Fri, 17 Aug 2018 18:19:13 +0800 Subject: [PATCH 173/223] add MongoDbTransaction to DI --- src/DotNetCore.CAP.MongoDB/CAP.MongoDBCapOptionsExtension.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/DotNetCore.CAP.MongoDB/CAP.MongoDBCapOptionsExtension.cs b/src/DotNetCore.CAP.MongoDB/CAP.MongoDBCapOptionsExtension.cs index e53752d..7b02a90 100644 --- a/src/DotNetCore.CAP.MongoDB/CAP.MongoDBCapOptionsExtension.cs +++ b/src/DotNetCore.CAP.MongoDB/CAP.MongoDBCapOptionsExtension.cs @@ -27,6 +27,7 @@ namespace DotNetCore.CAP.MongoDB services.AddScoped(); services.AddTransient(); + services.AddTransient(); var options = new MongoDBOptions(); _configure?.Invoke(options); From 57a004aeb3d76fff31a394c54b52c675f599e614 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Fri, 17 Aug 2018 18:19:49 +0800 Subject: [PATCH 174/223] add UserMongoDB extension method --- src/DotNetCore.CAP.MongoDB/CAP.Options.Extensions.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/DotNetCore.CAP.MongoDB/CAP.Options.Extensions.cs b/src/DotNetCore.CAP.MongoDB/CAP.Options.Extensions.cs index 3a64171..eb9b8df 100644 --- a/src/DotNetCore.CAP.MongoDB/CAP.Options.Extensions.cs +++ b/src/DotNetCore.CAP.MongoDB/CAP.Options.Extensions.cs @@ -15,6 +15,11 @@ namespace Microsoft.Extensions.DependencyInjection return options.UseMongoDB(x => { }); } + public static CapOptions UseMongoDB(this CapOptions options, string connectionString) + { + return options.UseMongoDB(x => { x.DatabaseConnection = connectionString; }); + } + public static CapOptions UseMongoDB(this CapOptions options, Action configure) { if (configure == null) From 690380a3cc244306f8aff6e803ef959b39aed149 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Fri, 17 Aug 2018 18:20:37 +0800 Subject: [PATCH 175/223] tweak transaction extension method --- src/DotNetCore.CAP.MongoDB/ICapTransaction.MongoDB.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/DotNetCore.CAP.MongoDB/ICapTransaction.MongoDB.cs b/src/DotNetCore.CAP.MongoDB/ICapTransaction.MongoDB.cs index e0a0054..fc7b1a6 100644 --- a/src/DotNetCore.CAP.MongoDB/ICapTransaction.MongoDB.cs +++ b/src/DotNetCore.CAP.MongoDB/ICapTransaction.MongoDB.cs @@ -45,6 +45,11 @@ namespace DotNetCore.CAP public static ICapTransaction Begin(this ICapTransaction transaction, IClientSessionHandle dbTransaction, bool autoCommit = false) { + if (!dbTransaction.IsInTransaction) + { + dbTransaction.StartTransaction(); + } + transaction.DbTransaction = dbTransaction; transaction.AutoCommit = autoCommit; From 9424cfd719983e228a8a447e0403f046bb8bf4b1 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Fri, 17 Aug 2018 18:22:57 +0800 Subject: [PATCH 176/223] Instead of manually configuring app.UseCAP using StartupFilter injection --- .../CAP.AppBuilderExtensions.cs | 16 ++++++++++- .../CAP.ServiceCollectionExtensions.cs | 27 ++++++++++--------- 2 files changed, 30 insertions(+), 13 deletions(-) diff --git a/src/DotNetCore.CAP/CAP.AppBuilderExtensions.cs b/src/DotNetCore.CAP/CAP.AppBuilderExtensions.cs index 2a8a586..53c048c 100644 --- a/src/DotNetCore.CAP/CAP.AppBuilderExtensions.cs +++ b/src/DotNetCore.CAP/CAP.AppBuilderExtensions.cs @@ -4,6 +4,7 @@ using System; using DotNetCore.CAP; using DotNetCore.CAP.Dashboard.GatewayProxy; +using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; // ReSharper disable once CheckNamespace @@ -12,7 +13,7 @@ namespace Microsoft.AspNetCore.Builder /// /// app extensions for /// - public static class AppBuilderExtensions + internal static class AppBuilderExtensions { /// /// Enables cap for the current application @@ -70,4 +71,17 @@ namespace Microsoft.AspNetCore.Builder } } } + + sealed class CapStartupFilter : IStartupFilter + { + public Action Configure(Action next) + { + return app => + { + app.UseCap(); + + next(app); + }; + } + } } \ No newline at end of file diff --git a/src/DotNetCore.CAP/CAP.ServiceCollectionExtensions.cs b/src/DotNetCore.CAP/CAP.ServiceCollectionExtensions.cs index 23b585b..f346693 100644 --- a/src/DotNetCore.CAP/CAP.ServiceCollectionExtensions.cs +++ b/src/DotNetCore.CAP/CAP.ServiceCollectionExtensions.cs @@ -8,6 +8,8 @@ using DotNetCore.CAP.Abstractions; using DotNetCore.CAP.Internal; using DotNetCore.CAP.Processor; using DotNetCore.CAP.Processor.States; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection.Extensions; // ReSharper disable once CheckNamespace @@ -24,9 +26,7 @@ namespace Microsoft.Extensions.DependencyInjection /// The services available in the application. /// An action to configure the . /// An for application services. - public static CapBuilder AddCap( - this IServiceCollection services, - Action setupAction) + public static CapBuilder AddCap(this IServiceCollection services, Action setupAction) { if (setupAction == null) { @@ -36,6 +36,7 @@ namespace Microsoft.Extensions.DependencyInjection services.TryAddSingleton(); services.Configure(setupAction); + //Consumer service AddSubscribeServices(services); //Serializer and model binder @@ -49,18 +50,18 @@ namespace Microsoft.Extensions.DependencyInjection services.TryAddSingleton(); //Bootstrapper and Processors - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); + services.TryAddEnumerable(ServiceDescriptor.Singleton()); + services.TryAddEnumerable(ServiceDescriptor.Singleton()); + services.TryAddSingleton(); + services.TryAddSingleton(); //Queue's message processor - services.AddTransient(); + services.TryAddSingleton(); //Sender and Executors - services.AddSingleton(); + services.TryAddSingleton(); // Warning: IPublishMessageSender need to inject at extension project. - services.AddSingleton(); + services.TryAddSingleton(); //Options and extension service var options = new CapOptions(); @@ -69,9 +70,11 @@ namespace Microsoft.Extensions.DependencyInjection { serviceExtension.AddServices(services); } - services.AddSingleton(options); + //Startup and Middleware + services.AddTransient(); + return new CapBuilder(services); } @@ -90,7 +93,7 @@ namespace Microsoft.Extensions.DependencyInjection foreach (var service in consumerListenerServices) { - services.AddTransient(service.Key, service.Value); + services.TryAddEnumerable(ServiceDescriptor.Transient(service.Key, service.Value)); } } } From 5fe7e5b226aecb358c4a2ff7647bc96005e654a0 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Sat, 18 Aug 2018 09:58:58 +0800 Subject: [PATCH 177/223] Rename the file to conform to the specification --- src/DotNetCore.CAP.MySql/CAP.MySqlCapOptionsExtension.cs | 4 ++-- .../{CapPublisher.cs => ICapPublisher.MySql.cs} | 6 +++--- .../{MySqlStorage.cs => IStorage.MySql.cs} | 0 ...ySqlStorageConnection.cs => IStorageConnection.MySql.cs} | 0 ...qlStorageTransaction.cs => IStorageTransaction.MySql.cs} | 0 5 files changed, 5 insertions(+), 5 deletions(-) rename src/DotNetCore.CAP.MySql/{CapPublisher.cs => ICapPublisher.MySql.cs} (91%) rename src/DotNetCore.CAP.MySql/{MySqlStorage.cs => IStorage.MySql.cs} (100%) rename src/DotNetCore.CAP.MySql/{MySqlStorageConnection.cs => IStorageConnection.MySql.cs} (100%) rename src/DotNetCore.CAP.MySql/{MySqlStorageTransaction.cs => IStorageTransaction.MySql.cs} (100%) diff --git a/src/DotNetCore.CAP.MySql/CAP.MySqlCapOptionsExtension.cs b/src/DotNetCore.CAP.MySql/CAP.MySqlCapOptionsExtension.cs index 7ba01c5..9319ca1 100644 --- a/src/DotNetCore.CAP.MySql/CAP.MySqlCapOptionsExtension.cs +++ b/src/DotNetCore.CAP.MySql/CAP.MySqlCapOptionsExtension.cs @@ -25,8 +25,8 @@ namespace DotNetCore.CAP services.AddSingleton(); services.AddSingleton(); - services.AddScoped(); - services.AddScoped(); + services.AddScoped(); + services.AddScoped(); services.AddTransient(); services.AddTransient(); diff --git a/src/DotNetCore.CAP.MySql/CapPublisher.cs b/src/DotNetCore.CAP.MySql/ICapPublisher.MySql.cs similarity index 91% rename from src/DotNetCore.CAP.MySql/CapPublisher.cs rename to src/DotNetCore.CAP.MySql/ICapPublisher.MySql.cs index 8ce9b8d..fe83676 100644 --- a/src/DotNetCore.CAP.MySql/CapPublisher.cs +++ b/src/DotNetCore.CAP.MySql/ICapPublisher.MySql.cs @@ -14,7 +14,7 @@ using MySql.Data.MySqlClient; namespace DotNetCore.CAP.MySql { - public class CapPublisher : CapPublisherBase, ICallbackPublisher, IDisposable + public class MySqlPublisher : CapPublisherBase, ICallbackPublisher, IDisposable { private readonly DbContext _dbContext; private readonly MySqlOptions _options; @@ -22,7 +22,7 @@ namespace DotNetCore.CAP.MySql private MySqlConnection _connection; - public CapPublisher(IServiceProvider provider, MySqlOptions options) : base(provider) + public MySqlPublisher(IServiceProvider provider, MySqlOptions options) : base(provider) { _options = options; @@ -49,7 +49,7 @@ namespace DotNetCore.CAP.MySql dbTrans = dbContextTrans.GetDbTransaction(); } var conn = dbTrans?.Connection; - return conn.ExecuteScalarAsync(PrepareSql(), message, dbTrans); + return conn.ExecuteAsync(PrepareSql(), message, dbTrans); } protected override object GetDbTransaction() diff --git a/src/DotNetCore.CAP.MySql/MySqlStorage.cs b/src/DotNetCore.CAP.MySql/IStorage.MySql.cs similarity index 100% rename from src/DotNetCore.CAP.MySql/MySqlStorage.cs rename to src/DotNetCore.CAP.MySql/IStorage.MySql.cs diff --git a/src/DotNetCore.CAP.MySql/MySqlStorageConnection.cs b/src/DotNetCore.CAP.MySql/IStorageConnection.MySql.cs similarity index 100% rename from src/DotNetCore.CAP.MySql/MySqlStorageConnection.cs rename to src/DotNetCore.CAP.MySql/IStorageConnection.MySql.cs diff --git a/src/DotNetCore.CAP.MySql/MySqlStorageTransaction.cs b/src/DotNetCore.CAP.MySql/IStorageTransaction.MySql.cs similarity index 100% rename from src/DotNetCore.CAP.MySql/MySqlStorageTransaction.cs rename to src/DotNetCore.CAP.MySql/IStorageTransaction.MySql.cs From c69d1f6f989393984a58dce4687b4cc9bb01ed90 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Sat, 18 Aug 2018 10:20:06 +0800 Subject: [PATCH 178/223] Refactor postgresql module for new transaction mode --- .../CAP.PostgreSqlCapOptionsExtension.cs | 7 +- .../CAP.PostgreSqlOptions.cs | 1 + src/DotNetCore.CAP.PostgreSql/CapPublisher.cs | 85 ----------------- .../ICapPublisher.PostgreSql.cs | 93 +++++++++++++++++++ .../ICapTransaction.PostgreSql.cs | 72 ++++++++++++++ ...ingApi.cs => IMonitoringApi.PostgreSql.cs} | 0 ...reSqlStorage.cs => IStorage.PostgreSql.cs} | 6 +- ...on.cs => IStorageConnection.PostgreSql.cs} | 14 +-- ...n.cs => IStorageTransaction.PostgreSql.cs} | 28 +----- .../PostgreSqlStorageConnectionTest.cs | 22 +++-- .../PostgreSqlStorageTest.cs | 2 - 11 files changed, 194 insertions(+), 136 deletions(-) delete mode 100644 src/DotNetCore.CAP.PostgreSql/CapPublisher.cs create mode 100644 src/DotNetCore.CAP.PostgreSql/ICapPublisher.PostgreSql.cs create mode 100644 src/DotNetCore.CAP.PostgreSql/ICapTransaction.PostgreSql.cs rename src/DotNetCore.CAP.PostgreSql/{PostgreSqlMonitoringApi.cs => IMonitoringApi.PostgreSql.cs} (100%) rename src/DotNetCore.CAP.PostgreSql/{PostgreSqlStorage.cs => IStorage.PostgreSql.cs} (96%) rename src/DotNetCore.CAP.PostgreSql/{PostgreSqlStorageConnection.cs => IStorageConnection.PostgreSql.cs} (89%) rename src/DotNetCore.CAP.PostgreSql/{PostgreSqlStorageTransaction.cs => IStorageTransaction.PostgreSql.cs} (64%) diff --git a/src/DotNetCore.CAP.PostgreSql/CAP.PostgreSqlCapOptionsExtension.cs b/src/DotNetCore.CAP.PostgreSql/CAP.PostgreSqlCapOptionsExtension.cs index edc1729..dc08d52 100644 --- a/src/DotNetCore.CAP.PostgreSql/CAP.PostgreSqlCapOptionsExtension.cs +++ b/src/DotNetCore.CAP.PostgreSql/CAP.PostgreSqlCapOptionsExtension.cs @@ -24,9 +24,12 @@ namespace DotNetCore.CAP services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); - services.AddScoped(); - services.AddScoped(); + + services.AddScoped(); + services.AddScoped(); + services.AddTransient(); + services.AddTransient(); AddSingletonPostgreSqlOptions(services); } diff --git a/src/DotNetCore.CAP.PostgreSql/CAP.PostgreSqlOptions.cs b/src/DotNetCore.CAP.PostgreSql/CAP.PostgreSqlOptions.cs index 9f52154..8932f3d 100644 --- a/src/DotNetCore.CAP.PostgreSql/CAP.PostgreSqlOptions.cs +++ b/src/DotNetCore.CAP.PostgreSql/CAP.PostgreSqlOptions.cs @@ -1,6 +1,7 @@ // Copyright (c) .NET Core Community. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. +// ReSharper disable once CheckNamespace namespace DotNetCore.CAP { public class PostgreSqlOptions : EFOptions diff --git a/src/DotNetCore.CAP.PostgreSql/CapPublisher.cs b/src/DotNetCore.CAP.PostgreSql/CapPublisher.cs deleted file mode 100644 index e299dc4..0000000 --- a/src/DotNetCore.CAP.PostgreSql/CapPublisher.cs +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright (c) .NET Core Community. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. - -using System; -using System.Data; -using System.Threading.Tasks; -using Dapper; -using DotNetCore.CAP.Abstractions; -using DotNetCore.CAP.Models; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Storage; -using Microsoft.Extensions.Logging; -using Npgsql; - -namespace DotNetCore.CAP.PostgreSql -{ - public class CapPublisher : CapPublisherBase, ICallbackPublisher - { - private readonly DbContext _dbContext; - private readonly PostgreSqlOptions _options; - - public CapPublisher(ILogger logger, IDispatcher dispatcher, - IServiceProvider provider, PostgreSqlOptions options) - : base(logger, dispatcher) - { - ServiceProvider = provider; - _options = options; - - if (_options.DbContextType != null) - { - IsUsingEF = true; - _dbContext = (DbContext) ServiceProvider.GetService(_options.DbContextType); - } - } - - public async Task PublishCallbackAsync(CapPublishedMessage message) - { - using (var conn = new NpgsqlConnection(_options.ConnectionString)) - { - var id = await conn.ExecuteScalarAsync(PrepareSql(), message); - message.Id = id; - Enqueue(message); - } - } - - protected override void PrepareConnectionForEF() - { - DbConnection = _dbContext.Database.GetDbConnection(); - var dbContextTransaction = _dbContext.Database.CurrentTransaction; - var dbTrans = dbContextTransaction?.GetDbTransaction(); - //DbTransaction is dispose in original - if (dbTrans?.Connection == null) - { - IsCapOpenedTrans = true; - dbContextTransaction?.Dispose(); - dbContextTransaction = _dbContext.Database.BeginTransaction(IsolationLevel.ReadCommitted); - dbTrans = dbContextTransaction.GetDbTransaction(); - } - - DbTransaction = dbTrans; - } - - protected override int Execute(IDbConnection dbConnection, IDbTransaction dbTransaction, - CapPublishedMessage message) - { - return dbConnection.ExecuteScalar(PrepareSql(), message, dbTransaction); - } - - protected override Task ExecuteAsync(IDbConnection dbConnection, IDbTransaction dbTransaction, - CapPublishedMessage message) - { - return dbConnection.ExecuteScalarAsync(PrepareSql(), message, dbTransaction); - } - - #region private methods - - private string PrepareSql() - { - return - $"INSERT INTO \"{_options.Schema}\".\"published\" (\"Name\",\"Content\",\"Retries\",\"Added\",\"ExpiresAt\",\"StatusName\")VALUES(@Name,@Content,@Retries,@Added,@ExpiresAt,@StatusName) RETURNING \"Id\";"; - } - - #endregion private methods - } -} \ No newline at end of file diff --git a/src/DotNetCore.CAP.PostgreSql/ICapPublisher.PostgreSql.cs b/src/DotNetCore.CAP.PostgreSql/ICapPublisher.PostgreSql.cs new file mode 100644 index 0000000..7222efd --- /dev/null +++ b/src/DotNetCore.CAP.PostgreSql/ICapPublisher.PostgreSql.cs @@ -0,0 +1,93 @@ +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; +using System.Data; +using System.Threading; +using System.Threading.Tasks; +using Dapper; +using DotNetCore.CAP.Abstractions; +using DotNetCore.CAP.Models; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Storage; +using Npgsql; + +namespace DotNetCore.CAP.PostgreSql +{ + public class PostgreSqlPublisher : CapPublisherBase, ICallbackPublisher + { + private readonly DbContext _dbContext; + private readonly PostgreSqlOptions _options; + private readonly bool _isUsingEF; + + private NpgsqlConnection _connection; + + public PostgreSqlPublisher(IServiceProvider provider, PostgreSqlOptions options): base(provider) + { + _options = options; + + if (_options.DbContextType == null) + { + return; + } + + _isUsingEF = true; + _dbContext = (DbContext)ServiceProvider.GetService(_options.DbContextType); + } + + public async Task PublishCallbackAsync(CapPublishedMessage message) + { + await PublishAsyncInternal(message); + } + + protected override Task ExecuteAsync(CapPublishedMessage message, ICapTransaction transaction, + CancellationToken cancel = default(CancellationToken)) + { + var dbTrans = transaction.DbTransaction as IDbTransaction; + if (dbTrans == null && transaction.DbTransaction is IDbContextTransaction dbContextTrans) + { + dbTrans = dbContextTrans.GetDbTransaction(); + } + var conn = dbTrans?.Connection; + return conn.ExecuteAsync(PrepareSql(), message, dbTrans); + } + + protected override object GetDbTransaction() + { + if (_isUsingEF) + { + var dbContextTransaction = _dbContext.Database.CurrentTransaction; + if (dbContextTransaction == null) + { + return InitDbConnection(); + } + + return dbContextTransaction; + } + + return InitDbConnection(); + } + + #region private methods + + private string PrepareSql() + { + return + $"INSERT INTO \"{_options.Schema}\".\"published\" (\"Id\",\"Name\",\"Content\",\"Retries\",\"Added\",\"ExpiresAt\",\"StatusName\")VALUES(@Id,@Name,@Content,@Retries,@Added,@ExpiresAt,@StatusName);"; + } + + private IDbTransaction InitDbConnection() + { + _connection = new NpgsqlConnection(_options.ConnectionString); + _connection.Open(); + return _connection.BeginTransaction(IsolationLevel.ReadCommitted); + } + #endregion private methods + + public void Dispose() + { + _dbContext?.Dispose(); + _connection?.Dispose(); + } + } +} \ No newline at end of file diff --git a/src/DotNetCore.CAP.PostgreSql/ICapTransaction.PostgreSql.cs b/src/DotNetCore.CAP.PostgreSql/ICapTransaction.PostgreSql.cs new file mode 100644 index 0000000..7599e39 --- /dev/null +++ b/src/DotNetCore.CAP.PostgreSql/ICapTransaction.PostgreSql.cs @@ -0,0 +1,72 @@ +using System.Data; +using System.Diagnostics; +using Microsoft.EntityFrameworkCore.Storage; + +// ReSharper disable once CheckNamespace +namespace DotNetCore.CAP +{ + public class PostgreSqlCapTransaction : CapTransactionBase + { + public PostgreSqlCapTransaction(IDispatcher dispatcher) : base(dispatcher) { } + + public override void Commit() + { + Debug.Assert(DbTransaction != null); + + switch (DbTransaction) + { + case IDbTransaction dbTransaction: + dbTransaction.Commit(); + break; + case IDbContextTransaction dbContextTransaction: + dbContextTransaction.Commit(); + break; + } + + Flush(); + } + + public override void Rollback() + { + Debug.Assert(DbTransaction != null); + + switch (DbTransaction) + { + case IDbTransaction dbTransaction: + dbTransaction.Rollback(); + break; + case IDbContextTransaction dbContextTransaction: + dbContextTransaction.Rollback(); + break; + } + } + + public override void Dispose() + { + (DbTransaction as IDbTransaction)?.Dispose(); + } + } + + public static class CapTransactionExtensions + { + public static ICapTransaction Begin(this ICapTransaction transaction, + IDbTransaction dbTransaction, bool autoCommit = false) + { + transaction.DbTransaction = dbTransaction; + transaction.AutoCommit = autoCommit; + + return transaction; + } + + public static ICapTransaction Begin(this ICapTransaction transaction, + IDbContextTransaction dbTransaction, bool autoCommit = false) + { + + transaction.DbTransaction = dbTransaction; + transaction.AutoCommit = autoCommit; + + return transaction; + } + } + +} diff --git a/src/DotNetCore.CAP.PostgreSql/PostgreSqlMonitoringApi.cs b/src/DotNetCore.CAP.PostgreSql/IMonitoringApi.PostgreSql.cs similarity index 100% rename from src/DotNetCore.CAP.PostgreSql/PostgreSqlMonitoringApi.cs rename to src/DotNetCore.CAP.PostgreSql/IMonitoringApi.PostgreSql.cs diff --git a/src/DotNetCore.CAP.PostgreSql/PostgreSqlStorage.cs b/src/DotNetCore.CAP.PostgreSql/IStorage.PostgreSql.cs similarity index 96% rename from src/DotNetCore.CAP.PostgreSql/PostgreSqlStorage.cs rename to src/DotNetCore.CAP.PostgreSql/IStorage.PostgreSql.cs index 34c8038..baa08e7 100644 --- a/src/DotNetCore.CAP.PostgreSql/PostgreSqlStorage.cs +++ b/src/DotNetCore.CAP.PostgreSql/IStorage.PostgreSql.cs @@ -100,10 +100,8 @@ namespace DotNetCore.CAP.PostgreSql var batchSql = $@" CREATE SCHEMA IF NOT EXISTS ""{schema}""; -DROP TABLE IF EXISTS ""{schema}"".""queue""; - CREATE TABLE IF NOT EXISTS ""{schema}"".""received""( - ""Id"" SERIAL PRIMARY KEY NOT NULL, + ""Id"" BIGINT PRIMARY KEY NOT NULL, ""Name"" VARCHAR(200) NOT NULL, ""Group"" VARCHAR(200) NULL, ""Content"" TEXT NULL, @@ -114,7 +112,7 @@ CREATE TABLE IF NOT EXISTS ""{schema}"".""received""( ); CREATE TABLE IF NOT EXISTS ""{schema}"".""published""( - ""Id"" SERIAL PRIMARY KEY NOT NULL, + ""Id"" BIGINT PRIMARY KEY NOT NULL, ""Name"" VARCHAR(200) NOT NULL, ""Content"" TEXT NULL, ""Retries"" INT NOT NULL, diff --git a/src/DotNetCore.CAP.PostgreSql/PostgreSqlStorageConnection.cs b/src/DotNetCore.CAP.PostgreSql/IStorageConnection.PostgreSql.cs similarity index 89% rename from src/DotNetCore.CAP.PostgreSql/PostgreSqlStorageConnection.cs rename to src/DotNetCore.CAP.PostgreSql/IStorageConnection.PostgreSql.cs index 3d8fba7..c5c8146 100644 --- a/src/DotNetCore.CAP.PostgreSql/PostgreSqlStorageConnection.cs +++ b/src/DotNetCore.CAP.PostgreSql/IStorageConnection.PostgreSql.cs @@ -28,7 +28,7 @@ namespace DotNetCore.CAP.PostgreSql return new PostgreSqlStorageTransaction(this); } - public async Task GetPublishedMessageAsync(int id) + public async Task GetPublishedMessageAsync(long id) { var sql = $"SELECT * FROM \"{Options.Schema}\".\"published\" WHERE \"Id\"={id} FOR UPDATE SKIP LOCKED"; @@ -50,7 +50,7 @@ namespace DotNetCore.CAP.PostgreSql } } - public async Task StoreReceivedMessageAsync(CapReceivedMessage message) + public void StoreReceivedMessage(CapReceivedMessage message) { if (message == null) { @@ -58,15 +58,15 @@ namespace DotNetCore.CAP.PostgreSql } var sql = - $"INSERT INTO \"{Options.Schema}\".\"received\"(\"Name\",\"Group\",\"Content\",\"Retries\",\"Added\",\"ExpiresAt\",\"StatusName\")VALUES(@Name,@Group,@Content,@Retries,@Added,@ExpiresAt,@StatusName) RETURNING \"Id\";"; + $"INSERT INTO \"{Options.Schema}\".\"received\"(\"Id\",\"Name\",\"Group\",\"Content\",\"Retries\",\"Added\",\"ExpiresAt\",\"StatusName\")VALUES(@Id,@Name,@Group,@Content,@Retries,@Added,@ExpiresAt,@StatusName) RETURNING \"Id\";"; using (var connection = new NpgsqlConnection(Options.ConnectionString)) { - return await connection.ExecuteScalarAsync(sql, message); + connection.Execute(sql, message); } } - public async Task GetReceivedMessageAsync(int id) + public async Task GetReceivedMessageAsync(long id) { var sql = $"SELECT * FROM \"{Options.Schema}\".\"received\" WHERE \"Id\"={id} FOR UPDATE SKIP LOCKED"; using (var connection = new NpgsqlConnection(Options.ConnectionString)) @@ -90,7 +90,7 @@ namespace DotNetCore.CAP.PostgreSql { } - public bool ChangePublishedState(int messageId, string state) + public bool ChangePublishedState(long messageId, string state) { var sql = $"UPDATE \"{Options.Schema}\".\"published\" SET \"Retries\"=\"Retries\"+1,\"ExpiresAt\"=NULL,\"StatusName\" = '{state}' WHERE \"Id\"={messageId}"; @@ -101,7 +101,7 @@ namespace DotNetCore.CAP.PostgreSql } } - public bool ChangeReceivedState(int messageId, string state) + public bool ChangeReceivedState(long messageId, string state) { var sql = $"UPDATE \"{Options.Schema}\".\"received\" SET \"Retries\"=\"Retries\"+1,\"ExpiresAt\"=NULL,\"StatusName\" = '{state}' WHERE \"Id\"={messageId}"; diff --git a/src/DotNetCore.CAP.PostgreSql/PostgreSqlStorageTransaction.cs b/src/DotNetCore.CAP.PostgreSql/IStorageTransaction.PostgreSql.cs similarity index 64% rename from src/DotNetCore.CAP.PostgreSql/PostgreSqlStorageTransaction.cs rename to src/DotNetCore.CAP.PostgreSql/IStorageTransaction.PostgreSql.cs index d64c46a..64dd30c 100644 --- a/src/DotNetCore.CAP.PostgreSql/PostgreSqlStorageTransaction.cs +++ b/src/DotNetCore.CAP.PostgreSql/IStorageTransaction.PostgreSql.cs @@ -35,9 +35,7 @@ namespace DotNetCore.CAP.PostgreSql } var sql = - $@"UPDATE ""{ - _schema - }"".""published"" SET ""Retries""=@Retries,""Content""= @Content,""ExpiresAt""=@ExpiresAt,""StatusName""=@StatusName WHERE ""Id""=@Id;"; + $@"UPDATE ""{_schema}"".""published"" SET ""Retries""=@Retries,""Content""= @Content,""ExpiresAt""=@ExpiresAt,""StatusName""=@StatusName WHERE ""Id""=@Id;"; _dbConnection.Execute(sql, message, _dbTransaction); } @@ -66,29 +64,5 @@ namespace DotNetCore.CAP.PostgreSql _dbTransaction.Dispose(); _dbConnection.Dispose(); } - - public void EnqueueMessage(CapPublishedMessage message) - { - if (message == null) - { - throw new ArgumentNullException(nameof(message)); - } - - var sql = $@"INSERT INTO ""{_schema}"".""queue"" values(@MessageId,@MessageType);"; - _dbConnection.Execute(sql, new CapQueue {MessageId = message.Id, MessageType = MessageType.Publish}, - _dbTransaction); - } - - public void EnqueueMessage(CapReceivedMessage message) - { - if (message == null) - { - throw new ArgumentNullException(nameof(message)); - } - - var sql = $@"INSERT INTO ""{_schema}"".""queue"" values(@MessageId,@MessageType);"; - _dbConnection.Execute(sql, new CapQueue {MessageId = message.Id, MessageType = MessageType.Subscribe}, - _dbTransaction); - } } } \ No newline at end of file diff --git a/test/DotNetCore.CAP.PostgreSql.Test/PostgreSqlStorageConnectionTest.cs b/test/DotNetCore.CAP.PostgreSql.Test/PostgreSqlStorageConnectionTest.cs index b1cb25c..98734ba 100644 --- a/test/DotNetCore.CAP.PostgreSql.Test/PostgreSqlStorageConnectionTest.cs +++ b/test/DotNetCore.CAP.PostgreSql.Test/PostgreSqlStorageConnectionTest.cs @@ -22,17 +22,18 @@ namespace DotNetCore.CAP.PostgreSql.Test [Fact] public async Task GetPublishedMessageAsync_Test() { - var sql = @"INSERT INTO ""cap"".""published""(""Name"",""Content"",""Retries"",""Added"",""ExpiresAt"",""StatusName"") VALUES(@Name,@Content,@Retries,@Added,@ExpiresAt,@StatusName) RETURNING ""Id"";"; + var sql = @"INSERT INTO ""cap"".""published""(""Id"",""Name"",""Content"",""Retries"",""Added"",""ExpiresAt"",""StatusName"") VALUES(@Id,@Name,@Content,@Retries,@Added,@ExpiresAt,@StatusName);"; + var insertedId = SnowflakeId.Default().NextId(); var publishMessage = new CapPublishedMessage { + Id = insertedId, Name = "PostgreSqlStorageConnectionTest", Content = "", StatusName = StatusName.Scheduled }; - var insertedId = default(int); using (var connection = ConnectionUtil.CreateConnection()) { - insertedId = connection.QueryFirst(sql, publishMessage); + await connection.ExecuteAsync(sql, publishMessage); } var message = await _storage.GetPublishedMessageAsync(insertedId); Assert.NotNull(message); @@ -41,10 +42,11 @@ namespace DotNetCore.CAP.PostgreSql.Test } [Fact] - public async Task StoreReceivedMessageAsync_Test() + public void StoreReceivedMessageAsync_Test() { var receivedMessage = new CapReceivedMessage { + Id = SnowflakeId.Default().NextId(), Name = "PostgreSqlStorageConnectionTest", Content = "", Group = "mygroup", @@ -54,7 +56,7 @@ namespace DotNetCore.CAP.PostgreSql.Test Exception exception = null; try { - await _storage.StoreReceivedMessageAsync(receivedMessage); + _storage.StoreReceivedMessage(receivedMessage); } catch (Exception ex) { @@ -67,19 +69,21 @@ namespace DotNetCore.CAP.PostgreSql.Test public async Task GetReceivedMessageAsync_Test() { var sql = $@" - INSERT INTO ""cap"".""received""(""Name"",""Group"",""Content"",""Retries"",""Added"",""ExpiresAt"",""StatusName"") - VALUES(@Name,@Group,@Content,@Retries,@Added,@ExpiresAt,@StatusName) RETURNING ""Id"";"; + INSERT INTO ""cap"".""received""(""Id"",""Name"",""Group"",""Content"",""Retries"",""Added"",""ExpiresAt"",""StatusName"") + VALUES(@Id,@Name,@Group,@Content,@Retries,@Added,@ExpiresAt,@StatusName);"; + var insertedId = SnowflakeId.Default().NextId(); var receivedMessage = new CapReceivedMessage { + Id= insertedId, Name = "PostgreSqlStorageConnectionTest", Content = "", Group = "mygroup", StatusName = StatusName.Scheduled }; - var insertedId = default(int); + using (var connection = ConnectionUtil.CreateConnection()) { - insertedId = connection.QueryFirst(sql, receivedMessage); + await connection.ExecuteAsync(sql, receivedMessage); } var message = await _storage.GetReceivedMessageAsync(insertedId); diff --git a/test/DotNetCore.CAP.PostgreSql.Test/PostgreSqlStorageTest.cs b/test/DotNetCore.CAP.PostgreSql.Test/PostgreSqlStorageTest.cs index ec0bc2b..4bf1d77 100644 --- a/test/DotNetCore.CAP.PostgreSql.Test/PostgreSqlStorageTest.cs +++ b/test/DotNetCore.CAP.PostgreSql.Test/PostgreSqlStorageTest.cs @@ -6,13 +6,11 @@ namespace DotNetCore.CAP.PostgreSql.Test [Collection("postgresql")] public class SqlServerStorageTest : DatabaseTestHost { - private readonly string _dbName; private readonly string _masterDbConnectionString; private readonly string _dbConnectionString; public SqlServerStorageTest() { - _dbName = ConnectionUtil.GetDatabaseName(); _masterDbConnectionString = ConnectionUtil.GetMasterConnectionString(); _dbConnectionString = ConnectionUtil.GetConnectionString(); } From 205e3b3e534fc542c55eba67b1f926ff94a6ebe4 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Sat, 18 Aug 2018 10:22:50 +0800 Subject: [PATCH 179/223] Refactor mongodb module for new transaction mode --- .../Controllers/ValuesController.cs | 13 ++++---- samples/Sample.RabbitMQ.MongoDB/Startup.cs | 23 ++++---------- .../MongoDBMonitoringApiTest.cs | 5 ++-- .../MongoDBStorageConnectionTest.cs | 18 +++++++---- .../MongoDBStorageTest.cs | 8 ----- .../MongoDBUtilTest.cs | 30 ------------------- 6 files changed, 27 insertions(+), 70 deletions(-) delete mode 100644 test/DotNetCore.CAP.MongoDB.Test/MongoDBUtilTest.cs diff --git a/samples/Sample.RabbitMQ.MongoDB/Controllers/ValuesController.cs b/samples/Sample.RabbitMQ.MongoDB/Controllers/ValuesController.cs index bf202ad..3eed63c 100644 --- a/samples/Sample.RabbitMQ.MongoDB/Controllers/ValuesController.cs +++ b/samples/Sample.RabbitMQ.MongoDB/Controllers/ValuesController.cs @@ -22,10 +22,13 @@ namespace Sample.RabbitMQ.MongoDB.Controllers [Route("~/publish")] public IActionResult PublishWithTrans() { + //var mycollection = _client.GetDatabase("test").GetCollection("test.collection"); + //mycollection.InsertOne(new BsonDocument { { "test", "test" } }); + using (var session = _client.StartSession()) using (var trans = _capPublisher.CapTransaction.Begin(session)) { - var collection = _client.GetDatabase("TEST").GetCollection("test"); + var collection = _client.GetDatabase("test").GetCollection("test.collection"); collection.InsertOne(session, new BsonDocument { { "hello", "world" } }); _capPublisher.Publish("sample.rabbitmq.mongodb", DateTime.Now); @@ -39,17 +42,17 @@ namespace Sample.RabbitMQ.MongoDB.Controllers public IActionResult PublishNotAutoCommit() { using (var session = _client.StartSession()) - using (_capPublisher.CapTransaction.Begin(session,true)) + using (_capPublisher.CapTransaction.Begin(session, true)) { - var collection = _client.GetDatabase("TEST").GetCollection("test"); - collection.InsertOne(session, new BsonDocument { { "hello", "world" } }); + var collection = _client.GetDatabase("test").GetCollection("test.collection"); + collection.InsertOne(session, new BsonDocument { { "hello2", "world2" } }); _capPublisher.Publish("sample.rabbitmq.mongodb", DateTime.Now); } return Ok(); } - + [Route("~/publish/without/trans")] public IActionResult PublishWithoutTrans() { diff --git a/samples/Sample.RabbitMQ.MongoDB/Startup.cs b/samples/Sample.RabbitMQ.MongoDB/Startup.cs index 637285d..2ef0da2 100644 --- a/samples/Sample.RabbitMQ.MongoDB/Startup.cs +++ b/samples/Sample.RabbitMQ.MongoDB/Startup.cs @@ -1,5 +1,4 @@ -using DotNetCore.CAP; -using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Configuration; @@ -19,21 +18,11 @@ namespace Sample.RabbitMQ.MongoDB public void ConfigureServices(IServiceCollection services) { - services.AddSingleton(new MongoClient(Configuration.GetConnectionString("MongoDB"))); + services.AddSingleton(new MongoClient("mongodb://192.168.10.110:27017,192.168.10.110:27018,192.168.10.110:27019/?replicaSet=rs0")); services.AddCap(x => { - x.UseMongoDB(); - - var mq = new RabbitMQOptions(); - Configuration.GetSection("RabbitMQ").Bind(mq); - x.UseRabbitMQ(cfg => - { - cfg.HostName = mq.HostName; - cfg.Port = mq.Port; - cfg.UserName = mq.UserName; - cfg.Password = mq.Password; - }); - + x.UseMongoDB("mongodb://192.168.10.110:27017,192.168.10.110:27018,192.168.10.110:27019/?replicaSet=rs0"); + x.UseRabbitMQ("localhost"); x.UseDashboard(); }); services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); @@ -46,9 +35,7 @@ namespace Sample.RabbitMQ.MongoDB app.UseDeveloperExceptionPage(); } - app.UseMvc(); - - app.UseCap(); + app.UseMvc(); } } } diff --git a/test/DotNetCore.CAP.MongoDB.Test/MongoDBMonitoringApiTest.cs b/test/DotNetCore.CAP.MongoDB.Test/MongoDBMonitoringApiTest.cs index 2592b43..cf5ed73 100644 --- a/test/DotNetCore.CAP.MongoDB.Test/MongoDBMonitoringApiTest.cs +++ b/test/DotNetCore.CAP.MongoDB.Test/MongoDBMonitoringApiTest.cs @@ -17,20 +17,19 @@ namespace DotNetCore.CAP.MongoDB.Test { _api = new MongoDBMonitoringApi(MongoClient, MongoDBOptions); - var helper = new MongoDBUtil(); var collection = Database.GetCollection(MongoDBOptions.PublishedCollection); collection.InsertMany(new[] { new CapPublishedMessage { - Id = helper.GetNextSequenceValue(Database,MongoDBOptions.PublishedCollection), + Id = SnowflakeId.Default().NextId(), Added = DateTime.Now.AddHours(-1), StatusName = "Failed", Content = "abc" }, new CapPublishedMessage { - Id = helper.GetNextSequenceValue(Database,MongoDBOptions.PublishedCollection), + Id = SnowflakeId.Default().NextId(), Added = DateTime.Now, StatusName = "Failed", Content = "bbc" diff --git a/test/DotNetCore.CAP.MongoDB.Test/MongoDBStorageConnectionTest.cs b/test/DotNetCore.CAP.MongoDB.Test/MongoDBStorageConnectionTest.cs index 2b326af..73fa5ba 100644 --- a/test/DotNetCore.CAP.MongoDB.Test/MongoDBStorageConnectionTest.cs +++ b/test/DotNetCore.CAP.MongoDB.Test/MongoDBStorageConnectionTest.cs @@ -11,20 +11,23 @@ namespace DotNetCore.CAP.MongoDB.Test [Collection("MongoDB")] public class MongoDBStorageConnectionTest : DatabaseTestHost { - private IStorageConnection _connection => + private IStorageConnection _connection => Provider.GetService().GetConnection(); [Fact] - public async void StoreReceivedMessageAsync_TestAsync() + public void StoreReceivedMessageAsync_TestAsync() { - var id = await _connection.StoreReceivedMessageAsync(new CapReceivedMessage(new MessageContext + var messageContext = new MessageContext { Group = "test", Name = "test", Content = "test-content" - })); + }; - id.Should().BeGreaterThan(0); + _connection.StoreReceivedMessage(new CapReceivedMessage(messageContext) + { + Id = SnowflakeId.Default().NextId() + }); } [Fact] @@ -45,14 +48,17 @@ namespace DotNetCore.CAP.MongoDB.Test msgs.Should().BeEmpty(); + var id = SnowflakeId.Default().NextId(); + var msg = new CapReceivedMessage { + Id = id, Group = "test", Name = "test", Content = "test-content", StatusName = StatusName.Failed }; - var id = await _connection.StoreReceivedMessageAsync(msg); + _connection.StoreReceivedMessage(msg); var collection = Database.GetCollection(MongoDBOptions.ReceivedCollection); diff --git a/test/DotNetCore.CAP.MongoDB.Test/MongoDBStorageTest.cs b/test/DotNetCore.CAP.MongoDB.Test/MongoDBStorageTest.cs index 1a5f4de..f27a9c5 100644 --- a/test/DotNetCore.CAP.MongoDB.Test/MongoDBStorageTest.cs +++ b/test/DotNetCore.CAP.MongoDB.Test/MongoDBStorageTest.cs @@ -1,6 +1,4 @@ using FluentAssertions; -using Microsoft.Extensions.DependencyInjection; -using MongoDB.Bson; using MongoDB.Driver; using Xunit; @@ -12,18 +10,12 @@ namespace DotNetCore.CAP.MongoDB.Test [Fact] public void InitializeAsync_Test() { - var storage = Provider.GetService(); var names = MongoClient.ListDatabaseNames()?.ToList(); names.Should().Contain(MongoDBOptions.DatabaseName); var collections = Database.ListCollectionNames()?.ToList(); collections.Should().Contain(MongoDBOptions.PublishedCollection); collections.Should().Contain(MongoDBOptions.ReceivedCollection); - collections.Should().Contain(MongoDBOptions.CounterCollection); - - var collection = Database.GetCollection(MongoDBOptions.CounterCollection); - collection.CountDocuments(new BsonDocument { { "_id", MongoDBOptions.PublishedCollection } }).Should().Be(1); - collection.CountDocuments(new BsonDocument { { "_id", MongoDBOptions.ReceivedCollection } }).Should().Be(1); } } } \ No newline at end of file diff --git a/test/DotNetCore.CAP.MongoDB.Test/MongoDBUtilTest.cs b/test/DotNetCore.CAP.MongoDB.Test/MongoDBUtilTest.cs deleted file mode 100644 index 45695f9..0000000 --- a/test/DotNetCore.CAP.MongoDB.Test/MongoDBUtilTest.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System.Collections.Concurrent; -using System.Threading.Tasks; -using FluentAssertions; -using Xunit; - -namespace DotNetCore.CAP.MongoDB.Test -{ - [Collection("MongoDB")] - public class MongoDBUtilTest : DatabaseTestHost - { - [Fact] - public async void GetNextSequenceValueAsync_Test() - { - var id = await new MongoDBUtil().GetNextSequenceValueAsync(Database, MongoDBOptions.ReceivedCollection); - id.Should().BeGreaterThan(0); - } - - [Fact] - public void GetNextSequenceValue_Concurrency_Test() - { - var dic = new ConcurrentDictionary(); - Parallel.For(0, 30, (x) => - { - var id = new MongoDBUtil().GetNextSequenceValue(Database, MongoDBOptions.ReceivedCollection); - id.Should().BeGreaterThan(0); - dic.TryAdd(id, x).Should().BeTrue(); //The id shouldn't be same. - }); - } - } -} \ No newline at end of file From c0dd2854747e186b27611145278d98111e2964b1 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Sat, 18 Aug 2018 10:23:03 +0800 Subject: [PATCH 180/223] remove unused code --- src/DotNetCore.CAP/Models/CapQueue.cs | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 src/DotNetCore.CAP/Models/CapQueue.cs diff --git a/src/DotNetCore.CAP/Models/CapQueue.cs b/src/DotNetCore.CAP/Models/CapQueue.cs deleted file mode 100644 index 9a5b9f9..0000000 --- a/src/DotNetCore.CAP/Models/CapQueue.cs +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright (c) .NET Core Community. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. - -namespace DotNetCore.CAP.Models -{ - public class CapQueue - { - public int MessageId { get; set; } - - /// - /// 0 is CapSentMessage, 1 is CapReceivedMessage - /// - public MessageType MessageType { get; set; } - } -} \ No newline at end of file From 5a51a708b166b2e4d02ea86b27ad405aa11aa715 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Sat, 18 Aug 2018 10:23:21 +0800 Subject: [PATCH 181/223] tweak mysql unit tests. --- .../MySqlStorageConnectionTest.cs | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/test/DotNetCore.CAP.MySql.Test/MySqlStorageConnectionTest.cs b/test/DotNetCore.CAP.MySql.Test/MySqlStorageConnectionTest.cs index f9fae9d..512e9f1 100644 --- a/test/DotNetCore.CAP.MySql.Test/MySqlStorageConnectionTest.cs +++ b/test/DotNetCore.CAP.MySql.Test/MySqlStorageConnectionTest.cs @@ -22,18 +22,18 @@ namespace DotNetCore.CAP.MySql.Test [Fact] public async Task GetPublishedMessageAsync_Test() { - var sql = "INSERT INTO `cap.published`(`Name`,`Content`,`Retries`,`Added`,`ExpiresAt`,`StatusName`) VALUES(@Name,@Content,@Retries,@Added,@ExpiresAt,@StatusName);SELECT @@IDENTITY;"; + var sql = "INSERT INTO `cap.published`(`Name`,`Content`,`Retries`,`Added`,`ExpiresAt`,`StatusName`) VALUES(@Name,@Content,@Retries,@Added,@ExpiresAt,@StatusName);"; + var insertedId = SnowflakeId.Default().NextId(); var publishMessage = new CapPublishedMessage { - Id = SnowflakeId.Default().NextId(), + Id = insertedId, Name = "MySqlStorageConnectionTest", Content = "", StatusName = StatusName.Scheduled }; - var insertedId = default(int); using (var connection = ConnectionUtil.CreateConnection()) { - insertedId = connection.QueryFirst(sql, publishMessage); + await connection.ExecuteAsync(sql, publishMessage); } var message = await _storage.GetPublishedMessageAsync(insertedId); Assert.NotNull(message); @@ -42,7 +42,7 @@ namespace DotNetCore.CAP.MySql.Test } [Fact] - public async Task StoreReceivedMessageAsync_Test() + public void StoreReceivedMessageAsync_Test() { var receivedMessage = new CapReceivedMessage { @@ -56,7 +56,7 @@ namespace DotNetCore.CAP.MySql.Test Exception exception = null; try { - await _storage.StoreReceivedMessageAsync(receivedMessage); + _storage.StoreReceivedMessage(receivedMessage); } catch (Exception ex) { @@ -69,24 +69,23 @@ namespace DotNetCore.CAP.MySql.Test public async Task GetReceivedMessageAsync_Test() { var sql = $@" - INSERT INTO `cap.received`(`Name`,`Group`,`Content`,`Retries`,`Added`,`ExpiresAt`,`StatusName`) - VALUES(@Name,@Group,@Content,@Retries,@Added,@ExpiresAt,@StatusName);SELECT @@IDENTITY;"; + INSERT INTO `cap.received`(`Id`,`Name`,`Group`,`Content`,`Retries`,`Added`,`ExpiresAt`,`StatusName`) + VALUES(@Id,@Name,@Group,@Content,@Retries,@Added,@ExpiresAt,@StatusName);"; + var insertedId = SnowflakeId.Default().NextId(); var receivedMessage = new CapReceivedMessage { - Id = SnowflakeId.Default().NextId(), + Id = insertedId, Name = "MySqlStorageConnectionTest", Content = "", Group = "mygroup", StatusName = StatusName.Scheduled }; - var insertedId = default(int); using (var connection = ConnectionUtil.CreateConnection()) { - insertedId = connection.QueryFirst(sql, receivedMessage); + await connection.ExecuteAsync(sql, receivedMessage); } var message = await _storage.GetReceivedMessageAsync(insertedId); - Assert.NotNull(message); Assert.Equal(StatusName.Scheduled, message.StatusName); Assert.Equal("MySqlStorageConnectionTest", message.Name); From 53c5c74cb3ef1e11487539f28b006ba27f4a63dc Mon Sep 17 00:00:00 2001 From: Savorboard Date: Sat, 18 Aug 2018 10:23:32 +0800 Subject: [PATCH 182/223] update samples. --- samples/Sample.RabbitMQ.MySql/Startup.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/samples/Sample.RabbitMQ.MySql/Startup.cs b/samples/Sample.RabbitMQ.MySql/Startup.cs index 007e78e..b9f16e7 100644 --- a/samples/Sample.RabbitMQ.MySql/Startup.cs +++ b/samples/Sample.RabbitMQ.MySql/Startup.cs @@ -30,8 +30,6 @@ namespace Sample.RabbitMQ.MySql public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { app.UseMvc(); - - app.UseCap(); } } } From 0308c030b50e72bc85bbdb43971f051d4dda3570 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Sat, 18 Aug 2018 11:57:13 +0800 Subject: [PATCH 183/223] Refactor mongodb module for new transaction mode --- .../CAP.SqlServerCapOptionsExtension.cs | 7 +- src/DotNetCore.CAP.SqlServer/CapPublisher.cs | 87 ---------------- .../ICapPublisher.SqlServer.cs | 93 ++++++++++++++++++ .../ICapTransaction.SqlServer.cs | 73 ++++++++++++++ ...ringApi.cs => IMonitoringApi.SqlServer.cs} | 0 ...ServerStorage.cs => IStorage.SqlServer.cs} | 11 +-- ...ion.cs => IStorageConnection.SqlServer.cs} | 20 ++-- ...on.cs => IStorageTransaction.SqlServer.cs} | 24 ----- .../DatabaseTestHost.cs | 49 +++++----- .../SqlServerStorageConnectionTest.cs | 29 +++--- .../DotNetCore.CAP.SqlServer.Test/TestHost.cs | 98 ------------------- 11 files changed, 219 insertions(+), 272 deletions(-) delete mode 100644 src/DotNetCore.CAP.SqlServer/CapPublisher.cs create mode 100644 src/DotNetCore.CAP.SqlServer/ICapPublisher.SqlServer.cs create mode 100644 src/DotNetCore.CAP.SqlServer/ICapTransaction.SqlServer.cs rename src/DotNetCore.CAP.SqlServer/{SqlServerMonitoringApi.cs => IMonitoringApi.SqlServer.cs} (100%) rename src/DotNetCore.CAP.SqlServer/{SqlServerStorage.cs => IStorage.SqlServer.cs} (95%) rename src/DotNetCore.CAP.SqlServer/{SqlServerStorageConnection.cs => IStorageConnection.SqlServer.cs} (86%) rename src/DotNetCore.CAP.SqlServer/{SqlServerStorageTransaction.cs => IStorageTransaction.SqlServer.cs} (70%) delete mode 100644 test/DotNetCore.CAP.SqlServer.Test/TestHost.cs diff --git a/src/DotNetCore.CAP.SqlServer/CAP.SqlServerCapOptionsExtension.cs b/src/DotNetCore.CAP.SqlServer/CAP.SqlServerCapOptionsExtension.cs index 91e04f7..db0d73f 100644 --- a/src/DotNetCore.CAP.SqlServer/CAP.SqlServerCapOptionsExtension.cs +++ b/src/DotNetCore.CAP.SqlServer/CAP.SqlServerCapOptionsExtension.cs @@ -24,9 +24,12 @@ namespace DotNetCore.CAP services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); - services.AddScoped(); - services.AddScoped(); + + services.AddScoped(); + services.AddScoped(); + services.AddTransient(); + services.AddTransient(); AddSqlServerOptions(services); } diff --git a/src/DotNetCore.CAP.SqlServer/CapPublisher.cs b/src/DotNetCore.CAP.SqlServer/CapPublisher.cs deleted file mode 100644 index b93ca68..0000000 --- a/src/DotNetCore.CAP.SqlServer/CapPublisher.cs +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright (c) .NET Core Community. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. - -using System; -using System.Data; -using System.Data.SqlClient; -using System.Threading.Tasks; -using Dapper; -using DotNetCore.CAP.Abstractions; -using DotNetCore.CAP.Models; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Storage; -using Microsoft.Extensions.Logging; - -namespace DotNetCore.CAP.SqlServer -{ - public class CapPublisher : CapPublisherBase, ICallbackPublisher - { - private readonly DbContext _dbContext; - private readonly SqlServerOptions _options; - - public CapPublisher(ILogger logger, IDispatcher dispatcher, - IServiceProvider provider, SqlServerOptions options) - : base(logger, dispatcher) - { - ServiceProvider = provider; - _options = options; - - if (_options.DbContextType == null) - { - return; - } - - IsUsingEF = true; - _dbContext = (DbContext) ServiceProvider.GetService(_options.DbContextType); - } - - public async Task PublishCallbackAsync(CapPublishedMessage message) - { - using (var conn = new SqlConnection(_options.ConnectionString)) - { - var id = await conn.ExecuteScalarAsync(PrepareSql(), message); - message.Id = id; - Enqueue(message); - } - } - - protected override void PrepareConnectionForEF() - { - DbConnection = _dbContext.Database.GetDbConnection(); - var dbContextTransaction = _dbContext.Database.CurrentTransaction; - var dbTrans = dbContextTransaction?.GetDbTransaction(); - //DbTransaction is dispose in original - if (dbTrans?.Connection == null) - { - IsCapOpenedTrans = true; - dbContextTransaction?.Dispose(); - dbContextTransaction = _dbContext.Database.BeginTransaction(IsolationLevel.ReadCommitted); - dbTrans = dbContextTransaction.GetDbTransaction(); - } - - DbTransaction = dbTrans; - } - - protected override int Execute(IDbConnection dbConnection, IDbTransaction dbTransaction, - CapPublishedMessage message) - { - return dbConnection.ExecuteScalar(PrepareSql(), message, dbTransaction); - } - - protected override Task ExecuteAsync(IDbConnection dbConnection, IDbTransaction dbTransaction, - CapPublishedMessage message) - { - return dbConnection.ExecuteScalarAsync(PrepareSql(), message, dbTransaction); - } - - #region private methods - - private string PrepareSql() - { - return - $"INSERT INTO {_options.Schema}.[Published] ([Name],[Content],[Retries],[Added],[ExpiresAt],[StatusName])VALUES(@Name,@Content,@Retries,@Added,@ExpiresAt,@StatusName);SELECT SCOPE_IDENTITY();"; - } - - #endregion private methods - } -} \ No newline at end of file diff --git a/src/DotNetCore.CAP.SqlServer/ICapPublisher.SqlServer.cs b/src/DotNetCore.CAP.SqlServer/ICapPublisher.SqlServer.cs new file mode 100644 index 0000000..d641735 --- /dev/null +++ b/src/DotNetCore.CAP.SqlServer/ICapPublisher.SqlServer.cs @@ -0,0 +1,93 @@ +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; +using System.Data; +using System.Data.SqlClient; +using System.Threading; +using System.Threading.Tasks; +using Dapper; +using DotNetCore.CAP.Abstractions; +using DotNetCore.CAP.Models; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Storage; + +namespace DotNetCore.CAP.SqlServer +{ + public class SqlServerPublisher : CapPublisherBase, ICallbackPublisher + { + private readonly DbContext _dbContext; + private readonly SqlServerOptions _options; + private readonly bool _isUsingEF; + + private SqlConnection _connection; + + public SqlServerPublisher(IServiceProvider provider, SqlServerOptions options) : base(provider) + { + _options = options; + + if (_options.DbContextType == null) + { + return; + } + + _isUsingEF = true; + _dbContext = (DbContext)ServiceProvider.GetService(_options.DbContextType); + } + + public async Task PublishCallbackAsync(CapPublishedMessage message) + { + await PublishAsyncInternal(message); + } + + protected override Task ExecuteAsync(CapPublishedMessage message, ICapTransaction transaction, + CancellationToken cancel = default(CancellationToken)) + { + var dbTrans = transaction.DbTransaction as IDbTransaction; + if (dbTrans == null && transaction.DbTransaction is IDbContextTransaction dbContextTrans) + { + dbTrans = dbContextTrans.GetDbTransaction(); + } + var conn = dbTrans?.Connection; + return conn.ExecuteAsync(PrepareSql(), message, dbTrans); + } + + protected override object GetDbTransaction() + { + if (_isUsingEF) + { + var dbContextTransaction = _dbContext.Database.CurrentTransaction; + if (dbContextTransaction == null) + { + return InitDbConnection(); + } + + return dbContextTransaction; + } + + return InitDbConnection(); + } + + #region private methods + private string PrepareSql() + { + return + $"INSERT INTO {_options.Schema}.[Published] ([Id],[Name],[Content],[Retries],[Added],[ExpiresAt],[StatusName])VALUES(@Id,@Name,@Content,@Retries,@Added,@ExpiresAt,@StatusName);"; + } + + private IDbTransaction InitDbConnection() + { + _connection = new SqlConnection(_options.ConnectionString); + _connection.Open(); + return _connection.BeginTransaction(IsolationLevel.ReadCommitted); + } + + #endregion private methods + + public void Dispose() + { + _dbContext?.Dispose(); + _connection?.Dispose(); + } + } +} \ No newline at end of file diff --git a/src/DotNetCore.CAP.SqlServer/ICapTransaction.SqlServer.cs b/src/DotNetCore.CAP.SqlServer/ICapTransaction.SqlServer.cs new file mode 100644 index 0000000..e44c7cb --- /dev/null +++ b/src/DotNetCore.CAP.SqlServer/ICapTransaction.SqlServer.cs @@ -0,0 +1,73 @@ +using System.Data; +using System.Diagnostics; +using Microsoft.EntityFrameworkCore.Storage; + +// ReSharper disable once CheckNamespace +namespace DotNetCore.CAP +{ + public class SqlServerCapTransaction : CapTransactionBase + { + public SqlServerCapTransaction(IDispatcher dispatcher) : base(dispatcher) { } + + public override void Commit() + { + Debug.Assert(DbTransaction != null); + + switch (DbTransaction) + { + case IDbTransaction dbTransaction: + dbTransaction.Commit(); + break; + case IDbContextTransaction dbContextTransaction: + dbContextTransaction.Commit(); + break; + } + + Flush(); + } + + public override void Rollback() + { + Debug.Assert(DbTransaction != null); + + switch (DbTransaction) + { + case IDbTransaction dbTransaction: + dbTransaction.Rollback(); + break; + case IDbContextTransaction dbContextTransaction: + dbContextTransaction.Rollback(); + break; + } + } + + public override void Dispose() + { + (DbTransaction as IDbTransaction)?.Dispose(); + } + } + + public static class CapTransactionExtensions + { + public static ICapTransaction Begin(this ICapTransaction transaction, + IDbTransaction dbTransaction, bool autoCommit = false) + { + + transaction.DbTransaction = dbTransaction; + transaction.AutoCommit = autoCommit; + + return transaction; + } + + public static ICapTransaction Begin(this ICapTransaction transaction, + IDbContextTransaction dbTransaction, bool autoCommit = false) + { + + transaction.DbTransaction = dbTransaction; + transaction.AutoCommit = autoCommit; + + return transaction; + } + } + +} diff --git a/src/DotNetCore.CAP.SqlServer/SqlServerMonitoringApi.cs b/src/DotNetCore.CAP.SqlServer/IMonitoringApi.SqlServer.cs similarity index 100% rename from src/DotNetCore.CAP.SqlServer/SqlServerMonitoringApi.cs rename to src/DotNetCore.CAP.SqlServer/IMonitoringApi.SqlServer.cs diff --git a/src/DotNetCore.CAP.SqlServer/SqlServerStorage.cs b/src/DotNetCore.CAP.SqlServer/IStorage.SqlServer.cs similarity index 95% rename from src/DotNetCore.CAP.SqlServer/SqlServerStorage.cs rename to src/DotNetCore.CAP.SqlServer/IStorage.SqlServer.cs index 4a0f164..5b6dbd5 100644 --- a/src/DotNetCore.CAP.SqlServer/SqlServerStorage.cs +++ b/src/DotNetCore.CAP.SqlServer/IStorage.SqlServer.cs @@ -38,7 +38,7 @@ namespace DotNetCore.CAP.SqlServer return new SqlServerMonitoringApi(this, _options); } - public async Task InitializeAsync(CancellationToken cancellationToken) + public async Task InitializeAsync(CancellationToken cancellationToken = default(CancellationToken)) { if (cancellationToken.IsCancellationRequested) { @@ -64,15 +64,10 @@ BEGIN EXEC('CREATE SCHEMA [{schema}]') END; -IF OBJECT_ID(N'[{schema}].[Queue]',N'U') IS NOT NULL -BEGIN - DROP TABLE [{schema}].[Queue]; -END; - IF OBJECT_ID(N'[{schema}].[Received]',N'U') IS NULL BEGIN CREATE TABLE [{schema}].[Received]( - [Id] [int] IDENTITY(1,1) NOT NULL, + [Id] [bigint] NOT NULL, [Name] [nvarchar](200) NOT NULL, [Group] [nvarchar](200) NULL, [Content] [nvarchar](max) NULL, @@ -90,7 +85,7 @@ END; IF OBJECT_ID(N'[{schema}].[Published]',N'U') IS NULL BEGIN CREATE TABLE [{schema}].[Published]( - [Id] [int] IDENTITY(1,1) NOT NULL, + [Id] [bigint] NOT NULL, [Name] [nvarchar](200) NOT NULL, [Content] [nvarchar](max) NULL, [Retries] [int] NOT NULL, diff --git a/src/DotNetCore.CAP.SqlServer/SqlServerStorageConnection.cs b/src/DotNetCore.CAP.SqlServer/IStorageConnection.SqlServer.cs similarity index 86% rename from src/DotNetCore.CAP.SqlServer/SqlServerStorageConnection.cs rename to src/DotNetCore.CAP.SqlServer/IStorageConnection.SqlServer.cs index b204fde..08c2274 100644 --- a/src/DotNetCore.CAP.SqlServer/SqlServerStorageConnection.cs +++ b/src/DotNetCore.CAP.SqlServer/IStorageConnection.SqlServer.cs @@ -28,7 +28,7 @@ namespace DotNetCore.CAP.SqlServer return new SqlServerStorageTransaction(this); } - public async Task GetPublishedMessageAsync(int id) + public async Task GetPublishedMessageAsync(long id) { var sql = $@"SELECT * FROM [{Options.Schema}].[Published] WITH (readpast) WHERE Id={id}"; @@ -50,7 +50,7 @@ namespace DotNetCore.CAP.SqlServer } } - public async Task StoreReceivedMessageAsync(CapReceivedMessage message) + public void StoreReceivedMessage(CapReceivedMessage message) { if (message == null) { @@ -58,16 +58,16 @@ namespace DotNetCore.CAP.SqlServer } var sql = $@" -INSERT INTO [{Options.Schema}].[Received]([Name],[Group],[Content],[Retries],[Added],[ExpiresAt],[StatusName]) -VALUES(@Name,@Group,@Content,@Retries,@Added,@ExpiresAt,@StatusName);SELECT SCOPE_IDENTITY();"; +INSERT INTO [{Options.Schema}].[Received]([Id],[Name],[Group],[Content],[Retries],[Added],[ExpiresAt],[StatusName]) +VALUES(@Id,@Name,@Group,@Content,@Retries,@Added,@ExpiresAt,@StatusName);"; using (var connection = new SqlConnection(Options.ConnectionString)) { - return await connection.ExecuteScalarAsync(sql, message); + connection.Execute(sql, message); } } - public async Task GetReceivedMessageAsync(int id) + public async Task GetReceivedMessageAsync(long id) { var sql = $@"SELECT * FROM [{Options.Schema}].[Received] WITH (readpast) WHERE Id={id}"; using (var connection = new SqlConnection(Options.ConnectionString)) @@ -87,7 +87,7 @@ VALUES(@Name,@Group,@Content,@Retries,@Added,@ExpiresAt,@StatusName);SELECT SCOP } } - public bool ChangePublishedState(int messageId, string state) + public bool ChangePublishedState(long messageId, string state) { var sql = $"UPDATE [{Options.Schema}].[Published] SET Retries=Retries+1,ExpiresAt=NULL,StatusName = '{state}' WHERE Id={messageId}"; @@ -98,7 +98,7 @@ VALUES(@Name,@Group,@Content,@Retries,@Added,@ExpiresAt,@StatusName);SELECT SCOP } } - public bool ChangeReceivedState(int messageId, string state) + public bool ChangeReceivedState(long messageId, string state) { var sql = $"UPDATE [{Options.Schema}].[Received] SET Retries=Retries+1,ExpiresAt=NULL,StatusName = '{state}' WHERE Id={messageId}"; @@ -108,9 +108,5 @@ VALUES(@Name,@Group,@Content,@Retries,@Added,@ExpiresAt,@StatusName);SELECT SCOP return connection.Execute(sql) > 0; } } - - public void Dispose() - { - } } } \ No newline at end of file diff --git a/src/DotNetCore.CAP.SqlServer/SqlServerStorageTransaction.cs b/src/DotNetCore.CAP.SqlServer/IStorageTransaction.SqlServer.cs similarity index 70% rename from src/DotNetCore.CAP.SqlServer/SqlServerStorageTransaction.cs rename to src/DotNetCore.CAP.SqlServer/IStorageTransaction.SqlServer.cs index 8889e4a..687639d 100644 --- a/src/DotNetCore.CAP.SqlServer/SqlServerStorageTransaction.cs +++ b/src/DotNetCore.CAP.SqlServer/IStorageTransaction.SqlServer.cs @@ -62,29 +62,5 @@ namespace DotNetCore.CAP.SqlServer _dbTransaction.Dispose(); _dbConnection.Dispose(); } - - public void EnqueueMessage(CapPublishedMessage message) - { - if (message == null) - { - throw new ArgumentNullException(nameof(message)); - } - - var sql = $"INSERT INTO [{_schema}].[Queue] values(@MessageId,@MessageType);"; - _dbConnection.Execute(sql, new CapQueue {MessageId = message.Id, MessageType = MessageType.Publish}, - _dbTransaction); - } - - public void EnqueueMessage(CapReceivedMessage message) - { - if (message == null) - { - throw new ArgumentNullException(nameof(message)); - } - - var sql = $"INSERT INTO [{_schema}].[Queue] values(@MessageId,@MessageType);"; - _dbConnection.Execute(sql, new CapQueue {MessageId = message.Id, MessageType = MessageType.Subscribe}, - _dbTransaction); - } } } \ No newline at end of file diff --git a/test/DotNetCore.CAP.SqlServer.Test/DatabaseTestHost.cs b/test/DotNetCore.CAP.SqlServer.Test/DatabaseTestHost.cs index 96828f6..9c40b85 100644 --- a/test/DotNetCore.CAP.SqlServer.Test/DatabaseTestHost.cs +++ b/test/DotNetCore.CAP.SqlServer.Test/DatabaseTestHost.cs @@ -1,46 +1,37 @@ +using System; using System.Data; using System.Data.SqlClient; -using System.Threading; using Dapper; +using Microsoft.Extensions.Logging; +using Moq; namespace DotNetCore.CAP.SqlServer.Test { - public abstract class DatabaseTestHost : TestHost + public abstract class DatabaseTestHost:IDisposable { - private static bool _sqlObjectInstalled; - public static object _lock = new object(); + protected ILogger Logger; + protected CapOptions CapOptions; + protected SqlServerOptions SqlSeverOptions; - protected override void PostBuildServices() + public bool SqlObjectInstalled; + + protected DatabaseTestHost() { - base.PostBuildServices(); - lock (_lock) - { - if (!_sqlObjectInstalled) - { - InitializeDatabase(); - } - } + Logger = new Mock>().Object; + CapOptions = new Mock().Object; + SqlSeverOptions = new Mock() + .SetupProperty(x => x.ConnectionString, ConnectionUtil.GetConnectionString()) + .Object; + + InitializeDatabase(); } - public override void Dispose() + public void Dispose() { DeleteAllData(); - base.Dispose(); } private void InitializeDatabase() - { - using (CreateScope()) - { - var storage = GetService(); - var token = new CancellationTokenSource().Token; - CreateDatabase(); - storage.InitializeAsync(token).GetAwaiter().GetResult(); - _sqlObjectInstalled = true; - } - } - - private void CreateDatabase() { var masterConn = ConnectionUtil.GetMasterConnectionString(); var databaseName = ConnectionUtil.GetDatabaseName(); @@ -50,8 +41,12 @@ namespace DotNetCore.CAP.SqlServer.Test IF NOT EXISTS (SELECT * FROM sysdatabases WHERE name = N'{databaseName}') CREATE DATABASE [{databaseName}];"); } + + new SqlServerStorage(Logger, CapOptions, SqlSeverOptions).InitializeAsync().GetAwaiter().GetResult(); + SqlObjectInstalled = true; } + private void DeleteAllData() { var conn = ConnectionUtil.GetConnectionString(); diff --git a/test/DotNetCore.CAP.SqlServer.Test/SqlServerStorageConnectionTest.cs b/test/DotNetCore.CAP.SqlServer.Test/SqlServerStorageConnectionTest.cs index 3408ee3..20d2c69 100644 --- a/test/DotNetCore.CAP.SqlServer.Test/SqlServerStorageConnectionTest.cs +++ b/test/DotNetCore.CAP.SqlServer.Test/SqlServerStorageConnectionTest.cs @@ -10,30 +10,31 @@ namespace DotNetCore.CAP.SqlServer.Test [Collection("sqlserver")] public class SqlServerStorageConnectionTest : DatabaseTestHost { - private SqlServerStorageConnection _storage; + private readonly SqlServerStorageConnection _storage; public SqlServerStorageConnectionTest() { - var options = GetService(); - var capOptions = GetService(); - _storage = new SqlServerStorageConnection(options, capOptions); + _storage = new SqlServerStorageConnection(SqlSeverOptions, CapOptions); } [Fact] public async Task GetPublishedMessageAsync_Test() { - var sql = "INSERT INTO [Cap].[Published]([Name],[Content],[Retries],[Added],[ExpiresAt],[StatusName]) OUTPUT INSERTED.Id VALUES(@Name,@Content,@Retries,@Added,@ExpiresAt,@StatusName);"; + var sql = "INSERT INTO [Cap].[Published]([Id],[Name],[Content],[Retries],[Added],[ExpiresAt],[StatusName]) VALUES(@Id,@Name,@Content,@Retries,@Added,@ExpiresAt,@StatusName);"; + var insertedId = SnowflakeId.Default().NextId(); var publishMessage = new CapPublishedMessage { + Id= insertedId, Name = "SqlServerStorageConnectionTest", Content = "", StatusName = StatusName.Scheduled }; - var insertedId = default(int); + using (var connection = ConnectionUtil.CreateConnection()) { - insertedId = connection.QueryFirst(sql, publishMessage); + await connection.ExecuteAsync(sql, publishMessage); } + var message = await _storage.GetPublishedMessageAsync(insertedId); Assert.NotNull(message); Assert.Equal("SqlServerStorageConnectionTest", message.Name); @@ -41,7 +42,7 @@ namespace DotNetCore.CAP.SqlServer.Test } [Fact] - public async Task StoreReceivedMessageAsync_Test() + public void StoreReceivedMessageAsync_Test() { var receivedMessage = new CapReceivedMessage { @@ -54,7 +55,7 @@ namespace DotNetCore.CAP.SqlServer.Test Exception exception = null; try { - await _storage.StoreReceivedMessageAsync(receivedMessage); + _storage.StoreReceivedMessage(receivedMessage); } catch (Exception ex) { @@ -66,20 +67,20 @@ namespace DotNetCore.CAP.SqlServer.Test [Fact] public async Task GetReceivedMessageAsync_Test() { - var sql = $@" - INSERT INTO [Cap].[Received]([Name],[Group],[Content],[Retries],[Added],[ExpiresAt],[StatusName]) OUTPUT INSERTED.Id - VALUES(@Name,@Group,@Content,@Retries,@Added,@ExpiresAt,@StatusName);"; + var sql = @"INSERT INTO [Cap].[Received]([Id],[Name],[Group],[Content],[Retries],[Added],[ExpiresAt],[StatusName]) VALUES(@Id,@Name,@Group,@Content,@Retries,@Added,@ExpiresAt,@StatusName);"; + var insertedId = SnowflakeId.Default().NextId(); var receivedMessage = new CapReceivedMessage { + Id= insertedId, Name = "SqlServerStorageConnectionTest", Content = "", Group = "mygroup", StatusName = StatusName.Scheduled }; - var insertedId = default(int); + using (var connection = ConnectionUtil.CreateConnection()) { - insertedId = connection.QueryFirst(sql, receivedMessage); + await connection.ExecuteAsync(sql, receivedMessage); } var message = await _storage.GetReceivedMessageAsync(insertedId); diff --git a/test/DotNetCore.CAP.SqlServer.Test/TestHost.cs b/test/DotNetCore.CAP.SqlServer.Test/TestHost.cs deleted file mode 100644 index fa7a7c6..0000000 --- a/test/DotNetCore.CAP.SqlServer.Test/TestHost.cs +++ /dev/null @@ -1,98 +0,0 @@ -using System; -using Microsoft.Extensions.DependencyInjection; - -namespace DotNetCore.CAP.SqlServer.Test -{ - public abstract class TestHost : IDisposable - { - protected IServiceCollection _services; - protected string _connectionString; - private IServiceProvider _provider; - private IServiceProvider _scopedProvider; - - public TestHost() - { - CreateServiceCollection(); - PreBuildServices(); - BuildServices(); - PostBuildServices(); - } - - protected IServiceProvider Provider => _scopedProvider ?? _provider; - - private void CreateServiceCollection() - { - var services = new ServiceCollection(); - - services.AddOptions(); - services.AddLogging(); - - _connectionString = ConnectionUtil.GetConnectionString(); - services.AddSingleton(new SqlServerOptions { ConnectionString = _connectionString }); - services.AddSingleton(new CapOptions()); - services.AddSingleton(); - - _services = services; - } - - protected virtual void PreBuildServices() - { - } - - private void BuildServices() - { - _provider = _services.BuildServiceProvider(); - } - - protected virtual void PostBuildServices() - { - } - - public IDisposable CreateScope() - { - var scope = CreateScope(_provider); - var loc = scope.ServiceProvider; - _scopedProvider = loc; - return new DelegateDisposable(() => - { - if (_scopedProvider == loc) - { - _scopedProvider = null; - } - scope.Dispose(); - }); - } - - public IServiceScope CreateScope(IServiceProvider provider) - { - var scope = provider.GetService().CreateScope(); - return scope; - } - - public T GetService() => Provider.GetService(); - - public T Ensure(ref T service) - where T : class - => service ?? (service = GetService()); - - public virtual void Dispose() - { - (_provider as IDisposable)?.Dispose(); - } - - private class DelegateDisposable : IDisposable - { - private Action _dispose; - - public DelegateDisposable(Action dispose) - { - _dispose = dispose; - } - - public void Dispose() - { - _dispose(); - } - } - } -} \ No newline at end of file From 32b64ac1350f61c55779d1a359758c0cddacfaa3 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Sat, 18 Aug 2018 12:03:07 +0800 Subject: [PATCH 184/223] reduce dependent nuget packages --- src/DotNetCore.CAP.MongoDB/MongoDBStorageConnection.cs | 6 +----- src/DotNetCore.CAP.MySql/IStorageConnection.MySql.cs | 6 +----- .../IStorageTransaction.PostgreSql.cs | 4 +--- src/DotNetCore.CAP/CAP.ServiceCollectionExtensions.cs | 1 - src/DotNetCore.CAP/DotNetCore.CAP.csproj | 3 --- src/DotNetCore.CAP/IStorageConnection.cs | 3 +-- src/DotNetCore.CAP/Processor/IProcessor.NeedRetry.cs | 5 ++--- 7 files changed, 6 insertions(+), 22 deletions(-) diff --git a/src/DotNetCore.CAP.MongoDB/MongoDBStorageConnection.cs b/src/DotNetCore.CAP.MongoDB/MongoDBStorageConnection.cs index 7b91026..4c9b07a 100644 --- a/src/DotNetCore.CAP.MongoDB/MongoDBStorageConnection.cs +++ b/src/DotNetCore.CAP.MongoDB/MongoDBStorageConnection.cs @@ -105,10 +105,6 @@ namespace DotNetCore.CAP.MongoDB var collection = _database.GetCollection(_options.ReceivedCollection); collection.InsertOne(message); - } - - public void Dispose() - { - } + } } } \ No newline at end of file diff --git a/src/DotNetCore.CAP.MySql/IStorageConnection.MySql.cs b/src/DotNetCore.CAP.MySql/IStorageConnection.MySql.cs index 97aadf6..0036ff2 100644 --- a/src/DotNetCore.CAP.MySql/IStorageConnection.MySql.cs +++ b/src/DotNetCore.CAP.MySql/IStorageConnection.MySql.cs @@ -109,10 +109,6 @@ VALUES(@Id,@Name,@Group,@Content,@Retries,@Added,@ExpiresAt,@StatusName);"; { return connection.Execute(sql) > 0; } - } - - public void Dispose() - { - } + } } } \ No newline at end of file diff --git a/src/DotNetCore.CAP.PostgreSql/IStorageTransaction.PostgreSql.cs b/src/DotNetCore.CAP.PostgreSql/IStorageTransaction.PostgreSql.cs index 64dd30c..28d75d8 100644 --- a/src/DotNetCore.CAP.PostgreSql/IStorageTransaction.PostgreSql.cs +++ b/src/DotNetCore.CAP.PostgreSql/IStorageTransaction.PostgreSql.cs @@ -47,9 +47,7 @@ namespace DotNetCore.CAP.PostgreSql } var sql = - $@"UPDATE ""{ - _schema - }"".""received"" SET ""Retries""=@Retries,""Content""= @Content,""ExpiresAt""=@ExpiresAt,""StatusName""=@StatusName WHERE ""Id""=@Id;"; + $@"UPDATE ""{_schema}"".""received"" SET ""Retries""=@Retries,""Content""= @Content,""ExpiresAt""=@ExpiresAt,""StatusName""=@StatusName WHERE ""Id""=@Id;"; _dbConnection.Execute(sql, message, _dbTransaction); } diff --git a/src/DotNetCore.CAP/CAP.ServiceCollectionExtensions.cs b/src/DotNetCore.CAP/CAP.ServiceCollectionExtensions.cs index f346693..9abf3f7 100644 --- a/src/DotNetCore.CAP/CAP.ServiceCollectionExtensions.cs +++ b/src/DotNetCore.CAP/CAP.ServiceCollectionExtensions.cs @@ -34,7 +34,6 @@ namespace Microsoft.Extensions.DependencyInjection } services.TryAddSingleton(); - services.Configure(setupAction); //Consumer service AddSubscribeServices(services); diff --git a/src/DotNetCore.CAP/DotNetCore.CAP.csproj b/src/DotNetCore.CAP/DotNetCore.CAP.csproj index 95556e8..0866cca 100644 --- a/src/DotNetCore.CAP/DotNetCore.CAP.csproj +++ b/src/DotNetCore.CAP/DotNetCore.CAP.csproj @@ -34,12 +34,9 @@ - - - diff --git a/src/DotNetCore.CAP/IStorageConnection.cs b/src/DotNetCore.CAP/IStorageConnection.cs index e305f58..ba9b413 100644 --- a/src/DotNetCore.CAP/IStorageConnection.cs +++ b/src/DotNetCore.CAP/IStorageConnection.cs @@ -1,7 +1,6 @@ // Copyright (c) .NET Core Community. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. -using System; using System.Collections.Generic; using System.Threading.Tasks; using DotNetCore.CAP.Models; @@ -11,7 +10,7 @@ namespace DotNetCore.CAP /// /// Represents a connection to the storage. /// - public interface IStorageConnection : IDisposable + public interface IStorageConnection { //Sent messages diff --git a/src/DotNetCore.CAP/Processor/IProcessor.NeedRetry.cs b/src/DotNetCore.CAP/Processor/IProcessor.NeedRetry.cs index 36a6deb..e9b68dc 100644 --- a/src/DotNetCore.CAP/Processor/IProcessor.NeedRetry.cs +++ b/src/DotNetCore.CAP/Processor/IProcessor.NeedRetry.cs @@ -4,7 +4,6 @@ using System; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; namespace DotNetCore.CAP.Processor { @@ -16,13 +15,13 @@ namespace DotNetCore.CAP.Processor private readonly TimeSpan _waitingInterval; public NeedRetryMessageProcessor( - IOptions options, + CapOptions options, ISubscriberExecutor subscriberExecutor, IPublishMessageSender publishMessageSender) { _subscriberExecutor = subscriberExecutor; _publishMessageSender = publishMessageSender; - _waitingInterval = TimeSpan.FromSeconds(options.Value.FailedRetryInterval); + _waitingInterval = TimeSpan.FromSeconds(options.FailedRetryInterval); } public async Task ProcessAsync(ProcessingContext context) From 6a4268cd0a648909394ff3b5427eb32fc6494edb Mon Sep 17 00:00:00 2001 From: Savorboard Date: Sat, 18 Aug 2018 16:36:11 +0800 Subject: [PATCH 185/223] update samples --- samples/Sample.Kafka.MySql/Controllers/ValuesController.cs | 2 +- samples/Sample.Kafka.MySql/Startup.cs | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/samples/Sample.Kafka.MySql/Controllers/ValuesController.cs b/samples/Sample.Kafka.MySql/Controllers/ValuesController.cs index 21510bb..f5ce61f 100644 --- a/samples/Sample.Kafka.MySql/Controllers/ValuesController.cs +++ b/samples/Sample.Kafka.MySql/Controllers/ValuesController.cs @@ -26,7 +26,7 @@ namespace Sample.Kafka.MySql.Controllers //your business code here - await _capBus.PublishAsync("xxx.xxx.test2", 123456, transaction); + await _capBus.PublishAsync("xxx.xxx.test2", 123456); transaction.Commit(); } diff --git a/samples/Sample.Kafka.MySql/Startup.cs b/samples/Sample.Kafka.MySql/Startup.cs index 59c4636..3a6a8f7 100644 --- a/samples/Sample.Kafka.MySql/Startup.cs +++ b/samples/Sample.Kafka.MySql/Startup.cs @@ -21,8 +21,6 @@ namespace Sample.Kafka.MySql public void Configure(IApplicationBuilder app) { app.UseMvc(); - - app.UseCap(); } } } \ No newline at end of file From 2f6b3502ee4ae769486effcf5b3db043de6cca65 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Mon, 20 Aug 2018 18:41:15 +0800 Subject: [PATCH 186/223] remove logger extensions to internal folder --- src/DotNetCore.CAP/{ => Internal}/LoggerExtensions.cs | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/DotNetCore.CAP/{ => Internal}/LoggerExtensions.cs (100%) diff --git a/src/DotNetCore.CAP/LoggerExtensions.cs b/src/DotNetCore.CAP/Internal/LoggerExtensions.cs similarity index 100% rename from src/DotNetCore.CAP/LoggerExtensions.cs rename to src/DotNetCore.CAP/Internal/LoggerExtensions.cs From 8485fb2f0c624e88e9d5b0fa1a13fba19be109e0 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Mon, 20 Aug 2018 18:54:07 +0800 Subject: [PATCH 187/223] rename CapTransaction to Transaction --- .../Controllers/ValuesController.cs | 17 ++++++++++++-- .../Abstractions/CapPublisherBase.cs | 22 +++++++++---------- src/DotNetCore.CAP/ICapPublisher.cs | 2 +- test/DotNetCore.CAP.Test/CAP.BuilderTest.cs | 2 +- 4 files changed, 28 insertions(+), 15 deletions(-) diff --git a/samples/Sample.RabbitMQ.MySql/Controllers/ValuesController.cs b/samples/Sample.RabbitMQ.MySql/Controllers/ValuesController.cs index 12c4c42..ec78a35 100644 --- a/samples/Sample.RabbitMQ.MySql/Controllers/ValuesController.cs +++ b/samples/Sample.RabbitMQ.MySql/Controllers/ValuesController.cs @@ -2,6 +2,7 @@ using System.Threading.Tasks; using DotNetCore.CAP; using Microsoft.AspNetCore.Mvc; +using MySql.Data.MySqlClient; namespace Sample.RabbitMQ.MySql.Controllers { @@ -28,7 +29,17 @@ namespace Sample.RabbitMQ.MySql.Controllers [Route("~/publish2")] public IActionResult PublishMessage2() { - _capBus.Publish("sample.kafka.sqlserver4", DateTime.Now); + using (var connection = new MySqlConnection("Server=192.168.10.110;Database=testcap;UserId=root;Password=123123;")) + { + using (var transaction = connection.BeginAndJoinToTransaction(_capBus)) + { + //your business code + + _capBus.Publish("sample.rabbitmq.mysql", DateTime.Now); + + transaction.Commit(); + } + } return Ok(); } @@ -37,7 +48,7 @@ namespace Sample.RabbitMQ.MySql.Controllers public async Task PublishMessageWithTransaction() { using (var trans = await _dbContext.Database.BeginTransactionAsync()) - using (var capTrans = _capBus.CapTransaction.Begin(trans)) + using (var capTrans = _capBus.Transaction.Begin(trans)) { for (int i = 0; i < 10; i++) { @@ -49,6 +60,8 @@ namespace Sample.RabbitMQ.MySql.Controllers return Ok(); } + + [NonAction] [CapSubscribe("#.rabbitmq.mysql")] public void ReceiveMessage(DateTime time) diff --git a/src/DotNetCore.CAP/Abstractions/CapPublisherBase.cs b/src/DotNetCore.CAP/Abstractions/CapPublisherBase.cs index 18700c9..f20bb1f 100644 --- a/src/DotNetCore.CAP/Abstractions/CapPublisherBase.cs +++ b/src/DotNetCore.CAP/Abstractions/CapPublisherBase.cs @@ -14,7 +14,7 @@ namespace DotNetCore.CAP.Abstractions { public abstract class CapPublisherBase : ICapPublisher { - private readonly CapTransactionBase _capTransaction; + private readonly CapTransactionBase _transaction; private readonly IMessagePacker _msgPacker; private readonly IContentSerializer _serializer; @@ -27,14 +27,14 @@ namespace DotNetCore.CAP.Abstractions protected CapPublisherBase(IServiceProvider service) { ServiceProvider = service; - _capTransaction = service.GetRequiredService(); + _transaction = service.GetRequiredService(); _msgPacker = service.GetRequiredService(); _serializer = service.GetRequiredService(); } protected IServiceProvider ServiceProvider { get; } - public ICapTransaction CapTransaction => _capTransaction; + public ICapTransaction Transaction => _transaction; public void Publish(string name, T contentObj, string callbackName = null) { @@ -65,10 +65,10 @@ namespace DotNetCore.CAP.Abstractions protected async Task PublishAsyncInternal(CapPublishedMessage message) { - if (CapTransaction.DbTransaction == null) + if (Transaction.DbTransaction == null) { NotUseTransaction = true; - CapTransaction.DbTransaction = GetDbTransaction(); + Transaction.DbTransaction = GetDbTransaction(); } Guid operationId = default(Guid); @@ -77,15 +77,15 @@ namespace DotNetCore.CAP.Abstractions { operationId = s_diagnosticListener.WritePublishMessageStoreBefore(message); - await ExecuteAsync(message, CapTransaction); + await ExecuteAsync(message, Transaction); - _capTransaction.AddToSent(message); + _transaction.AddToSent(message); s_diagnosticListener.WritePublishMessageStoreAfter(operationId, message); - if (NotUseTransaction || CapTransaction.AutoCommit) + if (NotUseTransaction || Transaction.AutoCommit) { - _capTransaction.Commit(); + _transaction.Commit(); } } catch (Exception e) @@ -96,9 +96,9 @@ namespace DotNetCore.CAP.Abstractions } finally { - if (NotUseTransaction || CapTransaction.AutoCommit) + if (NotUseTransaction || Transaction.AutoCommit) { - _capTransaction.Dispose(); + _transaction.Dispose(); } } } diff --git a/src/DotNetCore.CAP/ICapPublisher.cs b/src/DotNetCore.CAP/ICapPublisher.cs index a8fe3b2..9410516 100644 --- a/src/DotNetCore.CAP/ICapPublisher.cs +++ b/src/DotNetCore.CAP/ICapPublisher.cs @@ -11,7 +11,7 @@ namespace DotNetCore.CAP /// public interface ICapPublisher { - ICapTransaction CapTransaction { get; } + ICapTransaction Transaction { get; } /// /// Asynchronous publish an object message. diff --git a/test/DotNetCore.CAP.Test/CAP.BuilderTest.cs b/test/DotNetCore.CAP.Test/CAP.BuilderTest.cs index cbdb3fe..e74934a 100644 --- a/test/DotNetCore.CAP.Test/CAP.BuilderTest.cs +++ b/test/DotNetCore.CAP.Test/CAP.BuilderTest.cs @@ -119,7 +119,7 @@ namespace DotNetCore.CAP.Test private class MyProducerService : ICapPublisher { - public ICapTransaction CapTransaction { get; } + public ICapTransaction Transaction { get; } public Task PublishAsync(string name, T contentObj, string callbackName = null, CancellationToken cancellationToken = default(CancellationToken)) From 2ec580648ac62a3e890ba36ad5c92e879d93d027 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Mon, 20 Aug 2018 18:54:40 +0800 Subject: [PATCH 188/223] Add RelationDbTransaction and extensions --- .../Internal/RelationDbTransaction.cs | 38 ++++++++++++++++++ src/DotNetCore.CAP/TransactionExtensions.cs | 40 +++++++++++++++++++ 2 files changed, 78 insertions(+) create mode 100644 src/DotNetCore.CAP/Internal/RelationDbTransaction.cs create mode 100644 src/DotNetCore.CAP/TransactionExtensions.cs diff --git a/src/DotNetCore.CAP/Internal/RelationDbTransaction.cs b/src/DotNetCore.CAP/Internal/RelationDbTransaction.cs new file mode 100644 index 0000000..10cee48 --- /dev/null +++ b/src/DotNetCore.CAP/Internal/RelationDbTransaction.cs @@ -0,0 +1,38 @@ +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System.Data; + +namespace DotNetCore.CAP.Internal +{ + internal class RelationDbTransaction : IDbTransaction + { + private readonly ICapTransaction _capTransaction; + + public RelationDbTransaction(ICapTransaction capTransaction) + { + _capTransaction = capTransaction; + var dbTransaction = (IDbTransaction) capTransaction.DbTransaction; + Connection = dbTransaction.Connection; + IsolationLevel = dbTransaction.IsolationLevel; + } + + public void Dispose() + { + _capTransaction.Dispose(); + } + + public void Commit() + { + _capTransaction.Commit(); + } + + public void Rollback() + { + _capTransaction.Rollback(); + } + + public IDbConnection Connection { get; } + public IsolationLevel IsolationLevel { get; } + } +} \ No newline at end of file diff --git a/src/DotNetCore.CAP/TransactionExtensions.cs b/src/DotNetCore.CAP/TransactionExtensions.cs new file mode 100644 index 0000000..e8af0e4 --- /dev/null +++ b/src/DotNetCore.CAP/TransactionExtensions.cs @@ -0,0 +1,40 @@ +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System.Data; +using DotNetCore.CAP.Internal; + +namespace DotNetCore.CAP +{ + public static class TransactionExtensions + { + public static ICapTransaction Begin(this ICapTransaction transaction, + IDbTransaction dbTransaction, bool autoCommit = false) + { + + transaction.DbTransaction = dbTransaction; + transaction.AutoCommit = autoCommit; + + return transaction; + } + + public static IDbTransaction JoinToTransaction(this IDbTransaction dbTransaction, + ICapPublisher publisher, bool autoCommit = false) + { + dbTransaction = new RelationDbTransaction(publisher.Transaction.Begin(dbTransaction, autoCommit)); + return dbTransaction; + } + + public static IDbTransaction BeginAndJoinToTransaction(this IDbConnection dbConnection, + ICapPublisher publisher, bool autoCommit = false) + { + if (dbConnection.State == ConnectionState.Closed) + { + dbConnection.Open(); + } + + var dbTransaction = dbConnection.BeginTransaction(); + return dbTransaction.JoinToTransaction(publisher, autoCommit); + } + } +} \ No newline at end of file From 401cc6f0998de4150e82493558810b24a33bb8ca Mon Sep 17 00:00:00 2001 From: Savorboard Date: Mon, 20 Aug 2018 18:54:54 +0800 Subject: [PATCH 189/223] update extension impl --- .../ICapTransaction.MySql.cs | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/src/DotNetCore.CAP.MySql/ICapTransaction.MySql.cs b/src/DotNetCore.CAP.MySql/ICapTransaction.MySql.cs index 7d228e5..5a0bef8 100644 --- a/src/DotNetCore.CAP.MySql/ICapTransaction.MySql.cs +++ b/src/DotNetCore.CAP.MySql/ICapTransaction.MySql.cs @@ -7,7 +7,9 @@ namespace DotNetCore.CAP { public class MySqlCapTransaction : CapTransactionBase { - public MySqlCapTransaction(IDispatcher dispatcher) : base(dispatcher) { } + public MySqlCapTransaction(IDispatcher dispatcher) : base(dispatcher) + { + } public override void Commit() { @@ -49,16 +51,6 @@ namespace DotNetCore.CAP public static class CapTransactionExtensions { - public static ICapTransaction Begin(this ICapTransaction transaction, - IDbTransaction dbTransaction, bool autoCommit = false) - { - - transaction.DbTransaction = dbTransaction; - transaction.AutoCommit = autoCommit; - - return transaction; - } - public static ICapTransaction Begin(this ICapTransaction transaction, IDbContextTransaction dbTransaction, bool autoCommit = false) { @@ -69,5 +61,4 @@ namespace DotNetCore.CAP return transaction; } } - -} +} \ No newline at end of file From 797b1f71ba44108479975e3e4da0ad5c59aedf17 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Mon, 20 Aug 2018 18:55:02 +0800 Subject: [PATCH 190/223] update samples --- .../Sample.RabbitMQ.MongoDB/Controllers/ValuesController.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/samples/Sample.RabbitMQ.MongoDB/Controllers/ValuesController.cs b/samples/Sample.RabbitMQ.MongoDB/Controllers/ValuesController.cs index 3eed63c..07b377f 100644 --- a/samples/Sample.RabbitMQ.MongoDB/Controllers/ValuesController.cs +++ b/samples/Sample.RabbitMQ.MongoDB/Controllers/ValuesController.cs @@ -26,7 +26,7 @@ namespace Sample.RabbitMQ.MongoDB.Controllers //mycollection.InsertOne(new BsonDocument { { "test", "test" } }); using (var session = _client.StartSession()) - using (var trans = _capPublisher.CapTransaction.Begin(session)) + using (var trans = _capPublisher.Transaction.Begin(session)) { var collection = _client.GetDatabase("test").GetCollection("test.collection"); collection.InsertOne(session, new BsonDocument { { "hello", "world" } }); @@ -42,7 +42,7 @@ namespace Sample.RabbitMQ.MongoDB.Controllers public IActionResult PublishNotAutoCommit() { using (var session = _client.StartSession()) - using (_capPublisher.CapTransaction.Begin(session, true)) + using (_capPublisher.Transaction.Begin(session, true)) { var collection = _client.GetDatabase("test").GetCollection("test.collection"); collection.InsertOne(session, new BsonDocument { { "hello2", "world2" } }); From bc4a840fd1897e6ed641a7e4e6365739317a9896 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E6=99=93=E4=B8=9C?= Date: Mon, 20 Aug 2018 19:15:41 +0800 Subject: [PATCH 191/223] update readme --- README.zh-cn.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.zh-cn.md b/README.zh-cn.md index 639e4c5..07bc803 100644 --- a/README.zh-cn.md +++ b/README.zh-cn.md @@ -10,7 +10,7 @@ CAP 是一个基于 .NET Standard çš„ C# 库,它是一ç§å¤„ç†åˆ†å¸ƒå¼äº‹åŠ¡ ä½ å¯ä»¥åœ¨è¿™é‡Œ[CAP Wiki](https://github.com/dotnetcore/CAP/wiki)看到更多详细资料。 -## 预览(OverView) +## 预览(OverView) 在我们构建 SOA 或者 å¾®æœåŠ¡ç³»ç»Ÿçš„过程中,我们通常需è¦ä½¿ç”¨äº‹ä»¶æ¥å¯¹å„个æœåŠ¡è¿›è¡Œé›†æˆï¼Œåœ¨è¿™è¿‡ç¨‹ä¸­ç®€å•çš„使用消æ¯é˜Ÿåˆ—并ä¸èƒ½ä¿è¯æ•°æ®çš„最终一致性, CAP 采用的是和当å‰æ•°æ®åº“集æˆçš„本地消æ¯è¡¨çš„方案æ¥è§£å†³åœ¨åˆ†å¸ƒå¼ç³»ç»Ÿäº’相调用的å„个环节å¯èƒ½å‡ºçŽ°çš„异常,它能够ä¿è¯ä»»ä½•æƒ…况下事件消æ¯éƒ½æ˜¯ä¸ä¼šä¸¢å¤±çš„。 From 9ecc5254333c1d2d0755d4b41d330b177236e520 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Tue, 21 Aug 2018 11:08:42 +0800 Subject: [PATCH 192/223] update samples --- samples/Sample.RabbitMQ.MySql/AppDbContext.cs | 13 ++++- .../Controllers/ValuesController.cs | 47 ++++++++++--------- .../20180821021736_init.Designer.cs | 35 ++++++++++++++ .../Migrations/20180821021736_init.cs | 30 ++++++++++++ .../Migrations/AppDbContextModelSnapshot.cs | 33 +++++++++++++ 5 files changed, 136 insertions(+), 22 deletions(-) create mode 100644 samples/Sample.RabbitMQ.MySql/Migrations/20180821021736_init.Designer.cs create mode 100644 samples/Sample.RabbitMQ.MySql/Migrations/20180821021736_init.cs create mode 100644 samples/Sample.RabbitMQ.MySql/Migrations/AppDbContextModelSnapshot.cs diff --git a/samples/Sample.RabbitMQ.MySql/AppDbContext.cs b/samples/Sample.RabbitMQ.MySql/AppDbContext.cs index ec83423..9e9274c 100644 --- a/samples/Sample.RabbitMQ.MySql/AppDbContext.cs +++ b/samples/Sample.RabbitMQ.MySql/AppDbContext.cs @@ -2,11 +2,22 @@ namespace Sample.RabbitMQ.MySql { + public class Person + { + public int Id { get; set; } + + public string Name { get; set; } + } + public class AppDbContext : DbContext { + public const string ConnectionString = "Server=192.168.10.110;Database=testcap;UserId=root;Password=123123;IgnoreCommandTransaction=true;"; + + public DbSet Persons { get; set; } + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { - optionsBuilder.UseMySql("Server=192.168.10.110;Database=testcap;UserId=root;Password=123123;"); + optionsBuilder.UseMySql(ConnectionString); } } } diff --git a/samples/Sample.RabbitMQ.MySql/Controllers/ValuesController.cs b/samples/Sample.RabbitMQ.MySql/Controllers/ValuesController.cs index ec78a35..ba43424 100644 --- a/samples/Sample.RabbitMQ.MySql/Controllers/ValuesController.cs +++ b/samples/Sample.RabbitMQ.MySql/Controllers/ValuesController.cs @@ -1,5 +1,6 @@ using System; using System.Threading.Tasks; +using Dapper; using DotNetCore.CAP; using Microsoft.AspNetCore.Mvc; using MySql.Data.MySqlClient; @@ -9,64 +10,68 @@ namespace Sample.RabbitMQ.MySql.Controllers [Route("api/[controller]")] public class ValuesController : Controller { - private readonly AppDbContext _dbContext; private readonly ICapPublisher _capBus; - public ValuesController(AppDbContext dbContext, ICapPublisher capPublisher) + public ValuesController(ICapPublisher capPublisher) { - _dbContext = dbContext; _capBus = capPublisher; } - [Route("~/publish")] - public IActionResult PublishMessage() + [Route("~/without/transaction")] + public IActionResult WithoutTransaction() { _capBus.Publish("sample.rabbitmq.mysql", DateTime.Now); return Ok(); } - [Route("~/publish2")] - public IActionResult PublishMessage2() + [Route("~/adonet/transaction")] + public IActionResult AdonetWithTransaction() { - using (var connection = new MySqlConnection("Server=192.168.10.110;Database=testcap;UserId=root;Password=123123;")) + //NOTE: Add `IgnoreCommandTransaction=true;` to your connection string, see https://github.com/mysql-net/MySqlConnector/issues/474 + using (var connection = new MySqlConnection(AppDbContext.ConnectionString)) { - using (var transaction = connection.BeginAndJoinToTransaction(_capBus)) + using (var transaction = connection.BeginAndJoinToTransaction(_capBus, autoCommit: false)) { //your business code + connection.Execute("insert into test(name) values('test')", transaction); - _capBus.Publish("sample.rabbitmq.mysql", DateTime.Now); + for (int i = 0; i < 5; i++) + { + _capBus.Publish("sample.rabbitmq.mysql", DateTime.Now); + } transaction.Commit(); } - } + } return Ok(); } - [Route("~/publishWithTrans")] - public async Task PublishMessageWithTransaction() + [Route("~/ef/transaction")] + public async Task EntityFrameworkWithTransaction([FromServices]AppDbContext dbContext) { - using (var trans = await _dbContext.Database.BeginTransactionAsync()) - using (var capTrans = _capBus.Transaction.Begin(trans)) + using (var trans = dbContext.Database.BeginAndJoinToTransaction(_capBus, autoCommit: false)) { - for (int i = 0; i < 10; i++) + dbContext.Persons.Add(new Person() { Name = "ef.transaction" }); + + for (int i = 0; i < 5; i++) { await _capBus.PublishAsync("sample.rabbitmq.mysql", DateTime.Now); } - capTrans.Commit(); + dbContext.SaveChanges(); + + trans.Commit(); } return Ok(); } - - [NonAction] [CapSubscribe("#.rabbitmq.mysql")] - public void ReceiveMessage(DateTime time) + public void Subscriber(DateTime time) { - Console.WriteLine("[sample.rabbitmq.mysql] message received: " + DateTime.Now + ",sent time: " + time); + Console.WriteLine($@"{DateTime.Now}, Subscriber invoked, Sent time:{time}"); } } } diff --git a/samples/Sample.RabbitMQ.MySql/Migrations/20180821021736_init.Designer.cs b/samples/Sample.RabbitMQ.MySql/Migrations/20180821021736_init.Designer.cs new file mode 100644 index 0000000..11b19a0 --- /dev/null +++ b/samples/Sample.RabbitMQ.MySql/Migrations/20180821021736_init.Designer.cs @@ -0,0 +1,35 @@ +// +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Sample.RabbitMQ.MySql; + +namespace Sample.RabbitMQ.MySql.Migrations +{ + [DbContext(typeof(AppDbContext))] + [Migration("20180821021736_init")] + partial class init + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.1.1-rtm-30846") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + modelBuilder.Entity("Sample.RabbitMQ.MySql.Person", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Name"); + + b.HasKey("Id"); + + b.ToTable("Persons"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/samples/Sample.RabbitMQ.MySql/Migrations/20180821021736_init.cs b/samples/Sample.RabbitMQ.MySql/Migrations/20180821021736_init.cs new file mode 100644 index 0000000..762ddbf --- /dev/null +++ b/samples/Sample.RabbitMQ.MySql/Migrations/20180821021736_init.cs @@ -0,0 +1,30 @@ +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Sample.RabbitMQ.MySql.Migrations +{ + public partial class init : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "Persons", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + Name = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Persons", x => x.Id); + }); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "Persons"); + } + } +} diff --git a/samples/Sample.RabbitMQ.MySql/Migrations/AppDbContextModelSnapshot.cs b/samples/Sample.RabbitMQ.MySql/Migrations/AppDbContextModelSnapshot.cs new file mode 100644 index 0000000..c74c3cd --- /dev/null +++ b/samples/Sample.RabbitMQ.MySql/Migrations/AppDbContextModelSnapshot.cs @@ -0,0 +1,33 @@ +// +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Sample.RabbitMQ.MySql; + +namespace Sample.RabbitMQ.MySql.Migrations +{ + [DbContext(typeof(AppDbContext))] + partial class AppDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.1.1-rtm-30846") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + modelBuilder.Entity("Sample.RabbitMQ.MySql.Person", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Name"); + + b.HasKey("Id"); + + b.ToTable("Persons"); + }); +#pragma warning restore 612, 618 + } + } +} From 467fb54625ed8ee56f1d3077f1d8bf48c18856f0 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Tue, 21 Aug 2018 11:09:02 +0800 Subject: [PATCH 193/223] Add entityframework extension support --- .../ICapTransaction.MySql.cs | 9 +++++ .../IDbContextTransaction.CAP.cs | 35 +++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 src/DotNetCore.CAP.MySql/IDbContextTransaction.CAP.cs diff --git a/src/DotNetCore.CAP.MySql/ICapTransaction.MySql.cs b/src/DotNetCore.CAP.MySql/ICapTransaction.MySql.cs index 5a0bef8..7a79f91 100644 --- a/src/DotNetCore.CAP.MySql/ICapTransaction.MySql.cs +++ b/src/DotNetCore.CAP.MySql/ICapTransaction.MySql.cs @@ -1,6 +1,7 @@ using System.Data; using System.Diagnostics; using Microsoft.EntityFrameworkCore.Storage; +using Microsoft.EntityFrameworkCore.Infrastructure; // ReSharper disable once CheckNamespace namespace DotNetCore.CAP @@ -60,5 +61,13 @@ namespace DotNetCore.CAP return transaction; } + + public static IDbContextTransaction BeginAndJoinToTransaction(this DatabaseFacade database, + ICapPublisher publisher, bool autoCommit = false) + { + var trans = database.BeginTransaction(); + var capTrans = publisher.Transaction.Begin(trans); + return new CapEFDbTransaction(capTrans); + } } } \ No newline at end of file diff --git a/src/DotNetCore.CAP.MySql/IDbContextTransaction.CAP.cs b/src/DotNetCore.CAP.MySql/IDbContextTransaction.CAP.cs new file mode 100644 index 0000000..dbfa691 --- /dev/null +++ b/src/DotNetCore.CAP.MySql/IDbContextTransaction.CAP.cs @@ -0,0 +1,35 @@ +using System; +using DotNetCore.CAP; + +// ReSharper disable once CheckNamespace +namespace Microsoft.EntityFrameworkCore.Storage +{ + internal class CapEFDbTransaction : IDbContextTransaction + { + private readonly ICapTransaction _transaction; + + public CapEFDbTransaction(ICapTransaction transaction) + { + _transaction = transaction; + var dbContextTransaction = (IDbContextTransaction)_transaction.DbTransaction; + TransactionId = dbContextTransaction.TransactionId; + } + + public void Dispose() + { + _transaction.Dispose(); + } + + public void Commit() + { + _transaction.Commit(); + } + + public void Rollback() + { + _transaction.Rollback(); + } + + public Guid TransactionId { get; } + } +} From f13c6ef9673cefcd4aae34922dcf3bf3c16f12bb Mon Sep 17 00:00:00 2001 From: Savorboard Date: Tue, 21 Aug 2018 12:00:21 +0800 Subject: [PATCH 194/223] Add cap dbtransaction wapper --- .../IClientSessionHandle.CAP.cs | 76 +++++++++++++++++++ .../IDbContextTransaction.CAP.cs | 35 +++++++++ .../IDbContextTransaction.CAP.cs | 35 +++++++++ 3 files changed, 146 insertions(+) create mode 100644 src/DotNetCore.CAP.MongoDB/IClientSessionHandle.CAP.cs create mode 100644 src/DotNetCore.CAP.PostgreSql/IDbContextTransaction.CAP.cs create mode 100644 src/DotNetCore.CAP.SqlServer/IDbContextTransaction.CAP.cs diff --git a/src/DotNetCore.CAP.MongoDB/IClientSessionHandle.CAP.cs b/src/DotNetCore.CAP.MongoDB/IClientSessionHandle.CAP.cs new file mode 100644 index 0000000..3381d02 --- /dev/null +++ b/src/DotNetCore.CAP.MongoDB/IClientSessionHandle.CAP.cs @@ -0,0 +1,76 @@ +using System.Threading; +using System.Threading.Tasks; +using DotNetCore.CAP; +using MongoDB.Bson; +using MongoDB.Driver.Core.Bindings; + +// ReSharper disable once CheckNamespace +namespace MongoDB.Driver +{ + internal class CapMongoDbClientSessionHandle : IClientSessionHandle + { + private readonly ICapTransaction _transaction; + private readonly IClientSessionHandle _sessionHandle; + + public CapMongoDbClientSessionHandle(ICapTransaction transaction) + { + _transaction = transaction; + _sessionHandle = (IClientSessionHandle)_transaction.DbTransaction; + } + + public void Dispose() + { + _transaction.Dispose(); + } + + public void AbortTransaction(CancellationToken cancellationToken = default(CancellationToken)) + { + _transaction.Rollback(); + } + + public Task AbortTransactionAsync(CancellationToken cancellationToken = default(CancellationToken)) + { + _transaction.Rollback(); + return Task.CompletedTask; + } + + public void AdvanceClusterTime(BsonDocument newClusterTime) + { + _sessionHandle.AdvanceClusterTime(newClusterTime); + } + + public void AdvanceOperationTime(BsonTimestamp newOperationTime) + { + _sessionHandle.AdvanceOperationTime(newOperationTime); + } + + public void CommitTransaction(CancellationToken cancellationToken = default(CancellationToken)) + { + _transaction.Commit(); + } + + public Task CommitTransactionAsync(CancellationToken cancellationToken = default(CancellationToken)) + { + _transaction.Commit(); + return Task.CompletedTask; + } + + public void StartTransaction(TransactionOptions transactionOptions = null) + { + _sessionHandle.StartTransaction(transactionOptions); + } + + public IMongoClient Client => _sessionHandle.Client; + public BsonDocument ClusterTime => _sessionHandle.ClusterTime; + public bool IsImplicit => _sessionHandle.IsImplicit; + public bool IsInTransaction => _sessionHandle.IsInTransaction; + public BsonTimestamp OperationTime => _sessionHandle.OperationTime; + public ClientSessionOptions Options => _sessionHandle.Options; + public IServerSession ServerSession => _sessionHandle.ServerSession; + public ICoreSessionHandle WrappedCoreSession => _sessionHandle.WrappedCoreSession; + public IClientSessionHandle Fork() + { + return _sessionHandle.Fork(); + } + } +} diff --git a/src/DotNetCore.CAP.PostgreSql/IDbContextTransaction.CAP.cs b/src/DotNetCore.CAP.PostgreSql/IDbContextTransaction.CAP.cs new file mode 100644 index 0000000..dbfa691 --- /dev/null +++ b/src/DotNetCore.CAP.PostgreSql/IDbContextTransaction.CAP.cs @@ -0,0 +1,35 @@ +using System; +using DotNetCore.CAP; + +// ReSharper disable once CheckNamespace +namespace Microsoft.EntityFrameworkCore.Storage +{ + internal class CapEFDbTransaction : IDbContextTransaction + { + private readonly ICapTransaction _transaction; + + public CapEFDbTransaction(ICapTransaction transaction) + { + _transaction = transaction; + var dbContextTransaction = (IDbContextTransaction)_transaction.DbTransaction; + TransactionId = dbContextTransaction.TransactionId; + } + + public void Dispose() + { + _transaction.Dispose(); + } + + public void Commit() + { + _transaction.Commit(); + } + + public void Rollback() + { + _transaction.Rollback(); + } + + public Guid TransactionId { get; } + } +} diff --git a/src/DotNetCore.CAP.SqlServer/IDbContextTransaction.CAP.cs b/src/DotNetCore.CAP.SqlServer/IDbContextTransaction.CAP.cs new file mode 100644 index 0000000..dbfa691 --- /dev/null +++ b/src/DotNetCore.CAP.SqlServer/IDbContextTransaction.CAP.cs @@ -0,0 +1,35 @@ +using System; +using DotNetCore.CAP; + +// ReSharper disable once CheckNamespace +namespace Microsoft.EntityFrameworkCore.Storage +{ + internal class CapEFDbTransaction : IDbContextTransaction + { + private readonly ICapTransaction _transaction; + + public CapEFDbTransaction(ICapTransaction transaction) + { + _transaction = transaction; + var dbContextTransaction = (IDbContextTransaction)_transaction.DbTransaction; + TransactionId = dbContextTransaction.TransactionId; + } + + public void Dispose() + { + _transaction.Dispose(); + } + + public void Commit() + { + _transaction.Commit(); + } + + public void Rollback() + { + _transaction.Rollback(); + } + + public Guid TransactionId { get; } + } +} From 5be6739cde9fa960360b2832e5af7c2560473cf5 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Tue, 21 Aug 2018 12:01:02 +0800 Subject: [PATCH 195/223] add and refactor transaction extension --- .../ICapTransaction.MongoDB.cs | 23 +++++++++++++++---- .../ICapTransaction.MySql.cs | 3 +-- .../ICapTransaction.PostgreSql.cs | 15 ++++++------ .../ICapTransaction.SqlServer.cs | 16 ++++++------- 4 files changed, 33 insertions(+), 24 deletions(-) diff --git a/src/DotNetCore.CAP.MongoDB/ICapTransaction.MongoDB.cs b/src/DotNetCore.CAP.MongoDB/ICapTransaction.MongoDB.cs index fc7b1a6..4b85f1d 100644 --- a/src/DotNetCore.CAP.MongoDB/ICapTransaction.MongoDB.cs +++ b/src/DotNetCore.CAP.MongoDB/ICapTransaction.MongoDB.cs @@ -1,5 +1,4 @@ -using System.Data; -using System.Diagnostics; +using System.Diagnostics; using MongoDB.Driver; // ReSharper disable once CheckNamespace @@ -36,7 +35,7 @@ namespace DotNetCore.CAP public override void Dispose() { - (DbTransaction as IDbTransaction)?.Dispose(); + (DbTransaction as IClientSessionHandle)?.Dispose(); } } @@ -49,11 +48,25 @@ namespace DotNetCore.CAP { dbTransaction.StartTransaction(); } - + transaction.DbTransaction = dbTransaction; transaction.AutoCommit = autoCommit; return transaction; - } + } + + public static IClientSessionHandle BeginAndJoinToTransaction(this IClientSessionHandle clientSessionHandle, + ICapPublisher publisher, bool autoCommit = false) + { + var capTrans = publisher.Transaction.Begin(clientSessionHandle, autoCommit); + return new CapMongoDbClientSessionHandle(capTrans); + } + + public static IClientSessionHandle StartAndJoinToTransaction(this IMongoClient client, + ICapPublisher publisher, bool autoCommit = false) + { + var clientSessionHandle = client.StartSession(); + return BeginAndJoinToTransaction(clientSessionHandle, publisher, autoCommit); + } } } diff --git a/src/DotNetCore.CAP.MySql/ICapTransaction.MySql.cs b/src/DotNetCore.CAP.MySql/ICapTransaction.MySql.cs index 7a79f91..2dce16c 100644 --- a/src/DotNetCore.CAP.MySql/ICapTransaction.MySql.cs +++ b/src/DotNetCore.CAP.MySql/ICapTransaction.MySql.cs @@ -55,7 +55,6 @@ namespace DotNetCore.CAP public static ICapTransaction Begin(this ICapTransaction transaction, IDbContextTransaction dbTransaction, bool autoCommit = false) { - transaction.DbTransaction = dbTransaction; transaction.AutoCommit = autoCommit; @@ -66,7 +65,7 @@ namespace DotNetCore.CAP ICapPublisher publisher, bool autoCommit = false) { var trans = database.BeginTransaction(); - var capTrans = publisher.Transaction.Begin(trans); + var capTrans = publisher.Transaction.Begin(trans, autoCommit); return new CapEFDbTransaction(capTrans); } } diff --git a/src/DotNetCore.CAP.PostgreSql/ICapTransaction.PostgreSql.cs b/src/DotNetCore.CAP.PostgreSql/ICapTransaction.PostgreSql.cs index 7599e39..a14914c 100644 --- a/src/DotNetCore.CAP.PostgreSql/ICapTransaction.PostgreSql.cs +++ b/src/DotNetCore.CAP.PostgreSql/ICapTransaction.PostgreSql.cs @@ -1,5 +1,6 @@ using System.Data; using System.Diagnostics; +using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Storage; // ReSharper disable once CheckNamespace @@ -50,7 +51,7 @@ namespace DotNetCore.CAP public static class CapTransactionExtensions { public static ICapTransaction Begin(this ICapTransaction transaction, - IDbTransaction dbTransaction, bool autoCommit = false) + IDbContextTransaction dbTransaction, bool autoCommit = false) { transaction.DbTransaction = dbTransaction; transaction.AutoCommit = autoCommit; @@ -58,14 +59,12 @@ namespace DotNetCore.CAP return transaction; } - public static ICapTransaction Begin(this ICapTransaction transaction, - IDbContextTransaction dbTransaction, bool autoCommit = false) + public static IDbContextTransaction BeginAndJoinToTransaction(this DatabaseFacade database, + ICapPublisher publisher, bool autoCommit = false) { - - transaction.DbTransaction = dbTransaction; - transaction.AutoCommit = autoCommit; - - return transaction; + var trans = database.BeginTransaction(); + var capTrans = publisher.Transaction.Begin(trans, autoCommit); + return new CapEFDbTransaction(capTrans); } } diff --git a/src/DotNetCore.CAP.SqlServer/ICapTransaction.SqlServer.cs b/src/DotNetCore.CAP.SqlServer/ICapTransaction.SqlServer.cs index e44c7cb..14e80b0 100644 --- a/src/DotNetCore.CAP.SqlServer/ICapTransaction.SqlServer.cs +++ b/src/DotNetCore.CAP.SqlServer/ICapTransaction.SqlServer.cs @@ -1,5 +1,6 @@ using System.Data; using System.Diagnostics; +using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Storage; // ReSharper disable once CheckNamespace @@ -50,23 +51,20 @@ namespace DotNetCore.CAP public static class CapTransactionExtensions { public static ICapTransaction Begin(this ICapTransaction transaction, - IDbTransaction dbTransaction, bool autoCommit = false) + IDbContextTransaction dbTransaction, bool autoCommit = false) { - transaction.DbTransaction = dbTransaction; transaction.AutoCommit = autoCommit; return transaction; } - public static ICapTransaction Begin(this ICapTransaction transaction, - IDbContextTransaction dbTransaction, bool autoCommit = false) + public static IDbContextTransaction BeginAndJoinToTransaction(this DatabaseFacade database, + ICapPublisher publisher, bool autoCommit = false) { - - transaction.DbTransaction = dbTransaction; - transaction.AutoCommit = autoCommit; - - return transaction; + var trans = database.BeginTransaction(); + var capTrans = publisher.Transaction.Begin(trans, autoCommit); + return new CapEFDbTransaction(capTrans); } } From 406d338a44c91a398f9be926757f4d10f5e8f428 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Tue, 21 Aug 2018 12:01:15 +0800 Subject: [PATCH 196/223] update samples --- .../Controllers/ValuesController.cs | 50 +++++++++++------- samples/Sample.Kafka.MySql/Startup.cs | 7 +-- .../Controllers/ValuesController.cs | 52 +++++++++++-------- .../Controllers/ValuesController.cs | 5 +- 4 files changed, 67 insertions(+), 47 deletions(-) diff --git a/samples/Sample.Kafka.MySql/Controllers/ValuesController.cs b/samples/Sample.Kafka.MySql/Controllers/ValuesController.cs index f5ce61f..361c606 100644 --- a/samples/Sample.Kafka.MySql/Controllers/ValuesController.cs +++ b/samples/Sample.Kafka.MySql/Controllers/ValuesController.cs @@ -1,5 +1,5 @@ using System; -using System.Threading.Tasks; +using Dapper; using DotNetCore.CAP; using Microsoft.AspNetCore.Mvc; using MySql.Data.MySqlClient; @@ -7,37 +7,51 @@ using MySql.Data.MySqlClient; namespace Sample.Kafka.MySql.Controllers { [Route("api/[controller]")] - public class ValuesController : Controller, ICapSubscribe + public class ValuesController : Controller { private readonly ICapPublisher _capBus; - public ValuesController(ICapPublisher producer) + public ValuesController(ICapPublisher capPublisher) { - _capBus = producer; + _capBus = capPublisher; } - [Route("~/publish")] - public async Task PublishMessage() + [Route("~/without/transaction")] + public IActionResult WithoutTransaction() { - using (var connection = new MySqlConnection("Server=192.168.10.110;Database=testcap;UserId=root;Password=123123;")) - { - connection.Open(); - var transaction = connection.BeginTransaction(); - - //your business code here + _capBus.Publish("sample.rabbitmq.mysql", DateTime.Now); - await _capBus.PublishAsync("xxx.xxx.test2", 123456); + return Ok(); + } - transaction.Commit(); + [Route("~/adonet/transaction")] + public IActionResult AdonetWithTransaction() + { + //NOTE: Add `IgnoreCommandTransaction=true;` to your connection string, see https://github.com/mysql-net/MySqlConnector/issues/474 + using (var connection = new MySqlConnection(Startup.ConnectionString)) + { + using (var transaction = connection.BeginAndJoinToTransaction(_capBus, autoCommit: false)) + { + //your business code + connection.Execute("insert into test(name) values('test')", transaction); + + for (int i = 0; i < 5; i++) + { + _capBus.Publish("sample.rabbitmq.mysql", DateTime.Now); + } + + transaction.Commit(); + } } - return Ok("publish successful!"); + return Ok(); } - [CapSubscribe("#.test2")] - public void Test2(int value) + [NonAction] + [CapSubscribe("sample.rabbitmq.mysql")] + public void Subscriber(DateTime time) { - Console.WriteLine("Subscriber output message: " + value); + Console.WriteLine($@"{DateTime.Now}, Subscriber invoked, Sent time:{time}"); } } } \ No newline at end of file diff --git a/samples/Sample.Kafka.MySql/Startup.cs b/samples/Sample.Kafka.MySql/Startup.cs index 3a6a8f7..9ff4345 100644 --- a/samples/Sample.Kafka.MySql/Startup.cs +++ b/samples/Sample.Kafka.MySql/Startup.cs @@ -1,16 +1,17 @@ -using System; -using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.DependencyInjection; namespace Sample.Kafka.MySql { public class Startup { + public const string ConnectionString = "Server=localhost;Database=testcap;UserId=root;Password=123123;"; + public void ConfigureServices(IServiceCollection services) { services.AddCap(x => { - x.UseMySql("Server=localhost;Database=testcap;UserId=root;Password=123123;"); + x.UseMySql(ConnectionString); x.UseKafka("localhost:9092"); x.UseDashboard(); }); diff --git a/samples/Sample.RabbitMQ.MongoDB/Controllers/ValuesController.cs b/samples/Sample.RabbitMQ.MongoDB/Controllers/ValuesController.cs index 07b377f..c811c83 100644 --- a/samples/Sample.RabbitMQ.MongoDB/Controllers/ValuesController.cs +++ b/samples/Sample.RabbitMQ.MongoDB/Controllers/ValuesController.cs @@ -11,60 +11,66 @@ namespace Sample.RabbitMQ.MongoDB.Controllers public class ValuesController : ControllerBase { private readonly IMongoClient _client; - private readonly ICapPublisher _capPublisher; + private readonly ICapPublisher _capBus; - public ValuesController(IMongoClient client, ICapPublisher capPublisher) + public ValuesController(IMongoClient client, ICapPublisher capBus) { _client = client; - _capPublisher = capPublisher; + _capBus = capBus; } - [Route("~/publish")] - public IActionResult PublishWithTrans() + [Route("~/without/transaction")] + public IActionResult WithoutTransaction() { + _capBus.Publish("sample.rabbitmq.mongodb", DateTime.Now); + + return Ok(); + } + + [Route("~/transaction/not/autocommit")] + public IActionResult PublishNotAutoCommit() + { + //NOTE: before your test, your need to create database and collection at first + //注æ„:MongoDB ä¸èƒ½åœ¨äº‹åŠ¡ä¸­åˆ›å»ºæ•°æ®åº“和集åˆï¼Œæ‰€ä»¥ä½ éœ€è¦å•ç‹¬åˆ›å»ºå®ƒä»¬ï¼Œæ¨¡æ‹Ÿä¸€æ¡è®°å½•æ’入则会自动创建 //var mycollection = _client.GetDatabase("test").GetCollection("test.collection"); //mycollection.InsertOne(new BsonDocument { { "test", "test" } }); - using (var session = _client.StartSession()) - using (var trans = _capPublisher.Transaction.Begin(session)) + using (var session = _client.StartAndJoinToTransaction(_capBus, autoCommit: false)) { var collection = _client.GetDatabase("test").GetCollection("test.collection"); collection.InsertOne(session, new BsonDocument { { "hello", "world" } }); - _capPublisher.Publish("sample.rabbitmq.mongodb", DateTime.Now); + _capBus.Publish("sample.rabbitmq.mongodb", DateTime.Now); - trans.Commit(); + session.CommitTransaction(); } return Ok(); } - [Route("~/publish/autocommit")] - public IActionResult PublishNotAutoCommit() + [Route("~/transaction/autocommit")] + public IActionResult PublishWithoutTrans() { - using (var session = _client.StartSession()) - using (_capPublisher.Transaction.Begin(session, true)) + //NOTE: before your test, your need to create database and collection at first + //注æ„:MongoDB ä¸èƒ½åœ¨äº‹åŠ¡ä¸­åˆ›å»ºæ•°æ®åº“和集åˆï¼Œæ‰€ä»¥ä½ éœ€è¦å•ç‹¬åˆ›å»ºå®ƒä»¬ï¼Œæ¨¡æ‹Ÿä¸€æ¡è®°å½•æ’入则会自动创建 + //var mycollection = _client.GetDatabase("test").GetCollection("test.collection"); + //mycollection.InsertOne(new BsonDocument { { "test", "test" } }); + + using (var session = _client.StartAndJoinToTransaction(_capBus, autoCommit: true)) { var collection = _client.GetDatabase("test").GetCollection("test.collection"); - collection.InsertOne(session, new BsonDocument { { "hello2", "world2" } }); + collection.InsertOne(session, new BsonDocument { { "hello", "world" } }); - _capPublisher.Publish("sample.rabbitmq.mongodb", DateTime.Now); + _capBus.Publish("sample.rabbitmq.mongodb", DateTime.Now); } return Ok(); } - [Route("~/publish/without/trans")] - public IActionResult PublishWithoutTrans() - { - _capPublisher.Publish("sample.rabbitmq.mongodb", DateTime.Now); - return Ok(); - } - [NonAction] [CapSubscribe("sample.rabbitmq.mongodb")] public void ReceiveMessage(DateTime time) { - Console.WriteLine("[sample.rabbitmq.mongodb] message received: " + DateTime.Now + ",sent time: " + time); + Console.WriteLine($@"{DateTime.Now}, Subscriber invoked, Sent time:{time}"); } } } diff --git a/samples/Sample.RabbitMQ.MySql/Controllers/ValuesController.cs b/samples/Sample.RabbitMQ.MySql/Controllers/ValuesController.cs index ba43424..7f31680 100644 --- a/samples/Sample.RabbitMQ.MySql/Controllers/ValuesController.cs +++ b/samples/Sample.RabbitMQ.MySql/Controllers/ValuesController.cs @@ -1,5 +1,4 @@ using System; -using System.Threading.Tasks; using Dapper; using DotNetCore.CAP; using Microsoft.AspNetCore.Mvc; @@ -49,7 +48,7 @@ namespace Sample.RabbitMQ.MySql.Controllers } [Route("~/ef/transaction")] - public async Task EntityFrameworkWithTransaction([FromServices]AppDbContext dbContext) + public IActionResult EntityFrameworkWithTransaction([FromServices]AppDbContext dbContext) { using (var trans = dbContext.Database.BeginAndJoinToTransaction(_capBus, autoCommit: false)) { @@ -57,7 +56,7 @@ namespace Sample.RabbitMQ.MySql.Controllers for (int i = 0; i < 5; i++) { - await _capBus.PublishAsync("sample.rabbitmq.mysql", DateTime.Now); + _capBus.Publish("sample.rabbitmq.mysql", DateTime.Now); } dbContext.SaveChanges(); From 3f37a4531162e232ea35e4dfc6a07a6bc3ba167f Mon Sep 17 00:00:00 2001 From: Savorboard Date: Tue, 21 Aug 2018 16:07:15 +0800 Subject: [PATCH 197/223] =?UTF-8?q?=EF=BB=BFAdd=20file=20copyright?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ICapTransaction.MongoDB.cs | 7 +++++-- .../IClientSessionHandle.CAP.cs | 12 ++++++++---- src/DotNetCore.CAP.MySql/ICapTransaction.MySql.cs | 7 +++++-- .../IDbContextTransaction.CAP.cs | 9 ++++++--- .../ICapTransaction.PostgreSql.cs | 12 ++++++++---- .../IDbContextTransaction.CAP.cs | 9 ++++++--- .../ICapTransaction.SqlServer.cs | 12 ++++++++---- .../IDbContextTransaction.CAP.cs | 9 ++++++--- 8 files changed, 52 insertions(+), 25 deletions(-) diff --git a/src/DotNetCore.CAP.MongoDB/ICapTransaction.MongoDB.cs b/src/DotNetCore.CAP.MongoDB/ICapTransaction.MongoDB.cs index 4b85f1d..ab7d9ab 100644 --- a/src/DotNetCore.CAP.MongoDB/ICapTransaction.MongoDB.cs +++ b/src/DotNetCore.CAP.MongoDB/ICapTransaction.MongoDB.cs @@ -1,4 +1,7 @@ -using System.Diagnostics; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System.Diagnostics; using MongoDB.Driver; // ReSharper disable once CheckNamespace @@ -69,4 +72,4 @@ namespace DotNetCore.CAP return BeginAndJoinToTransaction(clientSessionHandle, publisher, autoCommit); } } -} +} \ No newline at end of file diff --git a/src/DotNetCore.CAP.MongoDB/IClientSessionHandle.CAP.cs b/src/DotNetCore.CAP.MongoDB/IClientSessionHandle.CAP.cs index 3381d02..7a34177 100644 --- a/src/DotNetCore.CAP.MongoDB/IClientSessionHandle.CAP.cs +++ b/src/DotNetCore.CAP.MongoDB/IClientSessionHandle.CAP.cs @@ -1,4 +1,7 @@ -using System.Threading; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System.Threading; using System.Threading.Tasks; using DotNetCore.CAP; using MongoDB.Bson; @@ -9,13 +12,13 @@ namespace MongoDB.Driver { internal class CapMongoDbClientSessionHandle : IClientSessionHandle { - private readonly ICapTransaction _transaction; private readonly IClientSessionHandle _sessionHandle; + private readonly ICapTransaction _transaction; public CapMongoDbClientSessionHandle(ICapTransaction transaction) { _transaction = transaction; - _sessionHandle = (IClientSessionHandle)_transaction.DbTransaction; + _sessionHandle = (IClientSessionHandle) _transaction.DbTransaction; } public void Dispose() @@ -68,9 +71,10 @@ namespace MongoDB.Driver public ClientSessionOptions Options => _sessionHandle.Options; public IServerSession ServerSession => _sessionHandle.ServerSession; public ICoreSessionHandle WrappedCoreSession => _sessionHandle.WrappedCoreSession; + public IClientSessionHandle Fork() { return _sessionHandle.Fork(); } } -} +} \ No newline at end of file diff --git a/src/DotNetCore.CAP.MySql/ICapTransaction.MySql.cs b/src/DotNetCore.CAP.MySql/ICapTransaction.MySql.cs index 2dce16c..7b0377b 100644 --- a/src/DotNetCore.CAP.MySql/ICapTransaction.MySql.cs +++ b/src/DotNetCore.CAP.MySql/ICapTransaction.MySql.cs @@ -1,7 +1,10 @@ -using System.Data; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System.Data; using System.Diagnostics; -using Microsoft.EntityFrameworkCore.Storage; using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage; // ReSharper disable once CheckNamespace namespace DotNetCore.CAP diff --git a/src/DotNetCore.CAP.MySql/IDbContextTransaction.CAP.cs b/src/DotNetCore.CAP.MySql/IDbContextTransaction.CAP.cs index dbfa691..980dc57 100644 --- a/src/DotNetCore.CAP.MySql/IDbContextTransaction.CAP.cs +++ b/src/DotNetCore.CAP.MySql/IDbContextTransaction.CAP.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using DotNetCore.CAP; // ReSharper disable once CheckNamespace @@ -11,7 +14,7 @@ namespace Microsoft.EntityFrameworkCore.Storage public CapEFDbTransaction(ICapTransaction transaction) { _transaction = transaction; - var dbContextTransaction = (IDbContextTransaction)_transaction.DbTransaction; + var dbContextTransaction = (IDbContextTransaction) _transaction.DbTransaction; TransactionId = dbContextTransaction.TransactionId; } @@ -32,4 +35,4 @@ namespace Microsoft.EntityFrameworkCore.Storage public Guid TransactionId { get; } } -} +} \ No newline at end of file diff --git a/src/DotNetCore.CAP.PostgreSql/ICapTransaction.PostgreSql.cs b/src/DotNetCore.CAP.PostgreSql/ICapTransaction.PostgreSql.cs index a14914c..799bc21 100644 --- a/src/DotNetCore.CAP.PostgreSql/ICapTransaction.PostgreSql.cs +++ b/src/DotNetCore.CAP.PostgreSql/ICapTransaction.PostgreSql.cs @@ -1,4 +1,7 @@ -using System.Data; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System.Data; using System.Diagnostics; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Storage; @@ -8,7 +11,9 @@ namespace DotNetCore.CAP { public class PostgreSqlCapTransaction : CapTransactionBase { - public PostgreSqlCapTransaction(IDispatcher dispatcher) : base(dispatcher) { } + public PostgreSqlCapTransaction(IDispatcher dispatcher) : base(dispatcher) + { + } public override void Commit() { @@ -67,5 +72,4 @@ namespace DotNetCore.CAP return new CapEFDbTransaction(capTrans); } } - -} +} \ No newline at end of file diff --git a/src/DotNetCore.CAP.PostgreSql/IDbContextTransaction.CAP.cs b/src/DotNetCore.CAP.PostgreSql/IDbContextTransaction.CAP.cs index dbfa691..980dc57 100644 --- a/src/DotNetCore.CAP.PostgreSql/IDbContextTransaction.CAP.cs +++ b/src/DotNetCore.CAP.PostgreSql/IDbContextTransaction.CAP.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using DotNetCore.CAP; // ReSharper disable once CheckNamespace @@ -11,7 +14,7 @@ namespace Microsoft.EntityFrameworkCore.Storage public CapEFDbTransaction(ICapTransaction transaction) { _transaction = transaction; - var dbContextTransaction = (IDbContextTransaction)_transaction.DbTransaction; + var dbContextTransaction = (IDbContextTransaction) _transaction.DbTransaction; TransactionId = dbContextTransaction.TransactionId; } @@ -32,4 +35,4 @@ namespace Microsoft.EntityFrameworkCore.Storage public Guid TransactionId { get; } } -} +} \ No newline at end of file diff --git a/src/DotNetCore.CAP.SqlServer/ICapTransaction.SqlServer.cs b/src/DotNetCore.CAP.SqlServer/ICapTransaction.SqlServer.cs index 14e80b0..a8e051e 100644 --- a/src/DotNetCore.CAP.SqlServer/ICapTransaction.SqlServer.cs +++ b/src/DotNetCore.CAP.SqlServer/ICapTransaction.SqlServer.cs @@ -1,4 +1,7 @@ -using System.Data; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System.Data; using System.Diagnostics; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Storage; @@ -8,7 +11,9 @@ namespace DotNetCore.CAP { public class SqlServerCapTransaction : CapTransactionBase { - public SqlServerCapTransaction(IDispatcher dispatcher) : base(dispatcher) { } + public SqlServerCapTransaction(IDispatcher dispatcher) : base(dispatcher) + { + } public override void Commit() { @@ -67,5 +72,4 @@ namespace DotNetCore.CAP return new CapEFDbTransaction(capTrans); } } - -} +} \ No newline at end of file diff --git a/src/DotNetCore.CAP.SqlServer/IDbContextTransaction.CAP.cs b/src/DotNetCore.CAP.SqlServer/IDbContextTransaction.CAP.cs index dbfa691..980dc57 100644 --- a/src/DotNetCore.CAP.SqlServer/IDbContextTransaction.CAP.cs +++ b/src/DotNetCore.CAP.SqlServer/IDbContextTransaction.CAP.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using DotNetCore.CAP; // ReSharper disable once CheckNamespace @@ -11,7 +14,7 @@ namespace Microsoft.EntityFrameworkCore.Storage public CapEFDbTransaction(ICapTransaction transaction) { _transaction = transaction; - var dbContextTransaction = (IDbContextTransaction)_transaction.DbTransaction; + var dbContextTransaction = (IDbContextTransaction) _transaction.DbTransaction; TransactionId = dbContextTransaction.TransactionId; } @@ -32,4 +35,4 @@ namespace Microsoft.EntityFrameworkCore.Storage public Guid TransactionId { get; } } -} +} \ No newline at end of file From 327ef56fd890558e746d31225185b0592a55c626 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Fri, 24 Aug 2018 17:58:20 +0800 Subject: [PATCH 198/223] use Diagnostics event feature to send the message after transaction commited for Sql Server. --- .../CAP.SqlServerCapOptionsExtension.cs | 2 + .../Diagnostics/DiagnosticObserver.cs | 65 ++++++++++++++++ .../DiagnosticProcessorObserver.cs | 38 ++++++++++ .../ICapTransaction.SqlServer.cs | 74 ++++++++++++------- .../IStorage.SqlServer.cs | 9 ++- .../IStorageTransaction.SqlServer.cs | 8 +- .../DatabaseTestHost.cs | 8 +- 7 files changed, 169 insertions(+), 35 deletions(-) create mode 100644 src/DotNetCore.CAP.SqlServer/Diagnostics/DiagnosticObserver.cs create mode 100644 src/DotNetCore.CAP.SqlServer/Diagnostics/DiagnosticProcessorObserver.cs diff --git a/src/DotNetCore.CAP.SqlServer/CAP.SqlServerCapOptionsExtension.cs b/src/DotNetCore.CAP.SqlServer/CAP.SqlServerCapOptionsExtension.cs index db0d73f..dec70ec 100644 --- a/src/DotNetCore.CAP.SqlServer/CAP.SqlServerCapOptionsExtension.cs +++ b/src/DotNetCore.CAP.SqlServer/CAP.SqlServerCapOptionsExtension.cs @@ -4,6 +4,7 @@ using System; using DotNetCore.CAP.Processor; using DotNetCore.CAP.SqlServer; +using DotNetCore.CAP.SqlServer.Diagnostics; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; @@ -22,6 +23,7 @@ namespace DotNetCore.CAP public void AddServices(IServiceCollection services) { services.AddSingleton(); + services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); diff --git a/src/DotNetCore.CAP.SqlServer/Diagnostics/DiagnosticObserver.cs b/src/DotNetCore.CAP.SqlServer/Diagnostics/DiagnosticObserver.cs new file mode 100644 index 0000000..72ac344 --- /dev/null +++ b/src/DotNetCore.CAP.SqlServer/Diagnostics/DiagnosticObserver.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Data.SqlClient; +using System.Reflection; +using DotNetCore.CAP.Models; + +namespace DotNetCore.CAP.SqlServer.Diagnostics +{ + internal class DiagnosticObserver : IObserver> + { + private readonly IDispatcher _dispatcher; + private readonly ConcurrentDictionary> _bufferList; + + public DiagnosticObserver(IDispatcher dispatcher, + ConcurrentDictionary> bufferList) + { + _dispatcher = dispatcher; + _bufferList = bufferList; + } + + private const string SqlClientPrefix = "System.Data.SqlClient."; + + public const string SqlAfterCommitTransaction = SqlClientPrefix + "WriteTransactionCommitAfter"; + public const string SqlErrorCommitTransaction = SqlClientPrefix + "WriteTransactionCommitError"; + + public void OnCompleted() + { + + } + + public void OnError(Exception error) + { + + } + + public void OnNext(KeyValuePair evt) + { + if (evt.Key == SqlAfterCommitTransaction) + { + var sqlConnection = (SqlConnection) GetProperty(evt.Value, "Connection"); + var transactionKey = sqlConnection.ClientConnectionId; + if (_bufferList.TryRemove(transactionKey, out var msgList)) + { + foreach (var message in msgList) + { + _dispatcher.EnqueueToPublish(message); + } + } + } + else if (evt.Key == SqlErrorCommitTransaction) + { + var sqlConnection = (SqlConnection) GetProperty(evt.Value, "Connection"); + var transactionKey = sqlConnection.ClientConnectionId; + + _bufferList.TryRemove(transactionKey, out _); + } + } + + static object GetProperty(object _this, string propertyName) + { + return _this.GetType().GetTypeInfo().GetDeclaredProperty(propertyName)?.GetValue(_this); + } + } +} \ No newline at end of file diff --git a/src/DotNetCore.CAP.SqlServer/Diagnostics/DiagnosticProcessorObserver.cs b/src/DotNetCore.CAP.SqlServer/Diagnostics/DiagnosticProcessorObserver.cs new file mode 100644 index 0000000..d7e5102 --- /dev/null +++ b/src/DotNetCore.CAP.SqlServer/Diagnostics/DiagnosticProcessorObserver.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; +using DotNetCore.CAP.Models; + +namespace DotNetCore.CAP.SqlServer.Diagnostics +{ + public class DiagnosticProcessorObserver : IObserver + { + private readonly IDispatcher _dispatcher; + public const string DiagnosticListenerName = "SqlClientDiagnosticListener"; + + public ConcurrentDictionary> BufferList { get; } + + public DiagnosticProcessorObserver(IDispatcher dispatcher) + { + _dispatcher = dispatcher; + BufferList = new ConcurrentDictionary>(); + } + + public void OnCompleted() + { + } + + public void OnError(Exception error) + { + } + + public void OnNext(DiagnosticListener listener) + { + if (listener.Name == DiagnosticListenerName) + { + listener.Subscribe(new DiagnosticObserver(_dispatcher, BufferList)); + } + } + } +} \ No newline at end of file diff --git a/src/DotNetCore.CAP.SqlServer/ICapTransaction.SqlServer.cs b/src/DotNetCore.CAP.SqlServer/ICapTransaction.SqlServer.cs index a8e051e..19d1eec 100644 --- a/src/DotNetCore.CAP.SqlServer/ICapTransaction.SqlServer.cs +++ b/src/DotNetCore.CAP.SqlServer/ICapTransaction.SqlServer.cs @@ -1,8 +1,12 @@ // Copyright (c) .NET Core Community. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. +using System; +using System.Collections.Generic; using System.Data; -using System.Diagnostics; +using System.Data.SqlClient; +using DotNetCore.CAP.Models; +using DotNetCore.CAP.SqlServer.Diagnostics; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Storage; @@ -11,50 +15,68 @@ namespace DotNetCore.CAP { public class SqlServerCapTransaction : CapTransactionBase { - public SqlServerCapTransaction(IDispatcher dispatcher) : base(dispatcher) + private readonly DiagnosticProcessorObserver _diagnosticProcessor; + + public SqlServerCapTransaction(IDispatcher dispatcher, + DiagnosticProcessorObserver diagnosticProcessor) : base(dispatcher) { + _diagnosticProcessor = diagnosticProcessor; } - public override void Commit() + protected override void AddToSent(CapPublishedMessage msg) { - Debug.Assert(DbTransaction != null); - - switch (DbTransaction) + var transactionKey = ((SqlConnection)((IDbTransaction)DbTransaction).Connection).ClientConnectionId; + if (_diagnosticProcessor.BufferList.TryGetValue(transactionKey, out var list)) { - case IDbTransaction dbTransaction: - dbTransaction.Commit(); - break; - case IDbContextTransaction dbContextTransaction: - dbContextTransaction.Commit(); - break; + list.Add(msg); } + else + { + var msgList = new List(1) { msg }; + _diagnosticProcessor.BufferList.TryAdd(transactionKey, msgList); + } + } - Flush(); + public override void Commit() + { + throw new NotImplementedException(); } public override void Rollback() { - Debug.Assert(DbTransaction != null); - - switch (DbTransaction) - { - case IDbTransaction dbTransaction: - dbTransaction.Rollback(); - break; - case IDbContextTransaction dbContextTransaction: - dbContextTransaction.Rollback(); - break; - } + throw new NotImplementedException(); } public override void Dispose() { - (DbTransaction as IDbTransaction)?.Dispose(); + } } public static class CapTransactionExtensions { + public static ICapTransaction Begin(this ICapTransaction transaction, + IDbTransaction dbTransaction, bool autoCommit = false) + { + transaction.DbTransaction = dbTransaction; + transaction.AutoCommit = autoCommit; + + return transaction; + } + + public static IDbTransaction BeginTransaction(this IDbConnection dbConnection, + ICapPublisher publisher, bool autoCommit = false) + { + if (dbConnection.State == ConnectionState.Closed) + { + dbConnection.Open(); + } + + var dbTransaction = dbConnection.BeginTransaction(); + var capTransaction = publisher.Transaction.Begin(dbTransaction, autoCommit); + return (IDbTransaction)capTransaction.DbTransaction; + } + public static ICapTransaction Begin(this ICapTransaction transaction, IDbContextTransaction dbTransaction, bool autoCommit = false) { @@ -64,7 +86,7 @@ namespace DotNetCore.CAP return transaction; } - public static IDbContextTransaction BeginAndJoinToTransaction(this DatabaseFacade database, + public static IDbContextTransaction BeginTransaction(this DatabaseFacade database, ICapPublisher publisher, bool autoCommit = false) { var trans = database.BeginTransaction(); diff --git a/src/DotNetCore.CAP.SqlServer/IStorage.SqlServer.cs b/src/DotNetCore.CAP.SqlServer/IStorage.SqlServer.cs index 5b6dbd5..f6995c3 100644 --- a/src/DotNetCore.CAP.SqlServer/IStorage.SqlServer.cs +++ b/src/DotNetCore.CAP.SqlServer/IStorage.SqlServer.cs @@ -4,10 +4,12 @@ using System; using System.Data; using System.Data.SqlClient; +using System.Diagnostics; using System.Threading; using System.Threading.Tasks; using Dapper; using DotNetCore.CAP.Dashboard; +using DotNetCore.CAP.SqlServer.Diagnostics; using Microsoft.Extensions.Logging; namespace DotNetCore.CAP.SqlServer @@ -18,12 +20,15 @@ namespace DotNetCore.CAP.SqlServer private readonly IDbConnection _existingConnection = null; private readonly ILogger _logger; private readonly SqlServerOptions _options; + private readonly DiagnosticProcessorObserver _diagnosticProcessorObserver; public SqlServerStorage(ILogger logger, CapOptions capOptions, - SqlServerOptions options) + SqlServerOptions options, + DiagnosticProcessorObserver diagnosticProcessorObserver) { _options = options; + _diagnosticProcessorObserver = diagnosticProcessorObserver; _logger = logger; _capOptions = capOptions; } @@ -53,6 +58,8 @@ namespace DotNetCore.CAP.SqlServer } _logger.LogDebug("Ensuring all create database tables script are applied."); + + DiagnosticListener.AllListeners.Subscribe(_diagnosticProcessorObserver); } protected virtual string CreateDbTablesScript(string schema) diff --git a/src/DotNetCore.CAP.SqlServer/IStorageTransaction.SqlServer.cs b/src/DotNetCore.CAP.SqlServer/IStorageTransaction.SqlServer.cs index 687639d..96cc849 100644 --- a/src/DotNetCore.CAP.SqlServer/IStorageTransaction.SqlServer.cs +++ b/src/DotNetCore.CAP.SqlServer/IStorageTransaction.SqlServer.cs @@ -14,7 +14,6 @@ namespace DotNetCore.CAP.SqlServer { private readonly IDbConnection _dbConnection; - private readonly IDbTransaction _dbTransaction; private readonly string _schema; public SqlServerStorageTransaction(SqlServerStorageConnection connection) @@ -24,7 +23,6 @@ namespace DotNetCore.CAP.SqlServer _dbConnection = new SqlConnection(options.ConnectionString); _dbConnection.Open(); - _dbTransaction = _dbConnection.BeginTransaction(IsolationLevel.ReadCommitted); } public void UpdateMessage(CapPublishedMessage message) @@ -36,7 +34,7 @@ namespace DotNetCore.CAP.SqlServer var sql = $"UPDATE [{_schema}].[Published] SET [Retries] = @Retries,[Content] = @Content,[ExpiresAt] = @ExpiresAt,[StatusName]=@StatusName WHERE Id=@Id;"; - _dbConnection.Execute(sql, message, _dbTransaction); + _dbConnection.Execute(sql, message); } public void UpdateMessage(CapReceivedMessage message) @@ -48,18 +46,16 @@ namespace DotNetCore.CAP.SqlServer var sql = $"UPDATE [{_schema}].[Received] SET [Retries] = @Retries,[Content] = @Content,[ExpiresAt] = @ExpiresAt,[StatusName]=@StatusName WHERE Id=@Id;"; - _dbConnection.Execute(sql, message, _dbTransaction); + _dbConnection.Execute(sql, message); } public Task CommitAsync() { - _dbTransaction.Commit(); return Task.CompletedTask; } public void Dispose() { - _dbTransaction.Dispose(); _dbConnection.Dispose(); } } diff --git a/test/DotNetCore.CAP.SqlServer.Test/DatabaseTestHost.cs b/test/DotNetCore.CAP.SqlServer.Test/DatabaseTestHost.cs index 9c40b85..218fedb 100644 --- a/test/DotNetCore.CAP.SqlServer.Test/DatabaseTestHost.cs +++ b/test/DotNetCore.CAP.SqlServer.Test/DatabaseTestHost.cs @@ -2,16 +2,18 @@ using System; using System.Data; using System.Data.SqlClient; using Dapper; +using DotNetCore.CAP.SqlServer.Diagnostics; using Microsoft.Extensions.Logging; using Moq; namespace DotNetCore.CAP.SqlServer.Test { - public abstract class DatabaseTestHost:IDisposable + public abstract class DatabaseTestHost : IDisposable { protected ILogger Logger; protected CapOptions CapOptions; protected SqlServerOptions SqlSeverOptions; + protected DiagnosticProcessorObserver DiagnosticProcessorObserver; public bool SqlObjectInstalled; @@ -23,6 +25,8 @@ namespace DotNetCore.CAP.SqlServer.Test .SetupProperty(x => x.ConnectionString, ConnectionUtil.GetConnectionString()) .Object; + DiagnosticProcessorObserver = new Mock().Object; + InitializeDatabase(); } @@ -42,7 +46,7 @@ IF NOT EXISTS (SELECT * FROM sysdatabases WHERE name = N'{databaseName}') CREATE DATABASE [{databaseName}];"); } - new SqlServerStorage(Logger, CapOptions, SqlSeverOptions).InitializeAsync().GetAwaiter().GetResult(); + new SqlServerStorage(Logger, CapOptions, SqlSeverOptions, DiagnosticProcessorObserver).InitializeAsync().GetAwaiter().GetResult(); SqlObjectInstalled = true; } From 60c028f11cdaeca58e98197e1f70c18fb57b6f6e Mon Sep 17 00:00:00 2001 From: Savorboard Date: Fri, 24 Aug 2018 17:58:41 +0800 Subject: [PATCH 199/223] refactor extension methods --- .../ICapTransaction.MongoDB.cs | 10 +---- .../ICapTransaction.MySql.cs | 24 ++++++++++- .../ICapTransaction.PostgreSql.cs | 23 ++++++++++- src/DotNetCore.CAP/ICapTransaction.Base.cs | 2 +- .../Internal/RelationDbTransaction.cs | 38 ------------------ src/DotNetCore.CAP/TransactionExtensions.cs | 40 ------------------- 6 files changed, 48 insertions(+), 89 deletions(-) delete mode 100644 src/DotNetCore.CAP/Internal/RelationDbTransaction.cs delete mode 100644 src/DotNetCore.CAP/TransactionExtensions.cs diff --git a/src/DotNetCore.CAP.MongoDB/ICapTransaction.MongoDB.cs b/src/DotNetCore.CAP.MongoDB/ICapTransaction.MongoDB.cs index ab7d9ab..a2ae5a0 100644 --- a/src/DotNetCore.CAP.MongoDB/ICapTransaction.MongoDB.cs +++ b/src/DotNetCore.CAP.MongoDB/ICapTransaction.MongoDB.cs @@ -58,18 +58,12 @@ namespace DotNetCore.CAP return transaction; } - public static IClientSessionHandle BeginAndJoinToTransaction(this IClientSessionHandle clientSessionHandle, + public static IClientSessionHandle StartTransaction(this IMongoClient client, ICapPublisher publisher, bool autoCommit = false) { + var clientSessionHandle = client.StartSession(); var capTrans = publisher.Transaction.Begin(clientSessionHandle, autoCommit); return new CapMongoDbClientSessionHandle(capTrans); } - - public static IClientSessionHandle StartAndJoinToTransaction(this IMongoClient client, - ICapPublisher publisher, bool autoCommit = false) - { - var clientSessionHandle = client.StartSession(); - return BeginAndJoinToTransaction(clientSessionHandle, publisher, autoCommit); - } } } \ No newline at end of file diff --git a/src/DotNetCore.CAP.MySql/ICapTransaction.MySql.cs b/src/DotNetCore.CAP.MySql/ICapTransaction.MySql.cs index 7b0377b..749622b 100644 --- a/src/DotNetCore.CAP.MySql/ICapTransaction.MySql.cs +++ b/src/DotNetCore.CAP.MySql/ICapTransaction.MySql.cs @@ -64,12 +64,34 @@ namespace DotNetCore.CAP return transaction; } - public static IDbContextTransaction BeginAndJoinToTransaction(this DatabaseFacade database, + public static ICapTransaction Begin(this ICapTransaction transaction, + IDbTransaction dbTransaction, bool autoCommit = false) + { + + transaction.DbTransaction = dbTransaction; + transaction.AutoCommit = autoCommit; + + return transaction; + } + + public static IDbContextTransaction BeginTransaction(this DatabaseFacade database, ICapPublisher publisher, bool autoCommit = false) { var trans = database.BeginTransaction(); var capTrans = publisher.Transaction.Begin(trans, autoCommit); return new CapEFDbTransaction(capTrans); } + + public static ICapTransaction BeginTransaction(this IDbConnection dbConnection, + ICapPublisher publisher, bool autoCommit = false) + { + if (dbConnection.State == ConnectionState.Closed) + { + dbConnection.Open(); + } + + var dbTransaction = dbConnection.BeginTransaction(); + return publisher.Transaction.Begin(dbTransaction, autoCommit); + } } } \ No newline at end of file diff --git a/src/DotNetCore.CAP.PostgreSql/ICapTransaction.PostgreSql.cs b/src/DotNetCore.CAP.PostgreSql/ICapTransaction.PostgreSql.cs index 799bc21..2b5b864 100644 --- a/src/DotNetCore.CAP.PostgreSql/ICapTransaction.PostgreSql.cs +++ b/src/DotNetCore.CAP.PostgreSql/ICapTransaction.PostgreSql.cs @@ -55,6 +55,15 @@ namespace DotNetCore.CAP public static class CapTransactionExtensions { + public static ICapTransaction Begin(this ICapTransaction transaction, + IDbTransaction dbTransaction, bool autoCommit = false) + { + transaction.DbTransaction = dbTransaction; + transaction.AutoCommit = autoCommit; + + return transaction; + } + public static ICapTransaction Begin(this ICapTransaction transaction, IDbContextTransaction dbTransaction, bool autoCommit = false) { @@ -64,7 +73,19 @@ namespace DotNetCore.CAP return transaction; } - public static IDbContextTransaction BeginAndJoinToTransaction(this DatabaseFacade database, + public static ICapTransaction BeginTransaction(this IDbConnection dbConnection, + ICapPublisher publisher, bool autoCommit = false) + { + if (dbConnection.State == ConnectionState.Closed) + { + dbConnection.Open(); + } + + var dbTransaction = dbConnection.BeginTransaction(); + return publisher.Transaction.Begin(dbTransaction, autoCommit); + } + + public static IDbContextTransaction BeginTransaction(this DatabaseFacade database, ICapPublisher publisher, bool autoCommit = false) { var trans = database.BeginTransaction(); diff --git a/src/DotNetCore.CAP/ICapTransaction.Base.cs b/src/DotNetCore.CAP/ICapTransaction.Base.cs index 3ab5fb2..141a300 100644 --- a/src/DotNetCore.CAP/ICapTransaction.Base.cs +++ b/src/DotNetCore.CAP/ICapTransaction.Base.cs @@ -19,7 +19,7 @@ namespace DotNetCore.CAP public object DbTransaction { get; set; } - protected internal void AddToSent(CapPublishedMessage msg) + protected internal virtual void AddToSent(CapPublishedMessage msg) { _bufferList.Add(msg); } diff --git a/src/DotNetCore.CAP/Internal/RelationDbTransaction.cs b/src/DotNetCore.CAP/Internal/RelationDbTransaction.cs deleted file mode 100644 index 10cee48..0000000 --- a/src/DotNetCore.CAP/Internal/RelationDbTransaction.cs +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (c) .NET Core Community. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. - -using System.Data; - -namespace DotNetCore.CAP.Internal -{ - internal class RelationDbTransaction : IDbTransaction - { - private readonly ICapTransaction _capTransaction; - - public RelationDbTransaction(ICapTransaction capTransaction) - { - _capTransaction = capTransaction; - var dbTransaction = (IDbTransaction) capTransaction.DbTransaction; - Connection = dbTransaction.Connection; - IsolationLevel = dbTransaction.IsolationLevel; - } - - public void Dispose() - { - _capTransaction.Dispose(); - } - - public void Commit() - { - _capTransaction.Commit(); - } - - public void Rollback() - { - _capTransaction.Rollback(); - } - - public IDbConnection Connection { get; } - public IsolationLevel IsolationLevel { get; } - } -} \ No newline at end of file diff --git a/src/DotNetCore.CAP/TransactionExtensions.cs b/src/DotNetCore.CAP/TransactionExtensions.cs deleted file mode 100644 index e8af0e4..0000000 --- a/src/DotNetCore.CAP/TransactionExtensions.cs +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (c) .NET Core Community. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. - -using System.Data; -using DotNetCore.CAP.Internal; - -namespace DotNetCore.CAP -{ - public static class TransactionExtensions - { - public static ICapTransaction Begin(this ICapTransaction transaction, - IDbTransaction dbTransaction, bool autoCommit = false) - { - - transaction.DbTransaction = dbTransaction; - transaction.AutoCommit = autoCommit; - - return transaction; - } - - public static IDbTransaction JoinToTransaction(this IDbTransaction dbTransaction, - ICapPublisher publisher, bool autoCommit = false) - { - dbTransaction = new RelationDbTransaction(publisher.Transaction.Begin(dbTransaction, autoCommit)); - return dbTransaction; - } - - public static IDbTransaction BeginAndJoinToTransaction(this IDbConnection dbConnection, - ICapPublisher publisher, bool autoCommit = false) - { - if (dbConnection.State == ConnectionState.Closed) - { - dbConnection.Open(); - } - - var dbTransaction = dbConnection.BeginTransaction(); - return dbTransaction.JoinToTransaction(publisher, autoCommit); - } - } -} \ No newline at end of file From dae03f33b78519141bec5f4154ebd2fab4702b8b Mon Sep 17 00:00:00 2001 From: Savorboard Date: Fri, 24 Aug 2018 17:58:51 +0800 Subject: [PATCH 200/223] update samples --- .../Controllers/ValuesController.cs | 16 ++++++++-------- samples/Sample.Kafka.MySql/Startup.cs | 4 +++- .../Controllers/ValuesController.cs | 4 ++-- .../Controllers/ValuesController.cs | 7 ++++--- 4 files changed, 17 insertions(+), 14 deletions(-) diff --git a/samples/Sample.Kafka.MySql/Controllers/ValuesController.cs b/samples/Sample.Kafka.MySql/Controllers/ValuesController.cs index 361c606..9535b1d 100644 --- a/samples/Sample.Kafka.MySql/Controllers/ValuesController.cs +++ b/samples/Sample.Kafka.MySql/Controllers/ValuesController.cs @@ -1,8 +1,11 @@ using System; +using System.Data; +using System.Data.SqlClient; +using System.Transactions; using Dapper; using DotNetCore.CAP; using Microsoft.AspNetCore.Mvc; -using MySql.Data.MySqlClient; +//using MySql.Data.MySqlClient; namespace Sample.Kafka.MySql.Controllers { @@ -28,17 +31,14 @@ namespace Sample.Kafka.MySql.Controllers public IActionResult AdonetWithTransaction() { //NOTE: Add `IgnoreCommandTransaction=true;` to your connection string, see https://github.com/mysql-net/MySqlConnector/issues/474 - using (var connection = new MySqlConnection(Startup.ConnectionString)) + using (var connection = new SqlConnection(Startup.ConnectionString)) { - using (var transaction = connection.BeginAndJoinToTransaction(_capBus, autoCommit: false)) + using (var transaction = connection.BeginTransaction(_capBus, autoCommit: false)) { //your business code - connection.Execute("insert into test(name) values('test')", transaction); + connection.Execute("insert into dbo.test1(tname) values('test');", transaction: (IDbTransaction)transaction.DbTransaction); - for (int i = 0; i < 5; i++) - { - _capBus.Publish("sample.rabbitmq.mysql", DateTime.Now); - } + _capBus.Publish("sample.rabbitmq.mysql", DateTime.Now); transaction.Commit(); } diff --git a/samples/Sample.Kafka.MySql/Startup.cs b/samples/Sample.Kafka.MySql/Startup.cs index 9ff4345..583a416 100644 --- a/samples/Sample.Kafka.MySql/Startup.cs +++ b/samples/Sample.Kafka.MySql/Startup.cs @@ -6,13 +6,15 @@ namespace Sample.Kafka.MySql public class Startup { public const string ConnectionString = "Server=localhost;Database=testcap;UserId=root;Password=123123;"; + //public const string ConnectionString = "Server=(localdb)\\ProjectsV13;Integrated Security=SSPI;Database=testcap"; public void ConfigureServices(IServiceCollection services) { services.AddCap(x => { x.UseMySql(ConnectionString); - x.UseKafka("localhost:9092"); + //x.UseSqlServer(ConnectionString); + x.UseKafka("192.168.10.110:9092"); x.UseDashboard(); }); diff --git a/samples/Sample.RabbitMQ.MongoDB/Controllers/ValuesController.cs b/samples/Sample.RabbitMQ.MongoDB/Controllers/ValuesController.cs index c811c83..067b6d4 100644 --- a/samples/Sample.RabbitMQ.MongoDB/Controllers/ValuesController.cs +++ b/samples/Sample.RabbitMQ.MongoDB/Controllers/ValuesController.cs @@ -35,7 +35,7 @@ namespace Sample.RabbitMQ.MongoDB.Controllers //var mycollection = _client.GetDatabase("test").GetCollection("test.collection"); //mycollection.InsertOne(new BsonDocument { { "test", "test" } }); - using (var session = _client.StartAndJoinToTransaction(_capBus, autoCommit: false)) + using (var session = _client.StartTransaction(_capBus, autoCommit: false)) { var collection = _client.GetDatabase("test").GetCollection("test.collection"); collection.InsertOne(session, new BsonDocument { { "hello", "world" } }); @@ -55,7 +55,7 @@ namespace Sample.RabbitMQ.MongoDB.Controllers //var mycollection = _client.GetDatabase("test").GetCollection("test.collection"); //mycollection.InsertOne(new BsonDocument { { "test", "test" } }); - using (var session = _client.StartAndJoinToTransaction(_capBus, autoCommit: true)) + using (var session = _client.StartTransaction(_capBus, autoCommit: true)) { var collection = _client.GetDatabase("test").GetCollection("test.collection"); collection.InsertOne(session, new BsonDocument { { "hello", "world" } }); diff --git a/samples/Sample.RabbitMQ.MySql/Controllers/ValuesController.cs b/samples/Sample.RabbitMQ.MySql/Controllers/ValuesController.cs index 7f31680..9fa3b82 100644 --- a/samples/Sample.RabbitMQ.MySql/Controllers/ValuesController.cs +++ b/samples/Sample.RabbitMQ.MySql/Controllers/ValuesController.cs @@ -1,4 +1,5 @@ using System; +using System.Data; using Dapper; using DotNetCore.CAP; using Microsoft.AspNetCore.Mvc; @@ -30,10 +31,10 @@ namespace Sample.RabbitMQ.MySql.Controllers //NOTE: Add `IgnoreCommandTransaction=true;` to your connection string, see https://github.com/mysql-net/MySqlConnector/issues/474 using (var connection = new MySqlConnection(AppDbContext.ConnectionString)) { - using (var transaction = connection.BeginAndJoinToTransaction(_capBus, autoCommit: false)) + using (var transaction = connection.BeginTransaction(_capBus, autoCommit: false)) { //your business code - connection.Execute("insert into test(name) values('test')", transaction); + connection.Execute("insert into test(name) values('test')", transaction: (IDbTransaction)transaction.DbTransaction); for (int i = 0; i < 5; i++) { @@ -50,7 +51,7 @@ namespace Sample.RabbitMQ.MySql.Controllers [Route("~/ef/transaction")] public IActionResult EntityFrameworkWithTransaction([FromServices]AppDbContext dbContext) { - using (var trans = dbContext.Database.BeginAndJoinToTransaction(_capBus, autoCommit: false)) + using (var trans = dbContext.Database.BeginTransaction(_capBus, autoCommit: false)) { dbContext.Persons.Add(new Person() { Name = "ef.transaction" }); From 45bd5ed2d157914a67c858ea7929bc9afee4fac9 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Fri, 24 Aug 2018 18:00:26 +0800 Subject: [PATCH 201/223] update samples --- samples/Sample.Kafka.MySql/Controllers/ValuesController.cs | 2 -- samples/Sample.RabbitMQ.MySql/AppDbContext.cs | 2 +- samples/Sample.RabbitMQ.MySql/Controllers/ValuesController.cs | 1 - 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/samples/Sample.Kafka.MySql/Controllers/ValuesController.cs b/samples/Sample.Kafka.MySql/Controllers/ValuesController.cs index 9535b1d..1f4adeb 100644 --- a/samples/Sample.Kafka.MySql/Controllers/ValuesController.cs +++ b/samples/Sample.Kafka.MySql/Controllers/ValuesController.cs @@ -1,7 +1,6 @@ using System; using System.Data; using System.Data.SqlClient; -using System.Transactions; using Dapper; using DotNetCore.CAP; using Microsoft.AspNetCore.Mvc; @@ -30,7 +29,6 @@ namespace Sample.Kafka.MySql.Controllers [Route("~/adonet/transaction")] public IActionResult AdonetWithTransaction() { - //NOTE: Add `IgnoreCommandTransaction=true;` to your connection string, see https://github.com/mysql-net/MySqlConnector/issues/474 using (var connection = new SqlConnection(Startup.ConnectionString)) { using (var transaction = connection.BeginTransaction(_capBus, autoCommit: false)) diff --git a/samples/Sample.RabbitMQ.MySql/AppDbContext.cs b/samples/Sample.RabbitMQ.MySql/AppDbContext.cs index 9e9274c..60f4622 100644 --- a/samples/Sample.RabbitMQ.MySql/AppDbContext.cs +++ b/samples/Sample.RabbitMQ.MySql/AppDbContext.cs @@ -11,7 +11,7 @@ namespace Sample.RabbitMQ.MySql public class AppDbContext : DbContext { - public const string ConnectionString = "Server=192.168.10.110;Database=testcap;UserId=root;Password=123123;IgnoreCommandTransaction=true;"; + public const string ConnectionString = "Server=192.168.10.110;Database=testcap;UserId=root;Password=123123;"; public DbSet Persons { get; set; } diff --git a/samples/Sample.RabbitMQ.MySql/Controllers/ValuesController.cs b/samples/Sample.RabbitMQ.MySql/Controllers/ValuesController.cs index 9fa3b82..503fcdb 100644 --- a/samples/Sample.RabbitMQ.MySql/Controllers/ValuesController.cs +++ b/samples/Sample.RabbitMQ.MySql/Controllers/ValuesController.cs @@ -28,7 +28,6 @@ namespace Sample.RabbitMQ.MySql.Controllers [Route("~/adonet/transaction")] public IActionResult AdonetWithTransaction() { - //NOTE: Add `IgnoreCommandTransaction=true;` to your connection string, see https://github.com/mysql-net/MySqlConnector/issues/474 using (var connection = new MySqlConnection(AppDbContext.ConnectionString)) { using (var transaction = connection.BeginTransaction(_capBus, autoCommit: false)) From 1bb0c8d5cd9bf5bb577df9216e9c6eda866f95a9 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Sat, 25 Aug 2018 18:52:14 +0800 Subject: [PATCH 202/223] add sqlserver samples --- CAP.sln | 18 +++++++++--------- .../Controllers/ValuesController.cs | 6 ++---- .../Program.cs | 2 +- .../Sample.Kafka.SqlServer.csproj} | 5 +++-- .../Startup.cs | 10 ++++------ .../appsettings.json | 0 6 files changed, 19 insertions(+), 22 deletions(-) rename samples/{Sample.Kafka.MySql => Sample.Kafka.SqlServer}/Controllers/ValuesController.cs (89%) rename samples/{Sample.Kafka.MySql => Sample.Kafka.SqlServer}/Program.cs (92%) rename samples/{Sample.Kafka.MySql/Sample.Kafka.MySql.csproj => Sample.Kafka.SqlServer/Sample.Kafka.SqlServer.csproj} (70%) rename samples/{Sample.Kafka.MySql => Sample.Kafka.SqlServer}/Startup.cs (59%) rename samples/{Sample.Kafka.MySql => Sample.Kafka.SqlServer}/appsettings.json (100%) diff --git a/CAP.sln b/CAP.sln index bf7efad..3455243 100644 --- a/CAP.sln +++ b/CAP.sln @@ -58,13 +58,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNetCore.CAP.PostgreSql", EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNetCore.CAP.PostgreSql.Test", "test\DotNetCore.CAP.PostgreSql.Test\DotNetCore.CAP.PostgreSql.Test.csproj", "{7CA3625D-1817-4695-881D-7E79A1E1DED2}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sample.Kafka.MySql", "samples\Sample.Kafka.MySql\Sample.Kafka.MySql.csproj", "{9CB51105-A85B-42A4-AFDE-A4FC34D9EA91}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNetCore.CAP.MongoDB.Test", "test\DotNetCore.CAP.MongoDB.Test\DotNetCore.CAP.MongoDB.Test.csproj", "{C143FCDF-E7F3-46F8-987E-A1BA38C1639D}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotNetCore.CAP.MongoDB.Test", "test\DotNetCore.CAP.MongoDB.Test\DotNetCore.CAP.MongoDB.Test.csproj", "{C143FCDF-E7F3-46F8-987E-A1BA38C1639D}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNetCore.CAP.MongoDB", "src\DotNetCore.CAP.MongoDB\DotNetCore.CAP.MongoDB.csproj", "{77C0AC02-C44B-49D5-B969-7D5305FC20A5}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotNetCore.CAP.MongoDB", "src\DotNetCore.CAP.MongoDB\DotNetCore.CAP.MongoDB.csproj", "{77C0AC02-C44B-49D5-B969-7D5305FC20A5}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sample.RabbitMQ.MongoDB", "samples\Sample.RabbitMQ.MongoDB\Sample.RabbitMQ.MongoDB.csproj", "{4473DE19-E8D2-4B57-80A8-C8AAA2BFA20F}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sample.RabbitMQ.MongoDB", "samples\Sample.RabbitMQ.MongoDB\Sample.RabbitMQ.MongoDB.csproj", "{4473DE19-E8D2-4B57-80A8-C8AAA2BFA20F}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sample.Kafka.SqlServer", "samples\Sample.Kafka.SqlServer\Sample.Kafka.SqlServer.csproj", "{CD276810-09A2-4105-8798-D65A8AA7C509}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -115,10 +115,6 @@ Global {7CA3625D-1817-4695-881D-7E79A1E1DED2}.Debug|Any CPU.Build.0 = Debug|Any CPU {7CA3625D-1817-4695-881D-7E79A1E1DED2}.Release|Any CPU.ActiveCfg = Release|Any CPU {7CA3625D-1817-4695-881D-7E79A1E1DED2}.Release|Any CPU.Build.0 = Release|Any CPU - {9CB51105-A85B-42A4-AFDE-A4FC34D9EA91}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {9CB51105-A85B-42A4-AFDE-A4FC34D9EA91}.Debug|Any CPU.Build.0 = Debug|Any CPU - {9CB51105-A85B-42A4-AFDE-A4FC34D9EA91}.Release|Any CPU.ActiveCfg = Release|Any CPU - {9CB51105-A85B-42A4-AFDE-A4FC34D9EA91}.Release|Any CPU.Build.0 = Release|Any CPU {C143FCDF-E7F3-46F8-987E-A1BA38C1639D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C143FCDF-E7F3-46F8-987E-A1BA38C1639D}.Debug|Any CPU.Build.0 = Debug|Any CPU {C143FCDF-E7F3-46F8-987E-A1BA38C1639D}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -131,6 +127,10 @@ Global {4473DE19-E8D2-4B57-80A8-C8AAA2BFA20F}.Debug|Any CPU.Build.0 = Debug|Any CPU {4473DE19-E8D2-4B57-80A8-C8AAA2BFA20F}.Release|Any CPU.ActiveCfg = Release|Any CPU {4473DE19-E8D2-4B57-80A8-C8AAA2BFA20F}.Release|Any CPU.Build.0 = Release|Any CPU + {CD276810-09A2-4105-8798-D65A8AA7C509}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CD276810-09A2-4105-8798-D65A8AA7C509}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CD276810-09A2-4105-8798-D65A8AA7C509}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CD276810-09A2-4105-8798-D65A8AA7C509}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -147,10 +147,10 @@ Global {9F3F9BFE-7B6A-4A7A-A6E6-8B517D611873} = {3A6B6931-A123-477A-9469-8B468B5385AF} {82C403AB-ED68-4084-9A1D-11334F9F08F9} = {9B2AE124-6636-4DE9-83A3-70360DABD0C4} {7CA3625D-1817-4695-881D-7E79A1E1DED2} = {C09CDAB0-6DD4-46E9-B7F3-3EF2A4741EA0} - {9CB51105-A85B-42A4-AFDE-A4FC34D9EA91} = {3A6B6931-A123-477A-9469-8B468B5385AF} {C143FCDF-E7F3-46F8-987E-A1BA38C1639D} = {C09CDAB0-6DD4-46E9-B7F3-3EF2A4741EA0} {77C0AC02-C44B-49D5-B969-7D5305FC20A5} = {9B2AE124-6636-4DE9-83A3-70360DABD0C4} {4473DE19-E8D2-4B57-80A8-C8AAA2BFA20F} = {3A6B6931-A123-477A-9469-8B468B5385AF} + {CD276810-09A2-4105-8798-D65A8AA7C509} = {3A6B6931-A123-477A-9469-8B468B5385AF} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {2E70565D-94CF-40B4-BFE1-AC18D5F736AB} diff --git a/samples/Sample.Kafka.MySql/Controllers/ValuesController.cs b/samples/Sample.Kafka.SqlServer/Controllers/ValuesController.cs similarity index 89% rename from samples/Sample.Kafka.MySql/Controllers/ValuesController.cs rename to samples/Sample.Kafka.SqlServer/Controllers/ValuesController.cs index 1f4adeb..da78c06 100644 --- a/samples/Sample.Kafka.MySql/Controllers/ValuesController.cs +++ b/samples/Sample.Kafka.SqlServer/Controllers/ValuesController.cs @@ -1,12 +1,10 @@ using System; -using System.Data; using System.Data.SqlClient; using Dapper; using DotNetCore.CAP; using Microsoft.AspNetCore.Mvc; -//using MySql.Data.MySqlClient; -namespace Sample.Kafka.MySql.Controllers +namespace Sample.Kafka.SqlServer.Controllers { [Route("api/[controller]")] public class ValuesController : Controller @@ -34,7 +32,7 @@ namespace Sample.Kafka.MySql.Controllers using (var transaction = connection.BeginTransaction(_capBus, autoCommit: false)) { //your business code - connection.Execute("insert into dbo.test1(tname) values('test');", transaction: (IDbTransaction)transaction.DbTransaction); + connection.Execute("insert into dbo.test1(tname) values('test');", transaction: transaction); _capBus.Publish("sample.rabbitmq.mysql", DateTime.Now); diff --git a/samples/Sample.Kafka.MySql/Program.cs b/samples/Sample.Kafka.SqlServer/Program.cs similarity index 92% rename from samples/Sample.Kafka.MySql/Program.cs rename to samples/Sample.Kafka.SqlServer/Program.cs index d92eedd..9fe450d 100644 --- a/samples/Sample.Kafka.MySql/Program.cs +++ b/samples/Sample.Kafka.SqlServer/Program.cs @@ -1,7 +1,7 @@ using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; -namespace Sample.Kafka.MySql +namespace Sample.Kafka.SqlServer { public class Program { diff --git a/samples/Sample.Kafka.MySql/Sample.Kafka.MySql.csproj b/samples/Sample.Kafka.SqlServer/Sample.Kafka.SqlServer.csproj similarity index 70% rename from samples/Sample.Kafka.MySql/Sample.Kafka.MySql.csproj rename to samples/Sample.Kafka.SqlServer/Sample.Kafka.SqlServer.csproj index e64e18b..6a4b68b 100644 --- a/samples/Sample.Kafka.MySql/Sample.Kafka.MySql.csproj +++ b/samples/Sample.Kafka.SqlServer/Sample.Kafka.SqlServer.csproj @@ -2,9 +2,10 @@ netcoreapp2.1 - Sample.Kafka.MySql + Sample.Kafka.SqlServer NU1701 NU1701 + Sample.Kafka.SqlServer @@ -12,7 +13,7 @@ - + diff --git a/samples/Sample.Kafka.MySql/Startup.cs b/samples/Sample.Kafka.SqlServer/Startup.cs similarity index 59% rename from samples/Sample.Kafka.MySql/Startup.cs rename to samples/Sample.Kafka.SqlServer/Startup.cs index 583a416..e74cae0 100644 --- a/samples/Sample.Kafka.MySql/Startup.cs +++ b/samples/Sample.Kafka.SqlServer/Startup.cs @@ -1,20 +1,18 @@ using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.DependencyInjection; -namespace Sample.Kafka.MySql +namespace Sample.Kafka.SqlServer { public class Startup { - public const string ConnectionString = "Server=localhost;Database=testcap;UserId=root;Password=123123;"; - //public const string ConnectionString = "Server=(localdb)\\ProjectsV13;Integrated Security=SSPI;Database=testcap"; + public const string ConnectionString = "Server=localhost;Integrated Security=SSPI;Database=testcap"; public void ConfigureServices(IServiceCollection services) { services.AddCap(x => { - x.UseMySql(ConnectionString); - //x.UseSqlServer(ConnectionString); - x.UseKafka("192.168.10.110:9092"); + x.UseSqlServer(ConnectionString); + x.UseKafka("localhost:9092"); x.UseDashboard(); }); diff --git a/samples/Sample.Kafka.MySql/appsettings.json b/samples/Sample.Kafka.SqlServer/appsettings.json similarity index 100% rename from samples/Sample.Kafka.MySql/appsettings.json rename to samples/Sample.Kafka.SqlServer/appsettings.json From ffba435d7e6f4faae82070dd13d8a65d70f70bc9 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Sat, 25 Aug 2018 19:12:11 +0800 Subject: [PATCH 203/223] update samples --- .../Sample.Kafka.SqlServer/Controllers/ValuesController.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/samples/Sample.Kafka.SqlServer/Controllers/ValuesController.cs b/samples/Sample.Kafka.SqlServer/Controllers/ValuesController.cs index da78c06..cc82a8c 100644 --- a/samples/Sample.Kafka.SqlServer/Controllers/ValuesController.cs +++ b/samples/Sample.Kafka.SqlServer/Controllers/ValuesController.cs @@ -19,7 +19,7 @@ namespace Sample.Kafka.SqlServer.Controllers [Route("~/without/transaction")] public IActionResult WithoutTransaction() { - _capBus.Publish("sample.rabbitmq.mysql", DateTime.Now); + _capBus.Publish("sample.kafka.sqlserver", DateTime.Now); return Ok(); } @@ -34,7 +34,7 @@ namespace Sample.Kafka.SqlServer.Controllers //your business code connection.Execute("insert into dbo.test1(tname) values('test');", transaction: transaction); - _capBus.Publish("sample.rabbitmq.mysql", DateTime.Now); + _capBus.Publish("sample.kafka.sqlserver", DateTime.Now); transaction.Commit(); } @@ -44,7 +44,7 @@ namespace Sample.Kafka.SqlServer.Controllers } [NonAction] - [CapSubscribe("sample.rabbitmq.mysql")] + [CapSubscribe("sample.kafka.sqlserver")] public void Subscriber(DateTime time) { Console.WriteLine($@"{DateTime.Now}, Subscriber invoked, Sent time:{time}"); From d753b1a2d6e6c2f035d83f6fcc8b5d0214e3edbf Mon Sep 17 00:00:00 2001 From: Savorboard Date: Sat, 25 Aug 2018 20:56:20 +0800 Subject: [PATCH 204/223] Add ef sent message demo to SqlServer samples --- .../Sample.Kafka.SqlServer/AppDbContext.cs | 23 +++++++++++ .../Controllers/ValuesController.cs | 38 ++++++++++++++++++- .../20180825123925_init.Designer.cs | 38 +++++++++++++++++++ .../Migrations/20180825123925_init.cs | 30 +++++++++++++++ .../Migrations/AppDbContextModelSnapshot.cs | 36 ++++++++++++++++++ samples/Sample.Kafka.SqlServer/Startup.cs | 6 +-- 6 files changed, 167 insertions(+), 4 deletions(-) create mode 100644 samples/Sample.Kafka.SqlServer/AppDbContext.cs create mode 100644 samples/Sample.Kafka.SqlServer/Migrations/20180825123925_init.Designer.cs create mode 100644 samples/Sample.Kafka.SqlServer/Migrations/20180825123925_init.cs create mode 100644 samples/Sample.Kafka.SqlServer/Migrations/AppDbContextModelSnapshot.cs diff --git a/samples/Sample.Kafka.SqlServer/AppDbContext.cs b/samples/Sample.Kafka.SqlServer/AppDbContext.cs new file mode 100644 index 0000000..d8eb605 --- /dev/null +++ b/samples/Sample.Kafka.SqlServer/AppDbContext.cs @@ -0,0 +1,23 @@ +using Microsoft.EntityFrameworkCore; + +namespace Sample.Kafka.SqlServer +{ + public class Person + { + public int Id { get; set; } + + public string Name { get; set; } + } + + public class AppDbContext : DbContext + { + public const string ConnectionString = "Server=localhost;Integrated Security=SSPI;Database=testcap"; + + public DbSet Persons { get; set; } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + optionsBuilder.UseSqlServer(ConnectionString); + } + } +} diff --git a/samples/Sample.Kafka.SqlServer/Controllers/ValuesController.cs b/samples/Sample.Kafka.SqlServer/Controllers/ValuesController.cs index cc82a8c..9891f0f 100644 --- a/samples/Sample.Kafka.SqlServer/Controllers/ValuesController.cs +++ b/samples/Sample.Kafka.SqlServer/Controllers/ValuesController.cs @@ -27,7 +27,7 @@ namespace Sample.Kafka.SqlServer.Controllers [Route("~/adonet/transaction")] public IActionResult AdonetWithTransaction() { - using (var connection = new SqlConnection(Startup.ConnectionString)) + using (var connection = new SqlConnection(AppDbContext.ConnectionString)) { using (var transaction = connection.BeginTransaction(_capBus, autoCommit: false)) { @@ -43,6 +43,42 @@ namespace Sample.Kafka.SqlServer.Controllers return Ok(); } + [Route("~/adonet/autocommit/transaction")] + public IActionResult AdonetAutoCommitWithTransaction() + { + using (var connection = new SqlConnection(AppDbContext.ConnectionString)) + { + using (var transaction = connection.BeginTransaction(_capBus, autoCommit: true)) + { + //your business code + connection.Execute("insert into dbo.test1(tname) values('test');", transaction: transaction); + + _capBus.Publish("sample.kafka.sqlserver", DateTime.Now); + } + } + + return Ok(); + } + + [Route("~/ef/transaction")] + public IActionResult EntityFrameworkWithTransaction([FromServices]AppDbContext dbContext) + { + using (var trans = dbContext.Database.BeginTransaction(_capBus, autoCommit: false)) + { + dbContext.Persons.Add(new Person() { Name = "ef.transaction" }); + + for (int i = 0; i < 2; i++) + { + _capBus.Publish("sample.kafka.sqlserver", DateTime.Now); + } + + dbContext.SaveChanges(); + + trans.Commit(); + } + return Ok(); + } + [NonAction] [CapSubscribe("sample.kafka.sqlserver")] public void Subscriber(DateTime time) diff --git a/samples/Sample.Kafka.SqlServer/Migrations/20180825123925_init.Designer.cs b/samples/Sample.Kafka.SqlServer/Migrations/20180825123925_init.Designer.cs new file mode 100644 index 0000000..57df6d5 --- /dev/null +++ b/samples/Sample.Kafka.SqlServer/Migrations/20180825123925_init.Designer.cs @@ -0,0 +1,38 @@ +// +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Sample.Kafka.SqlServer; + +namespace Sample.Kafka.SqlServer.Migrations +{ + [DbContext(typeof(AppDbContext))] + [Migration("20180825123925_init")] + partial class init + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.1.1-rtm-30846") + .HasAnnotation("Relational:MaxIdentifierLength", 128) + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + modelBuilder.Entity("Sample.Kafka.SqlServer.Person", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("Name"); + + b.HasKey("Id"); + + b.ToTable("Persons"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/samples/Sample.Kafka.SqlServer/Migrations/20180825123925_init.cs b/samples/Sample.Kafka.SqlServer/Migrations/20180825123925_init.cs new file mode 100644 index 0000000..453e685 --- /dev/null +++ b/samples/Sample.Kafka.SqlServer/Migrations/20180825123925_init.cs @@ -0,0 +1,30 @@ +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Sample.Kafka.SqlServer.Migrations +{ + public partial class init : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "Persons", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn), + Name = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Persons", x => x.Id); + }); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "Persons"); + } + } +} diff --git a/samples/Sample.Kafka.SqlServer/Migrations/AppDbContextModelSnapshot.cs b/samples/Sample.Kafka.SqlServer/Migrations/AppDbContextModelSnapshot.cs new file mode 100644 index 0000000..ca5ca05 --- /dev/null +++ b/samples/Sample.Kafka.SqlServer/Migrations/AppDbContextModelSnapshot.cs @@ -0,0 +1,36 @@ +// +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Sample.Kafka.SqlServer; + +namespace Sample.Kafka.SqlServer.Migrations +{ + [DbContext(typeof(AppDbContext))] + partial class AppDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.1.1-rtm-30846") + .HasAnnotation("Relational:MaxIdentifierLength", 128) + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + modelBuilder.Entity("Sample.Kafka.SqlServer.Person", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("Name"); + + b.HasKey("Id"); + + b.ToTable("Persons"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/samples/Sample.Kafka.SqlServer/Startup.cs b/samples/Sample.Kafka.SqlServer/Startup.cs index e74cae0..8654a28 100644 --- a/samples/Sample.Kafka.SqlServer/Startup.cs +++ b/samples/Sample.Kafka.SqlServer/Startup.cs @@ -5,13 +5,13 @@ namespace Sample.Kafka.SqlServer { public class Startup { - public const string ConnectionString = "Server=localhost;Integrated Security=SSPI;Database=testcap"; - public void ConfigureServices(IServiceCollection services) { + services.AddDbContext(); + services.AddCap(x => { - x.UseSqlServer(ConnectionString); + x.UseEntityFramework(); x.UseKafka("localhost:9092"); x.UseDashboard(); }); From b3d64a809427bb8a0d5df3e5d6178c17c126e517 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Sat, 25 Aug 2018 20:57:16 +0800 Subject: [PATCH 205/223] implement SqlServerCapTransaction --- .../ICapTransaction.SqlServer.cs | 36 +++++++++++++++++-- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/src/DotNetCore.CAP.SqlServer/ICapTransaction.SqlServer.cs b/src/DotNetCore.CAP.SqlServer/ICapTransaction.SqlServer.cs index 19d1eec..6afcfbf 100644 --- a/src/DotNetCore.CAP.SqlServer/ICapTransaction.SqlServer.cs +++ b/src/DotNetCore.CAP.SqlServer/ICapTransaction.SqlServer.cs @@ -25,7 +25,21 @@ namespace DotNetCore.CAP protected override void AddToSent(CapPublishedMessage msg) { - var transactionKey = ((SqlConnection)((IDbTransaction)DbTransaction).Connection).ClientConnectionId; + var dbTransaction = DbTransaction as IDbTransaction; + if (dbTransaction == null) + { + if (DbTransaction is IDbContextTransaction dbContextTransaction) + { + dbTransaction = dbContextTransaction.GetDbTransaction(); + } + + if (dbTransaction == null) + { + throw new ArgumentNullException(nameof(DbTransaction)); + } + } + + var transactionKey = ((SqlConnection)dbTransaction.Connection).ClientConnectionId; if (_diagnosticProcessor.BufferList.TryGetValue(transactionKey, out var list)) { list.Add(msg); @@ -39,12 +53,28 @@ namespace DotNetCore.CAP public override void Commit() { - throw new NotImplementedException(); + switch (DbTransaction) + { + case IDbTransaction dbTransaction: + dbTransaction.Commit(); + break; + case IDbContextTransaction dbContextTransaction: + dbContextTransaction.Commit(); + break; + } } public override void Rollback() { - throw new NotImplementedException(); + switch (DbTransaction) + { + case IDbTransaction dbTransaction: + dbTransaction.Rollback(); + break; + case IDbContextTransaction dbContextTransaction: + dbContextTransaction.Rollback(); + break; + } } public override void Dispose() From d2faadd89e04ca6ce634fe4e6ed57017863b98e1 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Sat, 25 Aug 2018 20:59:22 +0800 Subject: [PATCH 206/223] Add licensed --- src/DotNetCore.CAP.MySql/IDbContextTransaction.CAP.cs | 1 + .../Diagnostics/DiagnosticObserver.cs | 5 ++++- .../Diagnostics/DiagnosticProcessorObserver.cs | 5 ++++- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/DotNetCore.CAP.MySql/IDbContextTransaction.CAP.cs b/src/DotNetCore.CAP.MySql/IDbContextTransaction.CAP.cs index 980dc57..0f2354a 100644 --- a/src/DotNetCore.CAP.MySql/IDbContextTransaction.CAP.cs +++ b/src/DotNetCore.CAP.MySql/IDbContextTransaction.CAP.cs @@ -7,6 +7,7 @@ using DotNetCore.CAP; // ReSharper disable once CheckNamespace namespace Microsoft.EntityFrameworkCore.Storage { + // ReSharper disable once InconsistentNaming internal class CapEFDbTransaction : IDbContextTransaction { private readonly ICapTransaction _transaction; diff --git a/src/DotNetCore.CAP.SqlServer/Diagnostics/DiagnosticObserver.cs b/src/DotNetCore.CAP.SqlServer/Diagnostics/DiagnosticObserver.cs index 72ac344..b58b740 100644 --- a/src/DotNetCore.CAP.SqlServer/Diagnostics/DiagnosticObserver.cs +++ b/src/DotNetCore.CAP.SqlServer/Diagnostics/DiagnosticObserver.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Data.SqlClient; diff --git a/src/DotNetCore.CAP.SqlServer/Diagnostics/DiagnosticProcessorObserver.cs b/src/DotNetCore.CAP.SqlServer/Diagnostics/DiagnosticProcessorObserver.cs index d7e5102..dd3e579 100644 --- a/src/DotNetCore.CAP.SqlServer/Diagnostics/DiagnosticProcessorObserver.cs +++ b/src/DotNetCore.CAP.SqlServer/Diagnostics/DiagnosticProcessorObserver.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; From ea6506d1a7d16f32fd87bbc5e5ccfa4c30c3a3d8 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Sat, 25 Aug 2018 23:25:23 +0800 Subject: [PATCH 207/223] update samples --- .../Sample.Kafka.SqlServer/AppDbContext.cs | 2 +- .../Controllers/ValuesController.cs | 25 ++++++++++++++----- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/samples/Sample.Kafka.SqlServer/AppDbContext.cs b/samples/Sample.Kafka.SqlServer/AppDbContext.cs index d8eb605..809cfc5 100644 --- a/samples/Sample.Kafka.SqlServer/AppDbContext.cs +++ b/samples/Sample.Kafka.SqlServer/AppDbContext.cs @@ -11,7 +11,7 @@ namespace Sample.Kafka.SqlServer public class AppDbContext : DbContext { - public const string ConnectionString = "Server=localhost;Integrated Security=SSPI;Database=testcap"; + public const string ConnectionString = "Server=localhost;Integrated Security=SSPI;Database=testcap;"; public DbSet Persons { get; set; } diff --git a/samples/Sample.Kafka.SqlServer/Controllers/ValuesController.cs b/samples/Sample.Kafka.SqlServer/Controllers/ValuesController.cs index 9891f0f..737e709 100644 --- a/samples/Sample.Kafka.SqlServer/Controllers/ValuesController.cs +++ b/samples/Sample.Kafka.SqlServer/Controllers/ValuesController.cs @@ -1,5 +1,6 @@ using System; using System.Data.SqlClient; +using System.Threading.Tasks; using Dapper; using DotNetCore.CAP; using Microsoft.AspNetCore.Mvc; @@ -17,9 +18,9 @@ namespace Sample.Kafka.SqlServer.Controllers } [Route("~/without/transaction")] - public IActionResult WithoutTransaction() + public async Task WithoutTransaction() { - _capBus.Publish("sample.kafka.sqlserver", DateTime.Now); + await _capBus.PublishAsync("sample.kafka.sqlserver", DateTime.Now); return Ok(); } @@ -61,21 +62,33 @@ namespace Sample.Kafka.SqlServer.Controllers } [Route("~/ef/transaction")] - public IActionResult EntityFrameworkWithTransaction([FromServices]AppDbContext dbContext) + public IActionResult EntityFrameworkWithTransaction([FromServices] AppDbContext dbContext) { using (var trans = dbContext.Database.BeginTransaction(_capBus, autoCommit: false)) { - dbContext.Persons.Add(new Person() { Name = "ef.transaction" }); - for (int i = 0; i < 2; i++) { _capBus.Publish("sample.kafka.sqlserver", DateTime.Now); } - dbContext.SaveChanges(); + dbContext.Persons.Add(new Person() { Name = "ef.transaction" }); + // We will assist you set dbcontext save changes and commit transaction trans.Commit(); } + + return Ok(); + } + + [Route("~/ef/autocommit/transaction")] + public IActionResult EntityFrameworkAutoCommitWithTransaction([FromServices]AppDbContext dbContext) + { + using (dbContext.Database.BeginTransaction(_capBus, autoCommit: true)) + { + dbContext.Persons.Add(new Person() { Name = "ef.transaction" }); + + _capBus.Publish("sample.kafka.sqlserver", DateTime.Now); + } return Ok(); } From 08ac461b5dd1de0ae328b526d844a8f76726009d Mon Sep 17 00:00:00 2001 From: Savorboard Date: Sat, 25 Aug 2018 23:26:16 +0800 Subject: [PATCH 208/223] add save change when transaction commit --- .../ICapPublisher.SqlServer.cs | 2 +- .../ICapTransaction.SqlServer.cs | 26 ++++++++++++++++--- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/src/DotNetCore.CAP.SqlServer/ICapPublisher.SqlServer.cs b/src/DotNetCore.CAP.SqlServer/ICapPublisher.SqlServer.cs index d641735..810e6a4 100644 --- a/src/DotNetCore.CAP.SqlServer/ICapPublisher.SqlServer.cs +++ b/src/DotNetCore.CAP.SqlServer/ICapPublisher.SqlServer.cs @@ -14,7 +14,7 @@ using Microsoft.EntityFrameworkCore.Storage; namespace DotNetCore.CAP.SqlServer { - public class SqlServerPublisher : CapPublisherBase, ICallbackPublisher + public class SqlServerPublisher : CapPublisherBase, ICallbackPublisher, IDisposable { private readonly DbContext _dbContext; private readonly SqlServerOptions _options; diff --git a/src/DotNetCore.CAP.SqlServer/ICapTransaction.SqlServer.cs b/src/DotNetCore.CAP.SqlServer/ICapTransaction.SqlServer.cs index 6afcfbf..ca620fa 100644 --- a/src/DotNetCore.CAP.SqlServer/ICapTransaction.SqlServer.cs +++ b/src/DotNetCore.CAP.SqlServer/ICapTransaction.SqlServer.cs @@ -7,20 +7,29 @@ using System.Data; using System.Data.SqlClient; using DotNetCore.CAP.Models; using DotNetCore.CAP.SqlServer.Diagnostics; +using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Storage; +using Microsoft.Extensions.DependencyInjection; // ReSharper disable once CheckNamespace namespace DotNetCore.CAP { public class SqlServerCapTransaction : CapTransactionBase { + private readonly DbContext _dbContext; private readonly DiagnosticProcessorObserver _diagnosticProcessor; - public SqlServerCapTransaction(IDispatcher dispatcher, - DiagnosticProcessorObserver diagnosticProcessor) : base(dispatcher) + public SqlServerCapTransaction( + IDispatcher dispatcher, + SqlServerOptions sqlServerOptions, + IServiceProvider serviceProvider) : base(dispatcher) { - _diagnosticProcessor = diagnosticProcessor; + if (sqlServerOptions.DbContextType != null) + { + _dbContext = serviceProvider.GetService(sqlServerOptions.DbContextType) as DbContext; + } + _diagnosticProcessor = serviceProvider.GetRequiredService(); } protected override void AddToSent(CapPublishedMessage msg) @@ -59,6 +68,7 @@ namespace DotNetCore.CAP dbTransaction.Commit(); break; case IDbContextTransaction dbContextTransaction: + _dbContext?.SaveChanges(); dbContextTransaction.Commit(); break; } @@ -79,7 +89,15 @@ namespace DotNetCore.CAP public override void Dispose() { - + switch (DbTransaction) + { + case IDbTransaction dbTransaction: + dbTransaction.Dispose(); + break; + case IDbContextTransaction dbContextTransaction: + dbContextTransaction.Dispose(); + break; + } } } From b136e21d44b0453287eb2e15abd73f1ac7552b3d Mon Sep 17 00:00:00 2001 From: Savorboard Date: Sun, 26 Aug 2018 17:55:30 +0800 Subject: [PATCH 209/223] update samples. --- samples/Sample.RabbitMQ.MySql/AppDbContext.cs | 2 +- .../Sample.RabbitMQ.MySql/Controllers/ValuesController.cs | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/samples/Sample.RabbitMQ.MySql/AppDbContext.cs b/samples/Sample.RabbitMQ.MySql/AppDbContext.cs index 60f4622..ce3535d 100644 --- a/samples/Sample.RabbitMQ.MySql/AppDbContext.cs +++ b/samples/Sample.RabbitMQ.MySql/AppDbContext.cs @@ -11,7 +11,7 @@ namespace Sample.RabbitMQ.MySql public class AppDbContext : DbContext { - public const string ConnectionString = "Server=192.168.10.110;Database=testcap;UserId=root;Password=123123;"; + public const string ConnectionString = "Server=localhost;Database=testcap;UserId=root;Password=123123;"; public DbSet Persons { get; set; } diff --git a/samples/Sample.RabbitMQ.MySql/Controllers/ValuesController.cs b/samples/Sample.RabbitMQ.MySql/Controllers/ValuesController.cs index 503fcdb..a9c2c96 100644 --- a/samples/Sample.RabbitMQ.MySql/Controllers/ValuesController.cs +++ b/samples/Sample.RabbitMQ.MySql/Controllers/ValuesController.cs @@ -1,5 +1,6 @@ using System; using System.Data; +using System.Threading.Tasks; using Dapper; using DotNetCore.CAP; using Microsoft.AspNetCore.Mvc; @@ -18,9 +19,9 @@ namespace Sample.RabbitMQ.MySql.Controllers } [Route("~/without/transaction")] - public IActionResult WithoutTransaction() + public async Task WithoutTransaction() { - _capBus.Publish("sample.rabbitmq.mysql", DateTime.Now); + await _capBus.PublishAsync("sample.rabbitmq.mysql", DateTime.Now); return Ok(); } From 7d1fe75b85c3675a2313f8b52689632b546fd9ed Mon Sep 17 00:00:00 2001 From: Savorboard Date: Sun, 26 Aug 2018 17:55:56 +0800 Subject: [PATCH 210/223] rename mongdb publisher --- .../CAP.MongoDBCapOptionsExtension.cs | 4 +-- ...pPublisher.cs => ICapPublisher.MongoDB.cs} | 30 +++++++++++-------- 2 files changed, 20 insertions(+), 14 deletions(-) rename src/DotNetCore.CAP.MongoDB/{CapPublisher.cs => ICapPublisher.MongoDB.cs} (62%) diff --git a/src/DotNetCore.CAP.MongoDB/CAP.MongoDBCapOptionsExtension.cs b/src/DotNetCore.CAP.MongoDB/CAP.MongoDBCapOptionsExtension.cs index 7b02a90..1c049f0 100644 --- a/src/DotNetCore.CAP.MongoDB/CAP.MongoDBCapOptionsExtension.cs +++ b/src/DotNetCore.CAP.MongoDB/CAP.MongoDBCapOptionsExtension.cs @@ -23,8 +23,8 @@ namespace DotNetCore.CAP.MongoDB services.AddSingleton(); services.AddSingleton(); - services.AddScoped(); - services.AddScoped(); + services.AddScoped(); + services.AddScoped(); services.AddTransient(); services.AddTransient(); diff --git a/src/DotNetCore.CAP.MongoDB/CapPublisher.cs b/src/DotNetCore.CAP.MongoDB/ICapPublisher.MongoDB.cs similarity index 62% rename from src/DotNetCore.CAP.MongoDB/CapPublisher.cs rename to src/DotNetCore.CAP.MongoDB/ICapPublisher.MongoDB.cs index a02d3af..2441f07 100644 --- a/src/DotNetCore.CAP.MongoDB/CapPublisher.cs +++ b/src/DotNetCore.CAP.MongoDB/ICapPublisher.MongoDB.cs @@ -11,14 +11,16 @@ using MongoDB.Driver; namespace DotNetCore.CAP.MongoDB { - public class CapPublisher : CapPublisherBase, ICallbackPublisher + public class MongoDBPublisher : CapPublisherBase, ICallbackPublisher { private readonly MongoDBOptions _options; + private readonly IMongoClient _client; - public CapPublisher(IServiceProvider provider, MongoDBOptions options) + public MongoDBPublisher(IServiceProvider provider, MongoDBOptions options) : base(provider) { _options = options; + _client = ServiceProvider.GetRequiredService(); } public async Task PublishCallbackAsync(CapPublishedMessage message) @@ -29,22 +31,26 @@ namespace DotNetCore.CAP.MongoDB protected override Task ExecuteAsync(CapPublishedMessage message, ICapTransaction transaction, CancellationToken cancel = default(CancellationToken)) { - var dbTrans = (IClientSessionHandle)transaction.DbTransaction; + var insertOptions = new InsertOneOptions { BypassDocumentValidation = false }; - var collection = dbTrans.Client + var collection = _client .GetDatabase(_options.DatabaseName) .GetCollection(_options.PublishedCollection); - var insertOptions = new InsertOneOptions { BypassDocumentValidation = false }; + if (NotUseTransaction) + { + return collection.InsertOneAsync(message, insertOptions, cancel); + } + var dbTrans = (IClientSessionHandle)transaction.DbTransaction; return collection.InsertOneAsync(dbTrans, message, insertOptions, cancel); } - protected override object GetDbTransaction() - { - var client = ServiceProvider.GetRequiredService(); - var session = client.StartSession(new ClientSessionOptions()); - session.StartTransaction(); - return session; - } + //protected override object GetDbTransaction() + //{ + // var client = ServiceProvider.GetRequiredService(); + // var session = client.StartSession(new ClientSessionOptions()); + // session.StartTransaction(); + // return session; + //} } } \ No newline at end of file From cbc145b7028f6bc47503a5bec755320342508811 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Sun, 26 Aug 2018 17:56:49 +0800 Subject: [PATCH 211/223] Adjustments do not use transactions internally when a transaction is not available to publish a message --- .../ICapPublisher.MySql.cs | 65 +++++------------ .../ICapPublisher.PostgreSql.cs | 57 ++++----------- .../ICapPublisher.SqlServer.cs | 70 +++++++------------ .../ICapTransaction.SqlServer.cs | 10 +++ .../Abstractions/CapPublisherBase.cs | 5 +- .../Infrastructure/SnowflakeId.cs | 6 +- .../Internal/NoopTransaction.cs | 10 +++ 7 files changed, 84 insertions(+), 139 deletions(-) create mode 100644 src/DotNetCore.CAP/Internal/NoopTransaction.cs diff --git a/src/DotNetCore.CAP.MySql/ICapPublisher.MySql.cs b/src/DotNetCore.CAP.MySql/ICapPublisher.MySql.cs index fe83676..27f5d35 100644 --- a/src/DotNetCore.CAP.MySql/ICapPublisher.MySql.cs +++ b/src/DotNetCore.CAP.MySql/ICapPublisher.MySql.cs @@ -8,31 +8,19 @@ using System.Threading.Tasks; using Dapper; using DotNetCore.CAP.Abstractions; using DotNetCore.CAP.Models; -using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Storage; +using Microsoft.Extensions.DependencyInjection; using MySql.Data.MySqlClient; namespace DotNetCore.CAP.MySql { - public class MySqlPublisher : CapPublisherBase, ICallbackPublisher, IDisposable + public class MySqlPublisher : CapPublisherBase, ICallbackPublisher { - private readonly DbContext _dbContext; private readonly MySqlOptions _options; - private readonly bool _isUsingEF; - private MySqlConnection _connection; - - public MySqlPublisher(IServiceProvider provider, MySqlOptions options) : base(provider) + public MySqlPublisher(IServiceProvider provider) : base(provider) { - _options = options; - - if (_options.DbContextType == null) - { - return; - } - - _isUsingEF = true; - _dbContext = (DbContext)ServiceProvider.GetService(_options.DbContextType); + _options = provider.GetService(); } public async Task PublishCallbackAsync(CapPublishedMessage message) @@ -40,32 +28,26 @@ namespace DotNetCore.CAP.MySql await PublishAsyncInternal(message); } - protected override Task ExecuteAsync(CapPublishedMessage message, ICapTransaction transaction, + protected override async Task ExecuteAsync(CapPublishedMessage message, ICapTransaction transaction, CancellationToken cancel = default(CancellationToken)) { + if (NotUseTransaction) + { + using (var connection = new MySqlConnection(_options.ConnectionString)) + { + await connection.OpenAsync(cancel); + await connection.ExecuteAsync(PrepareSql(), message); + return; + } + } + var dbTrans = transaction.DbTransaction as IDbTransaction; if (dbTrans == null && transaction.DbTransaction is IDbContextTransaction dbContextTrans) { dbTrans = dbContextTrans.GetDbTransaction(); } var conn = dbTrans?.Connection; - return conn.ExecuteAsync(PrepareSql(), message, dbTrans); - } - - protected override object GetDbTransaction() - { - if (_isUsingEF) - { - var dbContextTransaction = _dbContext.Database.CurrentTransaction; - if (dbContextTransaction == null) - { - return InitDbConnection(); - } - - return dbContextTransaction; - } - - return InitDbConnection(); + await conn.ExecuteAsync(PrepareSql(), message, dbTrans); } #region private methods @@ -74,21 +56,8 @@ namespace DotNetCore.CAP.MySql { return $"INSERT INTO `{_options.TableNamePrefix}.published` (`Id`,`Name`,`Content`,`Retries`,`Added`,`ExpiresAt`,`StatusName`)VALUES(@Id,@Name,@Content,@Retries,@Added,@ExpiresAt,@StatusName);"; - } - - private IDbTransaction InitDbConnection() - { - _connection = new MySqlConnection(_options.ConnectionString); - _connection.Open(); - return _connection.BeginTransaction(IsolationLevel.ReadCommitted); - } + } #endregion private methods - - public void Dispose() - { - _dbContext?.Dispose(); - _connection?.Dispose(); - } } } \ No newline at end of file diff --git a/src/DotNetCore.CAP.PostgreSql/ICapPublisher.PostgreSql.cs b/src/DotNetCore.CAP.PostgreSql/ICapPublisher.PostgreSql.cs index 7222efd..db80c28 100644 --- a/src/DotNetCore.CAP.PostgreSql/ICapPublisher.PostgreSql.cs +++ b/src/DotNetCore.CAP.PostgreSql/ICapPublisher.PostgreSql.cs @@ -8,31 +8,18 @@ using System.Threading.Tasks; using Dapper; using DotNetCore.CAP.Abstractions; using DotNetCore.CAP.Models; -using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Storage; +using Microsoft.Extensions.DependencyInjection; using Npgsql; namespace DotNetCore.CAP.PostgreSql { public class PostgreSqlPublisher : CapPublisherBase, ICallbackPublisher { - private readonly DbContext _dbContext; private readonly PostgreSqlOptions _options; - private readonly bool _isUsingEF; - - private NpgsqlConnection _connection; - - public PostgreSqlPublisher(IServiceProvider provider, PostgreSqlOptions options): base(provider) + public PostgreSqlPublisher(IServiceProvider provider) : base(provider) { - _options = options; - - if (_options.DbContextType == null) - { - return; - } - - _isUsingEF = true; - _dbContext = (DbContext)ServiceProvider.GetService(_options.DbContextType); + _options = provider.GetService< PostgreSqlOptions>(); } public async Task PublishCallbackAsync(CapPublishedMessage message) @@ -43,6 +30,14 @@ namespace DotNetCore.CAP.PostgreSql protected override Task ExecuteAsync(CapPublishedMessage message, ICapTransaction transaction, CancellationToken cancel = default(CancellationToken)) { + if (NotUseTransaction) + { + using (var connection = InitDbConnection()) + { + return connection.ExecuteAsync(PrepareSql(), message); + } + } + var dbTrans = transaction.DbTransaction as IDbTransaction; if (dbTrans == null && transaction.DbTransaction is IDbContextTransaction dbContextTrans) { @@ -52,22 +47,6 @@ namespace DotNetCore.CAP.PostgreSql return conn.ExecuteAsync(PrepareSql(), message, dbTrans); } - protected override object GetDbTransaction() - { - if (_isUsingEF) - { - var dbContextTransaction = _dbContext.Database.CurrentTransaction; - if (dbContextTransaction == null) - { - return InitDbConnection(); - } - - return dbContextTransaction; - } - - return InitDbConnection(); - } - #region private methods private string PrepareSql() @@ -76,18 +55,12 @@ namespace DotNetCore.CAP.PostgreSql $"INSERT INTO \"{_options.Schema}\".\"published\" (\"Id\",\"Name\",\"Content\",\"Retries\",\"Added\",\"ExpiresAt\",\"StatusName\")VALUES(@Id,@Name,@Content,@Retries,@Added,@ExpiresAt,@StatusName);"; } - private IDbTransaction InitDbConnection() + private IDbConnection InitDbConnection() { - _connection = new NpgsqlConnection(_options.ConnectionString); - _connection.Open(); - return _connection.BeginTransaction(IsolationLevel.ReadCommitted); + var conn = new NpgsqlConnection(_options.ConnectionString); + conn.Open(); + return conn; } #endregion private methods - - public void Dispose() - { - _dbContext?.Dispose(); - _connection?.Dispose(); - } } } \ No newline at end of file diff --git a/src/DotNetCore.CAP.SqlServer/ICapPublisher.SqlServer.cs b/src/DotNetCore.CAP.SqlServer/ICapPublisher.SqlServer.cs index 810e6a4..76ada37 100644 --- a/src/DotNetCore.CAP.SqlServer/ICapPublisher.SqlServer.cs +++ b/src/DotNetCore.CAP.SqlServer/ICapPublisher.SqlServer.cs @@ -8,31 +8,20 @@ using System.Threading; using System.Threading.Tasks; using Dapper; using DotNetCore.CAP.Abstractions; +using DotNetCore.CAP.Internal; using DotNetCore.CAP.Models; -using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Storage; +using Microsoft.Extensions.DependencyInjection; namespace DotNetCore.CAP.SqlServer { - public class SqlServerPublisher : CapPublisherBase, ICallbackPublisher, IDisposable + public class SqlServerPublisher : CapPublisherBase, ICallbackPublisher { - private readonly DbContext _dbContext; private readonly SqlServerOptions _options; - private readonly bool _isUsingEF; - private SqlConnection _connection; - - public SqlServerPublisher(IServiceProvider provider, SqlServerOptions options) : base(provider) + public SqlServerPublisher(IServiceProvider provider) : base(provider) { - _options = options; - - if (_options.DbContextType == null) - { - return; - } - - _isUsingEF = true; - _dbContext = (DbContext)ServiceProvider.GetService(_options.DbContextType); + _options = ServiceProvider.GetService(); } public async Task PublishCallbackAsync(CapPublishedMessage message) @@ -40,54 +29,45 @@ namespace DotNetCore.CAP.SqlServer await PublishAsyncInternal(message); } - protected override Task ExecuteAsync(CapPublishedMessage message, ICapTransaction transaction, + protected override async Task ExecuteAsync(CapPublishedMessage message, ICapTransaction transaction, CancellationToken cancel = default(CancellationToken)) { - var dbTrans = transaction.DbTransaction as IDbTransaction; - if (dbTrans == null && transaction.DbTransaction is IDbContextTransaction dbContextTrans) + if (NotUseTransaction) { - dbTrans = dbContextTrans.GetDbTransaction(); - } - var conn = dbTrans?.Connection; - return conn.ExecuteAsync(PrepareSql(), message, dbTrans); - } - - protected override object GetDbTransaction() - { - if (_isUsingEF) - { - var dbContextTransaction = _dbContext.Database.CurrentTransaction; - if (dbContextTransaction == null) + using (var connection = new SqlConnection(_options.ConnectionString)) { - return InitDbConnection(); + await connection.OpenAsync(cancel); + await connection.ExecuteAsync(PrepareSql(), message); + return; } + } - return dbContextTransaction; + var dbTrans = transaction.DbTransaction as IDbTransaction; + if (dbTrans == null && transaction.DbTransaction is IDbContextTransaction dbContextTrans) + { + dbTrans = dbContextTrans.GetDbTransaction(); } - return InitDbConnection(); + var conn = dbTrans?.Connection; + await conn.ExecuteAsync(PrepareSql(), message, dbTrans); } #region private methods + private string PrepareSql() { return $"INSERT INTO {_options.Schema}.[Published] ([Id],[Name],[Content],[Retries],[Added],[ExpiresAt],[StatusName])VALUES(@Id,@Name,@Content,@Retries,@Added,@ExpiresAt,@StatusName);"; } - private IDbTransaction InitDbConnection() - { - _connection = new SqlConnection(_options.ConnectionString); - _connection.Open(); - return _connection.BeginTransaction(IsolationLevel.ReadCommitted); - } + //private IDbConnection InitDbConnection() + //{ + // var conn = ; + // conn.OpenAsync(); + // return conn; + //} #endregion private methods - public void Dispose() - { - _dbContext?.Dispose(); - _connection?.Dispose(); - } } } \ No newline at end of file diff --git a/src/DotNetCore.CAP.SqlServer/ICapTransaction.SqlServer.cs b/src/DotNetCore.CAP.SqlServer/ICapTransaction.SqlServer.cs index ca620fa..6cbba35 100644 --- a/src/DotNetCore.CAP.SqlServer/ICapTransaction.SqlServer.cs +++ b/src/DotNetCore.CAP.SqlServer/ICapTransaction.SqlServer.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Data; using System.Data.SqlClient; +using DotNetCore.CAP.Internal; using DotNetCore.CAP.Models; using DotNetCore.CAP.SqlServer.Diagnostics; using Microsoft.EntityFrameworkCore; @@ -34,6 +35,12 @@ namespace DotNetCore.CAP protected override void AddToSent(CapPublishedMessage msg) { + if (DbTransaction is NoopTransaction) + { + base.AddToSent(msg); + return; + } + var dbTransaction = DbTransaction as IDbTransaction; if (dbTransaction == null) { @@ -64,6 +71,9 @@ namespace DotNetCore.CAP { switch (DbTransaction) { + case NoopTransaction _: + Flush(); + break; case IDbTransaction dbTransaction: dbTransaction.Commit(); break; diff --git a/src/DotNetCore.CAP/Abstractions/CapPublisherBase.cs b/src/DotNetCore.CAP/Abstractions/CapPublisherBase.cs index f20bb1f..5e1b236 100644 --- a/src/DotNetCore.CAP/Abstractions/CapPublisherBase.cs +++ b/src/DotNetCore.CAP/Abstractions/CapPublisherBase.cs @@ -7,6 +7,7 @@ using System.Threading; using System.Threading.Tasks; using DotNetCore.CAP.Diagnostics; using DotNetCore.CAP.Infrastructure; +using DotNetCore.CAP.Internal; using DotNetCore.CAP.Models; using Microsoft.Extensions.DependencyInjection; @@ -68,7 +69,7 @@ namespace DotNetCore.CAP.Abstractions if (Transaction.DbTransaction == null) { NotUseTransaction = true; - Transaction.DbTransaction = GetDbTransaction(); + Transaction.DbTransaction = new NoopTransaction(); } Guid operationId = default(Guid); @@ -103,8 +104,6 @@ namespace DotNetCore.CAP.Abstractions } } - protected abstract object GetDbTransaction(); - protected abstract Task ExecuteAsync(CapPublishedMessage message, ICapTransaction transaction, CancellationToken cancel = default(CancellationToken)); diff --git a/src/DotNetCore.CAP/Infrastructure/SnowflakeId.cs b/src/DotNetCore.CAP/Infrastructure/SnowflakeId.cs index 59e4db4..d0e4a5f 100644 --- a/src/DotNetCore.CAP/Infrastructure/SnowflakeId.cs +++ b/src/DotNetCore.CAP/Infrastructure/SnowflakeId.cs @@ -23,6 +23,7 @@ namespace DotNetCore.CAP.Infrastructure private static SnowflakeId _snowflakeId; private readonly object _lock = new object(); + private static readonly object s_lock = new object(); private long _lastTimestamp = -1L; private SnowflakeId(long workerId, long datacenterId, long sequence = 0L) @@ -46,7 +47,10 @@ namespace DotNetCore.CAP.Infrastructure public static SnowflakeId Default(long datacenterId = 0) { - return _snowflakeId ?? (_snowflakeId = new SnowflakeId(AppDomain.CurrentDomain.Id, datacenterId)); + lock (s_lock) + { + return _snowflakeId ?? (_snowflakeId = new SnowflakeId(AppDomain.CurrentDomain.Id, datacenterId)); + } } public virtual long NextId() diff --git a/src/DotNetCore.CAP/Internal/NoopTransaction.cs b/src/DotNetCore.CAP/Internal/NoopTransaction.cs new file mode 100644 index 0000000..9be38f6 --- /dev/null +++ b/src/DotNetCore.CAP/Internal/NoopTransaction.cs @@ -0,0 +1,10 @@ +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +namespace DotNetCore.CAP.Internal +{ + public class NoopTransaction + { + + } +} From dd741532f5008f1185aeeb440ea6a701ea24771b Mon Sep 17 00:00:00 2001 From: Savorboard Date: Mon, 27 Aug 2018 18:01:37 +0800 Subject: [PATCH 212/223] fixed unit tests --- test/DotNetCore.CAP.MySql.Test/MySqlStorageConnectionTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/DotNetCore.CAP.MySql.Test/MySqlStorageConnectionTest.cs b/test/DotNetCore.CAP.MySql.Test/MySqlStorageConnectionTest.cs index 512e9f1..7db4a38 100644 --- a/test/DotNetCore.CAP.MySql.Test/MySqlStorageConnectionTest.cs +++ b/test/DotNetCore.CAP.MySql.Test/MySqlStorageConnectionTest.cs @@ -22,7 +22,7 @@ namespace DotNetCore.CAP.MySql.Test [Fact] public async Task GetPublishedMessageAsync_Test() { - var sql = "INSERT INTO `cap.published`(`Name`,`Content`,`Retries`,`Added`,`ExpiresAt`,`StatusName`) VALUES(@Name,@Content,@Retries,@Added,@ExpiresAt,@StatusName);"; + var sql = "INSERT INTO `cap.published`(`Id`,`Name`,`Content`,`Retries`,`Added`,`ExpiresAt`,`StatusName`) VALUES(@Id,@Name,@Content,@Retries,@Added,@ExpiresAt,@StatusName);"; var insertedId = SnowflakeId.Default().NextId(); var publishMessage = new CapPublishedMessage { From 323f35153be355cb15b1f01fb7652ee0d756babe Mon Sep 17 00:00:00 2001 From: Savorboard Date: Mon, 27 Aug 2018 18:11:06 +0800 Subject: [PATCH 213/223] fixed unit test error. --- test/DotNetCore.CAP.SqlServer.Test/DatabaseTestHost.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/DotNetCore.CAP.SqlServer.Test/DatabaseTestHost.cs b/test/DotNetCore.CAP.SqlServer.Test/DatabaseTestHost.cs index 218fedb..714f26e 100644 --- a/test/DotNetCore.CAP.SqlServer.Test/DatabaseTestHost.cs +++ b/test/DotNetCore.CAP.SqlServer.Test/DatabaseTestHost.cs @@ -21,10 +21,10 @@ namespace DotNetCore.CAP.SqlServer.Test { Logger = new Mock>().Object; CapOptions = new Mock().Object; - SqlSeverOptions = new Mock() - .SetupProperty(x => x.ConnectionString, ConnectionUtil.GetConnectionString()) - .Object; - + SqlSeverOptions = new SqlServerOptions() + { + ConnectionString = ConnectionUtil.GetConnectionString() + }; DiagnosticProcessorObserver = new Mock().Object; InitializeDatabase(); From 57a75740736edfbfb9d4a044e5c786d0d805f9c1 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Tue, 28 Aug 2018 09:30:31 +0800 Subject: [PATCH 214/223] update unit tests. --- test/DotNetCore.CAP.SqlServer.Test/DatabaseTestHost.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/DotNetCore.CAP.SqlServer.Test/DatabaseTestHost.cs b/test/DotNetCore.CAP.SqlServer.Test/DatabaseTestHost.cs index 714f26e..960bfc4 100644 --- a/test/DotNetCore.CAP.SqlServer.Test/DatabaseTestHost.cs +++ b/test/DotNetCore.CAP.SqlServer.Test/DatabaseTestHost.cs @@ -25,7 +25,8 @@ namespace DotNetCore.CAP.SqlServer.Test { ConnectionString = ConnectionUtil.GetConnectionString() }; - DiagnosticProcessorObserver = new Mock().Object; + + DiagnosticProcessorObserver = new DiagnosticProcessorObserver(new Mock().Object); InitializeDatabase(); } From 2df85b1944f8520595ed341f03aa6105f050d492 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Tue, 28 Aug 2018 11:42:53 +0800 Subject: [PATCH 215/223] optimized code --- src/DotNetCore.CAP.MongoDB/ICapPublisher.MongoDB.cs | 8 -------- src/DotNetCore.CAP.MySql/ICapPublisher.MySql.cs | 1 - .../ICapPublisher.PostgreSql.cs | 9 +++++---- src/DotNetCore.CAP.SqlServer/ICapPublisher.SqlServer.cs | 1 - 4 files changed, 5 insertions(+), 14 deletions(-) diff --git a/src/DotNetCore.CAP.MongoDB/ICapPublisher.MongoDB.cs b/src/DotNetCore.CAP.MongoDB/ICapPublisher.MongoDB.cs index 2441f07..f764154 100644 --- a/src/DotNetCore.CAP.MongoDB/ICapPublisher.MongoDB.cs +++ b/src/DotNetCore.CAP.MongoDB/ICapPublisher.MongoDB.cs @@ -44,13 +44,5 @@ namespace DotNetCore.CAP.MongoDB var dbTrans = (IClientSessionHandle)transaction.DbTransaction; return collection.InsertOneAsync(dbTrans, message, insertOptions, cancel); } - - //protected override object GetDbTransaction() - //{ - // var client = ServiceProvider.GetRequiredService(); - // var session = client.StartSession(new ClientSessionOptions()); - // session.StartTransaction(); - // return session; - //} } } \ No newline at end of file diff --git a/src/DotNetCore.CAP.MySql/ICapPublisher.MySql.cs b/src/DotNetCore.CAP.MySql/ICapPublisher.MySql.cs index 27f5d35..5347cc4 100644 --- a/src/DotNetCore.CAP.MySql/ICapPublisher.MySql.cs +++ b/src/DotNetCore.CAP.MySql/ICapPublisher.MySql.cs @@ -35,7 +35,6 @@ namespace DotNetCore.CAP.MySql { using (var connection = new MySqlConnection(_options.ConnectionString)) { - await connection.OpenAsync(cancel); await connection.ExecuteAsync(PrepareSql(), message); return; } diff --git a/src/DotNetCore.CAP.PostgreSql/ICapPublisher.PostgreSql.cs b/src/DotNetCore.CAP.PostgreSql/ICapPublisher.PostgreSql.cs index db80c28..bece2d7 100644 --- a/src/DotNetCore.CAP.PostgreSql/ICapPublisher.PostgreSql.cs +++ b/src/DotNetCore.CAP.PostgreSql/ICapPublisher.PostgreSql.cs @@ -19,7 +19,7 @@ namespace DotNetCore.CAP.PostgreSql private readonly PostgreSqlOptions _options; public PostgreSqlPublisher(IServiceProvider provider) : base(provider) { - _options = provider.GetService< PostgreSqlOptions>(); + _options = provider.GetService(); } public async Task PublishCallbackAsync(CapPublishedMessage message) @@ -27,14 +27,15 @@ namespace DotNetCore.CAP.PostgreSql await PublishAsyncInternal(message); } - protected override Task ExecuteAsync(CapPublishedMessage message, ICapTransaction transaction, + protected override async Task ExecuteAsync(CapPublishedMessage message, ICapTransaction transaction, CancellationToken cancel = default(CancellationToken)) { if (NotUseTransaction) { using (var connection = InitDbConnection()) { - return connection.ExecuteAsync(PrepareSql(), message); + await connection.ExecuteAsync(PrepareSql(), message); + return; } } @@ -44,7 +45,7 @@ namespace DotNetCore.CAP.PostgreSql dbTrans = dbContextTrans.GetDbTransaction(); } var conn = dbTrans?.Connection; - return conn.ExecuteAsync(PrepareSql(), message, dbTrans); + await conn.ExecuteAsync(PrepareSql(), message, dbTrans); } #region private methods diff --git a/src/DotNetCore.CAP.SqlServer/ICapPublisher.SqlServer.cs b/src/DotNetCore.CAP.SqlServer/ICapPublisher.SqlServer.cs index 76ada37..8e181ac 100644 --- a/src/DotNetCore.CAP.SqlServer/ICapPublisher.SqlServer.cs +++ b/src/DotNetCore.CAP.SqlServer/ICapPublisher.SqlServer.cs @@ -36,7 +36,6 @@ namespace DotNetCore.CAP.SqlServer { using (var connection = new SqlConnection(_options.ConnectionString)) { - await connection.OpenAsync(cancel); await connection.ExecuteAsync(PrepareSql(), message); return; } From 9c3f3a272c26195e199715a779014d89a7f66adb Mon Sep 17 00:00:00 2001 From: Savorboard Date: Tue, 28 Aug 2018 18:27:32 +0800 Subject: [PATCH 216/223] clean code --- .../ICapPublisher.SqlServer.cs | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/DotNetCore.CAP.SqlServer/ICapPublisher.SqlServer.cs b/src/DotNetCore.CAP.SqlServer/ICapPublisher.SqlServer.cs index 8e181ac..9f12e95 100644 --- a/src/DotNetCore.CAP.SqlServer/ICapPublisher.SqlServer.cs +++ b/src/DotNetCore.CAP.SqlServer/ICapPublisher.SqlServer.cs @@ -8,7 +8,6 @@ using System.Threading; using System.Threading.Tasks; using Dapper; using DotNetCore.CAP.Abstractions; -using DotNetCore.CAP.Internal; using DotNetCore.CAP.Models; using Microsoft.EntityFrameworkCore.Storage; using Microsoft.Extensions.DependencyInjection; @@ -57,14 +56,7 @@ namespace DotNetCore.CAP.SqlServer { return $"INSERT INTO {_options.Schema}.[Published] ([Id],[Name],[Content],[Retries],[Added],[ExpiresAt],[StatusName])VALUES(@Id,@Name,@Content,@Retries,@Added,@ExpiresAt,@StatusName);"; - } - - //private IDbConnection InitDbConnection() - //{ - // var conn = ; - // conn.OpenAsync(); - // return conn; - //} + } #endregion private methods From 57a3e4327154d29724347f332a4b380c74daec7d Mon Sep 17 00:00:00 2001 From: Savorboard Date: Wed, 29 Aug 2018 15:33:36 +0800 Subject: [PATCH 217/223] remove unused file --- CAP.sln | 12 +- .../Controllers/ValuesController.cs | 37 ++- samples/Sample.Kafka.MySql/Startup.cs | 2 - .../20180825123925_init.Designer.cs | 38 --- .../Migrations/20180825123925_init.cs | 30 --- .../Migrations/AppDbContextModelSnapshot.cs | 36 --- .../Sample.Kafka.SqlServer/appsettings.json | 8 - src/DotNetCore.CAP.MongoDB/CapPublisher.cs | 202 ---------------- .../MongoDBCollectProcessor.cs | 51 ---- .../MongoDBMonitoringApi.cs | 226 ------------------ src/DotNetCore.CAP.MongoDB/MongoDBUtil.cs | 65 ----- .../MongoTransaction.cs | 60 ----- .../MongoDBUtilTest.cs | 30 --- 13 files changed, 32 insertions(+), 765 deletions(-) delete mode 100644 samples/Sample.Kafka.SqlServer/Migrations/20180825123925_init.Designer.cs delete mode 100644 samples/Sample.Kafka.SqlServer/Migrations/20180825123925_init.cs delete mode 100644 samples/Sample.Kafka.SqlServer/Migrations/AppDbContextModelSnapshot.cs delete mode 100644 samples/Sample.Kafka.SqlServer/appsettings.json delete mode 100644 src/DotNetCore.CAP.MongoDB/CapPublisher.cs delete mode 100644 src/DotNetCore.CAP.MongoDB/MongoDBCollectProcessor.cs delete mode 100644 src/DotNetCore.CAP.MongoDB/MongoDBMonitoringApi.cs delete mode 100644 src/DotNetCore.CAP.MongoDB/MongoDBUtil.cs delete mode 100644 src/DotNetCore.CAP.MongoDB/MongoTransaction.cs delete mode 100644 test/DotNetCore.CAP.MongoDB.Test/MongoDBUtilTest.cs diff --git a/CAP.sln b/CAP.sln index 3455243..8fc6b2f 100644 --- a/CAP.sln +++ b/CAP.sln @@ -64,7 +64,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNetCore.CAP.MongoDB", "s EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sample.RabbitMQ.MongoDB", "samples\Sample.RabbitMQ.MongoDB\Sample.RabbitMQ.MongoDB.csproj", "{4473DE19-E8D2-4B57-80A8-C8AAA2BFA20F}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sample.Kafka.SqlServer", "samples\Sample.Kafka.SqlServer\Sample.Kafka.SqlServer.csproj", "{CD276810-09A2-4105-8798-D65A8AA7C509}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sample.Kafka.MySql", "samples\Sample.Kafka.MySql\Sample.Kafka.MySql.csproj", "{11563D1A-27CC-45CF-8C04-C16BCC21250A}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -127,10 +127,10 @@ Global {4473DE19-E8D2-4B57-80A8-C8AAA2BFA20F}.Debug|Any CPU.Build.0 = Debug|Any CPU {4473DE19-E8D2-4B57-80A8-C8AAA2BFA20F}.Release|Any CPU.ActiveCfg = Release|Any CPU {4473DE19-E8D2-4B57-80A8-C8AAA2BFA20F}.Release|Any CPU.Build.0 = Release|Any CPU - {CD276810-09A2-4105-8798-D65A8AA7C509}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {CD276810-09A2-4105-8798-D65A8AA7C509}.Debug|Any CPU.Build.0 = Debug|Any CPU - {CD276810-09A2-4105-8798-D65A8AA7C509}.Release|Any CPU.ActiveCfg = Release|Any CPU - {CD276810-09A2-4105-8798-D65A8AA7C509}.Release|Any CPU.Build.0 = Release|Any CPU + {11563D1A-27CC-45CF-8C04-C16BCC21250A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {11563D1A-27CC-45CF-8C04-C16BCC21250A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {11563D1A-27CC-45CF-8C04-C16BCC21250A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {11563D1A-27CC-45CF-8C04-C16BCC21250A}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -150,7 +150,7 @@ Global {C143FCDF-E7F3-46F8-987E-A1BA38C1639D} = {C09CDAB0-6DD4-46E9-B7F3-3EF2A4741EA0} {77C0AC02-C44B-49D5-B969-7D5305FC20A5} = {9B2AE124-6636-4DE9-83A3-70360DABD0C4} {4473DE19-E8D2-4B57-80A8-C8AAA2BFA20F} = {3A6B6931-A123-477A-9469-8B468B5385AF} - {CD276810-09A2-4105-8798-D65A8AA7C509} = {3A6B6931-A123-477A-9469-8B468B5385AF} + {11563D1A-27CC-45CF-8C04-C16BCC21250A} = {3A6B6931-A123-477A-9469-8B468B5385AF} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {2E70565D-94CF-40B4-BFE1-AC18D5F736AB} diff --git a/samples/Sample.Kafka.MySql/Controllers/ValuesController.cs b/samples/Sample.Kafka.MySql/Controllers/ValuesController.cs index 21510bb..1b935bd 100644 --- a/samples/Sample.Kafka.MySql/Controllers/ValuesController.cs +++ b/samples/Sample.Kafka.MySql/Controllers/ValuesController.cs @@ -1,5 +1,7 @@ using System; +using System.Data; using System.Threading.Tasks; +using Dapper; using DotNetCore.CAP; using Microsoft.AspNetCore.Mvc; using MySql.Data.MySqlClient; @@ -16,24 +18,37 @@ namespace Sample.Kafka.MySql.Controllers _capBus = producer; } - [Route("~/publish")] - public async Task PublishMessage() + [Route("~/without/transaction")] + public async Task WithoutTransaction() { - using (var connection = new MySqlConnection("Server=192.168.10.110;Database=testcap;UserId=root;Password=123123;")) - { - connection.Open(); - var transaction = connection.BeginTransaction(); - - //your business code here + await _capBus.PublishAsync("sample.rabbitmq.mysql", DateTime.Now); - await _capBus.PublishAsync("xxx.xxx.test2", 123456, transaction); + return Ok(); + } - transaction.Commit(); + [Route("~/adonet/transaction")] + public IActionResult AdonetWithTransaction() + { + using (var connection = new MySqlConnection("")) + { + using (var transaction = connection.BeginTransaction(_capBus, autoCommit: false)) + { + //your business code + connection.Execute("insert into test(name) values('test')", transaction: (IDbTransaction)transaction.DbTransaction); + + for (int i = 0; i < 5; i++) + { + _capBus.Publish("sample.rabbitmq.mysql", DateTime.Now); + } + + transaction.Commit(); + } } - return Ok("publish successful!"); + return Ok(); } + [CapSubscribe("#.test2")] public void Test2(int value) { diff --git a/samples/Sample.Kafka.MySql/Startup.cs b/samples/Sample.Kafka.MySql/Startup.cs index 59c4636..3a6a8f7 100644 --- a/samples/Sample.Kafka.MySql/Startup.cs +++ b/samples/Sample.Kafka.MySql/Startup.cs @@ -21,8 +21,6 @@ namespace Sample.Kafka.MySql public void Configure(IApplicationBuilder app) { app.UseMvc(); - - app.UseCap(); } } } \ No newline at end of file diff --git a/samples/Sample.Kafka.SqlServer/Migrations/20180825123925_init.Designer.cs b/samples/Sample.Kafka.SqlServer/Migrations/20180825123925_init.Designer.cs deleted file mode 100644 index 57df6d5..0000000 --- a/samples/Sample.Kafka.SqlServer/Migrations/20180825123925_init.Designer.cs +++ /dev/null @@ -1,38 +0,0 @@ -// -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Sample.Kafka.SqlServer; - -namespace Sample.Kafka.SqlServer.Migrations -{ - [DbContext(typeof(AppDbContext))] - [Migration("20180825123925_init")] - partial class init - { - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "2.1.1-rtm-30846") - .HasAnnotation("Relational:MaxIdentifierLength", 128) - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - - modelBuilder.Entity("Sample.Kafka.SqlServer.Person", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - - b.Property("Name"); - - b.HasKey("Id"); - - b.ToTable("Persons"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/samples/Sample.Kafka.SqlServer/Migrations/20180825123925_init.cs b/samples/Sample.Kafka.SqlServer/Migrations/20180825123925_init.cs deleted file mode 100644 index 453e685..0000000 --- a/samples/Sample.Kafka.SqlServer/Migrations/20180825123925_init.cs +++ /dev/null @@ -1,30 +0,0 @@ -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; - -namespace Sample.Kafka.SqlServer.Migrations -{ - public partial class init : Migration - { - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateTable( - name: "Persons", - columns: table => new - { - Id = table.Column(nullable: false) - .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn), - Name = table.Column(nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_Persons", x => x.Id); - }); - } - - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "Persons"); - } - } -} diff --git a/samples/Sample.Kafka.SqlServer/Migrations/AppDbContextModelSnapshot.cs b/samples/Sample.Kafka.SqlServer/Migrations/AppDbContextModelSnapshot.cs deleted file mode 100644 index ca5ca05..0000000 --- a/samples/Sample.Kafka.SqlServer/Migrations/AppDbContextModelSnapshot.cs +++ /dev/null @@ -1,36 +0,0 @@ -// -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Sample.Kafka.SqlServer; - -namespace Sample.Kafka.SqlServer.Migrations -{ - [DbContext(typeof(AppDbContext))] - partial class AppDbContextModelSnapshot : ModelSnapshot - { - protected override void BuildModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "2.1.1-rtm-30846") - .HasAnnotation("Relational:MaxIdentifierLength", 128) - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - - modelBuilder.Entity("Sample.Kafka.SqlServer.Person", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - - b.Property("Name"); - - b.HasKey("Id"); - - b.ToTable("Persons"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/samples/Sample.Kafka.SqlServer/appsettings.json b/samples/Sample.Kafka.SqlServer/appsettings.json deleted file mode 100644 index 20aa907..0000000 --- a/samples/Sample.Kafka.SqlServer/appsettings.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "Logging": { - "IncludeScopes": false, - "LogLevel": { - "Default": "Debug" - } - } -} diff --git a/src/DotNetCore.CAP.MongoDB/CapPublisher.cs b/src/DotNetCore.CAP.MongoDB/CapPublisher.cs deleted file mode 100644 index df19c48..0000000 --- a/src/DotNetCore.CAP.MongoDB/CapPublisher.cs +++ /dev/null @@ -1,202 +0,0 @@ -// Copyright (c) .NET Core Community. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. - -using System; -using System.Data; -using System.Threading.Tasks; -using DotNetCore.CAP.Abstractions; -using DotNetCore.CAP.Diagnostics; -using DotNetCore.CAP.Infrastructure; -using DotNetCore.CAP.Models; -using Microsoft.Extensions.Logging; -using MongoDB.Driver; - -namespace DotNetCore.CAP.MongoDB -{ - public class CapPublisher : CapPublisherBase, ICallbackPublisher - { - private readonly MongoDBOptions _options; - private readonly IMongoDatabase _database; - private bool _usingTransaction = true; - - public CapPublisher( - ILogger logger, - IDispatcher dispatcher, - IMongoClient client, - MongoDBOptions options, - IServiceProvider provider) - : base(logger, dispatcher) - { - _options = options; - _database = client.GetDatabase(_options.DatabaseName); - ServiceProvider = provider; - } - - public async Task PublishCallbackAsync(CapPublishedMessage message) - { - var collection = _database.GetCollection(_options.PublishedCollection); - message.Id = await new MongoDBUtil().GetNextSequenceValueAsync(_database, _options.PublishedCollection); - collection.InsertOne(message); - Enqueue(message); - } - - protected override int Execute(IDbConnection dbConnection, IDbTransaction dbTransaction, - CapPublishedMessage message) - { - throw new NotImplementedException("Not work for MongoDB"); - } - - protected override Task ExecuteAsync(IDbConnection dbConnection, IDbTransaction dbTransaction, - CapPublishedMessage message) - { - throw new NotImplementedException("Not work for MongoDB"); - } - - protected override void PrepareConnectionForEF() - { - throw new NotImplementedException("Not work for MongoDB"); - } - - public override void PublishWithMongo(string name, T contentObj, IMongoTransaction mongoTransaction = null, string callbackName = null) - { - if (mongoTransaction == null) - { - _usingTransaction = false; - mongoTransaction = new NullMongoTransaction(); - } - - PublishWithTransaction(name, contentObj, mongoTransaction, callbackName); - } - - public override async Task PublishWithMongoAsync(string name, T contentObj, IMongoTransaction mongoTransaction = null, string callbackName = null) - { - if (mongoTransaction == null) - { - _usingTransaction = false; - mongoTransaction = new NullMongoTransaction(); - } - - await PublishWithTransactionAsync(name, contentObj, mongoTransaction, callbackName); - } - - private void PublishWithTransaction(string name, T contentObj, IMongoTransaction transaction, string callbackName) - { - var operationId = default(Guid); - - var content = Serialize(contentObj, callbackName); - - var message = new CapPublishedMessage - { - Name = name, - Content = content, - StatusName = StatusName.Scheduled - }; - - var session = transaction.GetSession(); - - try - { - operationId = s_diagnosticListener.WritePublishMessageStoreBefore(message); - var id = Execute(session, message); - - if (transaction.AutoCommit) - { - session.CommitTransaction(); - } - - if (!_usingTransaction || (transaction.AutoCommit && id > 0)) - { - _logger.LogInformation($"message [{message}] has been persisted in the database."); - s_diagnosticListener.WritePublishMessageStoreAfter(operationId, message); - message.Id = id; - Enqueue(message); - } - } - catch (Exception e) - { - _logger.LogError(e, "An exception was occurred when publish message. message:" + name); - s_diagnosticListener.WritePublishMessageStoreError(operationId, message, e); - throw; - } - } - - private int Execute(IClientSessionHandle session, CapPublishedMessage message) - { - message.Id = new MongoDBUtil().GetNextSequenceValue(_database, _options.PublishedCollection, session); - - var collection = _database.GetCollection(_options.PublishedCollection); - if (_usingTransaction) - { - collection.InsertOne(session, message); - } - else - { - collection.InsertOne(message); - } - - return message.Id; - } - - - private async Task PublishWithTransactionAsync(string name, T contentObj, IMongoTransaction transaction, string callbackName) - { - var operationId = default(Guid); - var content = Serialize(contentObj, callbackName); - - var message = new CapPublishedMessage - { - Name = name, - Content = content, - StatusName = StatusName.Scheduled - }; - - var session = transaction.GetSession(); - - try - { - operationId = s_diagnosticListener.WritePublishMessageStoreBefore(message); - - var id = await ExecuteAsync(session, message); - - if (transaction.AutoCommit) - { - await session.CommitTransactionAsync(); - } - - if (!_usingTransaction || (transaction.AutoCommit && id > 0)) - { - _logger.LogInformation($"message [{message}] has been persisted in the database."); - s_diagnosticListener.WritePublishMessageStoreAfter(operationId, message); - - message.Id = id; - - Enqueue(message); - } - } - catch (Exception e) - { - _logger.LogError(e, "An exception was occurred when publish message async. exception message:" + name); - s_diagnosticListener.WritePublishMessageStoreError(operationId, message, e); - Console.WriteLine(e); - throw; - } - } - - private async Task ExecuteAsync(IClientSessionHandle session, CapPublishedMessage message) - { - message.Id = - await new MongoDBUtil().GetNextSequenceValueAsync(_database, _options.PublishedCollection, session); - var collection = _database.GetCollection(_options.PublishedCollection); - if (_usingTransaction) - { - await collection.InsertOneAsync(session, message); - } - else - { - await collection.InsertOneAsync(message); - } - - return message.Id; - } - } -} \ No newline at end of file diff --git a/src/DotNetCore.CAP.MongoDB/MongoDBCollectProcessor.cs b/src/DotNetCore.CAP.MongoDB/MongoDBCollectProcessor.cs deleted file mode 100644 index 6b19878..0000000 --- a/src/DotNetCore.CAP.MongoDB/MongoDBCollectProcessor.cs +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (c) .NET Core Community. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. - -using System; -using System.Threading.Tasks; -using DotNetCore.CAP.Models; -using DotNetCore.CAP.Processor; -using Microsoft.Extensions.Logging; -using MongoDB.Driver; - -namespace DotNetCore.CAP.MongoDB -{ - public class MongoDBCollectProcessor : ICollectProcessor - { - private readonly IMongoDatabase _database; - private readonly ILogger _logger; - private readonly MongoDBOptions _options; - private readonly TimeSpan _waitingInterval = TimeSpan.FromMinutes(5); - - public MongoDBCollectProcessor(ILogger logger, - MongoDBOptions options, - IMongoClient client) - { - _options = options; - _logger = logger; - _database = client.GetDatabase(_options.DatabaseName); - } - - public async Task ProcessAsync(ProcessingContext context) - { - _logger.LogDebug( - $"Collecting expired data from collection [{_options.PublishedCollection}]."); - - var publishedCollection = _database.GetCollection(_options.PublishedCollection); - var receivedCollection = _database.GetCollection(_options.ReceivedCollection); - - await publishedCollection.BulkWriteAsync(new[] - { - new DeleteManyModel( - Builders.Filter.Lt(x => x.ExpiresAt, DateTime.Now)) - }); - await receivedCollection.BulkWriteAsync(new[] - { - new DeleteManyModel( - Builders.Filter.Lt(x => x.ExpiresAt, DateTime.Now)) - }); - - await context.WaitAsync(_waitingInterval); - } - } -} \ No newline at end of file diff --git a/src/DotNetCore.CAP.MongoDB/MongoDBMonitoringApi.cs b/src/DotNetCore.CAP.MongoDB/MongoDBMonitoringApi.cs deleted file mode 100644 index 0553b1b..0000000 --- a/src/DotNetCore.CAP.MongoDB/MongoDBMonitoringApi.cs +++ /dev/null @@ -1,226 +0,0 @@ -// Copyright (c) .NET Core Community. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; -using DotNetCore.CAP.Dashboard; -using DotNetCore.CAP.Dashboard.Monitoring; -using DotNetCore.CAP.Infrastructure; -using DotNetCore.CAP.Models; -using MongoDB.Bson; -using MongoDB.Driver; - -namespace DotNetCore.CAP.MongoDB -{ - public class MongoDBMonitoringApi : IMonitoringApi - { - private readonly IMongoDatabase _database; - private readonly MongoDBOptions _options; - - public MongoDBMonitoringApi(IMongoClient client, MongoDBOptions options) - { - var mongoClient = client ?? throw new ArgumentNullException(nameof(client)); - _options = options ?? throw new ArgumentNullException(nameof(options)); - - _database = mongoClient.GetDatabase(_options.DatabaseName); - } - - public StatisticsDto GetStatistics() - { - var publishedCollection = _database.GetCollection(_options.PublishedCollection); - var receivedCollection = _database.GetCollection(_options.ReceivedCollection); - - var statistics = new StatisticsDto(); - - { - if (int.TryParse( - publishedCollection.CountDocuments(x => x.StatusName == StatusName.Succeeded).ToString(), - out var count)) - { - statistics.PublishedSucceeded = count; - } - } - { - if (int.TryParse(publishedCollection.CountDocuments(x => x.StatusName == StatusName.Failed).ToString(), - out var count)) - { - statistics.PublishedFailed = count; - } - } - { - if (int.TryParse( - receivedCollection.CountDocuments(x => x.StatusName == StatusName.Succeeded).ToString(), - out var count)) - { - statistics.ReceivedSucceeded = count; - } - } - { - if (int.TryParse(receivedCollection.CountDocuments(x => x.StatusName == StatusName.Failed).ToString(), - out var count)) - { - statistics.ReceivedFailed = count; - } - } - - return statistics; - } - - public IDictionary HourlyFailedJobs(MessageType type) - { - return GetHourlyTimelineStats(type, StatusName.Failed); - } - - public IDictionary HourlySucceededJobs(MessageType type) - { - return GetHourlyTimelineStats(type, StatusName.Succeeded); - } - - public IList Messages(MessageQueryDto queryDto) - { - queryDto.StatusName = StatusName.Standardized(queryDto.StatusName); - - var name = queryDto.MessageType == MessageType.Publish - ? _options.PublishedCollection - : _options.ReceivedCollection; - var collection = _database.GetCollection(name); - - var builder = Builders.Filter; - var filter = builder.Empty; - if (!string.IsNullOrEmpty(queryDto.StatusName)) - { - filter = filter & builder.Eq(x => x.StatusName, queryDto.StatusName); - } - - if (!string.IsNullOrEmpty(queryDto.Name)) - { - filter = filter & builder.Eq(x => x.Name, queryDto.Name); - } - - if (!string.IsNullOrEmpty(queryDto.Group)) - { - filter = filter & builder.Eq(x => x.Group, queryDto.Group); - } - - if (!string.IsNullOrEmpty(queryDto.Content)) - { - filter = filter & builder.Regex(x => x.Content, ".*" + queryDto.Content + ".*"); - } - - var result = collection - .Find(filter) - .SortByDescending(x => x.Added) - .Skip(queryDto.PageSize * queryDto.CurrentPage) - .Limit(queryDto.PageSize) - .ToList(); - - return result; - } - - public int PublishedFailedCount() - { - return GetNumberOfMessage(_options.PublishedCollection, StatusName.Failed); - } - - public int PublishedSucceededCount() - { - return GetNumberOfMessage(_options.PublishedCollection, StatusName.Succeeded); - } - - public int ReceivedFailedCount() - { - return GetNumberOfMessage(_options.ReceivedCollection, StatusName.Failed); - } - - public int ReceivedSucceededCount() - { - return GetNumberOfMessage(_options.ReceivedCollection, StatusName.Succeeded); - } - - private int GetNumberOfMessage(string collectionName, string statusName) - { - var collection = _database.GetCollection(collectionName); - var count = collection.CountDocuments(new BsonDocument {{"StatusName", statusName}}); - return int.Parse(count.ToString()); - } - - private IDictionary GetHourlyTimelineStats(MessageType type, string statusName) - { - var collectionName = - type == MessageType.Publish ? _options.PublishedCollection : _options.ReceivedCollection; - var endDate = DateTime.UtcNow; - - var groupby = new BsonDocument - { - { - "$group", new BsonDocument - { - { - "_id", new BsonDocument - { - { - "Key", new BsonDocument - { - { - "$dateToString", new BsonDocument - { - {"format", "%Y-%m-%d %H:00:00"}, - {"date", "$Added"} - } - } - } - } - } - }, - {"Count", new BsonDocument {{"$sum", 1}}} - } - } - }; - - var match = new BsonDocument - { - { - "$match", new BsonDocument - { - { - "Added", new BsonDocument - { - {"$gt", endDate.AddHours(-24)} - } - }, - { - "StatusName", - new BsonDocument - { - {"$eq", statusName} - } - } - } - } - }; - - var pipeline = new[] {match, groupby}; - - var collection = _database.GetCollection(collectionName); - var result = collection.Aggregate(pipeline).ToList(); - - var dic = new Dictionary(); - for (var i = 0; i < 24; i++) - { - dic.Add(DateTime.Parse(endDate.ToLocalTime().ToString("yyyy-MM-dd HH:00:00")), 0); - endDate = endDate.AddHours(-1); - } - - result.ForEach(d => - { - var key = d["_id"].AsBsonDocument["Key"].AsString; - if (DateTime.TryParse(key, out var dateTime)) - { - dic[dateTime.ToLocalTime()] = d["Count"].AsInt32; - } - }); - - return dic; - } - } -} \ No newline at end of file diff --git a/src/DotNetCore.CAP.MongoDB/MongoDBUtil.cs b/src/DotNetCore.CAP.MongoDB/MongoDBUtil.cs deleted file mode 100644 index 3d3c1f7..0000000 --- a/src/DotNetCore.CAP.MongoDB/MongoDBUtil.cs +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright (c) .NET Core Community. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. - -using System; -using System.Threading.Tasks; -using MongoDB.Bson; -using MongoDB.Driver; - -namespace DotNetCore.CAP.MongoDB -{ - internal class MongoDBUtil - { - private readonly FindOneAndUpdateOptions _options = new FindOneAndUpdateOptions - { - ReturnDocument = ReturnDocument.After - }; - - public async Task GetNextSequenceValueAsync(IMongoDatabase database, string collectionName, - IClientSessionHandle session = null) - { - //https://www.tutorialspoint.com/mongodb/mongodb_autoincrement_sequence.htm - var collection = database.GetCollection(MongoDBOptions.CounterCollection); - - var updateDef = Builders.Update.Inc("sequence_value", 1); - var filter = new BsonDocument { { "_id", collectionName } }; - - BsonDocument result; - if (session == null) - { - result = await collection.FindOneAndUpdateAsync(filter, updateDef, _options); - } - else - { - result = await collection.FindOneAndUpdateAsync(session, filter, updateDef, _options); - } - - if (result.TryGetValue("sequence_value", out var value)) - { - return value.ToInt32(); - } - - throw new Exception("Unable to get next sequence value."); - } - - public int GetNextSequenceValue(IMongoDatabase database, string collectionName, - IClientSessionHandle session = null) - { - var collection = database.GetCollection(MongoDBOptions.CounterCollection); - - var filter = new BsonDocument { { "_id", collectionName } }; - var updateDef = Builders.Update.Inc("sequence_value", 1); - - var result = session == null - ? collection.FindOneAndUpdate(filter, updateDef, _options) - : collection.FindOneAndUpdate(session, filter, updateDef, _options); - - if (result.TryGetValue("sequence_value", out var value)) - { - return value.ToInt32(); - } - - throw new Exception("Unable to get next sequence value."); - } - } -} \ No newline at end of file diff --git a/src/DotNetCore.CAP.MongoDB/MongoTransaction.cs b/src/DotNetCore.CAP.MongoDB/MongoTransaction.cs deleted file mode 100644 index 96aed37..0000000 --- a/src/DotNetCore.CAP.MongoDB/MongoTransaction.cs +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright (c) .NET Core Community. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. - -using System.Threading.Tasks; -using DotNetCore.CAP.Abstractions; -using MongoDB.Driver; - -namespace DotNetCore.CAP.MongoDB -{ - public class MongoTransaction : IMongoTransaction - { - private readonly IMongoClient _client; - - public MongoTransaction(IMongoClient client) - { - _client = client; - } - - public IClientSessionHandle Session { get; set; } - public bool AutoCommit { get; set; } - - public async Task BegeinAsync(bool autoCommit = true) - { - AutoCommit = autoCommit; - Session = await _client.StartSessionAsync(); - Session.StartTransaction(); - return this; - } - - public IMongoTransaction Begein(bool autoCommit = true) - { - AutoCommit = autoCommit; - Session = _client.StartSession(); - Session.StartTransaction(); - return this; - } - - public void Dispose() - { - Session?.Dispose(); - } - } - - public class NullMongoTransaction : MongoTransaction - { - public NullMongoTransaction(IMongoClient client = null) : base(client) - { - AutoCommit = false; - } - } - - public static class MongoTransactionExtensions - { - public static IClientSessionHandle GetSession(this IMongoTransaction mongoTransaction) - { - var trans = mongoTransaction as MongoTransaction; - return trans?.Session; - } - } -} \ No newline at end of file diff --git a/test/DotNetCore.CAP.MongoDB.Test/MongoDBUtilTest.cs b/test/DotNetCore.CAP.MongoDB.Test/MongoDBUtilTest.cs deleted file mode 100644 index 45695f9..0000000 --- a/test/DotNetCore.CAP.MongoDB.Test/MongoDBUtilTest.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System.Collections.Concurrent; -using System.Threading.Tasks; -using FluentAssertions; -using Xunit; - -namespace DotNetCore.CAP.MongoDB.Test -{ - [Collection("MongoDB")] - public class MongoDBUtilTest : DatabaseTestHost - { - [Fact] - public async void GetNextSequenceValueAsync_Test() - { - var id = await new MongoDBUtil().GetNextSequenceValueAsync(Database, MongoDBOptions.ReceivedCollection); - id.Should().BeGreaterThan(0); - } - - [Fact] - public void GetNextSequenceValue_Concurrency_Test() - { - var dic = new ConcurrentDictionary(); - Parallel.For(0, 30, (x) => - { - var id = new MongoDBUtil().GetNextSequenceValue(Database, MongoDBOptions.ReceivedCollection); - id.Should().BeGreaterThan(0); - dic.TryAdd(id, x).Should().BeTrue(); //The id shouldn't be same. - }); - } - } -} \ No newline at end of file From f75cd278fc877bf24ed25b4e3c41481319024551 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Wed, 29 Aug 2018 15:38:34 +0800 Subject: [PATCH 218/223] rename file --- .../{MongoDBStorage.cs => IStorage.MongoDB.cs} | 0 ...{MongoDBStorageConnection.cs => IStorageConnection.MongoDB.cs} | 0 ...ongoDBStorageTransaction.cs => IStorageTransaction.MongoDB.cs} | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename src/DotNetCore.CAP.MongoDB/{MongoDBStorage.cs => IStorage.MongoDB.cs} (100%) rename src/DotNetCore.CAP.MongoDB/{MongoDBStorageConnection.cs => IStorageConnection.MongoDB.cs} (100%) rename src/DotNetCore.CAP.MongoDB/{MongoDBStorageTransaction.cs => IStorageTransaction.MongoDB.cs} (100%) diff --git a/src/DotNetCore.CAP.MongoDB/MongoDBStorage.cs b/src/DotNetCore.CAP.MongoDB/IStorage.MongoDB.cs similarity index 100% rename from src/DotNetCore.CAP.MongoDB/MongoDBStorage.cs rename to src/DotNetCore.CAP.MongoDB/IStorage.MongoDB.cs diff --git a/src/DotNetCore.CAP.MongoDB/MongoDBStorageConnection.cs b/src/DotNetCore.CAP.MongoDB/IStorageConnection.MongoDB.cs similarity index 100% rename from src/DotNetCore.CAP.MongoDB/MongoDBStorageConnection.cs rename to src/DotNetCore.CAP.MongoDB/IStorageConnection.MongoDB.cs diff --git a/src/DotNetCore.CAP.MongoDB/MongoDBStorageTransaction.cs b/src/DotNetCore.CAP.MongoDB/IStorageTransaction.MongoDB.cs similarity index 100% rename from src/DotNetCore.CAP.MongoDB/MongoDBStorageTransaction.cs rename to src/DotNetCore.CAP.MongoDB/IStorageTransaction.MongoDB.cs From 0812b967385f65cc215597a94e0fdcb5264e1330 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Wed, 29 Aug 2018 17:46:28 +0800 Subject: [PATCH 219/223] modify mysql create table sql script --- src/DotNetCore.CAP.MySql/IStorage.MySql.cs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/DotNetCore.CAP.MySql/IStorage.MySql.cs b/src/DotNetCore.CAP.MySql/IStorage.MySql.cs index e0170de..7ade288 100644 --- a/src/DotNetCore.CAP.MySql/IStorage.MySql.cs +++ b/src/DotNetCore.CAP.MySql/IStorage.MySql.cs @@ -58,10 +58,8 @@ namespace DotNetCore.CAP.MySql { var batchSql = $@" -DROP TABLE IF EXISTS `{prefix}.queue`; - CREATE TABLE IF NOT EXISTS `{prefix}.received` ( - `Id` int(127) NOT NULL AUTO_INCREMENT, + `Id` bigint NOT NULL, `Name` varchar(400) NOT NULL, `Group` varchar(200) DEFAULT NULL, `Content` longtext, @@ -73,7 +71,7 @@ CREATE TABLE IF NOT EXISTS `{prefix}.received` ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE IF NOT EXISTS `{prefix}.published` ( - `Id` int(127) NOT NULL AUTO_INCREMENT, + `Id` bigint NOT NULL, `Name` varchar(200) NOT NULL, `Content` longtext, `Retries` int(11) DEFAULT NULL, @@ -82,9 +80,6 @@ CREATE TABLE IF NOT EXISTS `{prefix}.published` ( `StatusName` varchar(40) NOT NULL, PRIMARY KEY (`Id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; - -ALTER TABLE `{prefix}.published` MODIFY Id BIGINT NOT NULL; -ALTER TABLE `{prefix}.received` MODIFY Id BIGINT NOT NULL; "; return batchSql; } From 414cf26b97d97ba0437e8addee26da14d2a2ee05 Mon Sep 17 00:00:00 2001 From: Savorboard Date: Thu, 30 Aug 2018 11:28:57 +0800 Subject: [PATCH 220/223] update reamdme for v2.3 --- README.2.2.md | 241 ++++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 93 +++++++++---------- 2 files changed, 281 insertions(+), 53 deletions(-) create mode 100644 README.2.2.md diff --git a/README.2.2.md b/README.2.2.md new file mode 100644 index 0000000..4dbadde --- /dev/null +++ b/README.2.2.md @@ -0,0 +1,241 @@ +# CAP                       [中文](https://github.com/dotnetcore/CAP/blob/develop/README.zh-cn.md) +[![Travis branch](https://img.shields.io/travis/dotnetcore/CAP/develop.svg?label=travis-ci)](https://travis-ci.org/dotnetcore/CAP) +[![AppVeyor](https://ci.appveyor.com/api/projects/status/4mpe0tbu7n126vyw?svg=true)](https://ci.appveyor.com/project/yuleyule66/cap) +[![NuGet](https://img.shields.io/nuget/v/DotNetCore.CAP.svg)](https://www.nuget.org/packages/DotNetCore.CAP/) +[![NuGet Preview](https://img.shields.io/nuget/vpre/DotNetCore.CAP.svg?label=nuget-pre)](https://www.nuget.org/packages/DotNetCore.CAP/) +[![Member project of .NET Core Community](https://img.shields.io/badge/member%20project%20of-NCC-9e20c9.svg)](https://github.com/dotnetcore) +[![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/dotnetcore/CAP/master/LICENSE.txt) + +CAP is a library based on .Net standard, which is a solution to deal with distributed transactions, also has the function of EventBus, it is lightweight, easy to use, and efficiently. + +## OverView + +In the process of building an SOA or MicroService system, we usually need to use the event to integrate each services. In the process, the simple use of message queue does not guarantee the reliability. CAP is adopted the local message table program integrated with the current database to solve the exception may occur in the process of the distributed system calling each other. It can ensure that the event messages are not lost in any case. + +You can also use the CAP as an EventBus. The CAP provides a simpler way to implement event publishing and subscriptions. You do not need to inherit or implement any interface during the process of subscription and sending. + +This is a diagram of the CAP working in the ASP.NET Core MicroService architecture: + +![](http://images2015.cnblogs.com/blog/250417/201707/250417-20170705175827128-1203291469.png) + +> The solid line in the figure represents the user code, and the dotted line represents the internal implementation of the CAP. + +## Getting Started + +### NuGet + +You can run the following command to install the CAP in your project. + +``` +PM> Install-Package DotNetCore.CAP +``` + +If you want use Kafka to send integrating event, installing by: + +``` +PM> Install-Package DotNetCore.CAP.Kafka +``` + +If you want use RabbitMQ to send integrating event, installing by: + +``` +PM> Install-Package DotNetCore.CAP.RabbitMQ +``` + +CAP supports SqlServer, MySql, PostgreSql as event log storage. + +``` + +// select a database provider you are using, event log table will integrate into. + +PM> Install-Package DotNetCore.CAP.SqlServer +PM> Install-Package DotNetCore.CAP.MySql +PM> Install-Package DotNetCore.CAP.PostgreSql +``` + +### Configuration + +First,You need to config CAP in your Startup.cs: + +```cs +public void ConfigureServices(IServiceCollection services) +{ + //...... + + services.AddDbContext(); + + services.AddCap(x => + { + // If you are using EF, you need to add the following configuration: + // Notice: You don't need to config x.UseSqlServer(""") again! CAP can autodiscovery. + x.UseEntityFramework(); + + // If you are using ado.net,you need to add the configuration: + x.UseSqlServer("Your ConnectionStrings"); + x.UseMySql("Your ConnectionStrings"); + x.UsePostgreSql("Your ConnectionStrings"); + + // If you are using RabbitMQ, you need to add the configuration: + x.UseRabbitMQ("localhost"); + + // If you are using Kafka, you need to add the configuration: + x.UseKafka("localhost"); + }); +} + +public void Configure(IApplicationBuilder app) +{ + //..... + + app.UseCap(); +} + +``` + +### Publish + +Inject `ICapPublisher` in your Controller, then use the `ICapPublisher` to send message + +```c# +public class PublishController : Controller +{ + [Route("~/publishWithTransactionUsingEF")] + public async Task PublishMessageWithTransactionUsingEF([FromServices]AppDbContext dbContext, [FromServices]ICapPublisher publisher) + { + using (var trans = dbContext.Database.BeginTransaction()) + { + // your business code + + //If you are using EF, CAP will automatic discovery current environment transaction, so you do not need to explicit pass parameters. + //Achieving atomicity between original database operation and the publish event log thanks to a local transaction. + await publisher.PublishAsync("xxx.services.account.check", new Person { Name = "Foo", Age = 11 }); + + trans.Commit(); + } + return Ok(); + } + + [Route("~/publishWithTransactionUsingAdonet")] + public async Task PublishMessageWithTransactionUsingAdonet([FromServices]ICapPublisher publisher) + { + var connectionString = ""; + using (var sqlConnection = new SqlConnection(connectionString)) + { + sqlConnection.Open(); + using (var sqlTransaction = sqlConnection.BeginTransaction()) + { + // your business code + + publisher.Publish("xxx.services.account.check", new Person { Name = "Foo", Age = 11 }, sqlTransaction); + + sqlTransaction.Commit(); + } + } + return Ok(); + } +} + +``` + +### Subscribe + +**Action Method** + +Add the Attribute `[CapSubscribe()]` on Action to subscribe message: + +```c# +public class PublishController : Controller +{ + [CapSubscribe("xxx.services.account.check")] + public async Task CheckReceivedMessage(Person person) + { + Console.WriteLine(person.Name); + Console.WriteLine(person.Age); + return Task.CompletedTask; + } +} + +``` + +**Service Method** + +If your subscribe method is not in the Controller,then your subscribe class need to Inheritance `ICapSubscribe`: + +```c# + +namespace xxx.Service +{ + public interface ISubscriberService + { + public void CheckReceivedMessage(Person person); + } + + + public class SubscriberService: ISubscriberService, ICapSubscribe + { + [CapSubscribe("xxx.services.account.check")] + public void CheckReceivedMessage(Person person) + { + } + } +} + +``` + +Then inject your `ISubscriberService` class in Startup.cs + +```c# +public void ConfigureServices(IServiceCollection services) +{ + //Note: The injection of services needs before of `services.AddCap()` + services.AddTransient(); + + services.AddCap(x=>{}); +} +``` + +### Dashboard + +CAP 2.1 and above provides the dashboard pages, you can easily view the sent and received messages. In addition, you can also view the message status in real time on the dashboard. + +In the distributed environment, the dashboard built-in integrated [Consul](http://consul.io) as a node discovery, while the realization of the gateway agent function, you can also easily view the node or other node data, It's like you are visiting local resources. + +```c# +services.AddCap(x => +{ + //... + + // Register Dashboard + x.UseDashboard(); + + // Register to Consul + x.UseDiscovery(d => + { + d.DiscoveryServerHostName = "localhost"; + d.DiscoveryServerPort = 8500; + d.CurrentNodeHostName = "localhost"; + d.CurrentNodePort = 5800; + d.NodeId = 1; + d.NodeName = "CAP No.1 Node"; + }); +}); +``` + +The default dashboard address is :[http://localhost:xxx/cap](http://localhost:xxx/cap) , you can also change the `cap` suffix to others with `d.MatchPath` configuration options. + +![dashboard](http://images2017.cnblogs.com/blog/250417/201710/250417-20171004220827302-189215107.png) + +![received](http://images2017.cnblogs.com/blog/250417/201710/250417-20171004220934115-1107747665.png) + +![subscibers](http://images2017.cnblogs.com/blog/250417/201710/250417-20171004220949193-884674167.png) + +![nodes](http://images2017.cnblogs.com/blog/250417/201710/250417-20171004221001880-1162918362.png) + + +## Contribute + +One of the easiest ways to contribute is to participate in discussions and discuss issues. You can also contribute by submitting pull requests with code changes. + +### License + +[MIT](https://github.com/dotnetcore/CAP/blob/master/LICENSE.txt) diff --git a/README.md b/README.md index 4dbadde..c8bffb8 100644 --- a/README.md +++ b/README.md @@ -16,9 +16,7 @@ You can also use the CAP as an EventBus. The CAP provides a simpler way to imple This is a diagram of the CAP working in the ASP.NET Core MicroService architecture: -![](http://images2015.cnblogs.com/blog/250417/201707/250417-20170705175827128-1203291469.png) - -> The solid line in the figure represents the user code, and the dotted line represents the internal implementation of the CAP. +![cap.png](http://oowr92l0m.bkt.clouddn.com/cap.png) ## Getting Started @@ -30,27 +28,22 @@ You can run the following command to install the CAP in your project. PM> Install-Package DotNetCore.CAP ``` -If you want use Kafka to send integrating event, installing by: +CAP supports RabbitMQ and Kafka as message queue, select the packages you need to install: ``` PM> Install-Package DotNetCore.CAP.Kafka -``` - -If you want use RabbitMQ to send integrating event, installing by: - -``` PM> Install-Package DotNetCore.CAP.RabbitMQ ``` -CAP supports SqlServer, MySql, PostgreSql as event log storage. +CAP supports SqlServer, MySql, PostgreSql,MongoDB as event log storage. ``` - // select a database provider you are using, event log table will integrate into. PM> Install-Package DotNetCore.CAP.SqlServer PM> Install-Package DotNetCore.CAP.MySql PM> Install-Package DotNetCore.CAP.PostgreSql +PM> Install-Package DotNetCore.CAP.MongoDB //need MongoDB 4.0+ cluster ``` ### Configuration @@ -62,18 +55,21 @@ public void ConfigureServices(IServiceCollection services) { //...... - services.AddDbContext(); + services.AddDbContext(); //Options, If you are using EF as the ORM + services.AddSingleton(new MongoClient("")); //Options, If you are using MongoDB services.AddCap(x => { - // If you are using EF, you need to add the following configuration: - // Notice: You don't need to config x.UseSqlServer(""") again! CAP can autodiscovery. - x.UseEntityFramework(); + // If you are using EF, you need to add the configuration: + x.UseEntityFramework(); //Options, Notice: You don't need to config x.UseSqlServer(""") again! CAP can autodiscovery. - // If you are using ado.net,you need to add the configuration: + // If you are using Ado.Net, you need to add the configuration: x.UseSqlServer("Your ConnectionStrings"); x.UseMySql("Your ConnectionStrings"); x.UsePostgreSql("Your ConnectionStrings"); + + // If you are using MongoDB, you need to add the configuration: + x.UseMongoDB("Your ConnectionStrings"); //MongoDB 4.0+ cluster // If you are using RabbitMQ, you need to add the configuration: x.UseRabbitMQ("localhost"); @@ -83,13 +79,6 @@ public void ConfigureServices(IServiceCollection services) }); } -public void Configure(IApplicationBuilder app) -{ - //..... - - app.UseCap(); -} - ``` ### Publish @@ -99,38 +88,39 @@ Inject `ICapPublisher` in your Controller, then use the `ICapPublisher` to send ```c# public class PublishController : Controller { - [Route("~/publishWithTransactionUsingEF")] - public async Task PublishMessageWithTransactionUsingEF([FromServices]AppDbContext dbContext, [FromServices]ICapPublisher publisher) + private readonly ICapPublisher _capBus; + + public PublishController(ICapPublisher capPublisher) { - using (var trans = dbContext.Database.BeginTransaction()) - { - // your business code + _capBus = capPublisher; + } - //If you are using EF, CAP will automatic discovery current environment transaction, so you do not need to explicit pass parameters. - //Achieving atomicity between original database operation and the publish event log thanks to a local transaction. - await publisher.PublishAsync("xxx.services.account.check", new Person { Name = "Foo", Age = 11 }); + [Route("~/adonet/transaction")] + public IActionResult AdonetWithTransaction() + { + using (var connection = new MySqlConnection(ConnectionString)) + { + using (var transaction = connection.BeginTransaction(_capBus, autoCommit: true)) + { + //your business code - trans.Commit(); + _capBus.Publish("xxx.services.show.time", DateTime.Now); + } } + return Ok(); } - [Route("~/publishWithTransactionUsingAdonet")] - public async Task PublishMessageWithTransactionUsingAdonet([FromServices]ICapPublisher publisher) + [Route("~/ef/transaction")] + public IActionResult EntityFrameworkWithTransaction([FromServices]AppDbContext dbContext) { - var connectionString = ""; - using (var sqlConnection = new SqlConnection(connectionString)) + using (var trans = dbContext.Database.BeginTransaction(_capBus, autoCommit: true)) { - sqlConnection.Open(); - using (var sqlTransaction = sqlConnection.BeginTransaction()) - { - // your business code - - publisher.Publish("xxx.services.account.check", new Person { Name = "Foo", Age = 11 }, sqlTransaction); + //your business code - sqlTransaction.Commit(); - } + _capBus.Publish(""xxx.services.show.time", DateTime.Now); } + return Ok(); } } @@ -146,12 +136,10 @@ Add the Attribute `[CapSubscribe()]` on Action to subscribe message: ```c# public class PublishController : Controller { - [CapSubscribe("xxx.services.account.check")] - public async Task CheckReceivedMessage(Person person) + [CapSubscribe("xxx.services.show.time")] + public void CheckReceivedMessage(DateTime datetime) { - Console.WriteLine(person.Name); - Console.WriteLine(person.Age); - return Task.CompletedTask; + Console.WriteLine(datetime); } } @@ -170,11 +158,10 @@ namespace xxx.Service public void CheckReceivedMessage(Person person); } - public class SubscriberService: ISubscriberService, ICapSubscribe { - [CapSubscribe("xxx.services.account.check")] - public void CheckReceivedMessage(Person person) + [CapSubscribe("xxx.services.show.time")] + public void CheckReceivedMessage(DateTime datetime) { } } @@ -196,7 +183,7 @@ public void ConfigureServices(IServiceCollection services) ### Dashboard -CAP 2.1 and above provides the dashboard pages, you can easily view the sent and received messages. In addition, you can also view the message status in real time on the dashboard. +CAP v2.1+ provides the dashboard pages, you can easily view the sent and received messages. In addition, you can also view the message status in real time on the dashboard. In the distributed environment, the dashboard built-in integrated [Consul](http://consul.io) as a node discovery, while the realization of the gateway agent function, you can also easily view the node or other node data, It's like you are visiting local resources. From 4abfb252764808270079b610d35ae869a6b545dd Mon Sep 17 00:00:00 2001 From: Savorboard Date: Thu, 30 Aug 2018 11:35:23 +0800 Subject: [PATCH 221/223] format --- README.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index c8bffb8..9283672 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ PM> Install-Package DotNetCore.CAP.Kafka PM> Install-Package DotNetCore.CAP.RabbitMQ ``` -CAP supports SqlServer, MySql, PostgreSql,MongoDB as event log storage. +CAP supports SqlServer, MySql, PostgreSql,MongoDB as event log storage. ``` // select a database provider you are using, event log table will integrate into. @@ -56,7 +56,7 @@ public void ConfigureServices(IServiceCollection services) //...... services.AddDbContext(); //Options, If you are using EF as the ORM - services.AddSingleton(new MongoClient("")); //Options, If you are using MongoDB + services.AddSingleton(new MongoClient("")); //Options, If you are using MongoDB services.AddCap(x => { @@ -68,9 +68,9 @@ public void ConfigureServices(IServiceCollection services) x.UseMySql("Your ConnectionStrings"); x.UsePostgreSql("Your ConnectionStrings"); - // If you are using MongoDB, you need to add the configuration: - x.UseMongoDB("Your ConnectionStrings"); //MongoDB 4.0+ cluster - + // If you are using MongoDB, you need to add the configuration: + x.UseMongoDB("Your ConnectionStrings"); //MongoDB 4.0+ cluster + // If you are using RabbitMQ, you need to add the configuration: x.UseRabbitMQ("localhost"); @@ -88,7 +88,7 @@ Inject `ICapPublisher` in your Controller, then use the `ICapPublisher` to send ```c# public class PublishController : Controller { - private readonly ICapPublisher _capBus; + private readonly ICapPublisher _capBus; public PublishController(ICapPublisher capPublisher) { @@ -118,7 +118,7 @@ public class PublishController : Controller { //your business code - _capBus.Publish(""xxx.services.show.time", DateTime.Now); + _capBus.Publish(""xxx.services.show.time", DateTime.Now); } return Ok(); @@ -147,7 +147,7 @@ public class PublishController : Controller **Service Method** -If your subscribe method is not in the Controller,then your subscribe class need to Inheritance `ICapSubscribe`: +If your subscribe method is not in the Controller,then your subscribe class need to Inheritance `ICapSubscribe`: ```c# @@ -169,14 +169,14 @@ namespace xxx.Service ``` -Then inject your `ISubscriberService` class in Startup.cs +Then inject your `ISubscriberService` class in Startup.cs ```c# public void ConfigureServices(IServiceCollection services) { //Note: The injection of services needs before of `services.AddCap()` services.AddTransient(); - + services.AddCap(x=>{}); } ``` @@ -191,10 +191,10 @@ In the distributed environment, the dashboard built-in integrated [Consul](http: services.AddCap(x => { //... - + // Register Dashboard x.UseDashboard(); - + // Register to Consul x.UseDiscovery(d => { From b55693b6ef829a1e6257ac13c5fa1a8c5ea6e52b Mon Sep 17 00:00:00 2001 From: Savorboard Date: Thu, 30 Aug 2018 14:09:21 +0800 Subject: [PATCH 222/223] add summary comment --- README.md | 2 +- .../ICapTransaction.MongoDB.cs | 7 ++++ .../ICapTransaction.MySql.cs | 14 ++++++++ .../ICapTransaction.PostgreSql.cs | 14 ++++++++ .../ICapTransaction.SqlServer.cs | 32 +++++++++++++------ src/DotNetCore.CAP/ICapPublisher.cs | 3 ++ src/DotNetCore.CAP/ICapTransaction.cs | 22 +++++++++++-- 7 files changed, 82 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 9283672..3839343 100644 --- a/README.md +++ b/README.md @@ -118,7 +118,7 @@ public class PublishController : Controller { //your business code - _capBus.Publish(""xxx.services.show.time", DateTime.Now); + _capBus.Publish("xxx.services.show.time", DateTime.Now); } return Ok(); diff --git a/src/DotNetCore.CAP.MongoDB/ICapTransaction.MongoDB.cs b/src/DotNetCore.CAP.MongoDB/ICapTransaction.MongoDB.cs index a2ae5a0..ccf7899 100644 --- a/src/DotNetCore.CAP.MongoDB/ICapTransaction.MongoDB.cs +++ b/src/DotNetCore.CAP.MongoDB/ICapTransaction.MongoDB.cs @@ -58,6 +58,13 @@ namespace DotNetCore.CAP return transaction; } + /// + /// Start the CAP transaction + /// + /// The . + /// The . + /// Whether the transaction is automatically committed when the message is published + /// The of MongoDB transaction session object. public static IClientSessionHandle StartTransaction(this IMongoClient client, ICapPublisher publisher, bool autoCommit = false) { diff --git a/src/DotNetCore.CAP.MySql/ICapTransaction.MySql.cs b/src/DotNetCore.CAP.MySql/ICapTransaction.MySql.cs index 749622b..df72d60 100644 --- a/src/DotNetCore.CAP.MySql/ICapTransaction.MySql.cs +++ b/src/DotNetCore.CAP.MySql/ICapTransaction.MySql.cs @@ -74,6 +74,13 @@ namespace DotNetCore.CAP return transaction; } + /// + /// Start the CAP transaction + /// + /// The . + /// The . + /// Whether the transaction is automatically committed when the message is published + /// The of EF dbcontext transaction object. public static IDbContextTransaction BeginTransaction(this DatabaseFacade database, ICapPublisher publisher, bool autoCommit = false) { @@ -82,6 +89,13 @@ namespace DotNetCore.CAP return new CapEFDbTransaction(capTrans); } + /// + /// Start the CAP transaction + /// + /// The . + /// The . + /// Whether the transaction is automatically committed when the message is published + /// The object. public static ICapTransaction BeginTransaction(this IDbConnection dbConnection, ICapPublisher publisher, bool autoCommit = false) { diff --git a/src/DotNetCore.CAP.PostgreSql/ICapTransaction.PostgreSql.cs b/src/DotNetCore.CAP.PostgreSql/ICapTransaction.PostgreSql.cs index 2b5b864..136d132 100644 --- a/src/DotNetCore.CAP.PostgreSql/ICapTransaction.PostgreSql.cs +++ b/src/DotNetCore.CAP.PostgreSql/ICapTransaction.PostgreSql.cs @@ -73,6 +73,13 @@ namespace DotNetCore.CAP return transaction; } + /// + /// Start the CAP transaction + /// + /// The . + /// The . + /// Whether the transaction is automatically committed when the message is published + /// The object. public static ICapTransaction BeginTransaction(this IDbConnection dbConnection, ICapPublisher publisher, bool autoCommit = false) { @@ -85,6 +92,13 @@ namespace DotNetCore.CAP return publisher.Transaction.Begin(dbTransaction, autoCommit); } + /// + /// Start the CAP transaction + /// + /// The . + /// The . + /// Whether the transaction is automatically committed when the message is published + /// The of EF dbcontext transaction object. public static IDbContextTransaction BeginTransaction(this DatabaseFacade database, ICapPublisher publisher, bool autoCommit = false) { diff --git a/src/DotNetCore.CAP.SqlServer/ICapTransaction.SqlServer.cs b/src/DotNetCore.CAP.SqlServer/ICapTransaction.SqlServer.cs index 6cbba35..5ba0e54 100644 --- a/src/DotNetCore.CAP.SqlServer/ICapTransaction.SqlServer.cs +++ b/src/DotNetCore.CAP.SqlServer/ICapTransaction.SqlServer.cs @@ -122,6 +122,22 @@ namespace DotNetCore.CAP return transaction; } + public static ICapTransaction Begin(this ICapTransaction transaction, + IDbContextTransaction dbTransaction, bool autoCommit = false) + { + transaction.DbTransaction = dbTransaction; + transaction.AutoCommit = autoCommit; + + return transaction; + } + + /// + /// Start the CAP transaction + /// + /// The . + /// The . + /// Whether the transaction is automatically committed when the message is published + /// The object. public static IDbTransaction BeginTransaction(this IDbConnection dbConnection, ICapPublisher publisher, bool autoCommit = false) { @@ -135,15 +151,13 @@ namespace DotNetCore.CAP return (IDbTransaction)capTransaction.DbTransaction; } - public static ICapTransaction Begin(this ICapTransaction transaction, - IDbContextTransaction dbTransaction, bool autoCommit = false) - { - transaction.DbTransaction = dbTransaction; - transaction.AutoCommit = autoCommit; - - return transaction; - } - + /// + /// Start the CAP transaction + /// + /// The . + /// The . + /// Whether the transaction is automatically committed when the message is published + /// The of EF dbcontext transaction object. public static IDbContextTransaction BeginTransaction(this DatabaseFacade database, ICapPublisher publisher, bool autoCommit = false) { diff --git a/src/DotNetCore.CAP/ICapPublisher.cs b/src/DotNetCore.CAP/ICapPublisher.cs index 9410516..eebdbd8 100644 --- a/src/DotNetCore.CAP/ICapPublisher.cs +++ b/src/DotNetCore.CAP/ICapPublisher.cs @@ -11,6 +11,9 @@ namespace DotNetCore.CAP /// public interface ICapPublisher { + /// + /// CAP transaction context object + /// ICapTransaction Transaction { get; } /// diff --git a/src/DotNetCore.CAP/ICapTransaction.cs b/src/DotNetCore.CAP/ICapTransaction.cs index 7134438..f73865d 100644 --- a/src/DotNetCore.CAP/ICapTransaction.cs +++ b/src/DotNetCore.CAP/ICapTransaction.cs @@ -1,15 +1,33 @@ -using System; +// Copyright (c) .NET Core Community. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; namespace DotNetCore.CAP { + /// + /// CAP transaction wrapper, used to wrap database transactions, provides a consistent user interface + /// public interface ICapTransaction : IDisposable { + /// + /// A flag is used to indicate whether the transaction is automatically committed after the message is published + /// bool AutoCommit { get; set; } + /// + /// Database transaction object, can be converted to a specific database transaction object or IDBTransaction when used + /// object DbTransaction { get; set; } + /// + /// Submit the transaction context of the CAP, we will send the message to the message queue at the time of submission + /// void Commit(); + /// + /// We will delete the message data that has not been sstore in the buffer data of current transaction context. + /// void Rollback(); } -} +} \ No newline at end of file From 1e250edd43b63a498ae5f25d77e7dbad6ec85c6c Mon Sep 17 00:00:00 2001 From: Savorboard Date: Thu, 30 Aug 2018 14:43:00 +0800 Subject: [PATCH 223/223] Cleanup Code --- .../ICapPublisher.MongoDB.cs | 7 ++++--- .../ICapTransaction.MongoDB.cs | 6 +++--- .../IStorage.MongoDB.cs | 3 ++- .../IStorageConnection.MongoDB.cs | 2 +- .../CAP.MySqlCapOptionsExtension.cs | 2 +- src/DotNetCore.CAP.MySql/CAP.MySqlOptions.cs | 1 - .../ICapPublisher.MySql.cs | 5 +++-- .../ICapTransaction.MySql.cs | 13 ++++++------ .../ICollectProcessor.MySql.cs | 2 +- .../IMonitoringApi.MySql.cs | 4 ++-- .../IStorageConnection.MySql.cs | 2 +- .../CAP.PostgreSqlOptions.cs | 1 - .../ICapPublisher.PostgreSql.cs | 3 +++ .../ICapTransaction.PostgreSql.cs | 12 +++++------ .../IMonitoringApi.PostgreSql.cs | 4 ++-- .../IStorageConnection.PostgreSql.cs | 10 ++++----- .../CAP.SqlServerCapOptionsExtension.cs | 2 +- .../Diagnostics/DiagnosticObserver.cs | 15 ++++++------- .../DiagnosticProcessorObserver.cs | 6 +++--- .../ICapPublisher.SqlServer.cs | 3 +-- .../ICapTransaction.SqlServer.cs | 21 ++++++++++--------- .../IMonitoringApi.SqlServer.cs | 4 ++-- .../IStorage.SqlServer.cs | 2 +- .../IStorageConnection.SqlServer.cs | 2 +- 24 files changed, 66 insertions(+), 66 deletions(-) diff --git a/src/DotNetCore.CAP.MongoDB/ICapPublisher.MongoDB.cs b/src/DotNetCore.CAP.MongoDB/ICapPublisher.MongoDB.cs index f764154..5c4f6cc 100644 --- a/src/DotNetCore.CAP.MongoDB/ICapPublisher.MongoDB.cs +++ b/src/DotNetCore.CAP.MongoDB/ICapPublisher.MongoDB.cs @@ -13,8 +13,8 @@ namespace DotNetCore.CAP.MongoDB { public class MongoDBPublisher : CapPublisherBase, ICallbackPublisher { - private readonly MongoDBOptions _options; private readonly IMongoClient _client; + private readonly MongoDBOptions _options; public MongoDBPublisher(IServiceProvider provider, MongoDBOptions options) : base(provider) @@ -31,7 +31,7 @@ namespace DotNetCore.CAP.MongoDB protected override Task ExecuteAsync(CapPublishedMessage message, ICapTransaction transaction, CancellationToken cancel = default(CancellationToken)) { - var insertOptions = new InsertOneOptions { BypassDocumentValidation = false }; + var insertOptions = new InsertOneOptions {BypassDocumentValidation = false}; var collection = _client .GetDatabase(_options.DatabaseName) @@ -41,7 +41,8 @@ namespace DotNetCore.CAP.MongoDB { return collection.InsertOneAsync(message, insertOptions, cancel); } - var dbTrans = (IClientSessionHandle)transaction.DbTransaction; + + var dbTrans = (IClientSessionHandle) transaction.DbTransaction; return collection.InsertOneAsync(dbTrans, message, insertOptions, cancel); } } diff --git a/src/DotNetCore.CAP.MongoDB/ICapTransaction.MongoDB.cs b/src/DotNetCore.CAP.MongoDB/ICapTransaction.MongoDB.cs index ccf7899..dd0ce50 100644 --- a/src/DotNetCore.CAP.MongoDB/ICapTransaction.MongoDB.cs +++ b/src/DotNetCore.CAP.MongoDB/ICapTransaction.MongoDB.cs @@ -61,10 +61,10 @@ namespace DotNetCore.CAP /// /// Start the CAP transaction /// - /// The . - /// The . + /// The . + /// The . /// Whether the transaction is automatically committed when the message is published - /// The of MongoDB transaction session object. + /// The of MongoDB transaction session object. public static IClientSessionHandle StartTransaction(this IMongoClient client, ICapPublisher publisher, bool autoCommit = false) { diff --git a/src/DotNetCore.CAP.MongoDB/IStorage.MongoDB.cs b/src/DotNetCore.CAP.MongoDB/IStorage.MongoDB.cs index 805240c..1c6f526 100644 --- a/src/DotNetCore.CAP.MongoDB/IStorage.MongoDB.cs +++ b/src/DotNetCore.CAP.MongoDB/IStorage.MongoDB.cs @@ -55,7 +55,8 @@ namespace DotNetCore.CAP.MongoDB if (names.All(n => n != _options.PublishedCollection)) { - await database.CreateCollectionAsync(_options.PublishedCollection, cancellationToken: cancellationToken); + await database.CreateCollectionAsync(_options.PublishedCollection, + cancellationToken: cancellationToken); } _logger.LogDebug("Ensuring all create database tables script are applied."); diff --git a/src/DotNetCore.CAP.MongoDB/IStorageConnection.MongoDB.cs b/src/DotNetCore.CAP.MongoDB/IStorageConnection.MongoDB.cs index 4c9b07a..a58627b 100644 --- a/src/DotNetCore.CAP.MongoDB/IStorageConnection.MongoDB.cs +++ b/src/DotNetCore.CAP.MongoDB/IStorageConnection.MongoDB.cs @@ -105,6 +105,6 @@ namespace DotNetCore.CAP.MongoDB var collection = _database.GetCollection(_options.ReceivedCollection); collection.InsertOne(message); - } + } } } \ No newline at end of file diff --git a/src/DotNetCore.CAP.MySql/CAP.MySqlCapOptionsExtension.cs b/src/DotNetCore.CAP.MySql/CAP.MySqlCapOptionsExtension.cs index 9319ca1..cc63878 100644 --- a/src/DotNetCore.CAP.MySql/CAP.MySqlCapOptionsExtension.cs +++ b/src/DotNetCore.CAP.MySql/CAP.MySqlCapOptionsExtension.cs @@ -47,7 +47,7 @@ namespace DotNetCore.CAP using (var scope = x.CreateScope()) { var provider = scope.ServiceProvider; - var dbContext = (DbContext)provider.GetService(mysqlOptions.DbContextType); + var dbContext = (DbContext) provider.GetService(mysqlOptions.DbContextType); mysqlOptions.ConnectionString = dbContext.Database.GetDbConnection().ConnectionString; return mysqlOptions; } diff --git a/src/DotNetCore.CAP.MySql/CAP.MySqlOptions.cs b/src/DotNetCore.CAP.MySql/CAP.MySqlOptions.cs index e27650d..bcac400 100644 --- a/src/DotNetCore.CAP.MySql/CAP.MySqlOptions.cs +++ b/src/DotNetCore.CAP.MySql/CAP.MySqlOptions.cs @@ -1,7 +1,6 @@ // Copyright (c) .NET Core Community. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. -// ReSharper disable once CheckNamespace namespace DotNetCore.CAP { public class MySqlOptions : EFOptions diff --git a/src/DotNetCore.CAP.MySql/ICapPublisher.MySql.cs b/src/DotNetCore.CAP.MySql/ICapPublisher.MySql.cs index 5347cc4..8167409 100644 --- a/src/DotNetCore.CAP.MySql/ICapPublisher.MySql.cs +++ b/src/DotNetCore.CAP.MySql/ICapPublisher.MySql.cs @@ -20,7 +20,7 @@ namespace DotNetCore.CAP.MySql public MySqlPublisher(IServiceProvider provider) : base(provider) { - _options = provider.GetService(); + _options = provider.GetService(); } public async Task PublishCallbackAsync(CapPublishedMessage message) @@ -45,6 +45,7 @@ namespace DotNetCore.CAP.MySql { dbTrans = dbContextTrans.GetDbTransaction(); } + var conn = dbTrans?.Connection; await conn.ExecuteAsync(PrepareSql(), message, dbTrans); } @@ -55,7 +56,7 @@ namespace DotNetCore.CAP.MySql { return $"INSERT INTO `{_options.TableNamePrefix}.published` (`Id`,`Name`,`Content`,`Retries`,`Added`,`ExpiresAt`,`StatusName`)VALUES(@Id,@Name,@Content,@Retries,@Added,@ExpiresAt,@StatusName);"; - } + } #endregion private methods } diff --git a/src/DotNetCore.CAP.MySql/ICapTransaction.MySql.cs b/src/DotNetCore.CAP.MySql/ICapTransaction.MySql.cs index df72d60..7a77648 100644 --- a/src/DotNetCore.CAP.MySql/ICapTransaction.MySql.cs +++ b/src/DotNetCore.CAP.MySql/ICapTransaction.MySql.cs @@ -67,7 +67,6 @@ namespace DotNetCore.CAP public static ICapTransaction Begin(this ICapTransaction transaction, IDbTransaction dbTransaction, bool autoCommit = false) { - transaction.DbTransaction = dbTransaction; transaction.AutoCommit = autoCommit; @@ -77,10 +76,10 @@ namespace DotNetCore.CAP /// /// Start the CAP transaction /// - /// The . - /// The . + /// The . + /// The . /// Whether the transaction is automatically committed when the message is published - /// The of EF dbcontext transaction object. + /// The of EF dbcontext transaction object. public static IDbContextTransaction BeginTransaction(this DatabaseFacade database, ICapPublisher publisher, bool autoCommit = false) { @@ -92,10 +91,10 @@ namespace DotNetCore.CAP /// /// Start the CAP transaction /// - /// The . - /// The . + /// The . + /// The . /// Whether the transaction is automatically committed when the message is published - /// The object. + /// The object. public static ICapTransaction BeginTransaction(this IDbConnection dbConnection, ICapPublisher publisher, bool autoCommit = false) { diff --git a/src/DotNetCore.CAP.MySql/ICollectProcessor.MySql.cs b/src/DotNetCore.CAP.MySql/ICollectProcessor.MySql.cs index 4cd0a23..2232e9e 100644 --- a/src/DotNetCore.CAP.MySql/ICollectProcessor.MySql.cs +++ b/src/DotNetCore.CAP.MySql/ICollectProcessor.MySql.cs @@ -36,7 +36,7 @@ namespace DotNetCore.CAP.MySql foreach (var table in tables) { _logger.LogDebug($"Collecting expired data from table [{table}]."); - + int removedCount; do { diff --git a/src/DotNetCore.CAP.MySql/IMonitoringApi.MySql.cs b/src/DotNetCore.CAP.MySql/IMonitoringApi.MySql.cs index a8a4bdf..a67e8d6 100644 --- a/src/DotNetCore.CAP.MySql/IMonitoringApi.MySql.cs +++ b/src/DotNetCore.CAP.MySql/IMonitoringApi.MySql.cs @@ -126,7 +126,7 @@ select count(Id) from `{0}.received` where StatusName = N'Failed';", _prefix); { var sqlQuery = $"select count(Id) from `{_prefix}.{tableName}` where StatusName = @state"; - var count = connection.ExecuteScalar(sqlQuery, new { state = statusName }); + var count = connection.ExecuteScalar(sqlQuery, new {state = statusName}); return count; } @@ -169,7 +169,7 @@ select aggr.* from ( var valuesMap = connection.Query( sqlQuery, - new { keys = keyMaps.Keys, statusName }) + new {keys = keyMaps.Keys, statusName}) .ToDictionary(x => x.Key, x => x.Count); foreach (var key in keyMaps.Keys) diff --git a/src/DotNetCore.CAP.MySql/IStorageConnection.MySql.cs b/src/DotNetCore.CAP.MySql/IStorageConnection.MySql.cs index 0036ff2..9401136 100644 --- a/src/DotNetCore.CAP.MySql/IStorageConnection.MySql.cs +++ b/src/DotNetCore.CAP.MySql/IStorageConnection.MySql.cs @@ -109,6 +109,6 @@ VALUES(@Id,@Name,@Group,@Content,@Retries,@Added,@ExpiresAt,@StatusName);"; { return connection.Execute(sql) > 0; } - } + } } } \ No newline at end of file diff --git a/src/DotNetCore.CAP.PostgreSql/CAP.PostgreSqlOptions.cs b/src/DotNetCore.CAP.PostgreSql/CAP.PostgreSqlOptions.cs index 8932f3d..9f52154 100644 --- a/src/DotNetCore.CAP.PostgreSql/CAP.PostgreSqlOptions.cs +++ b/src/DotNetCore.CAP.PostgreSql/CAP.PostgreSqlOptions.cs @@ -1,7 +1,6 @@ // Copyright (c) .NET Core Community. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. -// ReSharper disable once CheckNamespace namespace DotNetCore.CAP { public class PostgreSqlOptions : EFOptions diff --git a/src/DotNetCore.CAP.PostgreSql/ICapPublisher.PostgreSql.cs b/src/DotNetCore.CAP.PostgreSql/ICapPublisher.PostgreSql.cs index bece2d7..6cb5e65 100644 --- a/src/DotNetCore.CAP.PostgreSql/ICapPublisher.PostgreSql.cs +++ b/src/DotNetCore.CAP.PostgreSql/ICapPublisher.PostgreSql.cs @@ -17,6 +17,7 @@ namespace DotNetCore.CAP.PostgreSql public class PostgreSqlPublisher : CapPublisherBase, ICallbackPublisher { private readonly PostgreSqlOptions _options; + public PostgreSqlPublisher(IServiceProvider provider) : base(provider) { _options = provider.GetService(); @@ -44,6 +45,7 @@ namespace DotNetCore.CAP.PostgreSql { dbTrans = dbContextTrans.GetDbTransaction(); } + var conn = dbTrans?.Connection; await conn.ExecuteAsync(PrepareSql(), message, dbTrans); } @@ -62,6 +64,7 @@ namespace DotNetCore.CAP.PostgreSql conn.Open(); return conn; } + #endregion private methods } } \ No newline at end of file diff --git a/src/DotNetCore.CAP.PostgreSql/ICapTransaction.PostgreSql.cs b/src/DotNetCore.CAP.PostgreSql/ICapTransaction.PostgreSql.cs index 136d132..ef6b4d7 100644 --- a/src/DotNetCore.CAP.PostgreSql/ICapTransaction.PostgreSql.cs +++ b/src/DotNetCore.CAP.PostgreSql/ICapTransaction.PostgreSql.cs @@ -76,10 +76,10 @@ namespace DotNetCore.CAP /// /// Start the CAP transaction /// - /// The . - /// The . + /// The . + /// The . /// Whether the transaction is automatically committed when the message is published - /// The object. + /// The object. public static ICapTransaction BeginTransaction(this IDbConnection dbConnection, ICapPublisher publisher, bool autoCommit = false) { @@ -95,10 +95,10 @@ namespace DotNetCore.CAP /// /// Start the CAP transaction /// - /// The . - /// The . + /// The . + /// The . /// Whether the transaction is automatically committed when the message is published - /// The of EF dbcontext transaction object. + /// The of EF dbcontext transaction object. public static IDbContextTransaction BeginTransaction(this DatabaseFacade database, ICapPublisher publisher, bool autoCommit = false) { diff --git a/src/DotNetCore.CAP.PostgreSql/IMonitoringApi.PostgreSql.cs b/src/DotNetCore.CAP.PostgreSql/IMonitoringApi.PostgreSql.cs index 2c73bff..17b81b2 100644 --- a/src/DotNetCore.CAP.PostgreSql/IMonitoringApi.PostgreSql.cs +++ b/src/DotNetCore.CAP.PostgreSql/IMonitoringApi.PostgreSql.cs @@ -128,7 +128,7 @@ select count(""Id"") from ""{0}"".""received"" where ""StatusName"" = N'Failed' var sqlQuery = $"select count(\"Id\") from \"{_options.Schema}\".\"{tableName}\" where Lower(\"StatusName\") = Lower(@state)"; - var count = connection.ExecuteScalar(sqlQuery, new { state = statusName }); + var count = connection.ExecuteScalar(sqlQuery, new {state = statusName}); return count; } @@ -170,7 +170,7 @@ with aggr as ( ) select ""Key"",""Count"" from aggr where ""Key""= Any(@keys);"; - var valuesMap = connection.Query(sqlQuery, new { keys = keyMaps.Keys.ToList(), statusName }) + var valuesMap = connection.Query(sqlQuery, new {keys = keyMaps.Keys.ToList(), statusName}) .ToList() .ToDictionary(x => x.Key, x => x.Count); diff --git a/src/DotNetCore.CAP.PostgreSql/IStorageConnection.PostgreSql.cs b/src/DotNetCore.CAP.PostgreSql/IStorageConnection.PostgreSql.cs index c5c8146..4190383 100644 --- a/src/DotNetCore.CAP.PostgreSql/IStorageConnection.PostgreSql.cs +++ b/src/DotNetCore.CAP.PostgreSql/IStorageConnection.PostgreSql.cs @@ -77,7 +77,7 @@ namespace DotNetCore.CAP.PostgreSql public async Task> GetReceivedMessagesOfNeedRetry() { - var fourMinsAgo = DateTime.Now.AddMinutes(-4).ToString("O"); + var fourMinsAgo = DateTime.Now.AddMinutes(-4).ToString("O"); var sql = $"SELECT * FROM \"{Options.Schema}\".\"received\" WHERE \"Retries\"<{_capOptions.FailedRetryCount} AND \"Added\"<'{fourMinsAgo}' AND (\"StatusName\"='{StatusName.Failed}' OR \"StatusName\"='{StatusName.Scheduled}') LIMIT 200;"; using (var connection = new NpgsqlConnection(Options.ConnectionString)) @@ -86,10 +86,6 @@ namespace DotNetCore.CAP.PostgreSql } } - public void Dispose() - { - } - public bool ChangePublishedState(long messageId, string state) { var sql = @@ -111,5 +107,9 @@ namespace DotNetCore.CAP.PostgreSql return connection.Execute(sql) > 0; } } + + public void Dispose() + { + } } } \ No newline at end of file diff --git a/src/DotNetCore.CAP.SqlServer/CAP.SqlServerCapOptionsExtension.cs b/src/DotNetCore.CAP.SqlServer/CAP.SqlServerCapOptionsExtension.cs index dec70ec..b5e3b97 100644 --- a/src/DotNetCore.CAP.SqlServer/CAP.SqlServerCapOptionsExtension.cs +++ b/src/DotNetCore.CAP.SqlServer/CAP.SqlServerCapOptionsExtension.cs @@ -49,7 +49,7 @@ namespace DotNetCore.CAP using (var scope = x.CreateScope()) { var provider = scope.ServiceProvider; - var dbContext = (DbContext)provider.GetService(sqlServerOptions.DbContextType); + var dbContext = (DbContext) provider.GetService(sqlServerOptions.DbContextType); sqlServerOptions.ConnectionString = dbContext.Database.GetDbConnection().ConnectionString; return sqlServerOptions; } diff --git a/src/DotNetCore.CAP.SqlServer/Diagnostics/DiagnosticObserver.cs b/src/DotNetCore.CAP.SqlServer/Diagnostics/DiagnosticObserver.cs index b58b740..2f466fd 100644 --- a/src/DotNetCore.CAP.SqlServer/Diagnostics/DiagnosticObserver.cs +++ b/src/DotNetCore.CAP.SqlServer/Diagnostics/DiagnosticObserver.cs @@ -12,8 +12,12 @@ namespace DotNetCore.CAP.SqlServer.Diagnostics { internal class DiagnosticObserver : IObserver> { - private readonly IDispatcher _dispatcher; + private const string SqlClientPrefix = "System.Data.SqlClient."; + + public const string SqlAfterCommitTransaction = SqlClientPrefix + "WriteTransactionCommitAfter"; + public const string SqlErrorCommitTransaction = SqlClientPrefix + "WriteTransactionCommitError"; private readonly ConcurrentDictionary> _bufferList; + private readonly IDispatcher _dispatcher; public DiagnosticObserver(IDispatcher dispatcher, ConcurrentDictionary> bufferList) @@ -22,19 +26,12 @@ namespace DotNetCore.CAP.SqlServer.Diagnostics _bufferList = bufferList; } - private const string SqlClientPrefix = "System.Data.SqlClient."; - - public const string SqlAfterCommitTransaction = SqlClientPrefix + "WriteTransactionCommitAfter"; - public const string SqlErrorCommitTransaction = SqlClientPrefix + "WriteTransactionCommitError"; - public void OnCompleted() { - } public void OnError(Exception error) { - } public void OnNext(KeyValuePair evt) @@ -60,7 +57,7 @@ namespace DotNetCore.CAP.SqlServer.Diagnostics } } - static object GetProperty(object _this, string propertyName) + private static object GetProperty(object _this, string propertyName) { return _this.GetType().GetTypeInfo().GetDeclaredProperty(propertyName)?.GetValue(_this); } diff --git a/src/DotNetCore.CAP.SqlServer/Diagnostics/DiagnosticProcessorObserver.cs b/src/DotNetCore.CAP.SqlServer/Diagnostics/DiagnosticProcessorObserver.cs index dd3e579..658f088 100644 --- a/src/DotNetCore.CAP.SqlServer/Diagnostics/DiagnosticProcessorObserver.cs +++ b/src/DotNetCore.CAP.SqlServer/Diagnostics/DiagnosticProcessorObserver.cs @@ -11,10 +11,8 @@ namespace DotNetCore.CAP.SqlServer.Diagnostics { public class DiagnosticProcessorObserver : IObserver { - private readonly IDispatcher _dispatcher; public const string DiagnosticListenerName = "SqlClientDiagnosticListener"; - - public ConcurrentDictionary> BufferList { get; } + private readonly IDispatcher _dispatcher; public DiagnosticProcessorObserver(IDispatcher dispatcher) { @@ -22,6 +20,8 @@ namespace DotNetCore.CAP.SqlServer.Diagnostics BufferList = new ConcurrentDictionary>(); } + public ConcurrentDictionary> BufferList { get; } + public void OnCompleted() { } diff --git a/src/DotNetCore.CAP.SqlServer/ICapPublisher.SqlServer.cs b/src/DotNetCore.CAP.SqlServer/ICapPublisher.SqlServer.cs index 9f12e95..8a61c80 100644 --- a/src/DotNetCore.CAP.SqlServer/ICapPublisher.SqlServer.cs +++ b/src/DotNetCore.CAP.SqlServer/ICapPublisher.SqlServer.cs @@ -56,9 +56,8 @@ namespace DotNetCore.CAP.SqlServer { return $"INSERT INTO {_options.Schema}.[Published] ([Id],[Name],[Content],[Retries],[Added],[ExpiresAt],[StatusName])VALUES(@Id,@Name,@Content,@Retries,@Added,@ExpiresAt,@StatusName);"; - } + } #endregion private methods - } } \ No newline at end of file diff --git a/src/DotNetCore.CAP.SqlServer/ICapTransaction.SqlServer.cs b/src/DotNetCore.CAP.SqlServer/ICapTransaction.SqlServer.cs index 5ba0e54..d06f33a 100644 --- a/src/DotNetCore.CAP.SqlServer/ICapTransaction.SqlServer.cs +++ b/src/DotNetCore.CAP.SqlServer/ICapTransaction.SqlServer.cs @@ -22,7 +22,7 @@ namespace DotNetCore.CAP private readonly DiagnosticProcessorObserver _diagnosticProcessor; public SqlServerCapTransaction( - IDispatcher dispatcher, + IDispatcher dispatcher, SqlServerOptions sqlServerOptions, IServiceProvider serviceProvider) : base(dispatcher) { @@ -30,6 +30,7 @@ namespace DotNetCore.CAP { _dbContext = serviceProvider.GetService(sqlServerOptions.DbContextType) as DbContext; } + _diagnosticProcessor = serviceProvider.GetRequiredService(); } @@ -55,14 +56,14 @@ namespace DotNetCore.CAP } } - var transactionKey = ((SqlConnection)dbTransaction.Connection).ClientConnectionId; + var transactionKey = ((SqlConnection) dbTransaction.Connection).ClientConnectionId; if (_diagnosticProcessor.BufferList.TryGetValue(transactionKey, out var list)) { list.Add(msg); } else { - var msgList = new List(1) { msg }; + var msgList = new List(1) {msg}; _diagnosticProcessor.BufferList.TryAdd(transactionKey, msgList); } } @@ -134,10 +135,10 @@ namespace DotNetCore.CAP /// /// Start the CAP transaction /// - /// The . - /// The . + /// The . + /// The . /// Whether the transaction is automatically committed when the message is published - /// The object. + /// The object. public static IDbTransaction BeginTransaction(this IDbConnection dbConnection, ICapPublisher publisher, bool autoCommit = false) { @@ -148,16 +149,16 @@ namespace DotNetCore.CAP var dbTransaction = dbConnection.BeginTransaction(); var capTransaction = publisher.Transaction.Begin(dbTransaction, autoCommit); - return (IDbTransaction)capTransaction.DbTransaction; + return (IDbTransaction) capTransaction.DbTransaction; } /// /// Start the CAP transaction /// - /// The . - /// The . + /// The . + /// The . /// Whether the transaction is automatically committed when the message is published - /// The of EF dbcontext transaction object. + /// The of EF dbcontext transaction object. public static IDbContextTransaction BeginTransaction(this DatabaseFacade database, ICapPublisher publisher, bool autoCommit = false) { diff --git a/src/DotNetCore.CAP.SqlServer/IMonitoringApi.SqlServer.cs b/src/DotNetCore.CAP.SqlServer/IMonitoringApi.SqlServer.cs index 67c7d22..38c4318 100644 --- a/src/DotNetCore.CAP.SqlServer/IMonitoringApi.SqlServer.cs +++ b/src/DotNetCore.CAP.SqlServer/IMonitoringApi.SqlServer.cs @@ -135,7 +135,7 @@ select count(Id) from [{0}].Received with (nolock) where StatusName = N'Failed'; var sqlQuery = $"select count(Id) from [{_options.Schema}].{tableName} with (nolock) where StatusName = @state"; - var count = connection.ExecuteScalar(sqlQuery, new { state = statusName }); + var count = connection.ExecuteScalar(sqlQuery, new {state = statusName}); return count; } @@ -189,7 +189,7 @@ select [Key], [Count] from aggr with (nolock) where [Key] in @keys;"; var valuesMap = connection.Query( _options.IsSqlServer2008 ? sqlQuery2008 : sqlQuery, - new { keys = keyMaps.Keys, statusName }) + new {keys = keyMaps.Keys, statusName}) .ToDictionary(x => x.Key, x => x.Count); foreach (var key in keyMaps.Keys) diff --git a/src/DotNetCore.CAP.SqlServer/IStorage.SqlServer.cs b/src/DotNetCore.CAP.SqlServer/IStorage.SqlServer.cs index f6995c3..abc19d4 100644 --- a/src/DotNetCore.CAP.SqlServer/IStorage.SqlServer.cs +++ b/src/DotNetCore.CAP.SqlServer/IStorage.SqlServer.cs @@ -17,10 +17,10 @@ namespace DotNetCore.CAP.SqlServer public class SqlServerStorage : IStorage { private readonly CapOptions _capOptions; + private readonly DiagnosticProcessorObserver _diagnosticProcessorObserver; private readonly IDbConnection _existingConnection = null; private readonly ILogger _logger; private readonly SqlServerOptions _options; - private readonly DiagnosticProcessorObserver _diagnosticProcessorObserver; public SqlServerStorage(ILogger logger, CapOptions capOptions, diff --git a/src/DotNetCore.CAP.SqlServer/IStorageConnection.SqlServer.cs b/src/DotNetCore.CAP.SqlServer/IStorageConnection.SqlServer.cs index 08c2274..b938d8b 100644 --- a/src/DotNetCore.CAP.SqlServer/IStorageConnection.SqlServer.cs +++ b/src/DotNetCore.CAP.SqlServer/IStorageConnection.SqlServer.cs @@ -63,7 +63,7 @@ VALUES(@Id,@Name,@Group,@Content,@Retries,@Added,@ExpiresAt,@StatusName);"; using (var connection = new SqlConnection(Options.ConnectionString)) { - connection.Execute(sql, message); + connection.Execute(sql, message); } }