如何将创建者设置为在 DbContext 中启动



进一步的堆栈溢出问题如何设置创建日期和修改日期以在用户LP13提出并由用户Ogglas回答的DB优先方法中启动。

我正在编写一个测试项目来学习新的开发方法,并且遇到了困难。我正在尝试实现 Ogglas 提供的答案,但我不确定如何在 AutoFac 中注册"包装器"?

Ogglas和我的代码示例

public interface IEntity
{
DateTime CreatedDate { get; set; }
string CreatedBy { get; set; }
DateTime UpdatedDate { get; set; }
string UpdatedBy { get; set; }
}
public interface IAuditableEntity
{
DateTime CreatedDate { get; set; }
string CreatedBy { get; set; }
DateTime UpdatedDate { get; set; }
string UpdatedBy { get; set; }
}
public interface ICurrentUser
{
string GetUsername();
}
public interface ICurrentUser
{
string Name();
string GetUserId();
bool IsUserAuthenticated();
bool IsUserAdmin();
bool IsUserManager();
}
public class ApplicationDbContextUserWrapper
{
public ApplicationDbContext Context;
public ApplicationDbContextUserWrapper(ApplicationDbContext context, ICurrentUser currentUser)
{
context.CurrentUser = currentUser;
this.Context = context;
}
}
public class MyDbContextWrapper
{
public IMyDbContext Context;
public MyDbContextWrapper(IMyDbContext context, ICurrentUser currentUser)
{
context.CurrentUser = currentUser;
Context = context;
}
}
public class ApplicationDbContext : DbContext
{
public ICurrentUser CurrentUser;
public override int SaveChanges()
{
var now = DateTime.Now;
foreach (var changedEntity in ChangeTracker.Entries())
{
if (changedEntity.Entity is IEntity entity)
{
switch (changedEntity.State)
{
case EntityState.Added:
entity.CreatedDate = now;
entity.UpdatedDate = now;
entity.CreatedBy = CurrentUser.GetUsername();
entity.UpdatedBy = CurrentUser.GetUsername();
break;
case EntityState.Modified:
Entry(entity).Property(x => x.CreatedBy).IsModified = false;
Entry(entity).Property(x => x.CreatedDate).IsModified = false;
entity.UpdatedDate = now;
entity.UpdatedBy = CurrentUser.GetUsername();
break;
}
}
}
return base.SaveChanges();
}
}
public class MyDbContext : DbContext, IMyDbContext
{
public ICurrentUser CurrentUser { get; set; }
public DbSet<Staff> Staff { get; set; }
public DbSet<AddressStaff> StaffAddresses { get; set; }
public MyDbContext() : base("Name=MyWebPortalConnection")
{
Database.SetInitializer(new MigrateDatabaseToLatestVersion<MyDbContext, MyWebPortalContextMigrationConfiguration>());
}
public override int SaveChanges()
{
var modifiedEntries = ChangeTracker.Entries().Where(x => x.Entity is IAuditableEntity
&& (x.State == EntityState.Added
|| x.State == EntityState.Modified));
foreach (var entry in modifiedEntries)
{
if (entry.Entity is IAuditableEntity entity)
{
var dateTimeZone = DateTimeZoneProviders.Tzdb["Europe/London"];
var zonedClock = SystemClock.Instance.InZone(dateTimeZone);
var localDateTime = zonedClock.GetCurrentLocalDateTime();
var dateTime = new DateTime(localDateTime.Year,
localDateTime.Month,
localDateTime.Day,
localDateTime.Hour,
localDateTime.Minute,
localDateTime.Second);
if (entry.State == EntityState.Added)
{
entity.CreatedBy = CurrentUser.Name();
entity.CreatedDate = dateTime;
}
else if (entry.State == EntityState.Modified)
{
entity.UpdatedBy = CurrentUser.Name();
entity.UpdatedDate = dateTime;
}
else
{
Entry(entity).Property(x => x.CreatedBy).IsModified = false;
Entry(entity).Property(x => x.CreatedDate).IsModified = false;
}
}
}
return base.SaveChanges();
}

我的自动 EF 模块已更新

public class EFModule : Module
{
protected override void Load(ContainerBuilder builder)
{
//builder.RegisterType<MyDbContextWrapper>().As<IMtDbContext>();
//builder.RegisterDecorator<MyDbContextWrapper, IMyDbContext>();
//builder.RegisterDecorator<MyDbContextWrapper, IMyDbContext>();
builder.RegisterType<MyDbContextWrapper>().AsSelf().InstancePerLifetimeScope();
builder.RegisterType(typeof(MyDbContext)).As(typeof(IMyDbContext)).As(typeof(DbContext)).InstancePerLifetimeScope();
builder.RegisterType(typeof(UnitOfWork)).As(typeof(IUnitOfWork)).InstancePerRequest();
builder.Register(_ => new HttpClient()).As<HttpClient>().InstancePerLifetimeScope();
}
}

我使用以下教程作为设置项目的指南 教程指南项目 我将非常感谢提供的任何帮助。谢谢。

通用存储库已更新

public abstract class GenericRepository<T> : IGenericRepository<T> where T : BaseEntity
{
protected MyDbContextWrapper DbContextWrapper;
protected DbContext GenericDbContext;
protected readonly IDbSet<T> GenericDbset;
protected GenericRepository(MyDbContextWrapper dbContextWrapper)
{
DbContextWrapper = dbContextWrapper;
GenericDbContext = (DbContext)DbContextWrapper.Context;
GenericDbset = GenericDbContext.Set<T>();
}

IMyDbContext 已更新

public interface IMyDbContext
{
ICurrentUser CurrentUser { get; set; }
DbSet<Staff> Staff { get; set; }
DbSet<AddressStaff> StaffAddresses { get; set; }
int SaveChanges();
}

我的当前用户自动建模模块

public class CurrentUserModule : Autofac.Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterAssemblyTypes(Assembly.Load("MyWebPortal.Model"))
.Where(t => t.Name.EndsWith("User"))
.AsImplementedInterfaces()
.InstancePerLifetimeScope();
}
}

这是一个最小的工作解决方案。

接口:

public interface IMyDbContext
{
DbSet<Staff> Staff { get; }
DbChangeTracker ChangeTracker { get; }
int SaveChanges();
}
public interface IAuditableEntity
{
string CreatedBy { get; set; }
}
public interface ICurrentUser
{
string Name();
}

实体:

public class Staff : IAuditableEntity
{
[Key]
public int Id { get; set; }
public string CreatedBy { get; set; }
}

嘲笑:

public class MockCurrentUser : ICurrentUser
{
public string Name() => "Mock";
}
public class MockDbContext : DbContext, IMyDbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
Database.SetInitializer<MockDbContext>(null);
base.OnModelCreating(modelBuilder);
}
public DbSet<Staff> Staff { get; set; }
public override int SaveChanges() => 1;
}

装饰:

public class ApplicationDbContextAuditDecorator : IMyDbContext
{
private readonly IMyDbContext context;
private readonly ICurrentUser currentUser;
public ApplicationDbContextAuditDecorator(IMyDbContext context, ICurrentUser currentUser)
{
this.context = context;
this.currentUser = currentUser;
}
public DbSet<Staff> Staff { get => this.context.Staff; }
public DbChangeTracker ChangeTracker => this.context.ChangeTracker;
public int SaveChanges()
{
foreach (var changedEntity in ChangeTracker.Entries())
{
if (changedEntity.Entity is IAuditableEntity entity)
{
switch (changedEntity.State)
{
case EntityState.Added:
entity.CreatedBy = this.currentUser.Name();
break;
}
}
}
return this.context.SaveChanges();
}
}

并测试:

[TestMethod]
public void TestMethod1()
{
var builder = new ContainerBuilder();
builder.RegisterType<MockDbContext>().As<IMyDbContext>().InstancePerLifetimeScope();
builder.RegisterDecorator<ApplicationDbContextAuditDecorator, IMyDbContext>();
builder.RegisterType<MockCurrentUser>().As<ICurrentUser>();
var container = builder.Build();
var context = container.Resolve<IMyDbContext>();
context.Staff.Add(new Staff());
context.SaveChanges();
Assert.AreEqual("Mock", context.Staff.Local.Single().CreatedBy);
}
builder.RegisterType<ApplicationDbContextUserWrapper>().AsSelf().

看这里: https://autofaccn.readthedocs.io/en/latest/register/registration.html

这很好地解释了它。 基本上,你告诉Autofac,"保存"某种类型,作为它自己。 您还可以指定接口和参数。 完成此操作后。您需要在构造函数中添加类型,以确保注入它。确保还注册所有依赖项,以便也可以解析它们。

通常,使用包装器作为装饰器也可能是一个好主意:

public class ApplicationDbContextUserWrapper : IDbContext
{
public ApplicationDbContext Context;
public ApplicationDbContextUserWrapper(ApplicationDbContext context, 
ICurrentUser currentUser)
{
context.CurrentUser = currentUser;
this.Context = context;
}
int IDbContext.SaveChanges() => context.SaveChanges();
...

这样,您就显式地与接口(与 dbcontext 相同的接口(通信,但包装了要调用的方法。 因此,您仍然可以在不接触基类的情况下更改行为。

最新更新