Frage Entity Framework: Wiederfinden von Objekten, die kürzlich dem Kontext hinzugefügt wurden


Ich benutze das Entity-Framework und habe ein Problem mit "neu gefundenen" Objekten, die ich gerade erstellt habe ... im Grunde geht es so:

string theId = "someId";

private void Test()
{
  using(MyEntities entities = new MyEntities())
  {
    EntityObject o = new EntityObject();
    o.Id = theId;
    entities.AddToEntityObject(o);
    CallSomeOtherMethod(entities);
  }
}

void CallSomeOtherMethod(MyEntities ents)
{
  EntityObject search = ents.EntityObject.FirstOrDefault(o => o.Id == theId);
  if(search == null) 
  {
    Console.WriteLine("wha happened???");
  }
}

(keine Garantie der Code funktioniert übrigens - es ist alles von meinem Kopf)

Warum "findet" die Abfrage nicht das EntityObject, das gerade erstellt wurde?

Wenn ich SaveChanges () nach dem AddToEntityObject aufruft, funktioniert es (was mich nicht überrascht), aber warum zieht es nicht richtig aus dem Cache?

Ich bin immer noch grün auf diesem Zeug, also hoffe ich, dass es eine wirklich einfache Sache gibt, die ich gerade übersehen habe ...

Vielen Dank


33
2018-03-31 00:58


Ursprung


Antworten:


Dies geschieht, weil ents.EntityObject.WhatEver immer die Datenquelle abfragt. Dies ist eine Designentscheidung. Sie tun es auf diese Weise, weil sie andernfalls die Abfrage gegen die Datenquelle gegen den lokalen Cache ausführen und dann die Ergebnisse zusammenführen müssten. Wie einer der Entwickler in einem Blog aufzeigte (kann sich nicht genau erinnern, wo genau), waren sie nicht in der Lage, dies konsistent zu handhaben.

Wie Sie sich vorstellen können, gibt es viele Ecken und Kanten, die Sie richtig handhaben müssen. Sie können nur eine ID finden, die Sie lokal erstellt haben und die von einer anderen Person in der Datenbank erstellt wurde. Dies würde Sie zwingen, bereit zu sein, Konflikte bei (fast) jeder Anfrage zu behandeln. Vielleicht hätten sie Methoden erstellen können, um den lokalen Cache abzufragen, und Methoden, um die Datenquelle abzufragen, aber das ist auch nicht schlau.

Sie können sich das ansehen Transparentes Lazy Loading für Entity Framework. Dies ersetzt den normalen Codegenerator und Sie erhalten Entitäten, die automatisch ihre zugehörigen Entitätssammlungen und Entitätsreferenzen beim Zugriff auffüllen. Dies vermeidet alle

if (!Entity.ReleatedEntities.IsLoaded)
{
   Entity.RelatedEntities.Load();
}

Codefragmente. Und Sie können die Sammlungen abfragen, da sie immer implizit geladen werden. Aber diese Lösung ist auch nicht perfekt. Es gibt einige Probleme. Wenn Sie beispielsweise eine neue Entität erstellen und auf eine Auflistung verwandter Entitäten zugreifen, erhalten Sie eine Ausnahme, da der Code die zugehörigen Entitäten nicht aus der Datenbank abrufen kann. Es gibt auch ein Problem in Bezug auf die Datenbindung und vielleicht sind mir noch einige nicht bekannt.

Die gute Sache ist, dass Sie den Quellcode bekommen und in der Lage sind, die Probleme selbst zu beheben, und ich werde das erste Problem untersuchen, wenn ich etwas Zeit finde. Aber ich bin mir ziemlich sicher, dass es nicht so einfach sein wird, das zu beheben, weil ich erwarte, dass einige Fälle die Datenbank nicht treffen, wenn die Entität gerade erstellt wurde, ist nicht das erwartete Verhalten.


18
2018-03-31 01:28



Das neu hinzugefügte Objekt befindet sich in der lokalen Datenquelle, da es noch nicht in der Datenbank gespeichert ist so kannst du sagen      EntityObject search = ents.EntityObject.FirstOrDefault(o => o.Id == theId) ?? ents.EntityObject.Local.FirstOrDefault(o => o.Id == theId);


18
2018-06-27 12:42



Ich war in der gleichen Situation. Ich habe diese Erweiterungsmethode geschrieben, die zumindest für mich das Problem löst (ich habe keine Probleme mit d. H. Konflikten in meinem Kontext ...)

    public static IEnumerable<T> WhereInclAdded<T>(this ObjectSet<T> set, Expression<Func<T, bool>> predicate)  where T : class
    {
        var dbResult = set.Where(predicate);

        var offlineResult = set.Context.ObjectStateManager.GetObjectStateEntries(EntityState.Added).Select(entry => entry.Entity).OfType<T>().Where(predicate.Compile());

        return offlineResult.Union(dbResult);
    }

17
2017-11-30 09:52



Die folgende Erweiterungsmethode lautet DbSet <>

public static T TryAttach<T>(this DbSet<T> dbSet, T entity, Expression<Func<T, bool>> predicate) where T : class
{
     T found = dbSet.Local.SingleOrDefault(predicate.Compile());
     if (found == null) dbSet.Attach(entity);
     return found ?? entity;
}

Wie benutzt man:

contextInstance.MyEntity.TryAttach(entityInstance, e => e.ID == entityInstance.ID);

Übrigens: Ich liebe Generika!


4
2017-07-12 15:09



Ich habe kürzlich mit dieser Frage gekämpft. Ich schreibe diese Antwort 2 Jahre nach der Frage, in der Hoffnung, dass dieser Code den Suchenden helfen kann.

Ich habe im Grunde eine Erweiterung Methode implementiert (wie von Alex James vorgeschlagen) namens "Find", die auf die gleiche Weise wie "Wo" funktioniert, aber "Find" überprüft auch den ObjectContext, um zu sehen, ob es hinzugefügte Entitäten gibt, die das gegebene erfüllen Prädikat. Dadurch können Sie eine Entität finden, auch wenn sie noch nicht in der Datenbank gespeichert wurde.

Find gibt ein IQueryable (von T) zurück, so dass Sie es wie jeden anderen LINQ-Operator verwenden können.

<Extension()>
Public Function Find(Of T As Class)(ByVal OSet As ObjectSet(Of T), _
       ByVal predicate As Expression(Of Func(Of T, Boolean))) _
       As System.Linq.IQueryable(Of T)

    'Check the object context for Added objects first.
    Dim AddedContextObjects = OSet.Context.ObjectStateManager _
                        .GetObjectStateEntries(EntityState.Added) _
                        .Select(Function(entity) entity.Entity).OfType(Of T)()


    Dim Cpredicate = predicate.Compile
    Dim MatchingObjects As New List(Of T)

    For Each TObj As T In AddedContextObjects
        If Cpredicate.Invoke(TObj) Then
            MatchingObjects.Add(TObj)
        End If
    Next

    'Now include a query to retrieve objects from the DB.
    Dim DBObjects = OSet.Where(predicate)

    If MatchingObjects.Count > 0 Then
        'We found some added objects in the context.
        'We want to return these objects as well as any Objects in DB
        'that satisfy the predicate.
        Return MatchingObjects.Union(DBObjects).AsQueryable
    Else
        'We didn't find any added objects in the context,
        'so we just return the DB query.
        Return DBObjects
    End If

End Function

2
2018-02-03 17:01



Sie haben eine Reihe von Optionen. Du könntest das verlängern ObjectContext mit einer anderen partiellen Klasse, um Ihren eigenen Mechanismus zum Abrufen von kürzlich hinzugefügten Informationen zu machen.

Oder Sie könnten einfach eine Erweiterungsmethode auf die ObjectContext das sieht durch die ObjectContext.ObjectStateManager Suche nach "hinzugefügt" ObjectStateEntries, und verwenden Sie LINQ to Objects, um zu finden, wonach Sie suchen.


1
2018-05-08 06:55