UI、ビジネス(エンティティ)、およびデータ(DbContext)レイヤーを備えたASP.NET MVC3Webアプリケーションがあります。私はEntityFramework4.1コードファーストを使用しています。現在、データレイヤーのDbContext.SaveChanges()
をオーバーライドして、ModifiedDate
インターフェイスを実装するエンティティオブジェクトに加えられたすべての変更に対してIAuditable
を設定できるようにしています。 DateTime.Now
を返す静的なDateProvider
クラスとメソッド(GetCurrentDate)があります(テストを実行している場合を除きます。テストを実行している場合は、指示した内容が返されます)。
ModifiedBy
プロパティも現在のユーザーに自動的に設定したいと思います。これを行うための最良の方法は何ですか?この情報にアクセスできるようにするフレームワークに組み込まれているものはありますか、それともDateProvider
クラスのようなものを設定する必要がありますか?これはActiveDirectory環境であり、IISではWindowsAuthentication
を使用します。
これが私のSaveChangesコードです:
public override int SaveChanges()
{
var changeSet = ChangeTracker.Entries<IAuditable>();
if (changeSet != null)
{
foreach (var entry in changeSet.Where(c => c.State != EntityState.Unchanged))
{
entry.Entity.ModifiedDate = DateProvider.GetCurrentDate();
}
}
return base.SaveChanges();
}
HttpContext.Current.User.Identity.Name
を使用して、現在のユーザーの名前を取得できます。
public override int SaveChanges()
{
var changeSet = ChangeTracker.Entries<IAuditable>();
if (changeSet != null)
{
foreach (var entry in changeSet.Where(c => c.State != EntityState.Unchanged))
{
entry.Entity.ModifiedDate = DateProvider.GetCurrentDate();
entry.Entity.ModifiedBy = HttpContext.Current.User.Identity.Name;
}
}
return base.SaveChanges();
}
これを行うためのより良い方法は、コンストラクターインジェクションを使用して現在のユーザーをコンテキストに渡すことです。
public class MyContext : DbContext
{
public MyContext(string userName)
{
UserName = userName;
}
public string UserName
{
get; private set;
}
public override int SaveChanges()
{
var changeSet = ChangeTracker.Entries<IAuditable>();
if (changeSet != null)
{
foreach (var entry in changeSet.Where(c => c.State != EntityState.Unchanged))
{
entry.Entity.ModifiedDate = DateProvider.GetCurrentDate();
entry.Entity.ModifiedBy = UserName;
}
}
return base.SaveChanges();
}
}
また、MVC 4/Entity Framework5アプリケーションの監査フィールドの入力を自動化したいと思いました。 @Erangaの回答とこのブログの情報を使用しました: http://lourenco.co.za/blog/2013/07/audit-trails-concurrency-and-soft-deletion-with-entity-framework/ Ninjectを使用してこのアプローチを機能させるには、他の人にとって価値のある場合に備えて投稿します。
インターフェイスと抽象クラスを作成しました:
public interface IAuditableEntity {
DateTime? CreatedDate { get; set; }
string CreatedBy { get; set; }
DateTime? LastModifiedDate { get; set; }
string LastModifiedBy { get; set; }
}
public abstract class AuditableEntity:IAuditableEntity {
public DateTime? CreatedDate { get; set; }
public string CreatedBy { get; set; }
public DateTime? LastModifiedDate { get; set; }
public string LastModifiedBy { get; set; }
}
私のエンティティでそれらを使用しました:
public class DataEntity : AuditableEntity {
public int DataEntityID { get; set; }
...
}
HttpContextを受け入れ、SaveChangesをオーバーライドするコンストラクターをMyDbContextに追加しました。
public EFDbContext(HttpContext context) {
_context = context;
}
public HttpContext _context {
get;
private set;
}
public override int SaveChanges() {
DateTime currentDateTime = DateTime.Now;
foreach (var auditableEntity in ChangeTracker.Entries<IAuditableEntity>()) {
if (auditableEntity.State == EntityState.Added || auditableEntity.State == EntityState.Modified) {
auditableEntity.Entity.LastModifiedDate = currentDateTime;
switch (auditableEntity.State) {
case EntityState.Added:
auditableEntity.Entity.CreatedDate = currentDateTime;
auditableEntity.Entity.CreatedBy = _context.User.Identity.Name;
break;
case EntityState.Modified:
auditableEntity.Entity.LastModifiedDate = currentDateTime;
auditableEntity.Entity.LastModifiedBy = _context.User.Identity.Name;
if (auditableEntity.Property(p => p.CreatedDate).IsModified || auditableEntity.Property(p => p.CreatedBy).IsModified) {
throw new DbEntityValidationException(string.Format("Attempt to change created audit trails on a modified {0}", auditableEntity.Entity.GetType().FullName));
}
break;
}
}
}
return base.SaveChanges();
}
最後に-リクエストごとにDbContextを用意し、この回答に基づいて以下のようにHttpContextを渡す必要があります: https://stackoverflow.com/a/3617961/1803682 -MyDbContext
はスコープをリクエストします。リポジトリも同様である必要があります。
kernel.Bind<IDataRepository>()
.To<EFDataRepository>()
.InRequestScope();
kernel.Bind<MyDbContext>().ToSelf()
.InRequestScope()
.WithConstructorArgument("context", ninjectContext=>HttpContext.Current);