Репозиторий MVC с единицей работы, Automapper и общего репозитория


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

Я использую шаблон репозитория с интерфейсами, использующими Automapper...вот урезанный пример:

public class BookingRepository : IBookingRepository
{
    Entities context = new Entities();

    public IEnumerable<BookingDto> GetBookings
    {
        get { return Mapper.Map<IQueryable<Booking>, IEnumerable<BookingDto>>(context.Bookings); }
    }

    public BookingDto GetBookingWithProduct(Guid bookingId)
    {
        return Mapper.Map<BookingDto>(context.Bookings.Include(c => c.Products).SingleOrDefault(c => c.BookingId == bookingId));
    }

    public void Update(BookingDto bookingDto)
    {
        var booking = Mapper.Map<Booking>(bookingDto);
        context.Entry(booking).State = EntityState.Modified;
    }

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

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

public interface IBookingRepository : IDisposable
{
    IEnumerable<BookingDto> GetBookings { get; }
    BookingDto GetBooking(Guid bookingId);  
    void Update(BookingDto bookingDto);  
    void Save();
}

С отдельным репозиторием для другой сущности, например

public class ProductRepository : IProductRepository
{
    Entities context = new Entities();

    public IEnumerable<ProductDto> GetProducts
    {
        get { return Mapper.Map<IQueryable<Product>, IEnumerable<ProductDto>>(context.Products); }
    }

    public ProductDto GetProductWithDesign(int productId)
    {
        return Mapper.Map<ProductDto>(context.Products.Include(c => c.Designs).SingleOrDefault(c => c.ProductId == productId));
    }

    public void Update(ProductDto productDto)
    {
        var product = Mapper.Map<Product>(productDto);
        context.Entry(product).State = EntityState.Modified;
    }

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

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

public interface IProductRepository : IDisposable
{
    IEnumerable<ProductDto> GetProducts { get; }
    ProductDto GetProduct(int productId);    
    void Update(ProductDto productDto);
    void Save();
}

Тогда в моем контроллере я использую репозитории следующим образом:

public class HomeController : Controller
{
    private readonly IBookingRepository bookingRepository;
    private readonly IProductRepository productRepository;

    public HomeController() : this(new BookingRepository(), new ProductRepository()) { }
    public HomeController(IBookingRepository bookingRepository, IProductRepository productRepository)
    {
        this.bookingRepository = bookingRepository;
        this.productRepository = productRepository;
    }

    protected override void Dispose(bool disposing)
    {
        base.Dispose(disposing);

        if (disposing && this.bookingRepository != null)
            this.bookingRepository.Dispose();

        if (disposing && this.productRepository != null)
            this.productRepository.Dispose();

    }
}

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

Кроме того, я видел этот пост

Шаблон репозитория с генераторами и DI

Который гласит: "Вы не должны иметь других интерфейсов репозитория, кроме вашего универсального репозиторий "и что пользовательские запросы" заслуживают своей собственной (общей) абстракции: "что добавляет еще одно осложнение к моему перегруженному мозгу, поскольку мои репозитории будут иметь пользовательские запросы, которые возвращают сложные связанные объекты с помощью операторов Include, поскольку ленивая загрузка отключена.

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

Заранее благодарю.

2 3

2 ответа:

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

Я имею в виду, что все, что раскрывает IQueryable<T>, заставляет пользователя узнать о слабостях, которые есть у базового или/M. Примеры: как orm обрабатывает ленивую загрузку? Как я могу охотно загружать связанные объекты? Как создать предложение IN?

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

Я написал об этом в блоге: http://blog.gauffin.org/2013/01/repository-pattern-done-right/

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

public abstract class BaseRepository<T> : IRepository<T>
{
    Entities context = new Entities();

    public virtual T GetAll()
    {
        return context.Set<T>();
    }
    // Add base implementation for normal CRUD here
}

Если вам не нужны специальные запросы, то вам не нужно создавать специальный интерфейс и классы (но вы можете, конечно, улучшить читаемость). Поэтому вы будете использовать, например:

var bookingsRepo = new BaseRepository<BookingsDto>();
var allBookings = bookingsRepo.GetAll();

Если вам нужны некоторые специальные запросы, вы создаете интерфейс, который расширяет базовый интерфейс:

public interface IProductRepository : IRepository<Product>
{
    Product GetSpecialOffer();
}

Затем создайте свой класс:

public class ProductRepository : BaseRepository<Product>, IProductRepository
{
    public Product GetSpecialOffer()
    {
        // your logic here
    }
}

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

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