web-dev-qa-db-ja.com

エンティティフレームワークのキャッシュの問題

Entity Frameworkは初めてです。

EFを使用して、データベース内のいくつかの値を取得しました。それは完全に戻り、値はラベルに表示されます。しかし、テーブル内のすべての値を(EFを使用せずに)削除すると、EFクエリは古い値を返します。 EFがキャッシュに値を保存し、その後の実行のためにキャッシュされたデータを返すことを知っています。これは正しいです?

データベースのすべての値を削除したが、EFが古い値を返す場合、どうすれば問題を解決できますか?

編集

今、私はdatamodel.SaveChanges()を使用しました。しかし、今では同じ古い値を返しています。

私のサンプルクエリは次のようになります。

SchoolBriefcaseEntities datamodel = new SchoolBriefcaseEntities();
datamodel.SaveChanges();
List<Compliance> compliance=new List<Compliance>();
IList<ComplianceModel> complianceModel;
if (HttpContext.Current.User.IsInRole("SuperAdmin"))
{
    compliance = datamodel.Compliances.Where(c => c.School.DistrictId == districtId).ToList();
}
49

EFの外部で変更が発生したことがわかっていて、特定のエンティティのctxtを更新する場合は、ObjectContext.Refreshを呼び出すことができます

datamodel.Refresh(RefreshMode.StoreWins, orders);

これが一般的な出来事であると思われる場合は、クエリでオブジェクトのキャッシュを無効にする必要があります。

SchoolBriefcaseEntities datamodel = new SchoolBriefcaseEntities();
datamodel.tblCities.MergeOption = MergeOption.NoTracking; 

または、特定のエンティティのオブジェクトレベルのキャッシュをオフにするには、

Context.Set<Compliances>().AsNoTracking();
36
Dave Alperovich

EFを使用すると、デフォルトで各エンティティがコンテキストごとに1回だけロードされます。最初のクエリは、エンティティインスタンスを作成し、内部に保存します。同じキーを持つエンティティを必要とする後続のクエリは、この格納されたインスタンスを返します。データストアの値が変更された場合でも、最初のクエリからの値を含むエンティティを受け取ります

慎重な答え:

https://stackoverflow.com/a/3653392/1863179

28
Morteza

コンテキストを再度クエリしない限り、EFは変更をロードしません。 EFはdbをクエリし、それらをオブジェクトにマップし、データベースではなくオブジェクトで実行した変更を監視します。 EFはデータベースに直接加えられた変更を追跡せず、追跡しません。

リストをロードしました。そのリストはメモリ内のキャッシュです。 [変更の保存]を呼び出しても更新されません。コンテキストをもう一度クエリする必要があります。つまり、新しいリストを作成します。

変更を確認するには、次の行をもう一度実行する必要があります。

datamodel.Compliances.Where(c => c.School.DistrictId == districtId).ToList()
20
Akash Kava

ここで他のソリューションのいくつかに従う必要があると思いますが、キャッシュをクリアしたいようです。これを実現するには、次を実行します。

var count = datamodel.Compliances.Local.Count; // number of items in cache (ex. 30)

datamodel.Compliances.Local.ToList().ForEach(c => {
    datamodel.Entry(c).State = EntityState.Detached;
});

count = datamodel.Compliances.Local.Count; // 0
7
David Sherret

以下のコードは、オブジェクトを新しいデータベース値で更新するのに役立ちました。 Entry(object).Reload()コマンドは、オブジェクトにデータベース値をリコールさせます

GM_MEMBERS member = DatabaseObjectContext.GM_MEMBERS.FirstOrDefault(p => p.Username == username && p.ApplicationName == this.ApplicationName);
DatabaseObjectContext.Entry(member).Reload();
4
HGMamaci

次のように、コンテキストを作成した後、すべてのEntitieSetに対してMergeOptionを使用することをお勧めします。

var objSetProps = ctx.GetType().GetProperties().Where(prop => prop.PropertyType.IsGenericType && prop.PropertyType.GetGenericTypeDefinition() == typeof(ObjectSet<>));
foreach (PropertyInfo objSetProp in objSetProps)
{
    ObjectQuery objSet = (ObjectQuery)objSetProp.GetValue(ctx, BindingFlags.GetProperty, null, null, null);
    objSet.MergeOption = MergeOption.PreserveChanges;
}

MergeOptionについては、こちらをご覧ください。 http://msdn.Microsoft.com/en-us/library/system.data.objects.mergeoption.aspx NoTrackingを使用すると思います。

ただし、「キャッシュされた」エンティティをクリアして、デタッチします。

var entidades = Ctx.ObjectStateManager.GetObjectStateEntries(EntityState.Added | EntityState.Deleted | EntityState.Modified | EntityState.Unchanged);
foreach (var objectStateEntry in entidades)
    Ctx.Detach(objectStateEntry.Entity);

Ctxは私のコンテキストです。

4
Tiago Gouvêa

必要なのはGetDatabaseValues()だと思います。次のように使用されます。

context.Entry(/*your entry*/).GetDatabaseValues();

以下の情報は msdn からのものです。

現在の値は、エンティティのプロパティに現在含まれている値です。元の値は、エンティティがクエリされたときにデータベースから読み取られた値です。データベース値は、現在データベースに格納されている値です。データベースの値を取得することは、データベースへの同時編集が別のユーザーによって行われた場合など、エンティティが照会されたためにデータベースの値が変更された可能性がある場合に役立ちます。

2
Adil Mammadov

ここでの根本的な問題は、DbContextが長すぎることです。 HttpContextを使用しているという事実から、Webアプリケーションがあること、および DbContextを使用する際の一般的なガイドライン include

Webアプリケーションを使用する場合、リクエストごとにコンテキストインスタンスを使用します。

MVCを使用している場合、次のようにコントローラーでDisposeパターンを使用できます。

public class EmployeeController : Controller
{
    private EmployeeContext _context;

    public EmployeeController()
    {
        _context = new EmployeeContext();
    }

    public ActionResult Index()
    {
        return View(_context.Employees.ToList());
    }

    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            _context.Dispose();
        }
        base.Dispose(disposing);
    }
}

しかし、あなたは本当に DbContextライフタイムを管理する への依存性注入を見るべきです。

2
Colin

まず、テストと開発のみを行う場合を除き、システム外部のデータベースを変更することはお勧めしません。

EF DbContextにはIDisposableインターフェイスが含まれています。キャッシュされたデータを解放するには、Dispose呼び出しを手動で行うか、データベースオブジェクトをusingブロック内に配置します。

        using (SchoolBriefcaseEntities datamodel = new SchoolBriefcaseEntities())
        {
            List<Compliance> compliance = new List<Compliance>();
            IList<ComplianceModel> complianceModel;
            if (HttpContext.Current.User.IsInRole("SuperAdmin"))
            {
                compliance = datamodel.Compliances.Where(c => c.School.DistrictId == districtId).ToList();
            }
        }

これにより、コンテキストがクリアされ、次回使用されるときに再作成されます。問題が発生している通話だけでなく、すべての通話に対してこれを行うようにしてください。

できることがいくつかあります。

  1. 新しいコンテキストを使用します。キャッシュされたエンティティはコンテキストに保存されます。新しいコンテキストを使用すると、キャッシュを使用できなくなります。
  2. グローバル/長期的なコンテキストが本当に必要な場合、2つのサブオプションがあります。a。)常にReloadメソッドを呼び出します。 db.Entry(entity).Reload()...これにより、コンテキストは強制的にそのエンティティをリロードします。 b。)SqlDependencyオブジェクトを使用して、レコードの変更を検出し、必要に応じてエンティティをリロードします。 https://code.msdn.Microsoft.com/How-to-use-SqlDependency-5c0da0b
1
Marc Johnston

私の場合、接続文字列が不良でした。エンティティは、コンテキストをローカルにし、最終的にエラーメッセージを表示するまで文句を言わなかったため、うまく機能しているように見えました。

0
Michael

EFは、コンテキストからデータを提供するfindメソッドとは異なる動作をします。他のクエリはdbからです。オブジェクトが既にコンテキストにある場合、既存のオブジェクトが返されます(エントリ内のオブジェクトのプロパティの現在および元の値は上書きされません)データベース値)。次の場合に、データベースに対してクエリが実行されます。

Microsoftリンク

Foreach(C#)またはFor Each(Visual Basic)ステートメントによって列挙されます。 ToArray、ToDictionary、ToListなどのコレクション操作によって列挙されます。 FirstやAnyなどのLINQ演算子は、クエリの最も外側の部分で指定されます。次のメソッドが呼び出されます:DbSet、DbEntityEntry.Reload、およびDatabase.ExecuteSqlCommandのLoad拡張メソッド。データベースから結果が返されると、コンテキストに存在しないオブジェクトがコンテキストに添付されます。オブジェクトがすでにコンテキストにある場合、既存のオブジェクトが返されます(エントリ内のオブジェクトのプロパティの現在および元の値は、データベース値で上書きされません)。

クエリを実行すると、コンテキストに追加されたが、まだデータベースに保存されていないエンティティは、結果セットの一部として返されません。コンテキストにあるデータを取得するには、ローカルデータを参照してください。

クエリがデータベースから行を返さない場合、結果はnullではなく空のコレクションになります。

0
Jin Thakur