27 changed files with 39 additions and 642 deletions
@ -0,0 +1,3 @@ |
|||||
|
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
||||
|
<ConfigureAwait ContinueOnCapturedContext="false" /> |
||||
|
</Weavers> |
||||
@ -0,0 +1,3 @@ |
|||||
|
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
||||
|
<ConfigureAwait ContinueOnCapturedContext="false" /> |
||||
|
</Weavers> |
||||
@ -0,0 +1,3 @@ |
|||||
|
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
||||
|
<ConfigureAwait ContinueOnCapturedContext="false" /> |
||||
|
</Weavers> |
||||
@ -1,18 +0,0 @@ |
|||||
<Project Sdk="Microsoft.NET.Sdk"> |
|
||||
|
|
||||
<Import Project="..\..\..\common.props" /> |
|
||||
|
|
||||
<PropertyGroup> |
|
||||
<TargetFramework>netstandard2.0</TargetFramework> |
|
||||
<RootNamespace /> |
|
||||
<Description>分布式锁Redis实现</Description> |
|
||||
</PropertyGroup> |
|
||||
|
|
||||
<ItemGroup> |
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="5.0.0" /> |
|
||||
<PackageReference Include="Microsoft.Extensions.Options" Version="5.0.0" /> |
|
||||
<PackageReference Include="Polly" Version="7.2.1" /> |
|
||||
<PackageReference Include="Volo.Abp.Core" Version="4.4.0" /> |
|
||||
</ItemGroup> |
|
||||
|
|
||||
</Project> |
|
||||
@ -1,14 +0,0 @@ |
|||||
using Microsoft.Extensions.DependencyInjection; |
|
||||
using Volo.Abp.Modularity; |
|
||||
|
|
||||
namespace LINGYUN.Abp.DistributedLock.Redis |
|
||||
{ |
|
||||
public class AbpRedisLockModule : AbpModule |
|
||||
{ |
|
||||
public override void ConfigureServices(ServiceConfigurationContext context) |
|
||||
{ |
|
||||
var configuration = context.Services.GetConfiguration(); |
|
||||
Configure<RedisLockOptions>(configuration.GetSection("DistributedLock:Redis")); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,316 +0,0 @@ |
|||||
using Microsoft.Extensions.DependencyInjection; |
|
||||
using Microsoft.Extensions.Logging; |
|
||||
using Microsoft.Extensions.Logging.Abstractions; |
|
||||
using Microsoft.Extensions.Options; |
|
||||
using Polly; |
|
||||
using StackExchange.Redis; |
|
||||
using System; |
|
||||
using System.Threading; |
|
||||
using System.Threading.Tasks; |
|
||||
using Volo.Abp; |
|
||||
using Volo.Abp.DependencyInjection; |
|
||||
|
|
||||
namespace LINGYUN.Abp.DistributedLock.Redis |
|
||||
{ |
|
||||
[ExposeServices(typeof(IDistributedLock))] |
|
||||
[Dependency(ServiceLifetime.Singleton, TryRegister = true)] |
|
||||
public class RedisDistributedLock : IDistributedLock |
|
||||
{ |
|
||||
public ILogger<RedisDistributedLock> Logger { protected get; set; } |
|
||||
|
|
||||
private volatile ConnectionMultiplexer _connection; |
|
||||
private IDatabase _redis; |
|
||||
|
|
||||
private readonly RedisLockOptions _options; |
|
||||
private readonly string _instance; |
|
||||
|
|
||||
private readonly SemaphoreSlim _connectionLock = new SemaphoreSlim(initialCount: 1, maxCount: 1); |
|
||||
|
|
||||
public RedisDistributedLock(IOptions<RedisLockOptions> optionsAccessor) |
|
||||
{ |
|
||||
if (optionsAccessor == null) |
|
||||
{ |
|
||||
throw new ArgumentNullException(nameof(optionsAccessor)); |
|
||||
} |
|
||||
|
|
||||
_options = optionsAccessor.Value; |
|
||||
|
|
||||
_instance = _options.InstanceName ?? string.Empty; |
|
||||
|
|
||||
Logger = NullLogger<RedisDistributedLock>.Instance; |
|
||||
} |
|
||||
|
|
||||
private void RegistenConnectionEvent(ConnectionMultiplexer connection) |
|
||||
{ |
|
||||
if (connection != null) |
|
||||
{ |
|
||||
connection.ConnectionFailed += OnConnectionFailed; |
|
||||
connection.ConnectionRestored += OnConnectionRestored; |
|
||||
connection.ErrorMessage += OnErrorMessage; |
|
||||
connection.ConfigurationChanged += OnConfigurationChanged; |
|
||||
connection.HashSlotMoved += OnHashSlotMoved; |
|
||||
connection.InternalError += OnInternalError; |
|
||||
connection.ConfigurationChangedBroadcast += OnConfigurationChangedBroadcast; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public bool Lock(string lockKey, string lockValue, int lockSecond = 30) |
|
||||
{ |
|
||||
Connect(); |
|
||||
return LockTakeSync(lockKey, lockValue, TimeSpan.FromSeconds(lockSecond)); |
|
||||
} |
|
||||
|
|
||||
public async Task<bool> LockAsync(string lockKey, string lockValue, int lockSecond = 30, CancellationToken token = default) |
|
||||
{ |
|
||||
await ConnectAsync(token); |
|
||||
return await LockTakeAsync(lockKey, lockValue, TimeSpan.FromSeconds(lockSecond)); |
|
||||
} |
|
||||
|
|
||||
public IDisposable Lock(string lockKey, int lockSecond = 30) |
|
||||
{ |
|
||||
Connect(); |
|
||||
var redisLockToken = Environment.MachineName; |
|
||||
var redisLockKey = _instance + lockKey; |
|
||||
var lockResult = LockTakeSync(redisLockKey, redisLockToken, TimeSpan.FromSeconds(lockSecond)); |
|
||||
if (lockResult) |
|
||||
{ |
|
||||
return new DisposeAction(() => |
|
||||
{ |
|
||||
LockReleaseSync(redisLockKey, redisLockToken); |
|
||||
}); |
|
||||
} |
|
||||
Logger.LogWarning("Redis lock failed of key: {0}", redisLockKey); |
|
||||
throw new DistributedLockException(redisLockKey); |
|
||||
} |
|
||||
|
|
||||
public async Task<IDisposable> LockAsync(string lockKey, int lockSecond = 30, CancellationToken token = default(CancellationToken)) |
|
||||
{ |
|
||||
await ConnectAsync(token); |
|
||||
var redisLockToken = Environment.MachineName; |
|
||||
var redisLockKey = _instance + lockKey; |
|
||||
var lockResult = await LockTakeAsync(redisLockKey, redisLockToken, TimeSpan.FromSeconds(lockSecond)); |
|
||||
|
|
||||
if (lockResult) |
|
||||
{ |
|
||||
return new DisposeAction(async () => |
|
||||
{ |
|
||||
await LockReleaseAsync(redisLockKey, redisLockToken); |
|
||||
}); |
|
||||
} |
|
||||
Logger.LogWarning("Redis lock failed of key: {0}", redisLockKey); |
|
||||
throw new DistributedLockException(redisLockKey); |
|
||||
} |
|
||||
|
|
||||
public bool Release(string lockKey, string lockValue) |
|
||||
{ |
|
||||
Connect(); |
|
||||
return LockReleaseSync(lockKey, lockValue); |
|
||||
} |
|
||||
|
|
||||
public async Task<bool> ReleaseAsync(string lockKey, string lockValue, CancellationToken token = default) |
|
||||
{ |
|
||||
await ConnectAsync(token); |
|
||||
return await LockReleaseAsync(lockKey, lockValue); |
|
||||
} |
|
||||
/// <summary>
|
|
||||
/// 同步加锁
|
|
||||
/// </summary>
|
|
||||
/// <param name="key"></param>
|
|
||||
/// <param name="value"></param>
|
|
||||
/// <param name="expiry"></param>
|
|
||||
/// <returns></returns>
|
|
||||
protected virtual bool LockTakeSync(RedisKey key, RedisValue value, TimeSpan expiry) |
|
||||
{ |
|
||||
// 定义重试策略
|
|
||||
var policy = Policy |
|
||||
.HandleResult<bool>(result => !result) |
|
||||
.WaitAndRetry( |
|
||||
retryCount: _options.FailedRetryCount, |
|
||||
sleepDurationProvider: sleep => TimeSpan.FromMilliseconds(_options.FailedRetryInterval), |
|
||||
onRetry: (result, timeSpan) => |
|
||||
{ |
|
||||
Logger.LogWarning("Redis lock take failed, retry policy timeSpan:{0}", timeSpan.ToString()); |
|
||||
}); |
|
||||
// 加锁
|
|
||||
var lockResult = policy.Execute(() => _redis.LockTake(key, value, expiry)); |
|
||||
|
|
||||
return lockResult; |
|
||||
} |
|
||||
/// <summary>
|
|
||||
/// 异步加锁
|
|
||||
/// </summary>
|
|
||||
/// <param name="key"></param>
|
|
||||
/// <param name="value"></param>
|
|
||||
/// <param name="expiry"></param>
|
|
||||
/// <returns></returns>
|
|
||||
protected virtual async Task<bool> LockTakeAsync(RedisKey key, RedisValue value, TimeSpan expiry) |
|
||||
{ |
|
||||
// 定义重试策略
|
|
||||
var policy = Policy |
|
||||
.HandleResult<bool>(result => !result) |
|
||||
.WaitAndRetryAsync( |
|
||||
retryCount: _options.FailedRetryCount, |
|
||||
sleepDurationProvider: sleep => TimeSpan.FromMilliseconds(_options.FailedRetryInterval), |
|
||||
onRetry: (result, timeSpan) => |
|
||||
{ |
|
||||
Logger.LogWarning("Redis lock take failed, retry policy timeSpan:{0}", timeSpan.ToString()); |
|
||||
}); |
|
||||
// 加锁
|
|
||||
var lockResult = await policy.ExecuteAsync(async () => |
|
||||
await _redis.LockTakeAsync(key, value, expiry)); |
|
||||
|
|
||||
return lockResult; |
|
||||
} |
|
||||
/// <summary>
|
|
||||
/// 同步释放锁
|
|
||||
/// </summary>
|
|
||||
/// <param name="key"></param>
|
|
||||
/// <param name="value"></param>
|
|
||||
/// <returns></returns>
|
|
||||
protected virtual bool LockReleaseSync(RedisKey key, RedisValue value) |
|
||||
{ |
|
||||
// 定义重试策略
|
|
||||
var policy = Policy |
|
||||
.HandleResult<bool>(result => !result) |
|
||||
.WaitAndRetry( |
|
||||
retryCount: _options.FailedRetryCount, |
|
||||
sleepDurationProvider: sleep => TimeSpan.FromMilliseconds(_options.FailedRetryInterval), |
|
||||
onRetry: (result, timeSpan) => |
|
||||
{ |
|
||||
Logger.LogWarning("Redis lock release failed, retry policy timeSpan:{0}", timeSpan.ToString()); |
|
||||
}); |
|
||||
// 释放锁
|
|
||||
var lockReleaseResult = policy.Execute(() => _redis.LockRelease(key, value)); |
|
||||
|
|
||||
return lockReleaseResult; |
|
||||
} |
|
||||
/// <summary>
|
|
||||
/// 异步释放锁
|
|
||||
/// </summary>
|
|
||||
/// <param name="key"></param>
|
|
||||
/// <param name="value"></param>
|
|
||||
/// <returns></returns>
|
|
||||
protected virtual async Task<bool> LockReleaseAsync(RedisKey key, RedisValue value) |
|
||||
{ |
|
||||
// 定义重试策略
|
|
||||
var policy = Policy |
|
||||
.HandleResult<bool>(result => !result) |
|
||||
.WaitAndRetryAsync( |
|
||||
retryCount: _options.FailedRetryCount, |
|
||||
sleepDurationProvider: sleep => TimeSpan.FromMilliseconds(_options.FailedRetryInterval), |
|
||||
onRetry: (result, timeSpan) => |
|
||||
{ |
|
||||
Logger.LogWarning("Redis lock release failed, retry policy timeSpan:{0}", timeSpan.ToString()); |
|
||||
}); |
|
||||
// 释放锁
|
|
||||
var lockReleaseResult = await policy.ExecuteAsync(async () => |
|
||||
await _redis.LockReleaseAsync(key, value)); |
|
||||
|
|
||||
return lockReleaseResult; |
|
||||
} |
|
||||
|
|
||||
private void Connect() |
|
||||
{ |
|
||||
if (_redis != null) |
|
||||
{ |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
_connectionLock.Wait(); |
|
||||
try |
|
||||
{ |
|
||||
if (_redis == null) |
|
||||
{ |
|
||||
if (_options.ConfigurationOptions != null) |
|
||||
{ |
|
||||
_connection = ConnectionMultiplexer.Connect(_options.ConfigurationOptions); |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
_connection = ConnectionMultiplexer.Connect(_options.Configuration); |
|
||||
} |
|
||||
RegistenConnectionEvent(_connection); |
|
||||
_redis = _connection.GetDatabase(); |
|
||||
} |
|
||||
} |
|
||||
finally |
|
||||
{ |
|
||||
_connectionLock.Release(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
|
|
||||
private async Task ConnectAsync(CancellationToken token = default(CancellationToken)) |
|
||||
{ |
|
||||
token.ThrowIfCancellationRequested(); |
|
||||
|
|
||||
if (_redis != null) |
|
||||
{ |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
await _connectionLock.WaitAsync(token); |
|
||||
try |
|
||||
{ |
|
||||
if (_redis == null) |
|
||||
{ |
|
||||
if (_options.ConfigurationOptions != null) |
|
||||
{ |
|
||||
_connection = await ConnectionMultiplexer.ConnectAsync(_options.ConfigurationOptions); |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
_connection = await ConnectionMultiplexer.ConnectAsync(_options.Configuration); |
|
||||
} |
|
||||
RegistenConnectionEvent(_connection); |
|
||||
_redis = _connection.GetDatabase(); |
|
||||
} |
|
||||
} |
|
||||
finally |
|
||||
{ |
|
||||
_connectionLock.Release(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
private void OnConfigurationChangedBroadcast(object sender, EndPointEventArgs e) |
|
||||
{ |
|
||||
Logger.LogInformation("Redis lock server master/slave changes"); |
|
||||
} |
|
||||
|
|
||||
private void OnInternalError(object sender, InternalErrorEventArgs e) |
|
||||
{ |
|
||||
Logger.LogError("Redis lock internal error, origin:{0}, connectionType:{1}", |
|
||||
e.Origin, e.ConnectionType); |
|
||||
Logger.LogError(e.Exception, "Redis lock internal error"); |
|
||||
|
|
||||
} |
|
||||
|
|
||||
private void OnHashSlotMoved(object sender, HashSlotMovedEventArgs e) |
|
||||
{ |
|
||||
Logger.LogInformation("Redis lock configuration changed"); |
|
||||
} |
|
||||
|
|
||||
private void OnConfigurationChanged(object sender, EndPointEventArgs e) |
|
||||
{ |
|
||||
Logger.LogInformation("Redis lock configuration changed"); |
|
||||
} |
|
||||
|
|
||||
private void OnErrorMessage(object sender, RedisErrorEventArgs e) |
|
||||
{ |
|
||||
Logger.LogWarning("Redis lock error, message:{0}", e.Message); |
|
||||
} |
|
||||
|
|
||||
private void OnConnectionRestored(object sender, ConnectionFailedEventArgs e) |
|
||||
{ |
|
||||
Logger.LogWarning("Redis lock connection restored, failureType:{0}, connectionType:{1}", |
|
||||
e.FailureType, e.ConnectionType); |
|
||||
} |
|
||||
|
|
||||
private void OnConnectionFailed(object sender, ConnectionFailedEventArgs e) |
|
||||
{ |
|
||||
Logger.LogError("Redis lock connection failed, failureType:{0}, connectionType:{1}", |
|
||||
e.FailureType, e.ConnectionType); |
|
||||
Logger.LogError(e.Exception, "Redis lock connection failed"); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,26 +0,0 @@ |
|||||
using Microsoft.Extensions.Options; |
|
||||
using StackExchange.Redis; |
|
||||
|
|
||||
namespace LINGYUN.Abp.DistributedLock.Redis |
|
||||
{ |
|
||||
public class RedisLockOptions : IOptions<RedisLockOptions> |
|
||||
{ |
|
||||
public string Configuration { get; set; } |
|
||||
public ConfigurationOptions ConfigurationOptions { get; set; } |
|
||||
public string InstanceName { get; set; } |
|
||||
/// <summary>
|
|
||||
/// 失败重试次数
|
|
||||
/// default: 3
|
|
||||
/// </summary>
|
|
||||
public int FailedRetryCount { get; set; } = 3; |
|
||||
/// <summary>
|
|
||||
/// 失败重试间隔 ms
|
|
||||
/// default: 1000
|
|
||||
/// </summary>
|
|
||||
public int FailedRetryInterval { get; set; } = 1000; |
|
||||
RedisLockOptions IOptions<RedisLockOptions>.Value |
|
||||
{ |
|
||||
get { return this; } |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,14 +0,0 @@ |
|||||
<Project Sdk="Microsoft.NET.Sdk"> |
|
||||
|
|
||||
<Import Project="..\..\..\common.props" /> |
|
||||
|
|
||||
<PropertyGroup> |
|
||||
<TargetFramework>netstandard2.0</TargetFramework> |
|
||||
<RootNamespace /> |
|
||||
</PropertyGroup> |
|
||||
|
|
||||
<ItemGroup> |
|
||||
<ProjectReference Include="..\LINGYUN.Abp.ExceptionHandling\LINGYUN.Abp.ExceptionHandling.csproj" /> |
|
||||
</ItemGroup> |
|
||||
|
|
||||
</Project> |
|
||||
@ -1,14 +0,0 @@ |
|||||
using LINGYUN.Abp.ExceptionHandling; |
|
||||
using System; |
|
||||
|
|
||||
namespace LINGYUN.Abp.DistributedLock |
|
||||
{ |
|
||||
public class DistributedLockException : Exception, IHasNotifierErrorMessage |
|
||||
{ |
|
||||
public DistributedLockException(string message) |
|
||||
: base(message) |
|
||||
{ |
|
||||
|
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,61 +0,0 @@ |
|||||
using System; |
|
||||
using System.Threading; |
|
||||
using System.Threading.Tasks; |
|
||||
|
|
||||
namespace LINGYUN.Abp.DistributedLock |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// 分布式锁接口
|
|
||||
/// </summary>
|
|
||||
public interface IDistributedLock |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// 分布式锁
|
|
||||
/// 需要手动释放锁
|
|
||||
/// </summary>
|
|
||||
/// <param name="lockKey">锁键名</param>
|
|
||||
/// <param name="lockValue">锁定对象</param>
|
|
||||
/// <param name="lockSecond">锁定时间(秒)</param>
|
|
||||
/// <returns></returns>
|
|
||||
bool Lock(string lockKey, string lockValue, int lockSecond = 30); |
|
||||
/// <summary>
|
|
||||
/// 分布式锁
|
|
||||
/// using块自动释放锁
|
|
||||
/// </summary>
|
|
||||
/// <param name="lockKey">锁键名</param>
|
|
||||
/// <param name="lockSecond">锁定时间(秒)</param>
|
|
||||
/// <returns></returns>
|
|
||||
IDisposable Lock(string lockKey, int lockSecond = 30); |
|
||||
/// <summary>
|
|
||||
/// 分布式锁
|
|
||||
/// using块自动释放锁
|
|
||||
/// </summary>
|
|
||||
/// <param name="lockKey">锁键名</param>
|
|
||||
/// <param name="lockSecond">锁定时间(秒)</param>
|
|
||||
/// <returns></returns>
|
|
||||
Task<IDisposable> LockAsync(string lockKey, int lockSecond = 30, CancellationToken token = default(CancellationToken)); |
|
||||
/// <summary>
|
|
||||
/// 分布式锁
|
|
||||
/// 需要手动释放锁
|
|
||||
/// </summary>
|
|
||||
/// <param name="lockKey">锁键名</param>
|
|
||||
/// <param name="lockValue">锁定对象</param>
|
|
||||
/// <param name="lockSecond">锁定时间(秒)</param>
|
|
||||
/// <returns></returns>
|
|
||||
Task<bool> LockAsync(string lockKey, string lockValue, int lockSecond = 30, CancellationToken token = default(CancellationToken)); |
|
||||
/// <summary>
|
|
||||
/// 释放锁资源
|
|
||||
/// </summary>
|
|
||||
/// <param name="lockKey">锁键名</param>
|
|
||||
/// <param name="lockValue">锁定对象</param>
|
|
||||
/// <returns></returns>
|
|
||||
bool Release(string lockKey, string lockValue); |
|
||||
/// <summary>
|
|
||||
/// 释放锁资源
|
|
||||
/// </summary>
|
|
||||
/// <param name="lockKey">锁键名</param>
|
|
||||
/// <param name="lockValue">锁定对象</param>
|
|
||||
/// <returns></returns>
|
|
||||
Task<bool> ReleaseAsync(string lockKey, string lockValue, CancellationToken token = default(CancellationToken)); |
|
||||
} |
|
||||
} |
|
||||
@ -1,12 +0,0 @@ |
|||||
<Project Sdk="Microsoft.NET.Sdk"> |
|
||||
|
|
||||
<PropertyGroup> |
|
||||
<TargetFramework>netstandard2.0</TargetFramework> |
|
||||
<RootNamespace /> |
|
||||
</PropertyGroup> |
|
||||
|
|
||||
<ItemGroup> |
|
||||
<PackageReference Include="Volo.Abp.Ddd.Domain" Version="4.4.0" /> |
|
||||
</ItemGroup> |
|
||||
|
|
||||
</Project> |
|
||||
@ -1,10 +0,0 @@ |
|||||
using Volo.Abp.Domain; |
|
||||
using Volo.Abp.Modularity; |
|
||||
|
|
||||
namespace LINGYUN.Abp.Domain.Entities.Events |
|
||||
{ |
|
||||
[DependsOn(typeof(AbpDddDomainModule))] |
|
||||
public class AbpDddDomainEntitesEventsModule : AbpModule |
|
||||
{ |
|
||||
} |
|
||||
} |
|
||||
@ -1,151 +0,0 @@ |
|||||
using Microsoft.Extensions.Logging; |
|
||||
using Microsoft.Extensions.Options; |
|
||||
using System; |
|
||||
using System.Collections.Generic; |
|
||||
using System.Linq; |
|
||||
using System.Threading.Tasks; |
|
||||
using Volo.Abp.DependencyInjection; |
|
||||
using Volo.Abp.Domain.Entities; |
|
||||
using Volo.Abp.Domain.Entities.Events; |
|
||||
using Volo.Abp.Domain.Entities.Events.Distributed; |
|
||||
using Volo.Abp.DynamicProxy; |
|
||||
using Volo.Abp.EventBus; |
|
||||
using Volo.Abp.Uow; |
|
||||
|
|
||||
namespace LINGYUN.Abp.Domain.Entities.Events |
|
||||
{ |
|
||||
[Dependency(Microsoft.Extensions.DependencyInjection.ServiceLifetime.Transient, ReplaceServices = true)] |
|
||||
[ExposeServices(typeof(IEntityChangeEventHelper), typeof(Volo.Abp.Domain.Entities.Events.EntityChangeEventHelper))] |
|
||||
[Obsolete("the component will be removed when the abp framework is upgraded to 3.1.0")] |
|
||||
public class EntityChangeEventHelper : Volo.Abp.Domain.Entities.Events.EntityChangeEventHelper |
|
||||
{ |
|
||||
public EntityChangeEventHelper( |
|
||||
IUnitOfWorkManager unitOfWorkManager, |
|
||||
IEntityToEtoMapper entityToEtoMapper, |
|
||||
IOptions<AbpDistributedEntityEventOptions> distributedEntityEventOptions) |
|
||||
: base(unitOfWorkManager, entityToEtoMapper, distributedEntityEventOptions) |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
protected override async Task TriggerEventWithEntity( |
|
||||
IEventBus eventPublisher, |
|
||||
Type genericEventType, |
|
||||
object entityOrEto, |
|
||||
object originalEntity, |
|
||||
bool triggerInCurrentUnitOfWork) |
|
||||
{ |
|
||||
var entityType = ProxyHelper.UnProxy(entityOrEto).GetType(); |
|
||||
var eventType = genericEventType.MakeGenericType(entityType); |
|
||||
var currentUow = UnitOfWorkManager.Current; |
|
||||
|
|
||||
if (triggerInCurrentUnitOfWork || currentUow == null) |
|
||||
{ |
|
||||
await eventPublisher.PublishAsync( |
|
||||
eventType, |
|
||||
Activator.CreateInstance(eventType, entityOrEto) |
|
||||
); |
|
||||
|
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
var eventList = GetEventList(currentUow); |
|
||||
var isFirstEvent = !eventList.Any(); |
|
||||
|
|
||||
eventList.AddUniqueEvent(eventPublisher, eventType, entityOrEto, originalEntity); |
|
||||
|
|
||||
/* Register to OnCompleted if this is the first item. |
|
||||
* Other items will already be in the list once the UOW completes. |
|
||||
*/ |
|
||||
if (isFirstEvent) |
|
||||
{ |
|
||||
currentUow.OnCompleted( |
|
||||
async () => |
|
||||
{ |
|
||||
foreach (var eventEntry in eventList) |
|
||||
{ |
|
||||
try |
|
||||
{ |
|
||||
// TODO: abp.io 3.1修复
|
|
||||
await eventEntry.EventBus.PublishAsync( |
|
||||
eventEntry.EventType, |
|
||||
Activator.CreateInstance(eventEntry.EventType, eventEntry.EntityOrEto) |
|
||||
); |
|
||||
} |
|
||||
catch (Exception ex) |
|
||||
{ |
|
||||
Logger.LogError( |
|
||||
$"Caught an exception while publishing the event '{eventType.FullName}' for the entity '{entityOrEto}'"); |
|
||||
Logger.LogException(ex); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
private EntityChangeEventList GetEventList(IUnitOfWork currentUow) |
|
||||
{ |
|
||||
return (EntityChangeEventList)currentUow.Items.GetOrAdd( |
|
||||
"AbpEntityChangeEventList", |
|
||||
() => new EntityChangeEventList() |
|
||||
); |
|
||||
} |
|
||||
|
|
||||
private class EntityChangeEventList : List<EntityChangeEventEntry> |
|
||||
{ |
|
||||
public void AddUniqueEvent(IEventBus eventBus, Type eventType, object entityOrEto, object originalEntity) |
|
||||
{ |
|
||||
var newEntry = new EntityChangeEventEntry(eventBus, eventType, entityOrEto, originalEntity); |
|
||||
|
|
||||
//Latest "same" event overrides the previous events.
|
|
||||
for (var i = 0; i < Count; i++) |
|
||||
{ |
|
||||
if (this[i].IsSameEvent(newEntry)) |
|
||||
{ |
|
||||
this[i] = newEntry; |
|
||||
return; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
//If this is a "new" event, add to the end
|
|
||||
Add(newEntry); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
private class EntityChangeEventEntry |
|
||||
{ |
|
||||
public IEventBus EventBus { get; } |
|
||||
|
|
||||
public Type EventType { get; } |
|
||||
|
|
||||
public object EntityOrEto { get; } |
|
||||
|
|
||||
public object OriginalEntity { get; } |
|
||||
|
|
||||
public EntityChangeEventEntry(IEventBus eventBus, Type eventType, object entityOrEto, object originalEntity) |
|
||||
{ |
|
||||
EventType = eventType; |
|
||||
EntityOrEto = entityOrEto; |
|
||||
OriginalEntity = originalEntity; |
|
||||
EventBus = eventBus; |
|
||||
} |
|
||||
|
|
||||
public bool IsSameEvent(EntityChangeEventEntry otherEntry) |
|
||||
{ |
|
||||
if (EventBus != otherEntry.EventBus || EventType != otherEntry.EventType) |
|
||||
{ |
|
||||
return false; |
|
||||
} |
|
||||
|
|
||||
var originalEntityRef = OriginalEntity as IEntity; |
|
||||
var otherOriginalEntityRef = otherEntry.OriginalEntity as IEntity; |
|
||||
if (originalEntityRef == null || otherOriginalEntityRef == null) |
|
||||
{ |
|
||||
return false; |
|
||||
} |
|
||||
|
|
||||
return EntityHelper.EntityEquals(originalEntityRef, otherOriginalEntityRef); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,3 @@ |
|||||
|
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
||||
|
<ConfigureAwait ContinueOnCapturedContext="false" /> |
||||
|
</Weavers> |
||||
@ -0,0 +1,3 @@ |
|||||
|
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
||||
|
<ConfigureAwait ContinueOnCapturedContext="false" /> |
||||
|
</Weavers> |
||||
@ -0,0 +1,3 @@ |
|||||
|
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
||||
|
<ConfigureAwait ContinueOnCapturedContext="false" /> |
||||
|
</Weavers> |
||||
@ -0,0 +1,3 @@ |
|||||
|
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
||||
|
<ConfigureAwait ContinueOnCapturedContext="false" /> |
||||
|
</Weavers> |
||||
@ -0,0 +1,3 @@ |
|||||
|
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
||||
|
<ConfigureAwait ContinueOnCapturedContext="false" /> |
||||
|
</Weavers> |
||||
@ -0,0 +1,3 @@ |
|||||
|
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
||||
|
<ConfigureAwait ContinueOnCapturedContext="false" /> |
||||
|
</Weavers> |
||||
@ -0,0 +1,3 @@ |
|||||
|
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
||||
|
<ConfigureAwait ContinueOnCapturedContext="false" /> |
||||
|
</Weavers> |
||||
@ -0,0 +1,3 @@ |
|||||
|
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
||||
|
<ConfigureAwait ContinueOnCapturedContext="false" /> |
||||
|
</Weavers> |
||||
Loading…
Reference in new issue