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