Ситуация: я использую LinqToSql (может считаться нерелевантным) для своего «уровня персистентности» и пытаюсь решить некоторые архитектурные проблемы, связанные с тем, куда должна идти определенная логика, связанная с бизнесом.
Сценарий: пользователь приложения создает новый заказ. Когда они это делают, набор ключей продуктов должен быть связан с этим заказом.
Моей первой попыткой было поместить весь этот джаз в класс OrderService. Затем я попытался включить его в свой DataContext, используя частичный метод:
partial void InsertOrder(Order instance)
{
var productKeys = this.ProductKeys
.Where(x => x.DeleteDate == null && x.Order == null && !x.Issued)
.Take(instance.NumberOfProductKeys);
if (productKeys.Count() != instance.NumberOfProductKeys)
throw new NotSupportedException("There needs to be more product keys in the database.");
instance.ProductKeys.AddRange(productKeys);
this.ExecuteDynamicInsert(instance);
}
Несмотря на то, что это не работает должным образом (ключи продукта никогда не связаны с заказом), я чувствую, что это убирает логику из моей бизнес-сферы и выталкивает ее на мой «уровень постоянства». Я также думал о том, чтобы поместить его в класс OrderService, но чувствовал, что он также просто забрал логику у сущностей домена и привел к скрипту транзакции. Введение фабрики заказов просто обходит проблему: данные и логика снова разделены.
Итак, мой вопрос заключается в следующем: чтобы избежать анемичной модели предметной области и, надо надеяться, иметь порядок, делающий что-то помимо прославленной структуры данных, существует ли надлежащий способ интеграции этой логики в мою модель предметной области?
Лучшее решение, которое я нашел, - это поместить логику в класс Order и убедиться, что это было сделано в хуке проверки:
public partial class Order
{
public void AssociateWithProductKeys(WIIIPDataContext context)
{
var productKeys = context.ProductKeys
.Where(x => x.DeleteDate == null && !x.Issued && x.Order == null && x.ProductType == ProductType)
.Take(NumberOfProductKeys);
if (productKeys.Count() != NumberOfProductKeys)
throw new ValidationException("There needs to be more product keys in the database for product type: " + ProductType.Description);
ProductKeys.AddRange(productKeys);
foreach (ProductKey productKey in ProductKeys)
{
productKey.Issued = true;
}
}
partial void OnValidate(ChangeAction action)
{
if (action == ChangeAction.Insert)
{
if (ProductType.Id == 3 && ProductKeys.Count() != 1)
throw new ValidationException("Attempted to associated more than 1 product key with a CD version.");
if (ProductKeys.Count() != NumberOfProductKeys)
throw new ValidationException("Number of product keys doesn't match expected value");
}
}
}
Код потребления будет выглядеть так:
// The Unit of Work
using (var context = new MyDataContext())
{
...
order.AssociateWithProductKeys(context);
context.Orders.InsertOnSubmit(order);
context.SubmitChanges();
}
== Обновление 29.03.2012 ==
Я принял шаблон команды / запроса при использовании LinqToSQL (и веб-форм) и больше не использую сущности, созданные LinqToSql DataContext, как нечто большее, чем отображение в мое хранилище данных. Все мои правила и все, что не входит в объекты команд, делают их, в некотором смысле, настоящим ядром приложения.