2

エンティティへの変更を追跡するための質問をすべて見てきましたが、それらのソリューションにはナビゲーション プロパティが含まれていませんでした。これは可能ですか?すべてをログに記録するだけの超汎用ソリューションは必要ありません。特定の「メイン」エンティティについてのみ追跡したい。また、監査ログは人間が判読できるカスタムのものになるため、ここの例で見たような oldData と newData を持つテーブルだけではありません。

ナビゲーション プロパティを追跡することを除いて、ほぼ良い点に到達しました。変更を追跡するかどうかを決定する POCO のサービスにインターフェイスがあり、entityService.Saveメソッドで次のチェックを行います。

var trackChangesService = this as ITrackChangesService<TClass>;
if (trackChangesService != null)
{
      TClass oldEntity = Repository.GetOldEntity(entity);
      trackChangesService.SaveChanges(entity, oldEntity);
}

変更の保存メソッドは、ITrackChanges インターフェイスが公開するものであり、エンティティごとにカスタム ロジックを持ちます (これには電子メール通知などが含まれます) が、問題は oldEntity を適切に取得できないことです。現在、メソッドは次のようになっています。

public TClass GetOldEntity(TClass entity)
    {
        //DbEntityEntry<TClass> oldEntry = Context.Entry(entity);
        ////oldEntry.CurrentValues.SetValues(oldEntry.OriginalValues);
        //var values = oldEntry.OriginalValues;
        //return (TClass)oldEntry.GetDatabaseValues().ToObject();

        return Context.Set<TClass>().AsNoTracking().FirstOrDefault(x => x.Id == entity.Id);
    }

私が遊んでいるコードを見ることができます。通常のプロパティの古い値と新しい値を正常に取得できますが、ナビゲーション プロパティは引き継がれません。しかし、この情報が必要です。たとえば、ユーザーを編集していて、ユーザーの編集画面でグループを削除および追加できるとします。保存ボタンをクリックすると、それらの更新が実行されます。

したがって、私の更新ビューは次のようになります。

private UserSaveModel Update(Guid id, UserSaveModel userSaveModel)
    {
        User user = _userService.GetOne(x => x.Id == id);
        user = _userService.ConvertFromSaveModel(userSaveModel, user);

        _userService.Save(user);

        return userSaveModel;
    }

問題を確認する 1 つの方法は、ナビゲーション プロパティを含め、既存のユーザー レコードのコンテキストでエンティティの値を調整している.ConvertFromSaveeModel()ため、save を呼び出すと、古いナビゲーション プロパティ データがなくなることです。

このワークフローを使用してエンティティの元のナビゲーション プロパティ情報を取得する方法はありますか? そうでない場合、これを可能にするためにワークフローを変更するにはどうすればよいですか? 最初にユーザー オブジェクトをクエリせずに最初から作成すると、viewModel で公開されていない可能性のある一部のデータが渡されないため、うまくいくかどうかわかりません。他の提案はありますか?変更を加える前に、エンティティを複製して元のエンティティとして使用することはできますか? それはナビゲーション プロパティを引き継いでいますか?

4

1 に答える 1

0

SaveChanges残念ながら、特に EF 7 (コア) バージョンの EF でナビゲーション プロパティの変更を検出する簡単な方法はありません。これこのMSDN の記事を確認してください。

したがって、関係エンティティがなく、エンティティが次のように関連付けられている場合:

public class User
{
    [Key]
    public int UserId { get; set; }
    public ICollection<Department> Departments { get; set; }
}

public class Department
{
    [Key]
    public int DepartmentId { get; set; }
    public ICollection<User> Users { get; set; }
}

ナビゲーション プロパティのみを変更します。たとえば、部門をユーザーに追加するとします。次のようにします。

using (var ctx = new MyContext())
{
    var user1 = ctx.Users.Include(u => u.Departments).First(u => u.Id == 1);
    var dept3 = ctx.Departments.First(d => d.Id == 3);

    user1.Departments.Add(dept3);

    ctx.SaveChanges();
}

エンティティの変更ではなく関係の変更を行ったため、ライブラリAudit.EntityFrameworkは変更を検出しません。

リレーションのエンティティ、つまりUserDepartmentクラスを作成することで、この問題を回避できます。したがって、関係への変更は、そのエンティティへの変更として表示されます。

これまでのところ、リレーション エンティティがない場合にリレーションシップの変更を取得する唯一の方法が見つかりましたしかし... EF 6でのみ機能するため、ライブラリには含めませんでした。

もう 1 つの回避策は、エンティティの別のプロパティ値 (つまり、LastUpdatedDate列) を変更するか、または の前にエンティティを Modified として明示的にマークするSaveChangesことです。これにより、ライブラリが変更を検出でき、少なくとも、エンティティが現在の状態で表示されます。ナビゲーション プロパティ。

例えば:

using (var ctx = new MyContext())
{
    var user1 = ctx.Users.Include(u => u.Departments).First(u => u.Id == 1);
    var dept3 = ctx.Departments.First(d => d.Id == 3);
    user1.Departments.Add(dept3);
    ctx.Entry(user1).State = EntityState.Modified; // <-- Here
    ctx.SaveChanges();
}

これで、ライブラリが変更を検出し、変更に関するデータが含まれます

出力 (JSON として) は次のようになります。

{
    "EventType": "MyContext",
    //...
    "EntityFrameworkEvent": {
        "Database": "Users",
        "Entries": [{
            "Table": "User",
            "Action": "Update",
            "PrimaryKey": {
                "UserId": 1
            },
            "Entity": {
                "UserId": 1,
                "Username": "federico",
                "Departments": [{
                    "DepartmentId": 3,
                    "DepartmentName": "Dept3",
                    "Users": []
                }]
            },
            "Changes": [],
            "ColumnValues": {
                "UserId": 1,
                "Username": "federico"
            },
            "Valid": true,
            "ValidationResults": []
        }],
        "Result": 1,
        "Success": true
    }
}

AuditDbContext から継承し、IncludeEntityObjects を true に設定するには、コンテキストが必要です。

[AuditDbContext(IncludeEntityObjects = true)]
public class MyContext : AuditDbContext
{
    public DbSet<User> Users { get; set; }
    public DbSet<Department> Departments { get; set; }
}

これがあなたの問題の解決策ではないことはわかっていますが、おそらく役立つでしょう。

于 2016-09-10T02:59:55.297 に答える