public virtual async Task Commit(CancellationToken cancellationToken = default)
        {
            using var _ = await AsyncLock.Lock(cancellationToken).ConfigureAwait(false);

            if (IsClosed)
            {
                throw Stl.Fusion.Operations.Internal.Errors.OperationScopeIsAlreadyClosed();
            }
            try {
                if (!IsUsed)
                {
                    IsConfirmed = true;
                    return;
                }

                Operation.CommitTime = Clock.Now;
                if (Operation.Command == null)
                {
                    throw Stl.Fusion.Operations.Internal.Errors.OperationHasNoCommand();
                }
                var dbContext = DbContext !;
                dbContext.DisableChangeTracking(); // Just to speed up things a bit
                var operation = await DbOperationLog.Add(dbContext, Operation, cancellationToken).ConfigureAwait(false);

                try {
                    await Transaction !.CommitAsync(cancellationToken).ConfigureAwait(false);
                    IsConfirmed = true;
                }
                catch (Exception) {
                    // See https://docs.microsoft.com/en-us/ef/ef6/fundamentals/connection-resiliency/commit-failures
                    try {
                        // We need a new connection here, since the old one might be broken
                        dbContext = DbContextFactory.CreateDbContext();
                        var committedOperation = await DbOperationLog.TryGet(dbContext, operation.Id, cancellationToken);

                        if (committedOperation != null)
                        {
                            IsConfirmed = true;
                        }
                    }
                    catch {
                        // Intended
                    }
                    if (IsConfirmed != true)
                    {
                        throw;
                    }
                }
            }
            finally {
                IsConfirmed ??= false;
                IsClosed = true;
            }
        }
Example #2
0
        public virtual async Task <IOperation?> CommitAsync(CancellationToken cancellationToken = default)
        {
            using var _ = await AsyncLock.LockAsync(cancellationToken).ConfigureAwait(false);

            if (IsCompleted)
            {
                throw Errors.TransactionScopeIsAlreadyClosed();
            }
            try {
                if (!IsUsed)
                {
                    return(null);
                }

                foreach (var dbContext in AllDbContexts)
                {
                    if (!(dbContext.ChangeTracker.AutoDetectChangesEnabled ||
                          dbContext.ChangeTracker.HasChanges()))
                    {
                        continue;
                    }
                    await dbContext.SaveChangesAsync(cancellationToken).ConfigureAwait(false);
                }

                if (Command == null)
                {
                    await Transaction !.CommitAsync(cancellationToken).ConfigureAwait(false);
                    return(null);
                }
                else
                {
                    var dbContext = PrimaryDbContext !.ReadWrite();
                    dbContext.ChangeTracker.Clear();
                    var operation = await DbOperationLog.AddAsync(dbContext, o => {
                        o.StartTime        = StartTime;
                        o.CommitTime       = Clock.Now;
                        o.Command          = Command;
                        o.InvalidationData = InvalidationData;
                    }, cancellationToken).ConfigureAwait(false);

                    await Transaction !.CommitAsync(cancellationToken).ConfigureAwait(false);
                    operation = await DbOperationLog.TryGetAsync(dbContext, operation.Id, cancellationToken);

                    if (operation == null)
                    {
                        throw Errors.OperationCommitFailed();
                    }
                    return(operation);
                }
            }
            finally {
                IsCompleted = true;
            }
        }
        public DbOperationScope(IServiceProvider services)
        {
            var loggerFactory = services.GetService <ILoggerFactory>();

            Log              = loggerFactory?.CreateLogger(GetType()) ?? NullLogger.Instance;
            Services         = services;
            Clock            = services.GetService <IMomentClock>() ?? SystemClock.Instance;
            DbContextFactory = services.GetRequiredService <IDbContextFactory <TDbContext> >();
            DbOperationLog   = services.GetRequiredService <IDbOperationLog <TDbContext> >();
            AsyncLock        = new AsyncLock(ReentryMode.CheckedPass);
            Operation        = DbOperationLog.New();
            CommandContext   = services.GetRequiredService <CommandContext>();
        }
Example #4
0
        public virtual async Task <IOperation?> CommitAsync(CancellationToken cancellationToken = default)
        {
            using var _ = await AsyncLock.LockAsync(cancellationToken).ConfigureAwait(false);

            if (IsCompleted)
            {
                throw Errors.TransactionScopeIsAlreadyClosed();
            }
            try {
                if (!IsUsed)
                {
                    return(null);
                }

                if (Command == null)
                {
                    await Transaction !.CommitAsync(cancellationToken).ConfigureAwait(false);
                    return(null);
                }
                else
                {
                    var dbContext = PrimaryDbContext !;
                    var operation = await DbOperationLog.AddAsync(dbContext, o => {
                        o.StartTime  = StartTime;
                        o.CommitTime = Clock.Now;
                        o.Command    = Command;
                        o.Items      = Items;
                    }, cancellationToken).ConfigureAwait(false);

                    await Transaction !.CommitAsync(cancellationToken).ConfigureAwait(false);
                    operation = await DbOperationLog.TryGetAsync(dbContext, operation.Id, cancellationToken);

                    if (operation == null)
                    {
                        throw Errors.OperationCommitFailed();
                    }
                    return(operation);
                }
            }
            finally {
                IsCompleted = true;
            }
        }
        public virtual async Task CommitAsync(CancellationToken cancellationToken = default)
        {
            using var _ = await AsyncLock.LockAsync(cancellationToken).ConfigureAwait(false);

            if (IsClosed)
            {
                throw Stl.Fusion.Operations.Internal.Errors.OperationScopeIsAlreadyClosed();
            }
            try {
                if (!IsUsed)
                {
                    IsConfirmed = true;
                    return;
                }

                Operation.CommitTime = Clock.Now;
                if (Operation.Command == null)
                {
                    throw Stl.Fusion.Operations.Internal.Errors.OperationHasNoCommand();
                }
                var dbContext = DbContext !;
                dbContext.DisableChangeTracking(); // Just to speed up things a bit
                var operation = await DbOperationLog.AddAsync(dbContext, Operation, cancellationToken).ConfigureAwait(false);

                await Transaction !.CommitAsync(cancellationToken).ConfigureAwait(false);
                operation = await DbOperationLog.TryGetAsync(dbContext, operation.Id, cancellationToken);

                if (operation == null)
                {
                    throw Errors.OperationCommitFailed();
                }
                IsConfirmed = true;
            }
            finally {
                IsConfirmed ??= false;
                IsClosed = true;
            }
        }