Entity Framework: Re-finding objects recently added to context

前端 未结 7 672
感情败类
感情败类 2020-11-30 03:53

I am using the entity framework and I\'m having a problem with \"re-finding\" objects I just created... basically it goes like this:

string theId = \"someId         


        
相关标签:
7条回答
  • 2020-11-30 04:05

    Entity Framework 6

    As per EF Docs Dbset always query against the database.

    Note that DbSet and IDbSet always create queries against the database and will always involve a round trip to the database even if the entities returned already exist in the context. A query is executed against the database when:

    It is enumerated by a foreach (C#) or For Each (Visual Basic) statement. It is enumerated by a collection operation such as ToArray, ToDictionary, or ToList. LINQ operators such as First or Any are specified in the outermost part of the query. The following methods are called: the Load extension method on a DbSet, DbEntityEntry.Reload, and Database.ExecuteSqlCommand. When results are returned from the database, objects that do not exist in the context are attached to the context. If an object is already in the context, the existing object is returned (the current and original values of the object's properties in the entry are not overwritten with database values).

    When you perform a query, entities that have been added to the context but have not yet been saved to the database are not returned as part of the result set. To get the data that is in the context, see Local Data

    If a query returns no rows from the database, the result will be an empty collection, rather than null.

    Below is a simple snippet with local data:

    await dbContext.Entity
          .Where(e => e.Title.Contains("Text"))
          .LoadAsync();
    
    var locaEntities = dbContext.Entity.Local;
    
    dbContext.Entity.Add(new Entity {});
    
    // call save post atomic operation is finished.
    await dbContext.SaveChangesAsync();
    
    0 讨论(0)
  • 2020-11-30 04:07

    I was in the same situation. I wrote this extension method that at least for me solves the problem (I don't have issues with i.e conflicts in my context...)

        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);
        }
    
    0 讨论(0)
  • 2020-11-30 04:09

    The newly added object is in the local DataSource, since it's not persisted yet in the database,

    so you may say:

    EntityObject search = ents.EntityObject.FirstOrDefault(o => o.Id == theId) ??
                          ents.EntityObject.Local.FirstOrDefault(o => o.Id == theId);
    
    0 讨论(0)
  • 2020-11-30 04:10

    The extension method bellow is to 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;
    }
    

    How to use:

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

    btw: I love generics!

    0 讨论(0)
  • 2020-11-30 04:12

    This happens because ents.EntityObject.WhatEver always queries the datasource. This is a design decision. They do it this way, because else they would have to execute the query against the datasource, against the local cache and then merge the results. As one of the developers pointed out in a blog (cannot remember where exactly) they were unable to handle this consistently.

    As you can imagine there are a lot of corner an edge cases you have to handle properly. You could just find a id you created locally, created by someone else in the database. This would force you to be prepared to handle conflicts on (almost) every query. Maybe they could have made methods to query the local cache and methods to query the datasource, but that is not to smart, too.

    You may have a look at Transparent Lazy Loading for Entity Framework. This replaces the normal code generator and you get entities that populate their related entity collections and entity references automatically on access. This avoids all the

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

    code fragments. And you can query the collections because they are always implicitly loaded. But this solution is not perfect, too. There are some issues. For example, if you create a new entity and access a collection of related entities, you will get an exception because the code is unable to retrieve the related entities from the database. There is also an issue concerning data binding and may be some more I am not aware of.

    The good thing is that you get the source code and are able to fix the issues yourself and I am going to examine the first issue if I find some time. But I am quite sure that it will not be that easy to fix, because I expect some case were just not hitting the database if the entity has just been created is not the expected behavior.

    0 讨论(0)
  • 2020-11-30 04:15

    I have recently struggled with this same question. I'm posting this answer 2 years after the question was asked in hopes that this bit of code may help someone searching for an answer.

    I have basically implemented an extension method (as suggested by Alex James) called "Find" that operates in the same way that "Where" does, but "Find" also checks the ObjectContext to see if there are any Added entities that satisfy the given predicate. This allows you to find an entity even if it hasn't been saved to the database yet.

    Find returns an IQueryable(of T) so that you can use it just like any other LINQ operator.

    <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
    
    0 讨论(0)
提交回复
热议问题