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