22 changed files with 438 additions and 46 deletions
@ -0,0 +1,13 @@ |
|||||
|
// Global using directives
|
||||
|
|
||||
|
global using System.Collections.Concurrent; |
||||
|
global using DotNetCore.CAP; |
||||
|
global using Microsoft.EntityFrameworkCore; |
||||
|
global using Microsoft.EntityFrameworkCore.Storage; |
||||
|
global using Microsoft.Extensions.DependencyInjection; |
||||
|
global using Microsoft.Extensions.Options; |
||||
|
global using Volo.Abp.DependencyInjection; |
||||
|
global using Volo.Abp.Modularity; |
||||
|
global using Volo.Abp.Threading; |
||||
|
global using Volo.Abp.Uow; |
||||
|
global using Volo.Abp.Uow.EntityFrameworkCore; |
||||
@ -0,0 +1,15 @@ |
|||||
|
<Project Sdk="Microsoft.NET.Sdk"> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<TargetFramework>net7.0</TargetFramework> |
||||
|
<ImplicitUsings>enable</ImplicitUsings> |
||||
|
<RootNamespace /> |
||||
|
<AssemblyName>Lion.AbpPro.CAP.EntityFrameworkCore</AssemblyName> |
||||
|
</PropertyGroup> |
||||
|
<ItemGroup> |
||||
|
<PackageReference Include="Volo.Abp.EntityFrameworkCore" /> |
||||
|
</ItemGroup> |
||||
|
<ItemGroup> |
||||
|
<ProjectReference Include="..\Lion.AbpPro.CAP\Lion.AbpPro.CAP.csproj" /> |
||||
|
</ItemGroup> |
||||
|
</Project> |
||||
@ -0,0 +1,64 @@ |
|||||
|
namespace Lion.AbpPro.CAP.EntityFrameworkCore; |
||||
|
|
||||
|
public class EfCoreLionAbpProCapTransactionApiFactory : ILionAbpProCapTransactionApiFactory, ITransientDependency |
||||
|
{ |
||||
|
public Type TransactionApiType { get; } = typeof(EfCoreTransactionApi); |
||||
|
|
||||
|
protected readonly ICapPublisher Publisher; |
||||
|
protected readonly LionAbpProEfCoreDbContextCapOptions Options; |
||||
|
protected readonly ILionAbpProCapDbProviderInfoProvider LionAbpProCapDbProviderInfoProvider; |
||||
|
protected readonly ICancellationTokenProvider CancellationTokenProvider; |
||||
|
|
||||
|
public EfCoreLionAbpProCapTransactionApiFactory( |
||||
|
ICapPublisher publisher, |
||||
|
IOptions<LionAbpProEfCoreDbContextCapOptions> options, |
||||
|
ILionAbpProCapDbProviderInfoProvider lionAbpProCapDbProviderInfoProvider, |
||||
|
ICancellationTokenProvider cancellationTokenProvider) |
||||
|
{ |
||||
|
Publisher = publisher; |
||||
|
Options = options.Value; |
||||
|
LionAbpProCapDbProviderInfoProvider = lionAbpProCapDbProviderInfoProvider; |
||||
|
CancellationTokenProvider = cancellationTokenProvider; |
||||
|
} |
||||
|
|
||||
|
public virtual ITransactionApi Create(ITransactionApi originalApi) |
||||
|
{ |
||||
|
var efApi = (EfCoreTransactionApi)originalApi; |
||||
|
|
||||
|
var capTrans = CreateCapTransactionOrNull(efApi); |
||||
|
|
||||
|
return capTrans is null |
||||
|
? originalApi |
||||
|
: new EfCoreTransactionApi(capTrans, efApi.StarterDbContext, CancellationTokenProvider); |
||||
|
} |
||||
|
|
||||
|
protected virtual IDbContextTransaction CreateCapTransactionOrNull(EfCoreTransactionApi originalApi) |
||||
|
{ |
||||
|
// TODO 通过数据库连接字符串判断数据库类型
|
||||
|
// if (Options.CapUsingDbConnectionString != originalApi.StarterDbContext.Database.GetConnectionString())
|
||||
|
// {
|
||||
|
// return null;
|
||||
|
// }
|
||||
|
|
||||
|
var dbProviderInfo = LionAbpProCapDbProviderInfoProvider.GetOrNull(originalApi.StarterDbContext.Database.ProviderName); |
||||
|
|
||||
|
if (dbProviderInfo?.CapTransactionType is null || dbProviderInfo.CapEfDbTransactionType is null) |
||||
|
{ |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
var capTransactionType = dbProviderInfo.CapTransactionType; |
||||
|
|
||||
|
if (ActivatorUtilities.CreateInstance(Publisher.ServiceProvider, capTransactionType) is not CapTransactionBase capTransaction) |
||||
|
{ |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
capTransaction.DbTransaction = originalApi.DbContextTransaction; |
||||
|
capTransaction.AutoCommit = false; |
||||
|
|
||||
|
Publisher.Transaction.Value = capTransaction; |
||||
|
|
||||
|
return (IDbContextTransaction)Activator.CreateInstance(dbProviderInfo.CapEfDbTransactionType, capTransaction); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,6 @@ |
|||||
|
namespace Lion.AbpPro.CAP.EntityFrameworkCore; |
||||
|
|
||||
|
public interface ILionAbpProCapDbProviderInfoProvider |
||||
|
{ |
||||
|
LionAbpProCapDbProviderInfo GetOrNull(string dbProviderName); |
||||
|
} |
||||
@ -0,0 +1,14 @@ |
|||||
|
namespace Lion.AbpPro.CAP.EntityFrameworkCore; |
||||
|
|
||||
|
public class LionAbpProCapDbProviderInfo |
||||
|
{ |
||||
|
public Type CapTransactionType { get; } |
||||
|
|
||||
|
public Type CapEfDbTransactionType { get; } |
||||
|
|
||||
|
public LionAbpProCapDbProviderInfo(string capTransactionTypeName, string capEfDbTransactionTypeName) |
||||
|
{ |
||||
|
CapTransactionType = Type.GetType(capTransactionTypeName, false); |
||||
|
CapEfDbTransactionType = Type.GetType(capEfDbTransactionTypeName, false); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,7 @@ |
|||||
|
namespace Lion.AbpPro.CAP.EntityFrameworkCore |
||||
|
{ |
||||
|
[DependsOn(typeof(LionAbpProCapModule))] |
||||
|
public class LionAbpProCapEntityFrameworkCoreModule : AbpModule |
||||
|
{ |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,18 @@ |
|||||
|
using Lion.AbpPro.CAP.EntityFrameworkCore; |
||||
|
|
||||
|
// ReSharper disable once CheckNamespace
|
||||
|
namespace Microsoft.Extensions.DependencyInjection |
||||
|
{ |
||||
|
public static class LionAbpProCapOptionsExtensions |
||||
|
{ |
||||
|
public static CapOptions SetCapDbConnectionString(this CapOptions options, string dbConnectionString) |
||||
|
{ |
||||
|
options.RegisterExtension(new LionAbpProEfCoreDbContextCapOptionsExtension |
||||
|
{ |
||||
|
CapUsingDbConnectionString = dbConnectionString |
||||
|
}); |
||||
|
|
||||
|
return options; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,6 @@ |
|||||
|
namespace Lion.AbpPro.CAP.EntityFrameworkCore; |
||||
|
|
||||
|
public class LionAbpProEfCoreDbContextCapOptions |
||||
|
{ |
||||
|
public string CapUsingDbConnectionString { get; set; } |
||||
|
} |
||||
@ -0,0 +1,14 @@ |
|||||
|
namespace Lion.AbpPro.CAP.EntityFrameworkCore; |
||||
|
|
||||
|
public class LionAbpProEfCoreDbContextCapOptionsExtension : ICapOptionsExtension |
||||
|
{ |
||||
|
public string CapUsingDbConnectionString { get; init; } |
||||
|
|
||||
|
public void AddServices(IServiceCollection services) |
||||
|
{ |
||||
|
services.Configure<LionAbpProEfCoreDbContextCapOptions>(options => |
||||
|
{ |
||||
|
options.CapUsingDbConnectionString = CapUsingDbConnectionString; |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,45 @@ |
|||||
|
namespace Lion.AbpPro.CAP.EntityFrameworkCore; |
||||
|
|
||||
|
public class LionAbpProLionAbpProCapDbProviderInfoProvider : ILionAbpProCapDbProviderInfoProvider, ITransientDependency |
||||
|
{ |
||||
|
protected ConcurrentDictionary<string, LionAbpProCapDbProviderInfo> CapDbProviderInfos { get; set; } = new(); |
||||
|
|
||||
|
public virtual LionAbpProCapDbProviderInfo GetOrNull(string dbProviderName) |
||||
|
{ |
||||
|
return CapDbProviderInfos.GetOrAdd(dbProviderName, InternalGetOrNull); |
||||
|
} |
||||
|
|
||||
|
protected virtual LionAbpProCapDbProviderInfo InternalGetOrNull(string databaseProviderName) |
||||
|
{ |
||||
|
switch (databaseProviderName) |
||||
|
{ |
||||
|
case "Microsoft.EntityFrameworkCore.SqlServer": |
||||
|
return new LionAbpProCapDbProviderInfo( |
||||
|
"DotNetCore.CAP.SqlServerCapTransaction, DotNetCore.CAP.SqlServer", |
||||
|
"Microsoft.EntityFrameworkCore.Storage.CapEFDbTransaction, DotNetCore.CAP.SqlServer"); |
||||
|
case "Npgsql.EntityFrameworkCore.PostgreSQL": |
||||
|
return new LionAbpProCapDbProviderInfo( |
||||
|
"DotNetCore.CAP.PostgreSqlCapTransaction, DotNetCore.CAP.PostgreSql", |
||||
|
"Microsoft.EntityFrameworkCore.Storage.CapEFDbTransaction, DotNetCore.CAP.PostgreSQL"); |
||||
|
case "Pomelo.EntityFrameworkCore.MySql": |
||||
|
return new LionAbpProCapDbProviderInfo( |
||||
|
"DotNetCore.CAP.MySqlCapTransaction, DotNetCore.CAP.MySql", |
||||
|
"Microsoft.EntityFrameworkCore.Storage.CapEFDbTransaction, DotNetCore.CAP.MySql"); |
||||
|
case "Oracle.EntityFrameworkCore": |
||||
|
case "Devart.Data.Oracle.Entity.EFCore": |
||||
|
return new LionAbpProCapDbProviderInfo( |
||||
|
"DotNetCore.CAP.OracleCapTransaction, DotNetCore.CAP.Oracle", |
||||
|
"Microsoft.EntityFrameworkCore.Storage.CapEFDbTransaction, DotNetCore.CAP.Oracle"); |
||||
|
case "Microsoft.EntityFrameworkCore.Sqlite": |
||||
|
return new LionAbpProCapDbProviderInfo( |
||||
|
"DotNetCore.CAP.SqliteCapTransaction, DotNetCore.CAP.Sqlite", |
||||
|
"Microsoft.EntityFrameworkCore.Storage.CapEFDbTransaction, DotNetCore.CAP.Sqlite"); |
||||
|
case "Microsoft.EntityFrameworkCore.InMemory": |
||||
|
return new LionAbpProCapDbProviderInfo( |
||||
|
"DotNetCore.CAP.InMemoryCapTransaction, DotNetCore.CAP.InMemoryStorage", |
||||
|
"Microsoft.EntityFrameworkCore.Storage.CapEFDbTransaction, DotNetCore.CAP.InMemoryStorage"); |
||||
|
default: |
||||
|
return null; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,8 @@ |
|||||
|
namespace Lion.AbpPro.CAP; |
||||
|
|
||||
|
public interface ILionAbpProCapTransactionApiFactory |
||||
|
{ |
||||
|
Type TransactionApiType { get; } |
||||
|
|
||||
|
ITransactionApi Create(ITransactionApi originalApi); |
||||
|
} |
||||
@ -0,0 +1,11 @@ |
|||||
|
namespace Lion.AbpPro.CAP; |
||||
|
|
||||
|
public static class LionAbpProCapPublisherExtension |
||||
|
{ |
||||
|
public static IDisposable UseTransaction(this ICapPublisher capPublisher, ICapTransaction capTransaction) |
||||
|
{ |
||||
|
var previousValue = capPublisher.Transaction.Value; |
||||
|
capPublisher.Transaction.Value = capTransaction; |
||||
|
return new DisposeAction(() => capPublisher.Transaction.Value = previousValue); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,51 @@ |
|||||
|
namespace Lion.AbpPro.CAP; |
||||
|
|
||||
|
[Dependency(TryRegister = true)] |
||||
|
public class LionAbpProCapUnitOfWork : UnitOfWork |
||||
|
{ |
||||
|
public ICapTransaction CapTransaction { get; protected set; } |
||||
|
|
||||
|
protected ICapPublisher CapPublisher { get; } |
||||
|
|
||||
|
public LionAbpProCapUnitOfWork( |
||||
|
IServiceProvider serviceProvider, |
||||
|
IUnitOfWorkEventPublisher unitOfWorkEventPublisher, |
||||
|
IOptions<AbpUnitOfWorkDefaultOptions> options, |
||||
|
ICapPublisher capPublisher) |
||||
|
: base(serviceProvider, unitOfWorkEventPublisher, options) |
||||
|
{ |
||||
|
CapPublisher = capPublisher; |
||||
|
} |
||||
|
|
||||
|
public override void AddTransactionApi(string key, ITransactionApi api) |
||||
|
{ |
||||
|
var factories = ServiceProvider.GetServices<ILionAbpProCapTransactionApiFactory>(); |
||||
|
|
||||
|
var factory = factories.FirstOrDefault(x => x.TransactionApiType == api.GetType()); |
||||
|
|
||||
|
if (factory is not null) |
||||
|
{ |
||||
|
api = factory.Create(api); |
||||
|
CapTransaction = CapPublisher.Transaction.Value; |
||||
|
} |
||||
|
|
||||
|
base.AddTransactionApi(key, api); |
||||
|
} |
||||
|
|
||||
|
public override ITransactionApi GetOrAddTransactionApi(string key, Func<ITransactionApi> factory) |
||||
|
{ |
||||
|
Check.NotNull(key, nameof(key)); |
||||
|
Check.NotNull(factory, nameof(factory)); |
||||
|
|
||||
|
var transactionApi = FindTransactionApi(key); |
||||
|
|
||||
|
if (transactionApi is not null) |
||||
|
{ |
||||
|
return transactionApi; |
||||
|
} |
||||
|
|
||||
|
AddTransactionApi(key, factory()); |
||||
|
|
||||
|
return FindTransactionApi(key); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,67 @@ |
|||||
|
using System.Text; |
||||
|
using System.Text.Json; |
||||
|
using DotNetCore.CAP.Messages; |
||||
|
using DotNetCore.CAP.Serialization; |
||||
|
using Volo.Abp.Json; |
||||
|
|
||||
|
namespace Lion.AbpPro.CAP; |
||||
|
|
||||
|
public class LionAbpProJsonSerializer : ISerializer, ISingletonDependency |
||||
|
{ |
||||
|
private readonly IJsonSerializer _jsonSerializer; |
||||
|
|
||||
|
public LionAbpProJsonSerializer(IJsonSerializer jsonSerializer) |
||||
|
{ |
||||
|
_jsonSerializer = jsonSerializer; |
||||
|
} |
||||
|
|
||||
|
public virtual string Serialize(Message message) |
||||
|
{ |
||||
|
return _jsonSerializer.Serialize(message); |
||||
|
} |
||||
|
|
||||
|
public virtual ValueTask<TransportMessage> SerializeAsync(Message message) |
||||
|
{ |
||||
|
if (message == null) |
||||
|
{ |
||||
|
throw new ArgumentNullException(nameof(message)); |
||||
|
} |
||||
|
|
||||
|
if (message.Value == null) |
||||
|
{ |
||||
|
return new ValueTask<TransportMessage>(new TransportMessage(message.Headers, null)); |
||||
|
} |
||||
|
|
||||
|
var json = _jsonSerializer.Serialize(message.Value); |
||||
|
|
||||
|
return new ValueTask<TransportMessage>(new TransportMessage(message.Headers, Encoding.UTF8.GetBytes(json))); |
||||
|
} |
||||
|
|
||||
|
public virtual Message Deserialize(string json) |
||||
|
{ |
||||
|
return _jsonSerializer.Deserialize<Message>(json); |
||||
|
} |
||||
|
|
||||
|
public virtual ValueTask<Message> DeserializeAsync(TransportMessage transportMessage, Type valueType) |
||||
|
{ |
||||
|
if (valueType == null || transportMessage.Body.IsEmpty) |
||||
|
{ |
||||
|
return new ValueTask<Message>(new Message(transportMessage.Headers, null)); |
||||
|
} |
||||
|
|
||||
|
var json = Encoding.UTF8.GetString(transportMessage.Body.ToArray()); |
||||
|
|
||||
|
return new ValueTask<Message>(new Message(transportMessage.Headers, |
||||
|
_jsonSerializer.Deserialize(valueType, json))); |
||||
|
} |
||||
|
|
||||
|
public virtual object Deserialize(object value, Type valueType) |
||||
|
{ |
||||
|
return _jsonSerializer.Deserialize(valueType, value.ToString()); |
||||
|
} |
||||
|
|
||||
|
public virtual bool IsJsonType(object jsonObject) |
||||
|
{ |
||||
|
return jsonObject is JsonElement; |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue