2

情况:我正在为我的“持久层”使用 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();
}

== 2012 年 3 月 29 日更新 ==

在使用 LinqToSQL(和 Web 窗体)时,我采用了命令/查询模式,不再将 LinqToSql DataContext 创建的实体用作到我的数据存储的映射。我所有的规则以及没有进入命令对象的内容,在某种程度上使它们成为应用程序的真正核心。

4

2 回答 2

0

I am using LinqToSql (could be considered irrelevant)

I wouldn't consider that irrelevant at all.

Disregarding the discussion of whether LINQ-to-SQL is a 'real' ORM, I find that all ORMs (and particular LINQ-to-SQL) are very prescriptive in the way that they want you to use them. Stick to their preferred pattern and everything is straightforward. Structure your code as you choose and you risk heading for a world of pain.

In this instance, LINQ-to-SQL works best when it comprises both the data access and logic (tightly coupled). This is a terrible practice once you have anything other than a very small project and will lead to debugging nightmares, but if you try to break data access and logic apart you'll hit some common problems.

Microsoft's recommendation is that a DataContext should be a short-lived object used for a unit of work. This approach doesn't work well because of the 'attached' / 'detached' model that LINQ-to-SQL uses. There are dozens of websites proposing the 'detach through serialization' hack which is, in my opinion, awful.

In addition, the ORM model lends itself to data objects rather than full-featured encapsulated objects. The DataContext needs to maintain the relationships between objects, so having them be responsible for their themselves will often lead to later headaches.

While I enjoy LINQ-to-SQL and have used it on a number of projects I can't recommend a great pattern, and would actively recommend against any object-oriented pattern at the data level. Forget what you know and instead try to write an easy to maintain data layer. In my experience, separating your logic and data access based on strict design rules will lead to significant problems later on with this toolkit.

于 2012-03-14T04:46:53.737 回答
0

客户端将订单传递给业务逻辑层 (BLL)。BLL 调用 DAL 方法来获取 n 个产品密钥。DAL 没有实现任何逻辑,它只是获取 n 个键。BLL 然后对 DAL 提供的内容做出反应。如果有足够的键,BLL 将这些键与订单相关联并返回。如果没有足够的键,则 BLL 不会将键与订单关联并抛出异常。然后,客户端根据逻辑层返回的内容向用户提供正确的 UI 消息。

我同意你自己的评估,即逻辑不应该存在于 DAL 中。这意味着 DAL 方法可能会从不同的用例场景中重用,我们不希望 DAL 决定任何事情,因为业务规则可能会改变并且可能会出现新的用例。

这有帮助吗?

于 2012-03-14T01:05:21.957 回答