もともと私はそれを信じていました
context.Configuration.AutoDetectChangesEnabled = false;
変更の追跡を無効にします。しかし、違います。現在AsNoTracking()
、すべてのLINQクエリで使用する必要があります(読み取り専用レイヤーの場合)。DbContextでの追跡を無効にするグローバル設定はありますか?
もともと私はそれを信じていました
context.Configuration.AutoDetectChangesEnabled = false;
変更の追跡を無効にします。しかし、違います。現在AsNoTracking()
、すべてのLINQクエリで使用する必要があります(読み取り専用レイヤーの場合)。DbContextでの追跡を無効にするグローバル設定はありますか?
この質問は特定のEFバージョンでタグ付けされていないため、 EFCoreでは動作をコンテキストレベルで構成できることを述べておきたいと思います。
コンテキストインスタンスレベルでデフォルトのトラッキング動作を変更することもできます。
using (var context = new BloggingContext())
{
context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
var blogs = context.Blogs.ToList();
}
派生コンテキストでこのようなメソッドを公開し、クエリに使用するのはどうでしょうか。
public IQueryable<T> GetQuery<T>() where T : class {
return this.Set<T>().AsNoTracking();
}
AsNoTracking
グローバルに設定することはできません。クエリごと、またはクエリごとに設定する必要がありますObjectSet
(ではありませんDbSet
)。後者のアプローチでは、ObjectContext
APIを使用する必要があります。
var objectContext = ((IObjectContextAdapter)dbContext).ObjectContext;
var set = objectContext.CreateObjectSet<T>();
set.MergeOption = MergeOption.NoTracking;
// And use set for queries
EntityFramework.Coreでは非常に簡単です。
この目的のために、UseQueryTrackingBehaviorメソッドを使用できます。
コードスニペットはここにあります:
services.AddDbContext<DatabaseContext>(options =>
{
options.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);
options.UseSqlServer(databaseSettings.DefaultConnection);
});
DbContextで次のようなことを行うことができます。
public void ObjectContext_OnObjectMaterialized(Object objSender, ObjectMaterializedEventArgs e)
{
Entry(e.Entity).State = EntityState.Detached;
}
オブジェクトがコンテキストによって具体化されるたびに、オブジェクトは切り離され、追跡されなくなります。
更新:これは実際には機能しませんでした。コメントをご覧ください!
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();
}
リフレクションを使用します。つまり、これはパフォーマンスの低下であると誰かがすぐにコメントします。しかし、それは本当に大ヒットですか?ユースケースによって異なります。
私の場合、コンテキスト全体を読み取り/書き込みではなく読み取り専用にする必要があったためです。
そこで、ttファイルに変更を加え、すべてのDbContextプロパティを変更してDbSetではなくDbQueryを返し、すべてのプロパティからセットを削除し、getsの場合は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));
}
Entity Frameworkコアを使用している場合は、DbContextを継承しているクラスのコンストラクターに次のコードを追加することもできます。
public NPCContext()
: base()
{
base.ChangeTracker.AutoDetectChangesEnabled = false;
}
または以下
public NPCContext(DbContextOptions<NPCContext> options)
: base(options)
{
base.ChangeTracker.AutoDetectChangesEnabled = false;
}