wake-up-neo.com

Caching-Problem beim Entity Framework

Ich bin neu im Entity Framework.

Ich habe einige Werte in meiner Datenbank mit EF erhalten. Es wird perfekt zurückgegeben und die Werte werden in Beschriftungen angezeigt. Wenn ich jedoch alle Werte in meiner Tabelle lösche (ohne EF), gibt die EF-Abfrage meine alten Werte zurück. Ich weiß, dass die EF die Werte im Cache speichert und die zwischengespeicherten Daten für nachfolgende Läufe zurückgibt. Ist das richtig? 

Wie kann ich also das Problem lösen, wenn ich alle Werte in meiner Datenbank gelöscht habe, aber EF die alten Werte zurückgibt?

Bearbeiten :

Jetzt habe ich datamodel.SaveChanges() verwendet. Aber jetzt gibt es auch die gleichen alten Werte zurück.

Meine Beispielabfrage sieht wie folgt aus:

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();
}
47

Wenn Sie wissen, dass Änderungen außerhalb von EF stattfanden und Ihren ctxt für eine bestimmte Entität aktualisieren möchten, können Sie ObjectContext.Refresh

datamodel.Refresh(RefreshMode.StoreWins, orders);

Wenn dies so aussieht, als würde es häufig vorkommen, sollten Sie das Objekt-Caching in Ihren Abfragen deaktivieren:

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

oder für das Deaktivieren der Zwischenspeicherung auf Objektebene für eine bestimmte Entität,

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

Wenn Sie EF verwenden, lädt es standardmäßig jede Entität nur einmal pro Kontext. Die erste Abfrage erstellt ein Entitätsinstace und speichert es intern. Irgendein eine nachfolgende Abfrage, die eine Entität mit demselben Schlüssel erfordert, gibt diese .__ zurück. gespeicherte Instanz. Wenn sich die Werte im Datenspeicher geändert haben, erhalten Sie immer noch die Entität mit Werten aus der ursprünglichen Abfrage

Eine vorsichtige Antwort:

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

27
Morteza

EF lädt Änderungen nur, wenn Sie den Kontext erneut abfragen. EF fragt db ab und lädt sie in Objekte, sie überwacht Änderungen, die Sie an Objekten und nicht an der Datenbank vornehmen. EF verfolgt keine Änderungen, die direkt an der Datenbank vorgenommen wurden, und verfolgt sie niemals.

Sie haben eine Liste geladen, diese Liste ist Ihr Cache-Speicher. Auch das Aufrufen von Save Changes wird nicht aktualisiert. Sie müssen den Kontext erneut abfragen, dh eine neue Liste erstellen.

Um Änderungen zu sehen, müssen Sie die folgende Zeile noch einmal ausführen,

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

Ich denke, Sie sollten hier einige der anderen Lösungen befolgen, aber es scheint, als wollten Sie den Cache leeren. Dies können Sie folgendermaßen erreichen:

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

Mit dem folgenden Code konnte mein Objekt mit neuen Datenbankwerten aktualisiert werden. Der Befehl Entry (object) .Reload () zwingt das Objekt, Datenbankwerte abzurufen

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

Ich empfehle Ihnen, nach dem Erstellen des Kontexts einige MergeOption für alle EntitieSet zu verwenden:

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;
}

Lesen Sie die MergeOption hier: http://msdn.Microsoft.com/de-de/library/system.data.objects.mergeoption.aspx Sie werden NoTracking verwenden, denke ich.

Aber du willst die "zwischengespeicherten" Entitäten CLEAR machen und sie trennen.

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

Wo Ctx mein Kontext ist.

4
Tiago Gouvêa

Ich denke, was Sie brauchen, ist GetDatabaseValues(). Es wird verwendet wie:

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

Die folgenden Informationen stammen von msdn :

Die aktuellen Werte sind die Werte, die die Eigenschaften der Entität derzeit enthalten. Die ursprünglichen Werte sind die Werte, die gelesen wurden aus der Datenbank, als die Entität abgefragt wurde. Die Datenbankwerte sind die Werte, wie sie aktuell in der Datenbank gespeichert sind. Die Datenbankwerte sind nützlich, wenn die Werte in der Datenbank möglicherweise __ haben. geändert, da die Entität abgefragt wurde, z. B. bei gleichzeitiger Bearbeitung von Die Datenbank wurde von einem anderen Benutzer erstellt.

2
Adil Mammadov

Ich vermute, dass das zugrunde liegende Problem hier ist, dass Ihre DbContext zu lange hängt. Ich kann der Tatsache entnehmen, dass Sie HttpContext verwenden, dass Sie über eine Webanwendung verfügen, und Allgemeine Richtlinien bei der Arbeit mit DbContext include 

Verwenden Sie beim Arbeiten mit Webanwendungen eine Kontextinstanz per anfordern.

Wenn Sie MVC verwenden, können Sie das Dispose-Muster in Ihrem Controller folgendermaßen verwenden:

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);
    }
}

Aber Sie sollten sich wirklich die Abhängigkeitsinjektion ansehen, um die DbContext-Lebensdauer zu verwalten .

2
Colin

Erstens würde ich nicht vorschlagen, die Datenbank außerhalb Ihres Systems zu ändern, es sei denn, Sie testen und entwickeln nur.

Der EF DbContext enthält eine IDisposable-Schnittstelle. Um freigegebene Daten freizugeben, führen Sie die Dispose-Aufrufe entweder manuell aus oder platzieren Sie Ihr Datenbankobjekt in einem Verwendungsblock.

        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();
            }
        }

Dadurch wird sichergestellt, dass der Kontext bei der nächsten Verwendung gelöscht und wiederhergestellt wird. Stellen Sie sicher, dass Sie dies für alle Ihre Anrufe tun, und nicht nur für den, mit dem Sie Probleme haben.

In meinem Fall war es eine schlechte Verbindungszeichenfolge. Entity sah so aus, als ob es großartig war, weil es sich nie beschwert hat, bis ich den Kontext lokal gemacht habe und es mir schließlich die Fehlermeldung gab.

0
Michael

EF arbeitet anders mit der find-Methode, die Daten aus dem Kontext liefert. Andere Abfragen stammen aus der Datenbank. Befindet sich ein Objekt bereits im Kontext, wird das vorhandene Objekt zurückgegeben (die aktuellen und ursprünglichen Werte der Objekteigenschaften im Eintrag werden nicht mit überschrieben Datenbankwerte). Eine Abfrage wird für die Datenbank ausgeführt, wenn:

Microsoft Link

Sie wird durch eine foreach-Anweisung (C #) oder eine For Each-Anweisung (Visual Basic) aufgezählt. Es wird durch eine Auflistungsoperation wie ToArray, ToDictionary oder ToList aufgezählt. LINQ-Operatoren wie First oder Any werden im äußersten Teil der Abfrage angegeben. Die folgenden Methoden werden aufgerufen: die Load-Erweiterungsmethode für ein DbSet, DbEntityEntry.Reload und Database.ExecuteSqlCommand. Wenn Ergebnisse aus der Datenbank zurückgegeben werden, werden Objekte, die nicht im Kontext vorhanden sind, an den Kontext angehängt. Befindet sich ein Objekt bereits im Kontext, wird das vorhandene Objekt zurückgegeben (die aktuellen und ursprünglichen Werte der Objekteigenschaften im Eintrag werden nicht mit Datenbankwerten überschrieben).

Wenn Sie eine Abfrage ausführen, werden Entitäten, die dem Kontext hinzugefügt, aber noch nicht in der Datenbank gespeichert wurden, nicht als Teil der Ergebnismenge zurückgegeben. Informationen zum Abrufen der im Kontext enthaltenen Daten finden Sie unter Lokale Daten.

Wenn eine Abfrage keine Zeilen aus der Datenbank zurückgibt, ist das Ergebnis keine Null, sondern eine leere Auflistung.

0
Jin Thakur

Paar Dinge, die Sie tun können. 

  1. Verwenden Sie einen neuen Kontext. Die zwischengespeicherten Entitäten werden im Kontext gespeichert. Durch die Verwendung eines neuen Kontexts kann der Cache nicht verwendet werden.
  2. Wenn Sie wirklich einen globalen/dauerhaften Kontext wünschen, haben Sie zwei Unteroptionen: A.) Rufen Sie immer die Reload-Methode auf. db.Entry (entity) .Reload () ... erzwingt, dass der Kontext diese Entität neu lädt . b.) verwendet ein SqlDependency-Objekt, um zu erkennen, wann Datensätze geändert werden, und lädt die Entitäten nach Bedarf. https://code.msdn.Microsoft.com/How-to-use-SqlDependency-5c0da0b3
0
Marc Johnston