Правильный способ передачи объектов Entity между слоями?

Я только изучаю Entity Framework и добился хорошего прогресса в его включении в свою многоуровневую структуру кода. У меня есть 2 визуальных слоя, бизнес-уровень и уровень доступа к данным.

Моя проблема заключается в передаче объекта объекта между слоями. Этот пример кода не работает:

// BLL
public static void Test1()
{
 List<User> users = (from u in GetActiveUsers()
      where u.ID == 1
      select u).ToList<User>();

 // Do something with users
}

// DAL
public static IQueryable<User> GetActiveUsers()
{
 using (var context = new CSEntities())
 {
  return from u in context.Users
      where u.Employee.FirstName == "Tom"
      select u;
 }
}

Я получаю сообщение об ошибке Экземпляр ObjectContext удален и больше не может использоваться для операций, требующих подключения.

Если я удаляю using из метода GetActiveUsers, он работает нормально.

Я знаю, что это опасная практика, поскольку GC может в любой момент избавиться от контекста и испортить мой BLL.

Итак, как правильно передавать информацию между слоями? Нужно ли мне также передавать контекст?


person Scottie    schedule 04.11.2010    source источник


Ответы (1)


Поскольку вы возвращаете IQueryable из своего DAL, вы не можете использовать оператор using.

Запрос отложен до тех пор, пока не будет запущен запрос - .ToList в вашем BLL.

К тому времени контекст утилизируется.

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

Поскольку вы все еще изучаете EF, я бы упростил:

// BLL
public static void Test1()
{
 List<User> users = GetActiveUsers();
 var singleUser = users.SingleOrDefault(u => u.ID == 1);

 // Do something with users
}

// DAL
public static ICollection<User> GetActiveUsers()
{
 using (var context = new CSEntities())
 {
  var users = from u in context.Users
      where u.Employee.FirstName == "Tom"
      select u;
  return users.ToList();
 }
}

Если вы хотите получить одного пользователя, создайте другой метод:

// DAL
public static User GetSingleActiveUser(int userId)
{
 using (var context = new CSEntities())
 {
  var users = from u in context.Users
      where u.Employee.UserId == userId
      select u;
  return users.SingleOrDefault();
 }
}
person RPM1984    schedule 05.11.2010
comment
Спасибо за предложение. Однако если я преобразую свою сущность в список в DAL, не потеряю ли я все функциональные возможности, которые я получаю с EF? Например, если я хочу иметь возможность листать страницы в Silverlight, мне не обязательно загружать весь набор данных в список, верно? - person Scottie; 05.11.2010
comment
Вроде, как бы, что-то вроде. С помощью IQueryable вы можете «создавать» запросы позже (т. е. отложенное выполнение). Что дает вам большую силу. Я также использую IQueryable. Но, как я уже сказал, вы не можете использовать оператор «using» при использовании IQueryable. Вам нужно вручную открыть/закрыть контекст из другого места, я настоятельно рекомендую для этого контейнер IoC (например, StructureMap). В противном случае у вас останутся старые связи. - person RPM1984; 06.11.2010
comment
И нет, вы на самом деле не теряете функциональность, вы просто теряете возможность грубого контроля над запросами вне DAL. Вы все еще можете выполнять разбиение на страницы, просто создайте метод, который принимает номер страницы и размер страницы, затем пролистайте внутри фактического метода DAL и верните список. Взгляните на некоторые из моих вопросов, которые я задал, если вы заинтересованы в IQueryable, потому что, как я уже сказал, я в настоящее время использую его. Кроме того, слишком поздно позволять приложению Silverlight выполнять запросы. Вы должны только отложить запрос на BLL, не позже. В противном случае вы можете получить неожиданные результаты. - person RPM1984; 06.11.2010