web-dev-qa-db-ja.com

Entity Framework / SQL 2008-エンティティの最終変更フィールドを自動的に更新する方法

次のエンティティがある場合:

public class PocoWithDates
{
   public string PocoName { get; set; }
   public DateTime CreatedOn { get; set; }
   public DateTime LastModified { get; set; }
}

これは、同じ名前/属性を持つSQL Server 2008テーブルに対応しています...

どうすれば自動的に

  1. レコードのCreatedOn/LastModifiedフィールドをnowに設定します(INSERTを実行する場合)
  2. レコードのLastModifiedフィールドをnowに設定します(UPDATEを実行する場合)

自動的にと言うとき、これを実行できるようにしたいということです:

poco.Name = "Changing the name";
repository.Save(); 

これではない:

poco.Name = "Changing the name";
poco.LastModified = DateTime.Now;
repository.Save();

舞台裏では、「何か」が自動的に日時フィールドを更新するはずです。その「何か」とは何ですか?

Entity Framework 4.0を使用しています-EFが自動的にそれを行う方法はありますか? (おそらくEDMXの特別な設定ですか?)

SQL Server側からはDefaultValueを使用できますが、これはINSERT's(UPDATEではない)に対してのみ機能します。

同様に、POCOのコンストラクターを使用してデフォルト値を設定できますが、これもオブジェクトをインスタンス化する場合にのみ機能します。

そしてもちろんi couldトリガーを使用しますが、それは理想的ではありません。

私はEntity Frameworkを使用しているため、ここでSavingChangesイベントにフックして日付フィールドを更新できますが、問題はPOCO(現時点ではリポジトリ)を「認識する」必要があることですジェネリックで実装されています)。私はある種のOOトリックを行う必要があります(POCOにインターフェイスを実装させ、その上でメソッドを呼び出すなど)。私はそれに悪影響を与えていませんが、それは、私はむしろ手動でフィールドを設定したいです。

基本的に、SQL Server 2008またはEntity Framework 4.0ソリューションを探しています。 (またはスマートな.NETの方法)

何か案は?

[〜#〜]編集[〜#〜]

@marc_sの回答に感謝しますが、私のシナリオに適した解決策を採用しました。

41
RPM1984

コントローラー(ASP.NET MVCを使用している)とリポジトリの間を仲介するサービスレイヤーがあるため、ここでフィールドを自動設定することにしました。

また、私のPOCOには関係/抽象化はなく、完全に独立しています。このようにして、仮想プロパティをマークしたり、基本クラスを作成したりしないようにしたいと思います。

そこで、IAutoGenerateDateFieldsというインターフェイスを作成しました。

public interface IAutoGenerateDateFields
{
   DateTime LastModified { get;set; }
   DateTime CreatedOn { get;set; }
}

POCOの場合、これらのフィールドを自動生成したいので、このインターフェースを実装します。

私の質問の例を使用すると:

public class PocoWithDates : IAutoGenerateDateFields
{
   public string PocoName { get; set; }
   public DateTime CreatedOn { get; set; }
   public DateTime LastModified { get; set; }
}

サービス層で、具象オブジェクトがインターフェースを実装しているかどうかを確認します。

public void Add(SomePoco poco)
{
   var autoDateFieldsPoco = poco as IAutoGenerateDateFields; // returns null if it's not.

   if (autoDateFieldsPoco != null) // if it implements interface
   {
      autoDateFieldsPoco.LastModified = DateTime.Now;
      autoDateFieldsPoco.CreatedOn = DateTime.Now;
   }

   // ..go on about other persistence work.
}

後で、ヘルパー/拡張メソッドへのAdd outでそのコードをおそらく破壊します。

しかし、これは私のシナリオにとって適切な解決策だと思います。保存で仮想マシンを使用したくないので(作業ユニット、リポジトリ、Pure POCOを使用しているため)、トリガーを使用したくないからです。

ご意見やご提案がございましたら、お知らせください。

9
RPM1984

私はパーティーに少し遅れていることを知っていますが、私が取り組んでいるプロジェクトのためにこれを解決し、自分の解決策を共有したいと思いました。

まず、ソリューションをより再利用しやすくするために、タイムスタンププロパティを持つ基本クラスを作成しました。

public class EntityBase
{
    public DateTime? CreatedDate { get; set; }
    public DateTime? LastModifiedDate { get; set; }
}

次に、DbContextのSaveChangesメソッドをオーバーライドしました。

public class MyContext : DbContext
{
    public override int SaveChanges()
    {
        ObjectContext context = ((IObjectContextAdapter)this).ObjectContext;

        //Find all Entities that are Added/Modified that inherit from my EntityBase
        IEnumerable<ObjectStateEntry> objectStateEntries =
            from e in context.ObjectStateManager.GetObjectStateEntries(EntityState.Added | EntityState.Modified)
            where
                e.IsRelationship == false &&
                e.Entity != null &&
                typeof(EntityBase).IsAssignableFrom(e.Entity.GetType())
            select e;

        var currentTime = DateTime.Now;

        foreach (var entry in objectStateEntries)
        {
            var entityBase = entry.Entity as EntityBase;

            if (entry.State == EntityState.Added)
            {
                entityBase.CreatedDate = currentTime;
            }

            entityBase.LastModifiedDate = currentTime;
        }

        return base.SaveChanges();
    }
}
53
Nick

また、遅い解決策を提案したいと思います。これは.NET Framework 4にのみ適用できますが、この種のタスクは簡単です。

var vs = oceContext.ObjectStateManager.GetObjectStateEntries(EntityState.Modified);
foreach (var v in vs)
{
    dynamic dv = v.Entity;
    dv.DateLastEdit = DateTime.Now;
}
6
chinupson

ここは以前の応答の編集されたバージョンです。以前のものは私のために更新のために動作しませんでした。

public override int SaveChanges()
    {
        var objectStateEntries = ChangeTracker.Entries()
            .Where(e => e.Entity is TrackedEntityBase && (e.State == EntityState.Modified || e.State == EntityState.Added)).ToList();
        var currentTime = DateTime.UtcNow;
        foreach (var entry in objectStateEntries)
        {
            var entityBase = entry.Entity as TrackedEntityBase;
            if (entityBase == null) continue;
            if (entry.State == EntityState.Added)
            {
                entityBase.CreatedDate = currentTime;
            }
            entityBase.LastModifiedDate = currentTime;
        }

        return base.SaveChanges();
    }
6
serg.salo

私のデータベースの最初の部分クラスからそれを拡張する基本エンティティを追加する必要がありました(これは理想的ではありません)

    public override int SaveChanges()
    {
        AddTimestamps();
        return base.SaveChanges();
    }

    public override async Task<int> SaveChangesAsync()
    {
        AddTimestamps();
        return await base.SaveChangesAsync();
    }

    private void AddTimestamps()
    {
        //var entities = ChangeTracker.Entries().Where(x => x.Entity is BaseEntity && (x.State == EntityState.Added || x.State == EntityState.Modified));

        //ObjectiveContext context = ((IObjectContextAdapter)this).ObjectContext;

        var entities = ChangeTracker.Entries().Where(e => e.Entity is BaseEntity && (e.State == EntityState.Modified || e.State == EntityState.Added)).ToList();

        var currentUsername = !string.IsNullOrEmpty(System.Web.HttpContext.Current?.User?.Identity?.Name)
            ? HttpContext.Current.User.Identity.Name
            : "Anonymous";

        foreach (var entity in entities)
        {
            if (entity.State == EntityState.Added)
            {
                ((BaseEntity)entity.Entity).CREATEDON = DateTime.UtcNow;
                ((BaseEntity)entity.Entity).CREATEDBY = currentUsername;
            }

            ((BaseEntity)entity.Entity).MODIFIEDON = DateTime.UtcNow;
            ((BaseEntity)entity.Entity).MODIFIEDBY = currentUsername;
        }
    }
1
cagedwhale

次の2つのオプションがあります。

  • すべてのビジネスエンティティクラスの基本クラスがあり、

    _poco.LastModified = DateTime.Now;
    _

    他のすべての人が呼び出す必要がある仮想.Save()メソッド内

  • データベースでトリガーを使用する

これを達成するための合理的に安全で簡単な方法は他にないと思います。

1
marc_s

これを実現するために、部分クラスを使用してSaveChangesメソッドをオーバーライドできます。

using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.Entity.Core.Objects;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web;


namespace TestDatamodel
{
    public partial class DiagnosisPrescriptionManagementEntities
    {
        public override int SaveChanges()
        {
            ObjectContext context = ((IObjectContextAdapter)this).ObjectContext;

            foreach (ObjectStateEntry entry in
                     (context.ObjectStateManager
                       .GetObjectStateEntries(EntityState.Added | EntityState.Modified)))
            {                    
                if (!entry.IsRelationship)
                {
                    CurrentValueRecord entryValues = entry.CurrentValues;
                    if (entryValues.GetOrdinal("ModifiedBy") > 0)
                    {
                        HttpContext currentContext = HttpContext.Current;
                        string userId = "nazrul";
                        DateTime now = DateTime.Now;

                        if (currContext.User.Identity.IsAuthenticated)
                        {
                            if (currentContext .Session["userId"] != null)
                            {
                                userId = (string)currentContext .Session["userId"];
                            }
                            else
                            {                                    
                                userId = UserAuthentication.GetUserId(currentContext .User.Identity.UserCode);
                            }
                        }

                        if (entry.State == EntityState.Modified)
                        {
                           entryValues.SetString(entryValues.GetOrdinal("ModifiedBy"), userId);
                           entryValues.SetDateTime(entryValues.GetOrdinal("ModifiedDate"), now);
                        }

                        if (entry.State == EntityState.Added)
                        {
                            entryValues.SetString(entryValues.GetOrdinal("CreatedBy"), userId);
                            entryValues.SetDateTime(entryValues.GetOrdinal("CreatedDate"), now);
                        }
                    }
                }
            }

            return base.SaveChanges();
        }
    }
}
0