4

Если вам не нужен код, который я написал, и вы просто хотите обсудить тему абстрагирования существующих абстракций ... перейдите к последним 3 абзацам.Я только что реализовал шаблон Unit Of Work поверх платформы Entity Framework, используя общие хранилища. Что я на самом деле выполнил?

Я познакомился с идеей репозиториев и подразделения работы над платформой Entity Framework через урок Pluralsight, который я наблюдал. Я также замалчивался на собственную страницу Microsoft, подробно описывающую этот процесс: http://www.asp.net/mvc/overview/older-versions/getting-started-with-ef-5-using-mvc-4/implementing-the-repository-and-unit-of-work-patterns-in-an-asp-net-mvc-application

Так что я решил попробовать. Я намеревался написать свой собственный класс Unit Of Work с родовыми репозиториями поверх Entity Framework, создавая абсолютно все на этом пути, используя интерфейсы, чтобы я мог вводить свои собственные макеты для тестирования.

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

Итак, я начал с DbContext. Должен убедиться, что я использую интерфейс!

public interface ISimpleBlogContext : IDisposable 
{ 
    IDbSet<Blog> Blogs { get; } 
    IDbSet<Post> Posts { get; } 

    void SaveChanges(); 
    IDbSet<T> Set<T>() where T : class; 
    DbEntityEntry Entry<T>(T entity) where T : class; 
} 

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

Так что теперь я подключил свой интерфейс в реальной конкретной DbContext:

public class SimpleBlogContext : DbContext, ISimpleBlogContext 
{ 
    public SimpleBlogContext() { 
     Database.SetInitializer<SimpleBlogContext>(new DropCreateDatabaseAlways<SimpleBlogContext>()); 
    } 

    public IDbSet<Blog> Blogs { get; set; } 
    public IDbSet<Post> Posts { get; set; } 

    public DbEntityEntry Entry<T>(T entity) where T : class 
    { 
     return Entry<T>(entity); 
    } 

    void ISimpleBlogContext.SaveChanges() 
    { 
     SaveChanges(); 
    } 

    IDbSet<T> ISimpleBlogContext.Set<T>() 
    { 
     return Set<T>(); 
    } 

инициализатор База данных только обеспечение того, чтобы для этого тестового приложения базы данных отбрасывается и воссозданы каждый раз, когда я запустить приложение. В конце концов, это было просто упражнение, а не реальное приложение. Вы можете увидеть методы SaveChanges, Set и Entry, реализованные здесь, и все они не что иное, как обертки для методов DbContext с тем же именем.

Так что теперь на репозитории ...

Я не собираюсь переписывать практически тот же самый код хранилища для каждого объекта я мог бы добавить к моему заявлению (хотя в данном случае только я ветер используя один репозиторий), поэтому я создал общий репозиторий. Не пропустите интерфейс!

public interface IGenericRepository<T> 
    where T : class 
{ 
    IEnumerable<T> GetAll(); 
    T GetById(object id); 
    IEnumerable<T> GetByExpression(Expression<Func<T, bool>> expression); 
    void Add(T entity); 
    void Delete(T entity); 
    void Update(T entity); 
} 

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

public class GenericRepository<T> : IGenericRepository<T> 
    where T : class 
{ 

    public GenericRepository(ISimpleBlogContext context) 
    { 
     this.context = context; 
    } 

    private ISimpleBlogContext context; 

    public void Add(T entity) 
    { 
     context.Set<T>().Add(entity); 
    } 

    public void Delete(T entity) 
    { 
     context.Set<T>().Remove(entity); 
    } 

    public IEnumerable<T> GetAll() 
    { 
     return context.Set<T>().ToList<T>(); 
    } 

    public IEnumerable<T> GetByExpression(Expression<Func<T, bool>> expression) 
    { 
     return context.Set<T>().Where<T>(expression).ToList<T>(); 
    } 

    public T GetById(object id) 
    { 
     return context.Set<T>().Find(id); 
    } 

    public void Update(T entity) 
    { 
     context.Entry(entity).State = EntityState.Modified; 
    } 
} 

и теперь, наконец, единица работы класса

public class UnitOfWork : IDisposable 
{ 

    public void Dispose() 
    { 
     if (context != null) 
     { 
      context.Dispose(); 
      context = null; 
     } 
    } 
    public UnitOfWork() 
    { 
     context = new SimpleBlogContext(); 
    } 

    public UnitOfWork(ISimpleBlogContext context) 
    { 
     this.context = context; 
    } 

    private ISimpleBlogContext context; 

    public GenericRepository<TEntity> GetRepository<TEntity>() where TEntity : class 
    { 
     return new GenericRepository<TEntity>(context); 
    } 

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

} 

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

Так что теперь мне просто нужно проверить все это. Поэтому я написал простое консольное приложение, которое не более чем генерирует пару поддельных блогов с несколькими фальшивыми сообщениями и сохраняет их с помощью класса Unit Of Work. Затем он просматривает блоги и сообщения и распечатывает их, чтобы я мог убедиться, что они были фактически сохранены в базе данных.

P.S. Джейк - моя собака, если тебе интересно, как лаять ...

class Program 
{ 
    static void Main(string[] args) 
    { 
     UnitOfWork unitOfWork = new UnitOfWork(); 

     GenericRepository<Blog> blogRepository = unitOfWork.GetRepository<Blog>(); 

     Blog paulsBlog = new Blog() 
     { 
      Author = "Paul", 
      Posts = new List<Post>() 
      { 
       new Post() 
       { 
        Title = "My First Post", 
        Body = "This is my first post" 
       }, 
       new Post() 
       { 
        Title = "My Second Post", 
        Body = "This is my second post" 
       } 
      } 
     }; 


     Blog jakesBlog = new Blog() 
     { 
      Author = "Jake", 
      Posts = new List<Post>() 
      { 
       new Post() 
       { 
        Title = "Dog thoughts", 
        Body = "Bark bark bark" 
       }, 
       new Post() 
       { 
        Title = "I like barking", 
        Body = "Bark bark bark" 
       } 
      } 
     }; 


     blogRepository.Add(paulsBlog); 
     blogRepository.Add(jakesBlog); 
     unitOfWork.Save(); 

     List<Blog> blogs = blogRepository.GetAll() as List<Blog>; 

     foreach (Blog blog in blogs) 
     { 
      System.Console.WriteLine("ID: {0}, Author: {1}\n", blog.Id, blog.Author); 
      if (blog.Posts != null && blog.Posts.Count > 0) 
      { 
       foreach (Post post in blog.Posts) 
       { 
        System.Console.WriteLine("Posted at: {0}, Title: {1}, Body: {2}", post.PostTime, post.Title, post.Body); 

       } 
      } 
      else 
      { 
       System.Console.WriteLine("No posts"); 
      } 
      System.Console.WriteLine("\n"); 
     } 
    } 
} 

Это работает. Ура!

Мой вопрос, однако, просто ... что я получил именно этим, выполнив все это?

Разве DbContext не является частью работы, а DbSet уже является хранилищем? Кажется, все, что я сделал, это написать действительно сложную оболочку для обеих вещей без каких-либо дополнительных функций. Вы можете сказать, что это более удобно для тестирования, поскольку все использует интерфейсы, но с издевательскими фреймворками, такими как Moq, уже можно высмеивать DbSets и DbContexts. Мои хранилища носят общий характер, поэтому существует буквально нулевая функциональность, характерная для бизнес-логики. Класс Unit Of Work - это всего лишь оболочка для DbContext, а общие хранилища - это просто оболочки для DbSet.

Может кто-нибудь объяснить мне, почему кто-нибудь это сделает? Я просмотрел урок 4-х Pluralsight по этому поводу, а также пережил все проблемы, связанные с этим, и я все еще не понимаю.

+2

Вы получили ничего, кроме кода bloat – ErikEJ

+1

a DbContext MEANT как единица работы - конечно, это не единица работы сама по себе, так как это в основном ваш DAL. DbSet также не являются репозиториями самостоятельно - это IQueryable , со всеми преимуществами и проблемами, которые это подразумевает. Помимо этого, это в значительной степени оболочка, которая инкапсулирует функциональность. Хотя я никогда не использовал Moq, я не знаю, будет ли это работать, чтобы издеваться над DbContexts, поскольку вы не можете реализовать там перехватчики. Таким образом, в основном это обертка с общим шаблоном и перераспределение фактической задачи выполнения. – DevilSuichiro

+2

Это [старая дискуссия] (https://ayende.com/blog/3955/repository-is-the-new-singleton). Вы ничего не получаете, вы теряете гибкость. И, кстати, репозиторий не должен распоряжаться контекстом. –

ответ

3

Недавно я сделал то же самое тонкое, и я считаю, что вы получаете гибкость тестирования своего кода, не требуя фактического доступа к базе данных SQL.

+0

*** Репозиторий зол *** https://ayende.com/blog/4784/architecting-in-the-pit-of-doom-the-evils-of-the-repository-abstraction-layer и http: //softwareengineering.stackexchange.com/questions/180851/why-shouldnt-i-use-the-repository-pattern-with-entity-framework/2 – Kiquenet

+0

IMHO, *** код король ***, лучше с * хорошим код реального образца * для более полного объяснения ', если вы считаете, что 100% используется при использовании репозитория'. _References _: [Сокращение допустимо, но более полные объяснения лучше) (http://stackoverflow.com/help/how-to-answer) *** jonskeet ***, '' Chuck Norris 'программирования' [Код является королем, укажите ссылки на связанные ресурсы, не бойтесь удалять (или редактировать тяжелые) бесполезные ответы, «Не отвечайте и не выполняйте»] (https://codeblog.jonskeet.uk/2009/02/ 17/ответ-технические-вопросы-услужливо /) – Kiquenet