3

Действительно впечатлен курсом двойного назначения Juile Lerman на тему «EF in Enterprise» и решил создать мое демо-приложение.Нужна помощь в применении принципов SOLID

Я использую VS 2012 и последние версии EF, SQL Server и MVC. Я создаю демо-приложение, которое применяет принципы SOLID. Я делаю это, чтобы лучше понять, как реализовать DI & модульное тестирование.

Я использовал первый подход DB для этого демонстрационного приложения. Он содержит только одну таблицу с именем UserDetails и ниже, как выглядит на SQL-сервере. Я буду использовать эту таблицу для операций CRUD. enter image description here

Ниже, как я слоистый мое заявление:

1. WESModel Решение: Этот слой содержит мой файл Model1.edmx и класс контекста, как показано ниже.

namespace WESModel 
{ 
    using System; 
    using System.Data.Entity; 
    using System.Data.Entity.Infrastructure; 
    using WESDomain; 

    public partial class WESMVCEntities : DbContext 
    { 
     public WESMVCEntities() 
      : base("name=WESMVCEntities") 
     { 
     } 

     protected override void OnModelCreating(DbModelBuilder modelBuilder) 
     { 
      throw new UnintentionalCodeFirstException(); 
     } 

     public DbSet<UserDetail> UserDetails { get; set; } 
    } 
} 

2. WESDomain Решение: Этот слой содержит мои классы домен (или классы POCO). Эти классы POCO были автоматически созданы на моем уровне WESModel. Я переместил их на этот слой. Вот как выглядит один класс POCO.

namespace WESDomain 
{ 
    using System; 
    using System.Collections.Generic; 

    public partial class UserDetail:IUserDetail 
    { 
     public int Id { get; set; } 
     public string UserName { get; set; } 
    } 
} 

3: WESDataLayer Решение: Этот слой содержит ссылки на библиотеки DLL из моих выше 2-х слоев. Этот уровень имеет мой класс репозитория, как показано ниже. На данный момент, я держу в IRepository в том же классе :)

namespace WESDataLayer 
{ 
    public class UserDetailRepository : IUserDetailRepository 
    { 
     WESMVCEntities context = new WESMVCEntities(); 

     public IQueryable<IUserDetail> All 
     { 
      get { return context.UserDetails; } 
     } 

     public IQueryable<IUserDetail> AllIncluding(params Expression<Func<IUserDetail, object>>[] includeProperties) 
     { 
      IQueryable<IUserDetail> query = context.UserDetails; 
      foreach (var includeProperty in includeProperties) { 
       query = query.Include(includeProperty); 
      } 
      return query; 
     } 

     public IUserDetail Find(int id) 
     { 
      return context.UserDetails.Find(id); 
     } 

     public void InsertOrUpdate(UserDetail userdetail) 
     { 
      if (userdetail.Id == default(int)) { 
       // New entity 
       context.UserDetails.Add(userdetail); 
      } else { 
       // Existing entity 
       context.Entry(userdetail).State = EntityState.Modified; 
      } 
     } 

     public void Delete(int id) 
     { 
      var userdetail = context.UserDetails.Find(id); 
      context.UserDetails.Remove(userdetail); 
     } 

     public void Save() 
     { 
      context.SaveChanges(); 
     } 

     public void Dispose() 
     { 
      context.Dispose(); 
     } 
    } 

    public interface IUserDetailRepository : IDisposable 
    { 
     IQueryable<IUserDetail> All { get; } 
     IQueryable<IUserDetail> AllIncluding(params Expression<Func<UserDetail, object>>[] includeProperties); 
     UserDetail Find(int id); 
     void InsertOrUpdate(UserDetail userdetail); 
     void Delete(int id); 
     void Save(); 
    } 
} 

4: ConsoleApplication1 Решение: Это мой слой UI. Это будет мое приложение MVC в моем конечном приложении. Здесь я просто запрашиваю DB и отображаю данные. Так выглядит код.

namespace ConsoleApplication1 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      IUserDetailRepository repo = new UserDetailRepository(); 

      var count = repo.All.ToList().Count().ToString(); 
      Console.WriteLine("Count: {0}", count); 
      Console.ReadLine(); 

     } 
    } 
} 

Вопрос: Мой слой UI не имеет реф к EF DLL. Однако он имеет экземпляр класса репозитория. В приложении MVC мой контроллер будет иметь экземпляр класса репозитория или UnitOfWork.

a) Правильно ли это делать?

б) Есть ли способ, чтобы я мог абстрагировать его?

c) Что делать, если в будущем я хочу поменять EF на Dapper или любой другой инструмент ORM?

d) Как я могу поместить свой инструмент DI в этот проект? В каком слое это должно быть?

e) Тестирование устройства. Я знаю StructureMap и хочу использовать его в этом проекте таким образом, чтобы в будущем я смог поменять его на Ninject. Как мне это достичь?

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

+0

'IRepository' не должен возвращать типы' IQueryable'. Пожалуйста, [см. Эту ссылку] (https://stackoverflow.com/questions/33755499/entity-framework-repository-pattern-why-not-return-iqueryable) – StepUp

ответ

3

Вопрос: Мой слой UI не имеет реф к EF DLL. Тем не менее, он имеет экземпляр класса репозитория. В приложении MVC мой контроллер будет иметь экземпляр класса репозитория или UnitOfWork.

Да, классы слоев пользовательского интерфейса не должны иметь ссылки на EF. Но для этого они не могут иметь ссылку на конкретный репозиторий. В приложении MVC, если вы не используете уровень обслуживания, контроллер будет иметь ссылку только на IUserDetailRepository и ждать конкретного типа из конструкции. О UnitOfWork, это зависит от вашей реализации :-)

a) Это правильная вещь?

Правильная вещь, называемая «свободная связь», кажется, что ваш дизайн выбирает этот путь.

b) Есть ли способ, которым я могу его абстрагировать?

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

c) Что делать, если в будущем я хочу поменять EF с помощью Dapper или любого другого инструмента ORM?

У вас должен быть уровень доступа к данным, например библиотека, содержащая конкретные реализации ваших контрактов IXxxRepository. В вашем случае это будет реализация EF. Когда вы измените для Dapper, вам придется повторно реализовать этот слой. Рефакторинг имеет приемлемый предел.

d) Как я могу поместить свой инструмент DI в этот проект? В каком слое это должно быть?

Лучшим местом для размещения инструмента DI будет слой UI. При запуске приложения вы настроите привязки зависимостей, и все будет работать автоматически;)

e) Тестирование модулей. Я знаю StructureMap и хочу использовать его в этом проекте таким образом, чтобы в будущем я смог поменять его на Ninject. Как мне это достичь?

Вы хотите отключить резонатор зависимостей, чтобы подключить другой? Нет проблем, просто представьте, когда конфигурация кодирования вашего DR должна иметь минимальную связь с вашим приложением. В некоторых случаях есть несколько советов по ограничению связи ... В проекте, над которым я сейчас работаю, у нас есть первое приложение MVC и уровень обслуживания, бизнес-уровень, уровень доступа к данным и уровень инфраструктуры. Мы используем Ninject как DR, а уровни infratructure и Web UI являются единственными, у которых есть ссылка на Ninject. Его очень легко отключить, и мы уже пробовали Unity таким образом.

Еще одна вещь: у вас не должно быть договора для UserDetail. В этом нет необходимости, используйте Injection Dependency для классов без состояния, а не для всех классов, таких как DTO.

+0

Wow..Nice описание. Теперь мне нужно немного больше информации о 1. Любые ссылки, указывающие на использование Dependency Resolver? 2. В конце вы сказали, что «не должно быть контракта на UserDetail». Именно на каком слое вы ссылаетесь? Спасибо за ваше время и подробный ответ. – NoobDeveloper

+0

Я имею в виду ваш класс UserDetail и его контракт IUserDetail в вашем доменном слое. Для меня это над архитектурой для создания контрактов для бизнес-объектов (или, возможно, у вас есть несколько реализаций). – Julien

+0

Вам нужны общие ссылки для использования Resolver Dependency? У меня просто есть ссылки для использования Ninject :) Но из примеров вы поймете общую архитектуру, используя DR ... Попробуйте этот http://stefanoricciardi.com/2011/01/21/ninject-mini-tutorial-part -1/ – Julien

0

Кратко:

Ваша модель имеет зависимость от IRepository (реализация IRepository может быть что угодно, Dapper, EF, ADO.NET и т.д.) для сохранения данных, сделать запрос и т.д. Ваша модель имеет бизнес правила.

Ваше мнение (консоль, WPF, Web) имеет зависимость от слоя между видом и моделью, либо presenter (MVP), controller (MVC) или viewmodel (MVVM).

Этот промежуточный уровень работает с моделью для сохранения данных.

Вы можете использовать точки зависимостей для использования DI.

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

IUserDetailRepository выглядит как репозиторий, и ваша модель должна использовать это. Таким образом, вы разделяете реализацию своей базы данных за абстракцией интерфейса, и, как было сказано ранее, реальная реализация может быть чем-то вроде EF, dapper и т. Д.

В модели MVC контроллер вызывает модель для применения бизнес-правил и сохранить данные.

+0

Мне нужно больше разъяснений. Вы сказали, что моя модель имеет зависимость от IRepository. Вы имеете в виду проект WESDataLayer Solution в приведенном выше коде? 2. В случае приложения MVC, как должен мой контроллер запрашивать данные?Должен ли я иметь еще один слой между контроллером и WESDataLayer Solution из предыдущего кода? На самом деле я смущен, что именно эта «модельная» вещь, о которой вы говорили выше. С моей точки зрения приложений, что такое «Модель» здесь? Спасибо за ваше время. – NoobDeveloper

2

Если вы используете неявное преобразование переменных вместо явного ввода переменных (например, исключение ключевого слова var), вы можете гораздо легче определить зависимости. По возможности предпочитайте использование интерфейса (IUserDetailRepository) над использованием класса (UserDetailRepository).

примеры:

1) позволяют компилятор определить тип

var repo = new UserDetailRepository();

2) типа определяется классом ссылки

UserDetailRepository repo = new UserDetailRepository();

3) Тип определяется интерфейсом

IUserDetailRepository repo = new UserDetailRepository();

Позволяя типу определить интерфейс, а не компилятором, вы можете поменять местами различные ссылки, соответствующие одному и тому же интерфейсу (т. IUserDetailRepository repo = new DapperUserDetailRepository();

Кроме того, вы находитесь на границе принципа называется Инверсией управления (IoC), что практика использования конкретного IoC контейнера (Ninject, CastleWinsor, Unity и т.д.), чтобы решить вашу зависимость автоматически, в результате чего вам никогда не называйте ключевое слово new напрямую.

Поскольку вы упоминаете StructureMap, вот пример того, как это работает:

namespace ConsoleApplication1 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      IContainer container = ConfigureDependencies(); 
      IUserDetailRepository repo = container.GetInstance<IUserDetailRepository>(); 

      var count = repo.All.ToList().Count().ToString(); 
      Console.WriteLine("Count: {0}", count); 
      Console.ReadLine(); 

     } 

     private static IContainer ConfigureDependencies() { 
      return new Container(x =>{ 
       x.For<IUserDetailRepository>().Use<UserDetailRepository>(); 
      }); 
     } 
    } 
} 
+0

На самом деле, я сделал то же самое, что и вы упомянули в своем ответе. Я обновил свое оригинальное сообщение, чтобы отразить то же самое. У меня мало вопросов. Я знаю StructureMap и хочу использовать его в этом проекте таким образом, чтобы в будущем я смог поменять его на Ninject. Как мне это достичь? – NoobDeveloper

+0

Еще раз спасибо Андрей. Прошу прощения за путаницу. Я должен был быть более ясным. Я знаю, как использовать метод StructureMap. Ваш примерный код - это самый простой способ, который я могу использовать его из уровня пользовательского интерфейса/в контроллере приложения MVC. Тем не менее, я хотел бы знать, как я могу абстрагироваться от него/свободно связывать его с UI/Controller, чтобы я мог поменять его на Ninject, если мне нужно. – NoobDeveloper

+0

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

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