web-dev-qa-db-ja.com

AsNoTracking()のグローバル設定?

もともと私は

_context.Configuration.AutoDetectChangesEnabled = false;
_

変更の追跡を無効にします。しかし、違います。現在、すべてのLINQクエリでAsNoTracking()を使用する必要があります(読み取り専用レイヤー用)。 DbContextで追跡を無効にするグローバル設定はありますか?

50
Vindberg

この質問には特定のEFバージョンがタグ付けされていないため、EF Coreで動作を コンテキストで設定可能)レベル

コンテキストインスタンスレベルでデフォルトの追跡動作を変更することもできます。

using (var context = new BloggingContext())
{
    context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;

    var blogs = context.Blogs.ToList();
}
19
Amr Elgarhy

派生コンテキストでこのようなメソッドを単純に公開し、クエリに使用するとどうなりますか?

public IQueryable<T> GetQuery<T>() where T : class {
    return this.Set<T>().AsNoTracking();
}

AsNoTrackingをグローバルに設定することはできません。クエリごと、またはObjectSetDbSetではなく)ごとに設定する必要があります。後者のアプローチでは、ObjectContext AP​​Iを使用する必要があります。

var objectContext = ((IObjectContextAdapter)dbContext).ObjectContext;
var set = objectContext.CreateObjectSet<T>();
set.MergeOption = MergeOption.NoTracking;
// And use set for queries
33
Ladislav Mrnka

DbContextで次のようなことができます。

public void ObjectContext_OnObjectMaterialized(Object objSender, ObjectMaterializedEventArgs e)
{
    Entry(e.Entity).State = EntityState.Detached;
}

オブジェクトがコンテキストによって具体化されるたびに、オブジェクトは切り離され、追跡されなくなります。

2
Gabriel G. Roy

更新:これは実際には機能しませんでした。コメントをご覧ください!

StackOverflowで検索すると嫌いで、答えは「できません!」です。または「できますが、これまでに行ったすべての通話を完全に変更した場合のみです。」

誰でも反射?これがDbContext設定になることを望んでいました。しかし、そうではないので、リフレクションを使用して作成しました。

この便利な小さなメソッドは、DbSet型のすべてのプロパティにAsNoTrackingを設定します。

    private void GloballySetAsNoTracking()
    {
        var dbSetProperties = GetType().GetProperties();
        foreach (PropertyInfo pi in dbSetProperties)
        {
            var obj = pi.GetValue(this, null);
            if (obj.GetType().IsGenericType && obj.GetType().GetGenericTypeDefinition() == typeof(DbSet<>))
            {
                var mi = obj.GetType().GetMethod("AsNoTracking");
                mi.Invoke(obj, null);
            }
        }
    }

オーバーロードされたDbContextコンストラクターに追加します。

    public ActivationDbContext(bool proxyCreationEnabled, bool lazyLoadingEnabled = true, bool asNoTracking = true)
    {
        Configuration.ProxyCreationEnabled = proxyCreationEnabled;
        Configuration.LazyLoadingEnabled = lazyLoadingEnabled;
        if (asNoTracking)
            GloballySetAsNoTracking();
    }

これはリフレクションを使用します。つまり、誰かがこれがパフォーマンスヒットであることをすばやくコメントします。しかし、それは本当に大ヒットですか?ユースケースに依存します。

1
Rhyous

私の場合、コンテキスト全体を読み取り/書き込みではなく読み取り専用にする必要がありました。

そのため、ttファイルを変更し、DbContextのすべてのプロパティを変更してDbSetではなくDbQueryを返し、すべてのプロパティからセットを削除し、取得のためにModel.AsNoTracking()を返しました。

例えば:

public virtual DbQuery<Campaign> Campaigns { get{ return Set<Campaign>().AsNoTracking();} }

Ttテンプレートでこれを行った方法は次のとおりです。

public string DbQuery(EntitySet entitySet)
    {
        return string.Format(
            CultureInfo.InvariantCulture,
            "{0} virtual DbQuery<{1}> {2} {{ get{{ return Set<{1}>().AsNoTracking();}} }}",
            Accessibility.ForReadOnlyProperty(entitySet),
            _typeMapper.GetTypeName(entitySet.ElementType),
            _code.Escape(entitySet));
    }
0
Ahmed IG