@@ -25,12 +25,13 @@ namespace Sample.Kafka | |||||
{ | { | ||||
services.AddDbContext<AppDbContext>(); | services.AddDbContext<AppDbContext>(); | ||||
services.AddCap(x=> { | |||||
services.AddCap(x => | |||||
{ | |||||
x.UseEntityFramework<AppDbContext>(); | x.UseEntityFramework<AppDbContext>(); | ||||
x.UseSqlServer("Server=DESKTOP-M9R8T31;Initial Catalog=Test;User Id=sa;Password=P@ssw0rd;MultipleActiveResultSets=True"); | x.UseSqlServer("Server=DESKTOP-M9R8T31;Initial Catalog=Test;User Id=sa;Password=P@ssw0rd;MultipleActiveResultSets=True"); | ||||
x.UseRabbitMQ("localhost"); | x.UseRabbitMQ("localhost"); | ||||
}); | }); | ||||
// Add framework services. | // Add framework services. | ||||
services.AddMvc(); | services.AddMvc(); | ||||
} | } | ||||
@@ -44,4 +44,4 @@ namespace Microsoft.Extensions.DependencyInjection | |||||
return options; | return options; | ||||
} | } | ||||
} | } | ||||
} | |||||
} |
@@ -1,5 +1,4 @@ | |||||
using System; | using System; | ||||
using Microsoft.EntityFrameworkCore; | |||||
using DotNetCore.CAP.EntityFrameworkCore; | using DotNetCore.CAP.EntityFrameworkCore; | ||||
using DotNetCore.CAP.Processor; | using DotNetCore.CAP.Processor; | ||||
using Microsoft.Extensions.DependencyInjection; | using Microsoft.Extensions.DependencyInjection; | ||||
@@ -17,8 +16,8 @@ namespace DotNetCore.CAP | |||||
public void AddServices(IServiceCollection services) | public void AddServices(IServiceCollection services) | ||||
{ | { | ||||
services.AddSingleton<IStorage, EFStorage>(); | |||||
services.AddScoped<IStorageConnection, EFStorageConnection>(); | |||||
services.AddSingleton<IStorage, SqlServerStorage>(); | |||||
services.AddScoped<IStorageConnection, SqlServerStorageConnection>(); | |||||
services.AddScoped<ICapPublisher, CapPublisher>(); | services.AddScoped<ICapPublisher, CapPublisher>(); | ||||
services.AddTransient<IAdditionalProcessor, DefaultAdditionalProcessor>(); | services.AddTransient<IAdditionalProcessor, DefaultAdditionalProcessor>(); | ||||
@@ -27,16 +26,6 @@ namespace DotNetCore.CAP | |||||
var sqlServerOptions = new SqlServerOptions(); | var sqlServerOptions = new SqlServerOptions(); | ||||
_configure(sqlServerOptions); | _configure(sqlServerOptions); | ||||
services.AddSingleton(sqlServerOptions); | services.AddSingleton(sqlServerOptions); | ||||
services.AddDbContext<CapDbContext>(options => | |||||
{ | |||||
options.UseSqlServer(sqlServerOptions.ConnectionString, sqlOpts => | |||||
{ | |||||
sqlOpts.MigrationsHistoryTable( | |||||
sqlServerOptions.MigrationsHistoryTableName, | |||||
sqlServerOptions.MigrationsHistoryTableSchema ?? sqlServerOptions.Schema); | |||||
}); | |||||
}); | |||||
} | } | ||||
} | } | ||||
} | } |
@@ -1,74 +0,0 @@ | |||||
using System.Data.Common; | |||||
using DotNetCore.CAP.Infrastructure; | |||||
using DotNetCore.CAP.Models; | |||||
using Microsoft.EntityFrameworkCore; | |||||
namespace DotNetCore.CAP.EntityFrameworkCore | |||||
{ | |||||
/// <summary> | |||||
/// Base class for the Entity Framework database context used for CAP. | |||||
/// </summary> | |||||
public class CapDbContext : DbContext | |||||
{ | |||||
private SqlServerOptions _sqlServerOptions; | |||||
/// <summary> | |||||
/// Initializes a new instance of the <see cref="CapDbContext"/>. | |||||
/// </summary> | |||||
public CapDbContext() { } | |||||
/// <summary> | |||||
/// Initializes a new instance of the <see cref="CapDbContext"/>. | |||||
/// </summary> | |||||
/// <param name="options">The options to be used by a <see cref="DbContext"/>.</param> | |||||
public CapDbContext(DbContextOptions<CapDbContext> options, SqlServerOptions sqlServerOptions) | |||||
: base(options) { | |||||
_sqlServerOptions = sqlServerOptions; | |||||
} | |||||
/// <summary> | |||||
/// Gets or sets the <see cref="CapSentMessage"/> of Messages. | |||||
/// </summary> | |||||
public DbSet<CapSentMessage> CapSentMessages { get; set; } | |||||
public DbSet<CapQueue> CapQueue { get; set; } | |||||
/// <summary> | |||||
/// Gets or sets the <see cref="CapReceivedMessages"/> of Messages. | |||||
/// </summary> | |||||
public DbSet<CapReceivedMessage> CapReceivedMessages { get; set; } | |||||
public DbConnection GetDbConnection() => Database.GetDbConnection(); | |||||
/// <summary> | |||||
/// Configures the schema for the identity framework. | |||||
/// </summary> | |||||
/// <param name="modelBuilder"> | |||||
/// The builder being used to construct the model for this context. | |||||
/// </param> | |||||
protected override void OnModelCreating(ModelBuilder modelBuilder) | |||||
{ | |||||
_sqlServerOptions = new SqlServerOptions(); | |||||
modelBuilder.HasDefaultSchema(_sqlServerOptions.Schema); | |||||
modelBuilder.Entity<CapSentMessage>(b => | |||||
{ | |||||
b.HasKey(m => m.Id); | |||||
b.HasIndex(x => x.StatusName); | |||||
b.Property(p => p.StatusName).IsRequired().HasMaxLength(50); | |||||
}); | |||||
modelBuilder.Entity<CapReceivedMessage>(b => | |||||
{ | |||||
b.HasKey(m => m.Id); | |||||
b.HasIndex(x => x.StatusName); | |||||
b.Property(p => p.StatusName).IsRequired().HasMaxLength(50); | |||||
}); | |||||
} | |||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) | |||||
{ | |||||
// optionsBuilder.UseSqlServer("Server=192.168.2.206;Initial Catalog=Test;User Id=cmswuliu;Password=h7xY81agBn*Veiu3;MultipleActiveResultSets=True"); | |||||
optionsBuilder.UseSqlServer("Server=DESKTOP-M9R8T31;Initial Catalog=Test;User Id=sa;Password=P@ssw0rd;MultipleActiveResultSets=True"); | |||||
} | |||||
} | |||||
} |
@@ -80,14 +80,14 @@ namespace DotNetCore.CAP.EntityFrameworkCore | |||||
private async Task PublishWithTrans(string topic, string content, IDbConnection dbConnection, IDbTransaction dbTransaction) | private async Task PublishWithTrans(string topic, string content, IDbConnection dbConnection, IDbTransaction dbTransaction) | ||||
{ | { | ||||
var message = new CapSentMessage | |||||
var message = new CapPublishedMessage | |||||
{ | { | ||||
KeyName = topic, | |||||
Name = topic, | |||||
Content = content, | Content = content, | ||||
StatusName = StatusName.Scheduled | StatusName = StatusName.Scheduled | ||||
}; | }; | ||||
var sql = $"INSERT INTO {_options.Schema}.[{nameof(CapDbContext.CapSentMessages)}] ([Id],[Added],[Content],[KeyName],[ExpiresAt],[Retries],[StatusName])VALUES(@Id,@Added,@Content,@KeyName,@ExpiresAt,@Retries,@StatusName)"; | |||||
var sql = $"INSERT INTO {_options.Schema}.[Published] ([Id],[Added],[Content],[KeyName],[ExpiresAt],[Retries],[StatusName])VALUES(@Id,@Added,@Content,@KeyName,@ExpiresAt,@Retries,@StatusName)"; | |||||
await dbConnection.ExecuteAsync(sql, message, transaction: dbTransaction); | await dbConnection.ExecuteAsync(sql, message, transaction: dbTransaction); | ||||
PublishQueuer.PulseEvent.Set(); | PublishQueuer.PulseEvent.Set(); | ||||
@@ -1,44 +0,0 @@ | |||||
using System; | |||||
using System.Threading; | |||||
using System.Threading.Tasks; | |||||
using Microsoft.EntityFrameworkCore; | |||||
using Microsoft.Extensions.DependencyInjection; | |||||
using Microsoft.Extensions.Logging; | |||||
namespace DotNetCore.CAP.EntityFrameworkCore | |||||
{ | |||||
public class EFStorage : IStorage | |||||
{ | |||||
private IServiceProvider _provider; | |||||
private ILogger _logger; | |||||
public EFStorage( | |||||
IServiceProvider provider, | |||||
ILogger<EFStorage> logger) | |||||
{ | |||||
_provider = provider; | |||||
_logger = logger; | |||||
} | |||||
public async Task InitializeAsync(CancellationToken cancellationToken) | |||||
{ | |||||
using (var scope = _provider.CreateScope()) | |||||
{ | |||||
if (cancellationToken.IsCancellationRequested) return; | |||||
var provider = scope.ServiceProvider; | |||||
var context = provider.GetRequiredService<CapDbContext>(); | |||||
_logger.LogDebug("Ensuring all migrations are applied to Jobs database."); | |||||
try | |||||
{ | |||||
await context.Database.MigrateAsync(cancellationToken); | |||||
} | |||||
catch (Exception ex) | |||||
{ | |||||
throw ex; | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -1,142 +0,0 @@ | |||||
using System; | |||||
using System.Data; | |||||
using System.Data.SqlClient; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
using Dapper; | |||||
using DotNetCore.CAP.Infrastructure; | |||||
using DotNetCore.CAP.Models; | |||||
using Microsoft.EntityFrameworkCore; | |||||
using Microsoft.EntityFrameworkCore.Storage; | |||||
using Microsoft.Extensions.Options; | |||||
namespace DotNetCore.CAP.EntityFrameworkCore | |||||
{ | |||||
public class EFStorageConnection : IStorageConnection | |||||
{ | |||||
private readonly CapDbContext _context; | |||||
private readonly SqlServerOptions _options; | |||||
public EFStorageConnection( | |||||
CapDbContext context, | |||||
IOptions<SqlServerOptions> options) | |||||
{ | |||||
_context = context; | |||||
_options = options.Value; | |||||
} | |||||
public CapDbContext Context => _context; | |||||
public SqlServerOptions Options => _options; | |||||
public IStorageTransaction CreateTransaction() | |||||
{ | |||||
return new EFStorageTransaction(this); | |||||
} | |||||
public Task<CapSentMessage> GetSentMessageAsync(string id) | |||||
{ | |||||
return _context.CapSentMessages.FirstOrDefaultAsync(x => x.Id == id); | |||||
} | |||||
public Task<IFetchedMessage> FetchNextMessageAsync() | |||||
{ | |||||
var sql = $@" | |||||
DELETE TOP (1) | |||||
FROM [{_options.Schema}].[{nameof(CapDbContext.CapQueue)}] WITH (readpast, updlock, rowlock) | |||||
OUTPUT DELETED.MessageId,DELETED.[Type];"; | |||||
return FetchNextMessageCoreAsync(sql); | |||||
} | |||||
public async Task<CapSentMessage> GetNextSentMessageToBeEnqueuedAsync() | |||||
{ | |||||
var sql = $@" | |||||
SELECT TOP (1) * | |||||
FROM [{_options.Schema}].[{nameof(CapDbContext.CapSentMessages)}] WITH (readpast) | |||||
WHERE StatusName = '{StatusName.Scheduled}'"; | |||||
var connection = _context.GetDbConnection(); | |||||
var message = (await connection.QueryAsync<CapSentMessage>(sql)).FirstOrDefault(); | |||||
if (message != null) | |||||
{ | |||||
_context.Attach(message); | |||||
} | |||||
return message; | |||||
} | |||||
// CapReceviedMessage | |||||
public Task StoreReceivedMessageAsync(CapReceivedMessage message) | |||||
{ | |||||
if (message == null) throw new ArgumentNullException(nameof(message)); | |||||
_context.Add(message); | |||||
return _context.SaveChangesAsync(); | |||||
} | |||||
public Task<CapReceivedMessage> GetReceivedMessageAsync(string id) | |||||
{ | |||||
return _context.CapReceivedMessages.FirstOrDefaultAsync(x => x.Id == id); | |||||
} | |||||
public async Task<CapReceivedMessage> GetNextReceviedMessageToBeEnqueuedAsync() | |||||
{ | |||||
var sql = $@" | |||||
SELECT TOP (1) * | |||||
FROM [{_options.Schema}].[{nameof(CapDbContext.CapReceivedMessages)}] WITH (readpast) | |||||
WHERE StatusName = '{StatusName.Enqueued}'"; | |||||
var connection = _context.GetDbConnection(); | |||||
var message = (await connection.QueryAsync<CapReceivedMessage>(sql)).FirstOrDefault(); | |||||
if (message != null) | |||||
{ | |||||
_context.Attach(message); | |||||
} | |||||
return message; | |||||
} | |||||
public void Dispose() | |||||
{ | |||||
} | |||||
private async Task<IFetchedMessage> FetchNextMessageCoreAsync(string sql, object args = null) | |||||
{ | |||||
FetchedMessage fetchedMessage = null; | |||||
var connection = _context.GetDbConnection(); | |||||
var transaction = _context.Database.CurrentTransaction; | |||||
transaction = transaction ?? await _context.Database.BeginTransactionAsync(IsolationLevel.ReadCommitted); | |||||
try | |||||
{ | |||||
fetchedMessage = | |||||
(await connection.QueryAsync<FetchedMessage>(sql, args, transaction.GetDbTransaction())) | |||||
.FirstOrDefault(); | |||||
} | |||||
catch (SqlException) | |||||
{ | |||||
transaction.Dispose(); | |||||
throw; | |||||
} | |||||
if (fetchedMessage == null) | |||||
{ | |||||
transaction.Rollback(); | |||||
transaction.Dispose(); | |||||
return null; | |||||
} | |||||
return new EFFetchedMessage( | |||||
fetchedMessage.MessageId, | |||||
fetchedMessage.Type, | |||||
connection, | |||||
transaction); | |||||
} | |||||
} | |||||
} |
@@ -1,63 +0,0 @@ | |||||
using System; | |||||
using System.Threading.Tasks; | |||||
using DotNetCore.CAP.Models; | |||||
namespace DotNetCore.CAP.EntityFrameworkCore | |||||
{ | |||||
public class EFStorageTransaction | |||||
: IStorageTransaction, IDisposable | |||||
{ | |||||
private EFStorageConnection _connection; | |||||
public EFStorageTransaction(EFStorageConnection connection) | |||||
{ | |||||
_connection = connection; | |||||
} | |||||
public void UpdateMessage(CapSentMessage message) | |||||
{ | |||||
if (message == null) throw new ArgumentNullException(nameof(message)); | |||||
// NOOP. EF will detect changes. | |||||
} | |||||
public void UpdateMessage(CapReceivedMessage message) | |||||
{ | |||||
if (message == null) throw new ArgumentNullException(nameof(message)); | |||||
// NOOP. EF will detect changes. | |||||
} | |||||
public void EnqueueMessage(CapSentMessage message) | |||||
{ | |||||
if (message == null) throw new ArgumentNullException(nameof(message)); | |||||
_connection.Context.Add(new CapQueue | |||||
{ | |||||
MessageId = message.Id, | |||||
Type = 0 | |||||
}); | |||||
} | |||||
public void EnqueueMessage(CapReceivedMessage message) | |||||
{ | |||||
if (message == null) throw new ArgumentNullException(nameof(message)); | |||||
_connection.Context.Add(new CapQueue | |||||
{ | |||||
MessageId = message.Id, | |||||
Type = MessageType.Subscribe | |||||
}); | |||||
} | |||||
public Task CommitAsync() | |||||
{ | |||||
return _connection.Context.SaveChangesAsync(); | |||||
} | |||||
public void Dispose() | |||||
{ | |||||
} | |||||
} | |||||
} |
@@ -7,8 +7,8 @@ namespace DotNetCore.CAP.EntityFrameworkCore | |||||
{ | { | ||||
public class FetchedMessage | public class FetchedMessage | ||||
{ | { | ||||
public string MessageId { get; set; } | |||||
public int MessageId { get; set; } | |||||
public MessageType Type { get; set; } | |||||
public MessageType MessageType { get; set; } | |||||
} | } | ||||
} | } |
@@ -1,5 +1,6 @@ | |||||
using System; | using System; | ||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
using System.Data.SqlClient; | |||||
using System.Text; | using System.Text; | ||||
using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
using Dapper; | using Dapper; | ||||
@@ -21,8 +22,7 @@ namespace DotNetCore.CAP.EntityFrameworkCore | |||||
private static readonly string[] Tables = | private static readonly string[] Tables = | ||||
{ | { | ||||
nameof(CapDbContext.CapSentMessages), | |||||
nameof(CapDbContext.CapReceivedMessages), | |||||
"Published","Received" | |||||
}; | }; | ||||
public DefaultAdditionalProcessor( | public DefaultAdditionalProcessor( | ||||
@@ -44,18 +44,14 @@ namespace DotNetCore.CAP.EntityFrameworkCore | |||||
var removedCount = 0; | var removedCount = 0; | ||||
do | do | ||||
{ | { | ||||
using (var scope = _provider.CreateScope()) | |||||
using(var connection = new SqlConnection(_options.ConnectionString)) | |||||
{ | { | ||||
var provider = scope.ServiceProvider; | |||||
var jobsDbContext = provider.GetService<CapDbContext>(); | |||||
var connection = jobsDbContext.GetDbConnection(); | |||||
removedCount = await connection.ExecuteAsync($@" | removedCount = await connection.ExecuteAsync($@" | ||||
DELETE TOP (@count) | DELETE TOP (@count) | ||||
FROM [{_options.Schema}].[{table}] WITH (readpast) | FROM [{_options.Schema}].[{table}] WITH (readpast) | ||||
WHERE ExpiresAt < @now;", new { now = DateTime.Now, count = MaxBatch }); | WHERE ExpiresAt < @now;", new { now = DateTime.Now, count = MaxBatch }); | ||||
} | } | ||||
if (removedCount != 0) | if (removedCount != 0) | ||||
{ | { | ||||
await context.WaitAsync(_delay); | await context.WaitAsync(_delay); | ||||
@@ -1,91 +0,0 @@ | |||||
using System; | |||||
using Microsoft.EntityFrameworkCore; | |||||
using Microsoft.EntityFrameworkCore.Infrastructure; | |||||
using Microsoft.EntityFrameworkCore.Metadata; | |||||
using Microsoft.EntityFrameworkCore.Migrations; | |||||
using DotNetCore.CAP.EntityFrameworkCore; | |||||
using DotNetCore.CAP.Models; | |||||
namespace DotNetCore.CAP.EntityFrameworkCore.Migrations | |||||
{ | |||||
[DbContext(typeof(CapDbContext))] | |||||
[Migration("20170714102709_InitializeDB")] | |||||
partial class InitializeDB | |||||
{ | |||||
protected override void BuildTargetModel(ModelBuilder modelBuilder) | |||||
{ | |||||
modelBuilder | |||||
.HasDefaultSchema("cap") | |||||
.HasAnnotation("ProductVersion", "1.1.2") | |||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); | |||||
modelBuilder.Entity("DotNetCore.CAP.Models.CapQueue", b => | |||||
{ | |||||
b.Property<int>("Id") | |||||
.ValueGeneratedOnAdd(); | |||||
b.Property<string>("MessageId"); | |||||
b.Property<int>("Type"); | |||||
b.HasKey("Id"); | |||||
b.ToTable("CapQueue"); | |||||
}); | |||||
modelBuilder.Entity("DotNetCore.CAP.Models.CapReceivedMessage", b => | |||||
{ | |||||
b.Property<string>("Id") | |||||
.ValueGeneratedOnAdd(); | |||||
b.Property<DateTime>("Added"); | |||||
b.Property<string>("Content"); | |||||
b.Property<DateTime?>("ExpiresAt"); | |||||
b.Property<string>("Group"); | |||||
b.Property<string>("KeyName"); | |||||
b.Property<int>("Retries"); | |||||
b.Property<string>("StatusName") | |||||
.IsRequired() | |||||
.HasMaxLength(50); | |||||
b.HasKey("Id"); | |||||
b.HasIndex("StatusName"); | |||||
b.ToTable("CapReceivedMessages"); | |||||
}); | |||||
modelBuilder.Entity("DotNetCore.CAP.Models.CapSentMessage", b => | |||||
{ | |||||
b.Property<string>("Id") | |||||
.ValueGeneratedOnAdd(); | |||||
b.Property<DateTime>("Added"); | |||||
b.Property<string>("Content"); | |||||
b.Property<DateTime?>("ExpiresAt"); | |||||
b.Property<string>("KeyName"); | |||||
b.Property<int>("Retries"); | |||||
b.Property<string>("StatusName") | |||||
.IsRequired() | |||||
.HasMaxLength(50); | |||||
b.HasKey("Id"); | |||||
b.HasIndex("StatusName"); | |||||
b.ToTable("CapSentMessages"); | |||||
}); | |||||
} | |||||
} | |||||
} |
@@ -1,95 +0,0 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using Microsoft.EntityFrameworkCore.Migrations; | |||||
using Microsoft.EntityFrameworkCore.Metadata; | |||||
namespace DotNetCore.CAP.EntityFrameworkCore.Migrations | |||||
{ | |||||
public partial class InitializeDB : Migration | |||||
{ | |||||
protected override void Up(MigrationBuilder migrationBuilder) | |||||
{ | |||||
migrationBuilder.EnsureSchema( | |||||
name: "cap"); | |||||
migrationBuilder.CreateTable( | |||||
name: "CapQueue", | |||||
schema: "cap", | |||||
columns: table => new | |||||
{ | |||||
Id = table.Column<int>(nullable: false) | |||||
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn), | |||||
MessageId = table.Column<string>(nullable: true), | |||||
Type = table.Column<int>(nullable: false) | |||||
}, | |||||
constraints: table => | |||||
{ | |||||
table.PrimaryKey("PK_CapQueue", x => x.Id); | |||||
}); | |||||
migrationBuilder.CreateTable( | |||||
name: "CapReceivedMessages", | |||||
schema: "cap", | |||||
columns: table => new | |||||
{ | |||||
Id = table.Column<string>(nullable: false), | |||||
Added = table.Column<DateTime>(nullable: false), | |||||
Content = table.Column<string>(nullable: true), | |||||
ExpiresAt = table.Column<DateTime>(nullable: true), | |||||
Group = table.Column<string>(nullable: true), | |||||
KeyName = table.Column<string>(nullable: true), | |||||
Retries = table.Column<int>(nullable: false), | |||||
StatusName = table.Column<string>(maxLength: 50, nullable: false) | |||||
}, | |||||
constraints: table => | |||||
{ | |||||
table.PrimaryKey("PK_CapReceivedMessages", x => x.Id); | |||||
}); | |||||
migrationBuilder.CreateTable( | |||||
name: "CapSentMessages", | |||||
schema: "cap", | |||||
columns: table => new | |||||
{ | |||||
Id = table.Column<string>(nullable: false), | |||||
Added = table.Column<DateTime>(nullable: false), | |||||
Content = table.Column<string>(nullable: true), | |||||
ExpiresAt = table.Column<DateTime>(nullable: true), | |||||
KeyName = table.Column<string>(nullable: true), | |||||
Retries = table.Column<int>(nullable: false), | |||||
StatusName = table.Column<string>(maxLength: 50, nullable: false) | |||||
}, | |||||
constraints: table => | |||||
{ | |||||
table.PrimaryKey("PK_CapSentMessages", x => x.Id); | |||||
}); | |||||
migrationBuilder.CreateIndex( | |||||
name: "IX_CapReceivedMessages_StatusName", | |||||
schema: "cap", | |||||
table: "CapReceivedMessages", | |||||
column: "StatusName"); | |||||
migrationBuilder.CreateIndex( | |||||
name: "IX_CapSentMessages_StatusName", | |||||
schema: "cap", | |||||
table: "CapSentMessages", | |||||
column: "StatusName"); | |||||
} | |||||
protected override void Down(MigrationBuilder migrationBuilder) | |||||
{ | |||||
migrationBuilder.DropTable( | |||||
name: "CapQueue", | |||||
schema: "cap"); | |||||
migrationBuilder.DropTable( | |||||
name: "CapReceivedMessages", | |||||
schema: "cap"); | |||||
migrationBuilder.DropTable( | |||||
name: "CapSentMessages", | |||||
schema: "cap"); | |||||
} | |||||
} | |||||
} |
@@ -1,90 +0,0 @@ | |||||
using System; | |||||
using Microsoft.EntityFrameworkCore; | |||||
using Microsoft.EntityFrameworkCore.Infrastructure; | |||||
using Microsoft.EntityFrameworkCore.Metadata; | |||||
using Microsoft.EntityFrameworkCore.Migrations; | |||||
using DotNetCore.CAP.EntityFrameworkCore; | |||||
using DotNetCore.CAP.Models; | |||||
namespace DotNetCore.CAP.EntityFrameworkCore.Migrations | |||||
{ | |||||
[DbContext(typeof(CapDbContext))] | |||||
partial class CapDbContextModelSnapshot : ModelSnapshot | |||||
{ | |||||
protected override void BuildModel(ModelBuilder modelBuilder) | |||||
{ | |||||
modelBuilder | |||||
.HasDefaultSchema("cap") | |||||
.HasAnnotation("ProductVersion", "1.1.2") | |||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); | |||||
modelBuilder.Entity("DotNetCore.CAP.Models.CapQueue", b => | |||||
{ | |||||
b.Property<int>("Id") | |||||
.ValueGeneratedOnAdd(); | |||||
b.Property<string>("MessageId"); | |||||
b.Property<int>("Type"); | |||||
b.HasKey("Id"); | |||||
b.ToTable("CapQueue"); | |||||
}); | |||||
modelBuilder.Entity("DotNetCore.CAP.Models.CapReceivedMessage", b => | |||||
{ | |||||
b.Property<string>("Id") | |||||
.ValueGeneratedOnAdd(); | |||||
b.Property<DateTime>("Added"); | |||||
b.Property<string>("Content"); | |||||
b.Property<DateTime?>("ExpiresAt"); | |||||
b.Property<string>("Group"); | |||||
b.Property<string>("KeyName"); | |||||
b.Property<int>("Retries"); | |||||
b.Property<string>("StatusName") | |||||
.IsRequired() | |||||
.HasMaxLength(50); | |||||
b.HasKey("Id"); | |||||
b.HasIndex("StatusName"); | |||||
b.ToTable("CapReceivedMessages"); | |||||
}); | |||||
modelBuilder.Entity("DotNetCore.CAP.Models.CapSentMessage", b => | |||||
{ | |||||
b.Property<string>("Id") | |||||
.ValueGeneratedOnAdd(); | |||||
b.Property<DateTime>("Added"); | |||||
b.Property<string>("Content"); | |||||
b.Property<DateTime?>("ExpiresAt"); | |||||
b.Property<string>("KeyName"); | |||||
b.Property<int>("Retries"); | |||||
b.Property<string>("StatusName") | |||||
.IsRequired() | |||||
.HasMaxLength(50); | |||||
b.HasKey("Id"); | |||||
b.HasIndex("StatusName"); | |||||
b.ToTable("CapSentMessages"); | |||||
}); | |||||
} | |||||
} | |||||
} |
@@ -9,29 +9,29 @@ using Microsoft.EntityFrameworkCore.Storage; | |||||
namespace DotNetCore.CAP.EntityFrameworkCore | namespace DotNetCore.CAP.EntityFrameworkCore | ||||
{ | { | ||||
public class EFFetchedMessage : IFetchedMessage | |||||
public class SqlServerFetchedMessage : IFetchedMessage | |||||
{ | { | ||||
private readonly IDbConnection _connection; | private readonly IDbConnection _connection; | ||||
private readonly IDbContextTransaction _transaction; | |||||
private readonly IDbTransaction _transaction; | |||||
private readonly Timer _timer; | private readonly Timer _timer; | ||||
private static readonly TimeSpan KeepAliveInterval = TimeSpan.FromMinutes(1); | private static readonly TimeSpan KeepAliveInterval = TimeSpan.FromMinutes(1); | ||||
private readonly object _lockObject = new object(); | private readonly object _lockObject = new object(); | ||||
public EFFetchedMessage(string messageId, | |||||
public SqlServerFetchedMessage(int messageId, | |||||
MessageType type, | MessageType type, | ||||
IDbConnection connection, | IDbConnection connection, | ||||
IDbContextTransaction transaction) | |||||
IDbTransaction transaction) | |||||
{ | { | ||||
MessageId = messageId; | MessageId = messageId; | ||||
Type = type; | |||||
MessageType = type; | |||||
_connection = connection; | _connection = connection; | ||||
_transaction = transaction; | _transaction = transaction; | ||||
_timer = new Timer(ExecuteKeepAliveQuery, null, KeepAliveInterval, KeepAliveInterval); | _timer = new Timer(ExecuteKeepAliveQuery, null, KeepAliveInterval, KeepAliveInterval); | ||||
} | } | ||||
public string MessageId { get; } | |||||
public int MessageId { get; } | |||||
public MessageType Type { get; } | |||||
public MessageType MessageType { get; } | |||||
public void RemoveFromQueue() | public void RemoveFromQueue() | ||||
{ | { | ||||
@@ -65,7 +65,7 @@ namespace DotNetCore.CAP.EntityFrameworkCore | |||||
{ | { | ||||
try | try | ||||
{ | { | ||||
_connection?.Execute("SELECT 1", _transaction.GetDbTransaction()); | |||||
_connection?.Execute("SELECT 1", _transaction); | |||||
} | } | ||||
catch | catch | ||||
{ | { |
@@ -0,0 +1,103 @@ | |||||
using System; | |||||
using System.Data.SqlClient; | |||||
using System.Threading; | |||||
using System.Threading.Tasks; | |||||
using Dapper; | |||||
using Microsoft.EntityFrameworkCore; | |||||
using Microsoft.Extensions.DependencyInjection; | |||||
using Microsoft.Extensions.Logging; | |||||
namespace DotNetCore.CAP.EntityFrameworkCore | |||||
{ | |||||
public class SqlServerStorage : IStorage | |||||
{ | |||||
private IServiceProvider _provider; | |||||
private ILogger _logger; | |||||
public SqlServerStorage( | |||||
IServiceProvider provider, | |||||
ILogger<SqlServerStorage> logger) | |||||
{ | |||||
_provider = provider; | |||||
_logger = logger; | |||||
} | |||||
public async Task InitializeAsync(CancellationToken cancellationToken) | |||||
{ | |||||
using (var scope = _provider.CreateScope()) | |||||
{ | |||||
if (cancellationToken.IsCancellationRequested) return; | |||||
var provider = scope.ServiceProvider; | |||||
var options = provider.GetRequiredService<SqlServerOptions>(); | |||||
var sql = CreateDbTablesScript(options.Schema); | |||||
using (var connection = new SqlConnection(options.ConnectionString)) | |||||
{ | |||||
await connection.ExecuteAsync(sql); | |||||
} | |||||
_logger.LogDebug("Ensuring all create database tables script are applied."); | |||||
} | |||||
} | |||||
protected virtual string CreateDbTablesScript(string schema) | |||||
{ | |||||
var batchSQL = | |||||
$@" | |||||
IF NOT EXISTS (SELECT * FROM sys.schemas WHERE name = '{schema}') | |||||
BEGIN | |||||
EXEC('CREATE SCHEMA {schema}') | |||||
END | |||||
GO | |||||
IF OBJECT_ID(N'[{schema}].[Queue]',N'U') IS NULL | |||||
BEGIN | |||||
CREATE TABLE [{schema}].[Queue]( | |||||
[MessageId] [int] NOT NULL, | |||||
[MessageType] [tinyint] NOT NULL | |||||
) ON [PRIMARY] | |||||
END | |||||
GO | |||||
IF OBJECT_ID(N'[{schema}].[Received]',N'U') IS NULL | |||||
BEGIN | |||||
CREATE TABLE [{schema}].[Received]( | |||||
[Id] [int] IDENTITY(1,1) NOT NULL, | |||||
[Name] [nvarchar](200) NOT NULL, | |||||
[Group] [nvarchar](200) NULL, | |||||
[Content] [nvarchar](max) NULL, | |||||
[Retries] [int] NOT NULL, | |||||
[Added] [datetime2](7) NOT NULL, | |||||
[ExpiresAt] [datetime2](7) NULL, | |||||
[StatusName] [nvarchar](50) NOT NULL, | |||||
CONSTRAINT [PK_{schema}.Received] PRIMARY KEY CLUSTERED | |||||
( | |||||
[Id] ASC | |||||
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] | |||||
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] | |||||
END | |||||
GO | |||||
IF OBJECT_ID(N'[{schema}].[Published]',N'U') IS NULL | |||||
BEGIN | |||||
CREATE TABLE [{schema}].[Published]( | |||||
[Id] [int] IDENTITY(1,1) NOT NULL, | |||||
[Name] [nvarchar](200) NOT NULL, | |||||
[Content] [nvarchar](max) NULL, | |||||
[Retries] [int] NOT NULL, | |||||
[Added] [datetime2](7) NOT NULL, | |||||
[ExpiresAt] [datetime2](7) NULL, | |||||
[StatusName] [nvarchar](50) NOT NULL, | |||||
CONSTRAINT [PK_{schema}.Published] PRIMARY KEY CLUSTERED | |||||
( | |||||
[Id] ASC | |||||
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] | |||||
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] | |||||
END | |||||
GO"; | |||||
return batchSQL; | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,122 @@ | |||||
using System; | |||||
using System.Data; | |||||
using System.Data.SqlClient; | |||||
using System.Threading.Tasks; | |||||
using Dapper; | |||||
using DotNetCore.CAP.Infrastructure; | |||||
using DotNetCore.CAP.Models; | |||||
using Microsoft.EntityFrameworkCore; | |||||
using Microsoft.Extensions.Options; | |||||
namespace DotNetCore.CAP.EntityFrameworkCore | |||||
{ | |||||
public class SqlServerStorageConnection : IStorageConnection | |||||
{ | |||||
private readonly SqlServerOptions _options; | |||||
public SqlServerStorageConnection(IOptions<SqlServerOptions> options) | |||||
{ | |||||
_options = options.Value; | |||||
} | |||||
public SqlServerOptions Options => _options; | |||||
public IStorageTransaction CreateTransaction() | |||||
{ | |||||
return new SqlServerStorageTransaction(this); | |||||
} | |||||
public Task<CapPublishedMessage> GetPublishedMessageAsync(int id) | |||||
{ | |||||
var sql = $@"SELECT * FROM [{_options.Schema}].[Published] WITH (readpast) WHERE Id={id}"; | |||||
using (var connection = new SqlConnection(_options.ConnectionString)) | |||||
{ | |||||
return connection.QueryFirstOrDefaultAsync<CapPublishedMessage>(sql); | |||||
} | |||||
} | |||||
public Task<IFetchedMessage> 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<CapPublishedMessage> GetNextPublishedMessageToBeEnqueuedAsync() | |||||
{ | |||||
var sql = $"SELECT TOP (1) * FROM [{_options.Schema}].[Published] WITH (readpast) WHERE StatusName = '{StatusName.Scheduled}'"; | |||||
using (var connection = new SqlConnection(_options.ConnectionString)) | |||||
{ | |||||
return await connection.QueryFirstOrDefaultAsync<CapPublishedMessage>(sql); | |||||
} | |||||
} | |||||
// CapReceviedMessage | |||||
public 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);"; | |||||
using (var connection = new SqlConnection(_options.ConnectionString)) | |||||
{ | |||||
return connection.ExecuteAsync(sql, message); | |||||
} | |||||
} | |||||
public Task<CapReceivedMessage> GetReceivedMessageAsync(int id) | |||||
{ | |||||
var sql = $@"SELECT * FROM [{_options.Schema}].[Received] WITH (readpast) WHERE Id={id}"; | |||||
using (var connection = new SqlConnection(_options.ConnectionString)) | |||||
{ | |||||
return connection.QueryFirstOrDefaultAsync<CapReceivedMessage>(sql); | |||||
} | |||||
} | |||||
public async Task<CapReceivedMessage> GetNextReceviedMessageToBeEnqueuedAsync() | |||||
{ | |||||
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<CapReceivedMessage>(sql); | |||||
} | |||||
} | |||||
public void Dispose() | |||||
{ | |||||
} | |||||
private async Task<IFetchedMessage> FetchNextMessageCoreAsync(string sql, object args = null) | |||||
{ | |||||
FetchedMessage fetched = null; | |||||
using (var connection = new SqlConnection(_options.ConnectionString)) | |||||
{ | |||||
using (var transaction = connection.BeginTransaction(IsolationLevel.ReadCommitted)) | |||||
{ | |||||
try | |||||
{ | |||||
fetched = await connection.QueryFirstOrDefaultAsync<FetchedMessage>(sql, args, transaction); | |||||
if (fetched == null) | |||||
return null; | |||||
return new SqlServerFetchedMessage(fetched.MessageId, fetched.MessageType, connection, transaction); | |||||
} | |||||
catch (Exception) | |||||
{ | |||||
transaction.Rollback(); | |||||
return null; | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,73 @@ | |||||
using System; | |||||
using System.Data; | |||||
using System.Data.SqlClient; | |||||
using System.Threading.Tasks; | |||||
using Dapper; | |||||
using DotNetCore.CAP.Models; | |||||
namespace DotNetCore.CAP.EntityFrameworkCore | |||||
{ | |||||
public class SqlServerStorageTransaction : IStorageTransaction, IDisposable | |||||
{ | |||||
private readonly SqlServerStorageConnection _connection; | |||||
private readonly SqlServerOptions _options; | |||||
private readonly string _schema; | |||||
private IDbTransaction _dbTransaction; | |||||
private IDbConnection _dbConnection; | |||||
public SqlServerStorageTransaction(SqlServerStorageConnection connection) | |||||
{ | |||||
_connection = connection; | |||||
_options = _connection.Options; | |||||
_schema = _options.Schema; | |||||
_dbConnection = new SqlConnection(_options.ConnectionString); | |||||
_dbTransaction = _dbConnection.BeginTransaction(IsolationLevel.ReadCommitted); | |||||
} | |||||
public void UpdateMessage(CapPublishedMessage message) | |||||
{ | |||||
if (message == null) throw new ArgumentNullException(nameof(message)); | |||||
var sql = $"UPDATE [{_schema}].[Published] SET [ExpiresAt] = @ExpiresAt,[StatusName]=@StatusName WHERE Id=@Id;"; | |||||
_dbConnection.Execute(sql, message); | |||||
} | |||||
public void UpdateMessage(CapReceivedMessage message) | |||||
{ | |||||
if (message == null) throw new ArgumentNullException(nameof(message)); | |||||
var sql = $"UPDATE [{_schema}].[Received] SET [ExpiresAt] = @ExpiresAt,[StatusName]=@StatusName WHERE Id=@Id;"; | |||||
_dbConnection.Execute(sql, message); | |||||
} | |||||
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 }); | |||||
} | |||||
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 }); | |||||
} | |||||
public Task CommitAsync() | |||||
{ | |||||
_dbTransaction.Commit(); | |||||
return Task.CompletedTask; | |||||
} | |||||
public void Dispose() | |||||
{ | |||||
_dbTransaction.Dispose(); | |||||
_dbConnection.Dispose(); | |||||
} | |||||
} | |||||
} |
@@ -16,9 +16,9 @@ namespace DotNetCore.CAP | |||||
/// otherwise you need to use overloaded method with IDbConnection and IDbTransaction. | /// otherwise you need to use overloaded method with IDbConnection and IDbTransaction. | ||||
/// </para> | /// </para> | ||||
/// </summary> | /// </summary> | ||||
/// <param name="topic">the topic name or exchange router key.</param> | |||||
/// <param name="name">the topic name or exchange router key.</param> | |||||
/// <param name="content">message body content.</param> | /// <param name="content">message body content.</param> | ||||
Task PublishAsync(string topic, string content); | |||||
Task PublishAsync(string name, string content); | |||||
/// <summary> | /// <summary> | ||||
/// Publis a object message to specified topic. | /// Publis a object message to specified topic. | ||||
@@ -28,25 +28,25 @@ namespace DotNetCore.CAP | |||||
/// </para> | /// </para> | ||||
/// </summary> | /// </summary> | ||||
/// <typeparam name="T">The type of conetent object.</typeparam> | /// <typeparam name="T">The type of conetent object.</typeparam> | ||||
/// <param name="topic">the topic name or exchange router key.</param> | |||||
/// <param name="name">the topic name or exchange router key.</param> | |||||
/// <param name="contentObj">object instance that will be serialized of json.</param> | /// <param name="contentObj">object instance that will be serialized of json.</param> | ||||
Task PublishAsync<T>(string topic, T contentObj); | |||||
Task PublishAsync<T>(string name, T contentObj); | |||||
/// <summary> | /// <summary> | ||||
/// Publish a string message to specified topic with transacton. | /// Publish a string message to specified topic with transacton. | ||||
/// </summary> | /// </summary> | ||||
/// <param name="topic">the topic name or exchange router key.</param> | |||||
/// <param name="name">the topic name or exchange router key.</param> | |||||
/// <param name="content">message body content.</param> | /// <param name="content">message body content.</param> | ||||
/// <param name="dbConnection">the dbConnection of <see cref="IDbConnection"/></param> | /// <param name="dbConnection">the dbConnection of <see cref="IDbConnection"/></param> | ||||
Task PublishAsync(string topic, string content, IDbConnection dbConnection); | |||||
Task PublishAsync(string name, string content, IDbConnection dbConnection); | |||||
/// <summary> | /// <summary> | ||||
/// Publish a string message to specified topic with transacton. | /// Publish a string message to specified topic with transacton. | ||||
/// </summary> | /// </summary> | ||||
/// <param name="topic">the topic name or exchange router key.</param> | |||||
/// <param name="name">the topic name or exchange router key.</param> | |||||
/// <param name="content">message body content.</param> | /// <param name="content">message body content.</param> | ||||
/// <param name="dbConnection">the connection of <see cref="IDbConnection"/></param> | /// <param name="dbConnection">the connection of <see cref="IDbConnection"/></param> | ||||
/// <param name="dbTransaction">the transaction of <see cref="IDbTransaction"/></param> | /// <param name="dbTransaction">the transaction of <see cref="IDbTransaction"/></param> | ||||
Task PublishAsync(string topic, string content, IDbConnection dbConnection, IDbTransaction dbTransaction); | |||||
Task PublishAsync(string name, string content, IDbConnection dbConnection, IDbTransaction dbTransaction); | |||||
} | } | ||||
} | } |
@@ -101,7 +101,7 @@ namespace DotNetCore.CAP | |||||
{ | { | ||||
client.MessageReceieved += (sender, message) => | client.MessageReceieved += (sender, message) => | ||||
{ | { | ||||
_logger.EnqueuingReceivedMessage(message.KeyName, message.Content); | |||||
_logger.EnqueuingReceivedMessage(message.Name, message.Content); | |||||
using (var scope = _serviceProvider.CreateScope()) | using (var scope = _serviceProvider.CreateScope()) | ||||
{ | { | ||||
@@ -3,14 +3,14 @@ using DotNetCore.CAP.Models; | |||||
namespace DotNetCore.CAP | namespace DotNetCore.CAP | ||||
{ | { | ||||
public interface IFetchedMessage : IDisposable | |||||
{ | |||||
string MessageId { get; } | |||||
public interface IFetchedMessage : IDisposable | |||||
{ | |||||
int MessageId { get; } | |||||
MessageType Type { get; } | |||||
MessageType MessageType { get; } | |||||
void RemoveFromQueue(); | |||||
void RemoveFromQueue(); | |||||
void Requeue(); | |||||
} | |||||
} | |||||
void Requeue(); | |||||
} | |||||
} |
@@ -1,15 +1,12 @@ | |||||
using System; | using System; | ||||
using System.Collections.Generic; | |||||
using System.Diagnostics; | using System.Diagnostics; | ||||
using System.Text; | |||||
using System.Threading; | |||||
using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
using DotNetCore.CAP.Abstractions; | using DotNetCore.CAP.Abstractions; | ||||
using DotNetCore.CAP.Infrastructure; | using DotNetCore.CAP.Infrastructure; | ||||
using DotNetCore.CAP.Internal; | using DotNetCore.CAP.Internal; | ||||
using DotNetCore.CAP.Models; | |||||
using DotNetCore.CAP.Processor; | using DotNetCore.CAP.Processor; | ||||
using DotNetCore.CAP.Processor.States; | using DotNetCore.CAP.Processor.States; | ||||
using DotNetCore.CAP.Models; | |||||
using Microsoft.Extensions.Logging; | using Microsoft.Extensions.Logging; | ||||
namespace DotNetCore.CAP | namespace DotNetCore.CAP | ||||
@@ -92,7 +89,7 @@ namespace DotNetCore.CAP | |||||
} | } | ||||
catch (Exception ex) | catch (Exception ex) | ||||
{ | { | ||||
_logger.ExceptionOccuredWhileExecutingJob(message?.KeyName, ex); | |||||
_logger.ExceptionOccuredWhileExecutingJob(message?.Name, ex); | |||||
return OperateResult.Failed(ex); | return OperateResult.Failed(ex); | ||||
} | } | ||||
} | } | ||||
@@ -102,11 +99,11 @@ namespace DotNetCore.CAP | |||||
{ | { | ||||
try | try | ||||
{ | { | ||||
var executeDescriptorGroup = _selector.GetTopicExector(receivedMessage.KeyName); | |||||
var executeDescriptorGroup = _selector.GetTopicExector(receivedMessage.Name); | |||||
if (!executeDescriptorGroup.ContainsKey(receivedMessage.Group)) | if (!executeDescriptorGroup.ContainsKey(receivedMessage.Group)) | ||||
{ | { | ||||
throw new SubscriberNotFoundException(receivedMessage.KeyName + " has not been found."); | |||||
throw new SubscriberNotFoundException(receivedMessage.Name + " has not been found."); | |||||
} | } | ||||
// If there are multiple consumers in the same group, we will take the first | // If there are multiple consumers in the same group, we will take the first | ||||
@@ -123,7 +120,7 @@ namespace DotNetCore.CAP | |||||
} | } | ||||
catch (Exception ex) | catch (Exception ex) | ||||
{ | { | ||||
_logger.ConsumerMethodExecutingFailed($"Group:{receivedMessage.Group}, Topic:{receivedMessage.KeyName}", ex); | |||||
_logger.ConsumerMethodExecutingFailed($"Group:{receivedMessage.Group}, Topic:{receivedMessage.Name}", ex); | |||||
return OperateResult.Failed(ex); | return OperateResult.Failed(ex); | ||||
} | } | ||||
} | } | ||||
@@ -148,6 +145,5 @@ namespace DotNetCore.CAP | |||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
} | } | ||||
} | |||||
} |
@@ -4,7 +4,7 @@ | |||||
{ | { | ||||
public string Group { get; set; } | public string Group { get; set; } | ||||
public string KeyName { get; set; } | |||||
public string Name { get; set; } | |||||
public string Content { get; set; } | public string Content { get; set; } | ||||
} | } |
@@ -2,13 +2,11 @@ | |||||
{ | { | ||||
public class CapQueue | public class CapQueue | ||||
{ | { | ||||
public int Id { get; set; } | |||||
public string MessageId { get; set; } | |||||
public int MessageId { get; set; } | |||||
/// <summary> | /// <summary> | ||||
/// 0 is CapSentMessage, 1 is CapReceviedMessage | /// 0 is CapSentMessage, 1 is CapReceviedMessage | ||||
/// </summary> | /// </summary> | ||||
public MessageType Type { get; set; } | |||||
public MessageType MessageType { get; set; } | |||||
} | } | ||||
} | } |
@@ -13,22 +13,21 @@ namespace DotNetCore.CAP.Models | |||||
/// </remarks> | /// </remarks> | ||||
public CapReceivedMessage() | public CapReceivedMessage() | ||||
{ | { | ||||
Id = Guid.NewGuid().ToString(); | |||||
Added = DateTime.Now; | Added = DateTime.Now; | ||||
} | } | ||||
public CapReceivedMessage(MessageContext message) : this() | public CapReceivedMessage(MessageContext message) : this() | ||||
{ | { | ||||
Group = message.Group; | Group = message.Group; | ||||
KeyName = message.KeyName; | |||||
Name = message.Name; | |||||
Content = message.Content; | Content = message.Content; | ||||
} | } | ||||
public string Id { get; set; } | |||||
public int Id { get; set; } | |||||
public string Group { get; set; } | public string Group { get; set; } | ||||
public string KeyName { get; set; } | |||||
public string Name { get; set; } | |||||
public string Content { get; set; } | public string Content { get; set; } | ||||
@@ -45,7 +44,7 @@ namespace DotNetCore.CAP.Models | |||||
return new MessageContext | return new MessageContext | ||||
{ | { | ||||
Group = Group, | Group = Group, | ||||
KeyName = KeyName, | |||||
Name = Name, | |||||
Content = Content | Content = Content | ||||
}; | }; | ||||
} | } | ||||
@@ -85,7 +85,7 @@ namespace DotNetCore.CAP.Processor | |||||
{ | { | ||||
using (fetched) | using (fetched) | ||||
{ | { | ||||
var queueExecutor = _queueExecutorFactory.GetInstance(fetched.Type); | |||||
var queueExecutor = _queueExecutorFactory.GetInstance(fetched.MessageType); | |||||
await queueExecutor.ExecuteAsync(connection, fetched); | await queueExecutor.ExecuteAsync(connection, fetched); | ||||
} | } | ||||
} | } | ||||
@@ -38,13 +38,13 @@ namespace DotNetCore.CAP.Processor | |||||
{ | { | ||||
using (var scope = _provider.CreateScope()) | using (var scope = _provider.CreateScope()) | ||||
{ | { | ||||
CapSentMessage sentMessage; | |||||
CapPublishedMessage sentMessage; | |||||
var provider = scope.ServiceProvider; | var provider = scope.ServiceProvider; | ||||
var connection = provider.GetRequiredService<IStorageConnection>(); | var connection = provider.GetRequiredService<IStorageConnection>(); | ||||
while ( | while ( | ||||
!context.IsStopping && | !context.IsStopping && | ||||
(sentMessage = await connection.GetNextSentMessageToBeEnqueuedAsync()) != null) | |||||
(sentMessage = await connection.GetNextPublishedMessageToBeEnqueuedAsync()) != null) | |||||
{ | { | ||||
var state = new EnqueuedState(); | var state = new EnqueuedState(); | ||||