За последние несколько месяцев я изучал MVC5 с EF6. Честно говоря, это был роман любви/ненависти не к самой структуре, а к онлайн-учебникам Microsoft. Не все, но большинство их уроков, кажется, заканчиваются как последний сезон телесериала. Зрители дома ломают голову, пытаясь понять остальную часть истории или причину, по которой они вообще начали ее смотреть.
Во всяком случае, мой последний камень преткновения заключается в том, следует ли мне реализовать шаблон репозитория и единицу работы с EF6? Причина, по которой я заинтересован в любом виде дополнительного уровня, заключается в том, чтобы помочь уменьшить объем логического кода в моих контроллерах.
Прежде чем рассматривать репозиторий, у меня в папке моделей был простой общедоступный класс с именем productservice.cs, который позволял мне передавать модель продукта из контроллера для выполнения некоторых манипуляций с данными и возвращать ее в представление для сохранения. С моей точки зрения, это работало безупречно, но я вызывал дополнительный dbcontext в этом сервисном классе, что, как мне кажется, казалось неверным.
После некоторых исследований в Интернете я начал реализовывать репозиторий, который позволил бы мне выполнять те же манипуляции с данными, что и в моем productservice.cs, но, похоже, следовал хорошо установленному шаблону и позволял передавать контекст от контроллера к репозиторию. где я могу выполнить некоторые манипуляции перед сохранением. Нужно написать больше кода, учитывая, что функции сохранения, обновления и удаления EF работают нормально, но я не возражаю против написания большего количества кода, если это улучшит мое положение в будущем.
Теперь вопрос в том, как мне получить списки для моих выпадающих списков в контроллере? Быстрое решение состояло в том, чтобы поместить Get() в мой productdepository для каждого списка. Это сработало, но кажется, что я пишу больше кода, чем нужно. Я вижу преимущество в написании отдельных репозиториев для каждой модели, поскольку они могут иметь разные методы хранения и извлечения в будущем. Будет ли правильным решением с использованием репозитория создать единицу работы, которая ссылается на каждый репозиторий, необходимый для этого контроллера? Вот часть моего кода
Интерфейс репозитория продукта:
public interface IProductRepository : IDisposable
{
IEnumerable<Category> GetCategories();
IEnumerable<Manufacturer> GetManufacturers();
IEnumerable<ProductType> GetProductTypes();
IEnumerable<Availability> GetAvailabilities();
IEnumerable<ShipMethod> GetShipMethods();
IEnumerable<Product> GetProducts();
IEnumerable<Product> GetProductsByName(string productName);
Product GetProductById(int productId);
void InsertProduct(Product product);
void DeleteProduct(int productId);
void UpdateProduct(Product product);
void Save();
}
Верхняя часть моего контроллера:
public class ProductController : Controller
{
private IProductRepository productRepository;
public ProductController()
{
this.productRepository = new ProductRepository(new ProductContext())
}
public ProductController(IProductRepository productRepository)
{
this.productRepository = productRepository;
}
public ActionResult Create()
{
Product product = new Product();
product.Created = DateTime.Now;
ViewBag.AvailabilityId = new SelectList(productRepository.GetAvailabilities(), "AvailabilityId", "Name");
ViewBag.CategoryId = new SelectList(productRepository.GetCategories(), "CategoryId", "Name");
ViewBag.ManufacturerId = new SelectList(productRepository.GetManufacturers(), "ManufacturerId", "Name");
ViewBag.ProductTypeId = new SelectList(productRepository.GetProductTypes(), "ProductTypeId", "Name");
ViewBag.ShipMethodId = new SelectList(productRepository.GetShipMethods(), "ShipMethodId", "Name");
return View(product);
}
Потратив некоторое время на поиск окончательного решения, я продолжаю возвращаться к одному и тому же вопросу. Почему я не могу просто превратить IProductRepository и Repository в IProductService и ProductService?
В основном держите все CRUD в контроллере и вызывайте службу, если это необходимо, которую можно передать обратно в контроллер для окончательного хранения или представления? Какой реальный смысл создавать кучу CRUD-методов для каждой сущности, если EF уже делает это за меня в контроллере?
Спасибо заранее за любую помощь. Просто немного запутался.
ОБНОВЛЕНИЕ. Я хотел показать пример моего оригинального контроллера и идеи службы. Я понимаю, что логика проста и может быть в контроллере, но некоторые другие мои сервисы, такие как обрезка и сохранение миниатюры и исходного изображения при создании нового продукта, занимают много места в контроллере.
Блок a от контроллера:
public class ProductTesterController : Controller
{
private ProductContext db = new ProductContext();
private ProductService service = new ProductService();
// GET: Dashboard/ProductManager/Details/5
public ActionResult Detail(int id)
{
Product product = db.Products.Find(id);
if (product == null)
{
return HttpNotFound();
}
return View(product);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(Product product)
{
if (ModelState.IsValid)
{
service.UpdatePid(product, db);
db.Entry(product).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Detail", new { id = product.ProductId });
}
ViewBag.AvailabilityId = new SelectList(db.Availability, "AvailabilityId", "Name", product.AvailabilityId);
ViewBag.CategoryId = new SelectList(db.Categories, "CategoryId", "Name", product.CategoryId);
ViewBag.ManufacturerId = new SelectList(db.Manufacturers, "ManufacturerId", "Name", product.ManufacturerId);
ViewBag.ProductTypeId = new SelectList(db.ProductTypes, "ProductTypeId", "Name", product.ProductTypeId);
ViewBag.ShipMethodId = new SelectList(db.ShipMethods, "ShipMethodId", "Name", product.ShipMethodId);
return View(product);
}
А дальше сервис:
public class ProductService
{
public Product CreatePid(Product product, ProductContext context)
{
int lastProductId = context.Products.OrderByDescending(x => x.ProductId).First().ProductId;
product.PID = Convert.ToInt32("" + (100 * product.CategoryId) + (lastProductId + 1));
return (product);
}
public Product UpdatePid(Product product, ProductContext context)
{
product.PID = Convert.ToInt32("" + (100 * product.CategoryId) + product.ProductId);
return (product);
}
}
Я не создаю новый контекст внутри службы, просто передаю контекст из контроллера и возвращаю то, что мне нужно с контекстом. Для размера моего проекта и отсутствия реального опыта работы с репозиторием/uow это, похоже, подходит для моей ситуации. Я имею в виду, что вся причина для класса обслуживания заключается в том, чтобы уменьшить размер моего контроллера и сделать его простым. Есть ли какой-либо негативный эффект от того, что вы просто делаете это, а не создаете репозиторий/uow/сервис? Спасибо!
ОБНОВЛЕНИЕ
Потратив много времени на изучение этого, я наткнулся на этот блог, который, кажется, именно то, что я искал. После этого поста я также начал использовать Autofac для внедрения моего контекста в конструкторы. Работает как шарм.
Вот ссылка, которая, надеюсь, поможет тем, кто ищет похожее решение: Как сделать Entity Framework более пригодным для модульного тестирования — Джош Кодрофф