web-dev-qa-db-ja.com

MVCおよびEntity Frameworkを使用した監査ログ/変更履歴の実装

Entity Frameworkを使用しているMVCアプリへの変更履歴/監査ログを作成しています。

具体的には、編集メソッドpublic ActionResult Edit(ViewModel vm)で、更新しようとしているオブジェクトを見つけ、TryUpdateModel(object)を使用して、フォームから値を、目的のオブジェクトに転置します。更新。

そのオブジェクトのフィールドが変更されたときに、変更をログに記録したいと思います。したがって、基本的に必要なのは、編集する前にオブジェクトのコピーを作成し、TryUpdateModel(object)が処理を行った後にオブジェクトを比較することです。つまり.

_[HttpPost]
public ActionResult Edit(ViewModel vm)
{
    //Need to take the copy here
    var object = EntityFramework.Object.Single(x=>x.ID = vm.ID);

    if (ModelState.IsValid)
    {
        //Form the un edited view model
        var uneditedVM = BuildViewModel(vm.ID); //this line seems to confuse the EntityFramework (BuildViewModel() is used to build the model when originally displaying the form)
        //Compare with old view model
        WriteChanges(uneditedVM, vm);
        ...
        TryUpdateModel(object);
    }
    ...
}
_

しかし、問題は、コードが「未編集のvm」を取得するときです。これにより、EntityFrameworkで予期しない変更が発生するため、TryUpdateModel(object);UpdateExceptionをスローします。

だから質問です-この状況では-EntityFrameworkの外部でobjectのコピーを作成して、変更/監査履歴を比較して、 EntityFrameworkで

編集:トリガーを使用したくない。それをしたユーザー名を記録する必要があります。

edit1:EFv4を使用して、SaveChanges()をオーバーライドする方法についてはあまりよくわかりませんが、オプションかもしれません


この単純な要件のため、このルートはどこにも行かないようです!ようやく適切にオーバーライドできるようになりましたが、そのコードで例外が発生しました。

_public partial class Entities
{
    public override int SaveChanges(SaveOptions options)
    {
        DetectChanges();
        var modifiedEntities = ObjectStateManager.GetObjectStateEntries(EntityState.Modified);
        foreach (var entry in modifiedEntities)
        {
            var modifiedProps = ObjectStateManager.GetObjectStateEntry(entry).GetModifiedProperties(); //This line throws exception The ObjectStateManager does not contain an ObjectStateEntry with a reference to an object of type 'System.Data.Objects.EntityEntry'.
            var currentValues = ObjectStateManager.GetObjectStateEntry(entry).CurrentValues;
            foreach (var propName in modifiedProps)
            {
                var newValue = currentValues[propName];
                //log changes
            }
        }

        //return base.SaveChanges();
        return base.SaveChanges(options);
    }
}
_
31
baron

EF 4を使用している場合は、SavingChangesイベントにサブスクライブできます。

Entitiesは部分クラスであるため、別のファイルに機能を追加できます。したがって、Entitiesという名前の新しいファイルを作成し、そこにイベントをフックする部分メソッドOnContextCreatedを実装します

public partial class Entities
{
    partial void OnContextCreated()
    {
        SavingChanges += OnSavingChanges;
    }

    void OnSavingChanges(object sender, EventArgs e)
    {

        var modifiedEntities = ObjectStateManager.GetObjectStateEntries(EntityState.Modified);
        foreach (var entry in modifiedEntities)
        {
            var modifiedProps = ObjectStateManager.GetObjectStateEntry(entry.EntityKey).GetModifiedProperties();
            var currentValues = ObjectStateManager.GetObjectStateEntry(entry.EntityKey).CurrentValues;
            foreach (var propName in modifiedProps)
            {
                var newValue = currentValues[propName];
                //log changes
            }
        }
    }
}

EF 4.1を使用している場合は、 この記事 を介して変更を抽出できます。

23
Eranga

FrameLog を参照してください。これは、この目的のために作成したEntity Frameworkロギングライブラリです。商用利用を含むオープンソースです。

これを行う方法を示すコードスニペットを表示する方がよいことはわかっていますが、関係の変更や多対多の変更など、ロギングのすべてのケースを適切に処理するには、コードが非常に大きくなります。ライブラリがあなたのニーズに合っていることを願っていますが、そうでない場合は自由にコードを調整できます。

FrameLogは、すべてのスカラープロパティとナビゲーションプロパティへの変更を記録できます。また、ログに関心のあるサブセットを指定することもできます。

15
Martin Eden

Codeprojectに高い評価の記事があります: Entity Frameworkを使用した監査証跡の実装 。それはあなたが望むことをするようです。プロジェクトでこのソリューションを使用し始めました。最初にデータベースのT-SQLでトリガーを作成しましたが、オブジェクトモデルの変更が常に発生するため、トリガーを維持するのは難しすぎました。

1