Заполняет ли Queryability и Lazy Loading в C # границы доступа к данным и бизнес-логики? - PullRequest
6 голосов
/ 29 сентября 2010

Я переживаю философский архитектурный кризис середины карьеры. Я вижу очень четкие границы между тем, что считается клиентским кодом (пользовательский интерфейс, веб-службы, MVC, MVP и т. Д.) И уровнем обслуживания. Тем не менее, линии от сервисного уровня с каждой минутой становятся более размытыми. И все началось с возможности запроса кода с помощью Linq и концепции отложенной загрузки.

Я создал бизнес-уровень, который состоит из контрактов и реализаций. Реализации могут иметь зависимости от других контрактов и так далее. Это обрабатывается через IoC-контейнер с DI. Существует один сервис, который обрабатывает DataAccess, и все, что он делает, это возвращает UnitOfWork. Этот UnitOfWork создает транзакцию после ее завершения и фиксирует данные в методе Commit. [ Просмотреть эту статью (Testable and Entity Framework 4.0) ]:

public interface IUnitOfWork : IDisposable {
   IRepository<T> GetRepository<T>() where T : class;
   void Commit();
}

Репозиторий является универсальным и работает с двумя реализациями (EF4 и InMemory DataStore). T состоит из POCO, которые генерируются из схемы базы данных или отображений EF4. Тестируемость встроена в дизайн репозитория. Мы можем использовать реализацию в памяти, чтобы утверждать результаты с ожиданиями.

public interface IRepository<T> where T : class {
   IQueryable<T> Table { get; }
   void Add(T entity);
   void Remove(T entity);
}

Хотя источник данных абстрагирован, IQueryable по-прежнему дает мне возможность создавать запросы в любом месте в рамках бизнес-логики. Вот пример.

public interface IFoo {
   Bar[] GetAll();
}

public class FooImpl : IFoo {
   IDataAccess _dataAccess;
   public FooImpl(IDataAccess dataAccess) {
      _dataAccess = dataAccess;
   }

   public Bar[] GetAll() {
      Bar[] output;
      using (var work = _dataAccess.DoWork()) {
          output = work.GetRepository<Bar>().Table.ToArray();
      }
      return output;
   }
}

Теперь вы можете видеть, как запросы могут становиться еще более сложными при выполнении объединений со сложными фильтрами.

Поэтому мои вопросы:

  1. Имеет ли значение, что нет четкого различия между BLL и DAL? .
  2. Считается ли возможность запросов к доступу к данным или бизнес-логикой, если за слоем репозитория действует абстракция InMemory?

Дополнение: Чем больше я об этом думаю, может, второй вопрос был единственным, который нужно было задать.

Ответы [ 3 ]

6 голосов
/ 29 сентября 2010

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

На мой взгляд, причины просты:Держите бизнес-логику отдельно от уровня данных, потому что бизнес-логика находится там, где значение, уровень данных и бизнес-логика должны будут меняться со временем более или менее независимо друг от друга, а бизнес-логика должна быть читаемой без необходимостичтобы иметь подробные знания о том, что делает весь слой доступа к данным.

Итак, лакмусовая бумажка для вашего запроса гимнастики сводится к следующему:

  1. Можете ли вы внести изменения в схему данныхв вашей системе, не нарушая значительную часть бизнес-логики?
  2. Читается ли ваша бизнес-логика для вас и других разработчиков C #?
1 голос
/ 29 сентября 2010

1. Имеет ли значение, что нет четкого различия между BLL и DAL?.

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

2. Рассматривается ли возможность запросов к доступу к данным или бизнес-логике, когда за слоем репозитория действует абстракция InMemory?

Абстракция - это одеяло, под которым мы скрываем свои проблемы.

Если ваша абстракция безупречна, то запросы можно абстрактно рассматривать как работающие с коллекциями в памяти, и, следовательно, они не являются доступом к данным.

Тем не менее, утечка абстракций. Если вам нужны запросы, которые имеют смысл в мире данных, необходимо приложить усилия, чтобы работать над абстракцией и за ее пределами. Это дополнительное усилие (которое побеждает абстракцию) создает код доступа к данным.


Некоторые примеры:

output = work.GetRepository<Bar>().Table.ToArray(); 

Это код (абстрактно) в порядке. Но в мире данных это приводит к сканированию всей таблицы и является (по крайней мере, вообще) глупым!


badquery = work.GetRepository<Customer>().Table.Where(c => c.Name.Contains("Bob")).ToArray(); 
goodquery = work.GetRepository<Customer>().Table.Where(c => c.Name.StartsWith("Bob")).ToArray(); 

Goodquery лучше, чем плохой запрос, если есть индекс на Customer.Name. Но этот факт нам недоступен, пока мы не снимаем абстракцию.


badquery = work.GetRepository<Customer>().Table
  .GroupBy(c => c.Orders.Count())
  .Select(g => new
  {
    TheCount = g.Key,
    TheCustomers = g.ToList()
  }).ToArray();
goodquery = work.GetRepository<Customer>().Table
  .Select(c => new {Customer = c, theCount = c.Orders.Count())
  .ToArray()
  .GroupBy(x => x.theCount)
  .Select(g => new
  {
    TheCount = g.Key,
    TheCustomers = g.Select(x => x.Customer).ToList()
  })
  .ToArray();

goodquery лучше, чем плохой запрос, поскольку badquery будет запрашивать базу данных по групповому ключу для каждой группы (и, что еще хуже, вряд ли найдется индекс, помогающий фильтровать клиентов по c.Orders.Count()).


Тестируемость встроена в дизайн репозитория. Мы можем использовать реализацию InMemory, чтобы утверждать результаты с ожиданиями.

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

1 голос
/ 29 сентября 2010

1. Только если вы заботитесь больше о философии, чем о том, как что-то сделать.:)

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

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

...