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}]"; } } }