В отличие от рамирамулу, я бы не стал вводить слишком много абстракций.
Если вы используете EF, ваш DAL на самом деле является Entity Framework, не нужно абстрагироваться от этого. Многие люди пытаются это сделать, но это только сильно усложняет ваш код без всякой выгоды. Если вы выполняли SQL-запросы и вызывали хранимые процедуры напрямую, тогда был бы полезен DAL, но создание абстракции поверх EF (которая является другой абстракцией или над NHibernate) - плохая идея.
Кроме того, чистые DTO как абстракции все больше и больше осуждаются, но их можно использовать, если у вас есть промежуточное программное обеспечение и вы не имеете прямого доступа к базе данных - например, шина сообщений, такая как NServiceBus: в этом случае сообщения будут считаться DTO.
Если вы не делаете очень простой и чистый CRUD (в этом случае, давайте, поместите логику в контроллеры - нет причин для добавления сложности для довольно простого бизнеса), вам обязательно следует вынести бизнес-логику за пределы своих контроллеров. Для этого у вас есть много вариантов, но два из самых популярных: модель расширенного домена с управляемым доменом дизайн или многофункциональные бизнес-сервисы с сервис-ориентированным дизайном. Есть много способов сделать это, но эти два иллюстрируют очень разные подходы.
Богатый домен (контроллер на агрегат)
В первом случае ваш контроллер будет отвечать за получение объекта домена, вызов логики и возврат модели представления. Они служат мостом между миром просмотра и миром модели. Процесс получения объекта (ов) предметной области должен быть в некоторой степени абстрактным, часто простые виртуальные методы отлично работают - пусть это будет просто.
Совокупный корень:
public class Item
{
public string itemId { get; set; }
public string itemDescription { get; set; }
public float unitPrice { get; set; }
// more fields
public virtual ItemProductLine itemProductLine { get; set; }
// Example of logic, should always be in your aggregate and not in ItemProductLine for example
public void UpdatePrice(float newPrice)
{
// ... Implement logic
}
}
Просмотреть модель:
public class ItemViewModel
{
public int id { get; set; }
public string itemNumber { get; set; }
public String itemDescription { get; set; }
public Double unitPrice { get; set; }
public string productLine { get; set; }
}
Контроллер:
public class ItemController : Controller
{
[HttpGet]
public ActionResult Edit(int id)
{
var item = GetById(id);
// Some logic to map to the VM, maybe automapper, valueinjector, etc.
var model = item.MapTo<ItemViewModel>();
return View(model);
}
[HttpPost]
public ActionResult Update(int id, ItemViewModel model)
{
// Do some validation
if (!model.IsValid)
{
View("Edit", model); // return edit view
}
var item = GetById(model.id);
// Execute logic
item.UpdatePrice(model.unitPrice);
// ... maybe more logic calls
Save(item);
return RedirectToAction("Edit");
}
public virtual Item GetById(int id)
{
return dbContext.Items.Find(id);
}
public virtual bool Save(Item item)
{
// probably could/should be abstracted in a Unit of Work
dbContext.Items.Update(item);
dbContext.Save();
}
}
Это отлично работает с логикой, которая просачивается вниз и очень зависит от модели. Также замечательно, когда вы не используете CRUD и очень ориентированы на действия (например, кнопка для обновления только цены по сравнению со страницей редактирования, где вы можете изменить все значения элементов). Он довольно развязан, и существует разделение задач - вы можете редактировать и тестировать бизнес-логику самостоятельно, вы можете тестировать контроллеры без бэкэнда (путем переопределения виртуальных функций), и у вас нет сотен абстракций, построенных друг на друге. . Вы можете развернуть виртуальную функцию в классе репозитория, но по опыту у вас всегда есть очень специфические фильтры и проблемы, которые зависят от контроллера / представления, и часто вы получаете один контроллер на совокупный корень, поэтому контроллеры - хорошее место для них. (например, .GetAllItemsWithAPriceGreaterThan(10.0)
)
В такой архитектуре нужно быть осторожным с границами. Например, у вас может быть контроллер / агрегат продукта и вы хотите перечислить все элементы, связанные с этим продуктом, но он должен быть доступен только для чтения - вы не можете вызывать какие-либо компании по элементам из продуктов - вам нужно перейти к контроллеру элементов для этого. Лучший способ сделать это - автоматически сопоставить ViewModel:
public class ProductController : Controller
{
// ...
public virtual IEnumerable<ItemViewModel> GetItemsByProductId(int id)
{
return dbContext.Items
.Where(x => ...)
.Select(x => x.MapTo<ItemViewModel>())
.ToList();
// No risks of editing Items
}
}
Богатые службы (Контроллер на службу)
Используя богатый набор сервисов, вы создаете абстракцию, более ориентированную на сервисы. Это замечательно, когда бизнес-логика порождает несколько границ и моделей. Сервисы играют роль моста между представлением и моделью. Они НИКОГДА не должны раскрывать базовые модели, только определенные модели представления (которые в этом случае играют роль DTO). Это очень хорошо, когда у вас есть сайт MVC и некоторые REST WebApi, работающие, например, с одним и тем же набором данных, они могут повторно использовать одни и те же службы.
Модель:
public class Item
{
public string itemId { get; set; }
public string itemDescription { get; set; }
public float unitPrice { get; set; }
// more fields
public virtual ItemProductLine itemProductLine { get; set; }
}
Просмотреть модель:
public class ItemViewModel
{
public int id { get; set; }
public string itemNumber { get; set; }
public String itemDescription { get; set; }
public Double unitPrice { get; set; }
public string productLine { get; set; }
}
Услуга:
public class ItemService
{
public ItemViewModel Load(int id)
{
return dbContext.Items.Find(id).MapTo<ItemViewModel>();
}
public bool Update(ItemViewModel model)
{
var item = dbContext.Items.Find(model.id);
// update item with model and check rules/validate
// ...
if (valid)
{
dbContext.Items.Update(item);
dbContext.Save();
return true;
}
return false;
}
}
Контроллер:
public class ItemController : Controller
{
public ItemService Service { get; private set; }
public ItemController(ItemService service)
{
this.Service = service;
}
[HttpGet]
public ActionResult Edit(int id)
{
return View(Service.Load(id));
}
[HttpPost]
public ActionResult Update(int id, ItemViewModel model)
{
// Do some validation and update
if (!model.IsValid || !Service.Update(model))
{
View("Edit", model); // return edit view
}
return RedirectToAction("Edit");
}
}
Контроллеры существуют только для вызова Сервисов и составления результатов для Представлений. Они «глупы» по сравнению с контроллерами, ориентированными на домен, но если у вас много сложностей с представлениями (тонны составных представлений, ajax, сложная проверка, обработка json / xml вместе с HTML и т. Д.), Это предпочтительный подход.
Кроме того, в этом случае услуги не должны относиться только к одной модели. Одна и та же служба может управлять несколькими типами моделей, если они разделяют бизнес-логику. Таким образом, OrderService может получить доступ к инвентарю и вносить там корректировки и т. Д. Они больше основаны на процессах, чем на моделях.
person
Karhgath
schedule
24.01.2014
select item
. Есть ли причина, по которой вы хотите преобразовать получаемые вами предметы в другие? - person Kenneth   schedule 24.01.2014select new Item()
я буду использоватьselect Item()
? - person janinaj   schedule 24.01.2014select item
. Ваш запрос вернетIQueryable<Item>
, и вы сможете затем вызватьToList()
для возврата списка из вашего метода репозитория. В этот момент ваш домен будет работать сList<Item>
, избавляя вас от необходимости работать с DTO. Сохраните свой отдельныйItemViewModel
для вида и, как предложил Майк, используйте AutoMapper для сопоставления между ними. - person Brian Cauthon   schedule 24.01.2014