2015-11-12 3 views
0

Если вы запрашиваете DbSet DbContext, запрос действителен до тех пор, пока не будет удален DbContext. Ниже приведет к исключению:Entity Framework присоединяет запрос к новому DbContext

IQueryable<Video> allVideos = null; 
using (var context = new MyDbContext()) 
{ 
    allVideos = context.Videos; 
} 
var firstVideo = allVideos.first(); 

Видимо используется DbSet хранится где-то в возвращенном объекте, который реализует IQueryable.

Однако MSDN советует (Link)

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

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

Пример: Предположим, что в моей базе данных есть страны сбора, в которых есть города, у которых есть улицы, в которых есть дома, в которых есть семьи, у которых есть имена, которые имеют имена.

Если кто-то просит IQueryable, то может случиться так, что он хочет найти имя самого старого человека, живущего на Даунинг-стрит № 10 в Лондоне в Соединенном Королевстве.

Если бы я вернул последовательность с помощью ToList(), все города, улицы, дома, лица и т. Д. Были бы возвращены, что было бы довольно пустой тратой, если бы ему было нужно только имя этого человека. Это хорошая вещь о отложенном исполнении Linq.

Поэтому я не могу вернуть ToList(), я должен вернуть IQueryable.

Так что я хотел бы сделать, это открыть новый DbContext, и как-то сказать запрос, он должен использовать новый DbContext:

IQueryable<Video> allVideos = null; 
using (var context = new MyDbContext()) 
{ 
    allVideos = context.Videos; 
} 
// do something else 
using (var context = new MyDbContext()) 
{ 
    // here some code to attach the query to the new context 
    var firstVideo = allVideos.first(); 
} 

Как это сделать?

+0

Зачем это нужно, просто вызовите ToList() в первом контексте, чтобы заставить запрос выполнить – 3dd

+0

Так как @ 3dd предложил просто выполнить запрос. Помните, что ваш запрос - это просто выражение, которое удерживается контекстом. Он либо выполняется, либо отбрасывается. Хотя можно сохранить выражения и выполнить их позже, в этом случае вам действительно не нужно. –

ответ

0

Произошел мимо местный гуру. Он объяснил мне, что ошибка в моем дизайне заключалась в том, что я уже использую DbContext, пока я только составляю запрос. Мой интерфейс должен быть таким, что мне нужен только DbContext при фактическом материализации запрашиваемых объектов.

Вопрос был упрощенный вариант следующее:

У меня есть DbContext, с несколькими свойствами общественного DbSet.Эти свойства отражают фактическую базу данных. Я хочу скрыть фактическую реализацию базы данных в моем абстрактном слое базы данных для защиты моих данных. Я не хочу, чтобы кто-либо предоставлял доступ к моему изменению содержимого базы данных, не проверив, правильно ли это содержимое.

Это просто: просто не подвергайте свой фактический DbContext внешнему миру, а выставляйте фасад, который скрывает фактически используемый DbContext. Этот фасад связывается с фактическим DbContext.

С большинством функций, возвращающих IQueryable Мне нужен DbContext для доступа к DbSets. Вот почему я решил создать контекст, построить запрос и удалить контекст. Но из-за отсроченного исполнения контекст по-прежнему необходим.

Решение заключается не в создании собственного контекста, а в том, чтобы контекст был одним из параметров функции. В этом случае внешний пользователь может вызывать несколько функций моего фасада для конкатенации запроса, даже смешивать с запросами Linq в DbContext без создания и удаления контекста. Так, как и другие: - Создайте контекст - Вызовите несколько из моих функций, которые возвращают запрос - Выполните запрос с помощью ToList()/ToArray()/First()/Count() и т. Д. - Утилите контекст

Asn метода расширения

public static IQueryable<Video> GetObsoleteVideos(this MyDbContext context) 
{ 
    // perform several difficult Linq statements on context 
    return ... 
} 

использование:

using (var myContext = new MyDbContext()) 
{ 
    var difficultQuery = myContext.GetObsoleteVideos() 
     .Where(video => ....) 
     .GetBoxingVideos()  // another extension method 
     .Take(10); 
    // query still deferred 
    var result = difficultQuery.ToList() 
} 

Таким образом (и особенно, если я создаю интерфейсы) Я могу запретить доступ к моей DBSets. Я даже свободен для внутренней реорганизации моего Db и DbContext, но внешние пользователи ничего не замечают.

-1

Существуют методы в контексте объекта, чтобы сделать это:

var objectContext = ((IObjectContextAdapter)context).ObjectContext; 
objectContext.Detach(entity); 
objectContext.Attach(entity); 

Однако, как говорится в цитате из MSDN, вы должны использовать один экземпляр контекста EF на запрос. Это относится к HttpRequest не к одному запросу. Когда вы выполняете операции по одному запросу, вы не должны размещать блоки вокруг вашего контекста EF, и вы должны продлить его срок службы. Для новых запросов, желательно не держать состояние между запросами, а скорее следовать протоколу

  1. Query элемент снова и перезагрузить (другой запрос может изменить ее в то же время)
  2. Сделать модификации
  3. Сэкономьте
+0

Эти методы могут прикрепить только объект, а не выражение, которое ищет OP – 3dd

Смежные вопросы