Further to the Stack Overflow question How to set created date and Modified Date to enitites in DB first approach asked by user LP13 and answered by user Ogglas.
I am writing a test project to learn new development approaches and have hit a wall. I am trying to implement the answer provided by Ogglas, however I am unsure how to register the "Wrapper" in AutoFac?
Ogglas's and My Code Example
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();
}
My AutoFac EF Module Updated
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();
}
}
I have used the following tutorial as a guide to setting up my project Tutorial Guide Project I would very much appreciate any assistance given. Thank you.
Generic Repository Updated
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 Updated
public interface IMyDbContext
{
ICurrentUser CurrentUser { get; set; }
DbSet<Staff> Staff { get; set; }
DbSet<AddressStaff> StaffAddresses { get; set; }
int SaveChanges();
}
My CurrentUser AutoFac Module
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();
}
}
Here's a minimal working solution.
Interfaces:
public interface IMyDbContext
{
DbSet<Staff> Staff { get; }
DbChangeTracker ChangeTracker { get; }
int SaveChanges();
}
public interface IAuditableEntity
{
string CreatedBy { get; set; }
}
public interface ICurrentUser
{
string Name();
}
Entity:
public class Staff : IAuditableEntity
{
[Key]
public int Id { get; set; }
public string CreatedBy { get; set; }
}
Mocks:
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;
}
Decorator:
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();
}
}
And test:
[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);
}