using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading.Tasks;
using Abp.Extensions;
using Abp.Runtime.Session;
using Abp.Utils.Etc;
using Castle.Core;
namespace Abp.Domain.Uow
{
///
/// Base for all Unit Of Work classes.
///
public abstract class UnitOfWorkBase : IUnitOfWork
{
public string Id { get; }
[DoNotWire]
public IUnitOfWork Outer { get; set; }
///
public event EventHandler Completed;
///
public event EventHandler Failed;
///
public event EventHandler Disposed;
///
public UnitOfWorkOptions Options { get; private set; }
///
public IReadOnlyList Filters
{
get { return _filters.ToImmutableList(); }
}
private readonly List _filters;
public Dictionary Items { get; set; }
///
/// Gets default UOW options.
///
protected IUnitOfWorkDefaultOptions DefaultOptions { get; }
///
/// Gets the connection string resolver.
///
protected IConnectionStringResolver ConnectionStringResolver { get; }
///
/// Gets a value indicates that this unit of work is disposed or not.
///
public bool IsDisposed { get; private set; }
///
/// Reference to current ABP session.
///
public IAbpSession AbpSession { protected get; set; }
protected IUnitOfWorkFilterExecuter FilterExecuter { get; }
///
/// Is method called before?
///
private bool _isBeginCalledBefore;
///
/// Is method called before?
///
private bool _isCompleteCalledBefore;
///
/// Is this unit of work successfully completed.
///
private bool _succeed;
///
/// A reference to the exception if this unit of work failed.
///
private Exception _exception;
private int? _tenantId;
///
/// Constructor.
///
protected UnitOfWorkBase(
IConnectionStringResolver connectionStringResolver,
IUnitOfWorkDefaultOptions defaultOptions,
IUnitOfWorkFilterExecuter filterExecuter)
{
FilterExecuter = filterExecuter;
DefaultOptions = defaultOptions;
ConnectionStringResolver = connectionStringResolver;
Id = Guid.NewGuid().ToString("N");
_filters = defaultOptions.Filters.ToList();
AbpSession = NullAbpSession.Instance;
Items = new Dictionary();
}
///
public void Begin(UnitOfWorkOptions options)
{
Check.NotNull(options, nameof(options));
PreventMultipleBegin();
Options = options; //TODO: Do not set options like that, instead make a copy?
SetFilters(options.FilterOverrides);
SetTenantId(AbpSession.TenantId, false);
BeginUow();
}
///
public abstract void SaveChanges();
///
public abstract Task SaveChangesAsync();
///
public IDisposable DisableFilter(params string[] filterNames)
{
//TODO: Check if filters exists?
var disabledFilters = new List();
foreach (var filterName in filterNames)
{
var filterIndex = GetFilterIndex(filterName);
if (_filters[filterIndex].IsEnabled)
{
disabledFilters.Add(filterName);
_filters[filterIndex] = new DataFilterConfiguration(_filters[filterIndex], false);
}
}
disabledFilters.ForEach(ApplyDisableFilter);
return new DisposeAction(() => EnableFilter(disabledFilters.ToArray()));
}
///
public IDisposable EnableFilter(params string[] filterNames)
{
//TODO: Check if filters exists?
var enabledFilters = new List();
foreach (var filterName in filterNames)
{
var filterIndex = GetFilterIndex(filterName);
if (!_filters[filterIndex].IsEnabled)
{
enabledFilters.Add(filterName);
_filters[filterIndex] = new DataFilterConfiguration(_filters[filterIndex], true);
}
}
enabledFilters.ForEach(ApplyEnableFilter);
return new DisposeAction(() => DisableFilter(enabledFilters.ToArray()));
}
///
public bool IsFilterEnabled(string filterName)
{
return GetFilter(filterName).IsEnabled;
}
///
public IDisposable SetFilterParameter(string filterName, string parameterName, object value)
{
var filterIndex = GetFilterIndex(filterName);
var newfilter = new DataFilterConfiguration(_filters[filterIndex]);
//Store old value
object oldValue = null;
var hasOldValue = newfilter.FilterParameters.ContainsKey(parameterName);
if (hasOldValue)
{
oldValue = newfilter.FilterParameters[parameterName];
}
newfilter.FilterParameters[parameterName] = value;
_filters[filterIndex] = newfilter;
ApplyFilterParameterValue(filterName, parameterName, value);
return new DisposeAction(() =>
{
//Restore old value
if (hasOldValue)
{
SetFilterParameter(filterName, parameterName, oldValue);
}
});
}
public virtual IDisposable SetTenantId(int? tenantId)
{
return SetTenantId(tenantId, true);
}
public virtual IDisposable SetTenantId(int? tenantId, bool switchMustHaveTenantEnableDisable)
{
var oldTenantId = _tenantId;
_tenantId = tenantId;
IDisposable mustHaveTenantEnableChange;
if (switchMustHaveTenantEnableDisable)
{
mustHaveTenantEnableChange = tenantId == null
? DisableFilter(AbpDataFilters.MustHaveTenant)
: EnableFilter(AbpDataFilters.MustHaveTenant);
}
else
{
mustHaveTenantEnableChange = NullDisposable.Instance;
}
var mayHaveTenantChange = SetFilterParameter(AbpDataFilters.MayHaveTenant, AbpDataFilters.Parameters.TenantId, tenantId);
var mustHaveTenantChange = SetFilterParameter(AbpDataFilters.MustHaveTenant, AbpDataFilters.Parameters.TenantId, tenantId ?? 0);
return new DisposeAction(() =>
{
mayHaveTenantChange.Dispose();
mustHaveTenantChange.Dispose();
mustHaveTenantEnableChange.Dispose();
_tenantId = oldTenantId;
});
}
public int? GetTenantId()
{
return _tenantId;
}
///
public void Complete()
{
PreventMultipleComplete();
try
{
CompleteUow();
_succeed = true;
OnCompleted();
}
catch (Exception ex)
{
_exception = ex;
throw;
}
}
///
public async Task CompleteAsync()
{
PreventMultipleComplete();
try
{
await CompleteUowAsync();
_succeed = true;
OnCompleted();
}
catch (Exception ex)
{
_exception = ex;
throw;
}
}
///
public void Dispose()
{
if (!_isBeginCalledBefore || IsDisposed)
{
return;
}
IsDisposed = true;
if (!_succeed)
{
OnFailed(_exception);
}
DisposeUow();
OnDisposed();
}
///
/// Can be implemented by derived classes to start UOW.
///
protected virtual void BeginUow()
{
}
///
/// Should be implemented by derived classes to complete UOW.
///
protected abstract void CompleteUow();
///
/// Should be implemented by derived classes to complete UOW.
///
protected abstract Task CompleteUowAsync();
///
/// Should be implemented by derived classes to dispose UOW.
///
protected abstract void DisposeUow();
protected virtual void ApplyDisableFilter(string filterName)
{
FilterExecuter.ApplyDisableFilter(this, filterName);
}
protected virtual void ApplyEnableFilter(string filterName)
{
FilterExecuter.ApplyEnableFilter(this, filterName);
}
protected virtual void ApplyFilterParameterValue(string filterName, string parameterName, object value)
{
FilterExecuter.ApplyFilterParameterValue(this, filterName, parameterName, value);
}
protected virtual string ResolveConnectionString(ConnectionStringResolveArgs args)
{
return ConnectionStringResolver.GetNameOrConnectionString(args);
}
///
/// Called to trigger event.
///
protected virtual void OnCompleted()
{
Completed.InvokeSafely(this);
}
///
/// Called to trigger event.
///
/// Exception that cause failure
protected virtual void OnFailed(Exception exception)
{
Failed.InvokeSafely(this, new UnitOfWorkFailedEventArgs(exception));
}
///
/// Called to trigger event.
///
protected virtual void OnDisposed()
{
Disposed.InvokeSafely(this);
}
private void PreventMultipleBegin()
{
if (_isBeginCalledBefore)
{
throw new AbpException("This unit of work has started before. Can not call Start method more than once.");
}
_isBeginCalledBefore = true;
}
private void PreventMultipleComplete()
{
if (_isCompleteCalledBefore)
{
throw new AbpException("Complete is called before!");
}
_isCompleteCalledBefore = true;
}
private void SetFilters(List filterOverrides)
{
for (var i = 0; i < _filters.Count; i++)
{
var filterOverride = filterOverrides.FirstOrDefault(f => f.FilterName == _filters[i].FilterName);
if (filterOverride != null)
{
_filters[i] = filterOverride;
}
}
if (AbpSession.TenantId == null)
{
ChangeFilterIsEnabledIfNotOverrided(filterOverrides, AbpDataFilters.MustHaveTenant, false);
}
}
private void ChangeFilterIsEnabledIfNotOverrided(List filterOverrides, string filterName, bool isEnabled)
{
if (filterOverrides.Any(f => f.FilterName == filterName))
{
return;
}
var index = _filters.FindIndex(f => f.FilterName == filterName);
if (index < 0)
{
return;
}
if (_filters[index].IsEnabled == isEnabled)
{
return;
}
_filters[index] = new DataFilterConfiguration(filterName, isEnabled);
}
private DataFilterConfiguration GetFilter(string filterName)
{
var filter = _filters.FirstOrDefault(f => f.FilterName == filterName);
if (filter == null)
{
throw new AbpException("Unknown filter name: " + filterName + ". Be sure this filter is registered before.");
}
return filter;
}
private int GetFilterIndex(string filterName)
{
var filterIndex = _filters.FindIndex(f => f.FilterName == filterName);
if (filterIndex < 0)
{
throw new AbpException("Unknown filter name: " + filterName + ". Be sure this filter is registered before.");
}
return filterIndex;
}
public override string ToString()
{
return $"[UnitOfWork {Id}]";
}
}
}