Подтвердить что ты не робот

Entity Framework: переименование объектов, недавно добавленных в контекст

Я использую структуру сущности, и у меня возникла проблема с "повторным обнаружением" объектов, которые я только что создал... в основном это выглядит следующим образом:

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

(нет гарантии, что код работает btw - все это с моей головы)

Почему запрос "найти" EntityObject не был создан?

Если я вызову SaveChanges() после AddToEntityObject, он работает (что меня не удивляет), но почему он не извлекает из кэша должным образом?

Я все еще зеленый на этом материале, поэтому я надеюсь, что там действительно очень просто, что я просто не замечаю...

Спасибо

4b9b3361

Ответ 1

Это происходит потому, что ents.EntityObject.What всегда запрашивает источник данных. Это дизайнерское решение. Они делают это так, потому что иначе им придется выполнить запрос к источнику данных, к локальному кешу и затем объединить результаты. Как заметил один из разработчиков в блоге (не помню, где именно), они не смогли обработать это последовательно.

Как вы можете себе представить, есть много углов, которые вы должны обрабатывать должным образом. Вы можете просто найти идентификатор, созданный локально, созданный кем-то еще в базе данных. Это заставит вас быть готовыми к разрешению конфликтов (почти) каждого запроса. Возможно, они могли бы заставить методы запрашивать локальный кеш и методы для запроса источника данных, но это тоже не разумно.

Вы можете взглянуть на Прозрачная ленивая загрузка для платформы Entity Framework. Это заменяет генератор нормального кода, и вы получаете объекты, которые автоматически заполняют связанные с ними сущности и ссылки на объекты. Это позволяет избежать всех

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

фрагменты кода. И вы можете запрашивать коллекции, потому что они всегда неявно загружаются. Но это решение тоже не идеально. Есть некоторые проблемы. Например, если вы создаете новый объект и получаете доступ к коллекции связанных объектов, вы получите исключение, потому что код не может получить связанные объекты из базы данных. Существует также проблема, связанная с привязкой данных, и может быть еще более я не знаю.

Хорошо, что вы получаете исходный код и можете сами исправить проблемы, и я рассмотрю первую проблему, если найду какое-то время. Но я совершенно уверен, что это будет не так просто исправить, потому что я ожидаю, что какой-то случай просто не ударил базу данных, если только что был создан объект, это не ожидаемое поведение.

Ответ 2

Я был в той же ситуации. Я написал этот метод расширения, по крайней мере, для меня решает проблему (у меня нет проблем с конфликтами в моем контексте...)

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

Ответ 3

вновь добавленный объект находится в локальном источнике данных, поскольку он еще не сохраняется в базе данных так что вы можете сказать   EntityObject search = ents.EntityObject.FirstOrDefault(o => o.Id == theId) ?? ents.EntityObject.Local.FirstOrDefault(o => o.Id == theId);

Ответ 4

Метод расширения ниже 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;
}

Как использовать:

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

btw: Мне нравятся дженерики!

Ответ 5

Я недавно боролся с этим же вопросом. Я отправляю этот ответ через 2 года после того, как был задан вопрос в надежде, что этот бит кода может помочь кому-то найти ответ.

Я в основном реализовал метод расширения (как предложил Алекс Джеймс) под названием "Найти", который работает так же, как "Где", но "Найти" также проверяет ObjectContext, чтобы увидеть, есть ли какие-либо добавленные объекты, которые удовлетворяют заданному предикату. Это позволяет вам найти объект, даже если он еще не был сохранен в базе данных.

Найти возвращает IQueryable (из T), чтобы вы могли использовать его, как и любой другой оператор LINQ.

<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

Ответ 6

У вас есть несколько вариантов. Вы можете расширить ObjectContext другим частичным классом, чтобы создать собственный механизм для извлечения недавно добавленной информации.

Или вы можете просто поместить метод расширения в ObjectContext, который просматривает ObjectContext.ObjectStateManager, ища "добавленный" ObjectStateEntries, а затем используйте LINQ to Objects, чтобы найти то, что вы ищете.