Files
qrcodeService/Infrastructure/AppDbContext.cs
2021-02-24 10:00:37 +08:00

152 lines
5.9 KiB
C#

using Domain.AggregateModel.AppAggregate;
using Domain.AggregateModel.LinkAggregate;
using Domain.SeedWork;
using Infrastructure.EntityConfigurations;
using MediatR;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
using Microsoft.EntityFrameworkCore.Storage;
using Pomelo.EntityFrameworkCore.MySql.Infrastructure;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Infrastructure
{
public class AppDbContext : DbContext, IUnitOfWork
{
public DbSet<App> Apps { get; set; }
public DbSet<Link> Links { get; set; }
private readonly IMediator _mediator;
private IDbContextTransaction _currentTransaction;
public bool HasActiveTransaction => _currentTransaction != null;
public IDbContextTransaction GetCurrentTransaction() => _currentTransaction;
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }
public AppDbContext(DbContextOptions<AppDbContext> options, IMediator mediator) : base(options)
{
_mediator = mediator ?? throw new ArgumentNullException(nameof(mediator));
System.Diagnostics.Debug.WriteLine("OrderingContext::ctor ->" + this.GetHashCode());
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfiguration(new AppEntityTypeConfiguration());
modelBuilder.ApplyConfiguration(new LinkEntityTypeConfiguration());
}
public async Task<bool> SaveEntitiesAsync(CancellationToken cancellationToken = default)
{
// Dispatch Domain Events collection.
// Choices:
// A) Right BEFORE committing data (EF SaveChanges) into the DB will make a single transaction including
// side effects from the domain event handlers which are using the same DbContext with "InstancePerLifetimeScope" or "scoped" lifetime
// B) Right AFTER committing data (EF SaveChanges) into the DB will make multiple transactions.
// You will need to handle eventual consistency and compensatory actions in case of failures in any of the Handlers.
await _mediator.DispatchDomainEventsAsync(this);
// After executing this line all the changes (from the Command Handler and Domain Event Handlers)
// performed through the DbContext will be committed
var result = await base.SaveChangesAsync(cancellationToken);
return true;
}
public async Task<IDbContextTransaction> BeginTransactionAsync()
{
if (_currentTransaction != null) return null;
_currentTransaction = await Database.BeginTransactionAsync(IsolationLevel.ReadCommitted);
return _currentTransaction;
}
public async Task CommitTransactionAsync(IDbContextTransaction transaction)
{
if (transaction == null) throw new ArgumentNullException(nameof(transaction));
if (transaction != _currentTransaction) throw new InvalidOperationException($"Transaction {transaction.TransactionId} is not current");
try
{
await SaveChangesAsync();
transaction.Commit();
}
catch
{
RollbackTransaction();
throw;
}
finally
{
if (_currentTransaction != null)
{
_currentTransaction.Dispose();
_currentTransaction = null;
}
}
}
public void RollbackTransaction()
{
try
{
_currentTransaction?.Rollback();
}
finally
{
if (_currentTransaction != null)
{
_currentTransaction.Dispose();
_currentTransaction = null;
}
}
}
}
public class AppDbContextDesignFactory : IDesignTimeDbContextFactory<AppDbContext>
{
public AppDbContext CreateDbContext(string[] args)
{
var optionsBuilder = new DbContextOptionsBuilder<AppDbContext>()
.UseMySql(
"server=localhost;user=root;password=root;database=qrcode",
// For common usages, see pull request #1233.
new MariaDbServerVersion(new Version(10, 5, 9)), // use MariaDbServerVersion for MariaDB
mySqlOptions => mySqlOptions
.CharSetBehavior(CharSetBehavior.NeverAppend)
.MigrationsAssembly("QRCodeService"))
// Everything from this point on is optional but helps with debugging.
.EnableSensitiveDataLogging()
.EnableDetailedErrors();
return new AppDbContext(optionsBuilder.Options, new NoMediator());
}
class NoMediator : IMediator
{
public Task Publish<TNotification>(TNotification notification, CancellationToken cancellationToken = default(CancellationToken)) where TNotification : INotification
{
return Task.CompletedTask;
}
public Task Publish(object notification, CancellationToken cancellationToken = default)
{
return Task.CompletedTask;
}
public Task<TResponse> Send<TResponse>(IRequest<TResponse> request, CancellationToken cancellationToken = default(CancellationToken))
{
return Task.FromResult(default(TResponse));
}
public Task<object> Send(object request, CancellationToken cancellationToken = default)
{
return Task.FromResult(default(object));
}
}
}
}