1

我正在开发一个 ASP.NET MVC C# 应用程序,其中 MVC 应用程序作为 3 轮胎应用程序中的 UI 层,EF CodeFirst 5 作为 DAL。

DAL -> DTL
DTL <- BLL -> DAL
DTL <- UI -> BLL

箭头表示用途。(所以 DAL 使用 DTL 等等......)

我使用 POCO 作为数据传输对象,并根据 EF Code First 进行数据相关验证。我的 POCO 有关系,而关系中的 POCO 也有关系。很标准...

BL 中的所有类都根据单一职责规则处理单一 POCO 类型。令我困惑的是,当 POCO 的关系是其他 BL 关心的(以保持单一责任)时,我如何在 POCO 上应用业务规则和验证?

我将尝试给出一个简化的示例:(将其作为伪代码阅读更多内容)

public class Customer{
    public int Id { get; set; }
    public string Name { get; set; }
    public virtual Enumerable<Person> Users { get; set; }
}

public class Person{
    public int Id { get; set; }
    public string Name { get; set; }
    public Customer BelongsTo { get; set; }
    public virtual Enumerable<Property> Properties { get; set; }
}

public class Property{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Value { get; set; }
}
public class CustomerBL{

    private DALContext = new DALContext();        

    public void Add(Customer customer){
        DALContext.Customers.Add(customer);
        DALContext.SaveChanges();
    }
}

public class PersonBL{

    private DALContext = new DALContext();        

    public void Add(Person person){
        if(person.Properties.Count() < 3)
             throw new ApplicationException("Each person must have at least three properties");

        DALContext.People.Add(person);
        DALContext.SaveChanges();
    }
}

所以我的问题是,在添加新客户时,如何确保所有用户(如果在客户对象上设置了任何用户)都按照 PersonBL 的 Add 方法中的说明进行验证,而不会破坏单一责任规则(或至少以一种好的方式打破它)?

请记住,Person 可以拥有其他关系,这些关系具有其他关系,并且都具有特定的 BL 和业务规则。我提供了一个“特定”示例,但我正在寻找更通用的解决方案。谢谢!

4

3 回答 3

2

您可能需要查看聚合根设计。

请记住,AR 应始终处于一致状态。如果您发现需要验证不应该验证的内容,那么您的聚合根扩展得太远了。当您从导航的角度考虑您的对象模型时,可能会发生这种情况:从 AI 想要到达 B,然后到 C。这是一个很容易出错的地方。如果您不使用域模型进行查询,则不需要导航。延迟加载是使用域模型进行查询/导航的另一个症状。

在您的示例Customer中,有一个Person名为Users. APerson似乎是另一个 AR。AR 不应包含其他 AR 的实例,因为这会使您的生活变得痛苦。打破这种束缚。

所以 aCustomer可能有一个列表Users(或在您的域中有意义的任何内容)。用户可能具有以下结构:

public class User{
    public int PersonId { get; set; }
}

就如此容易。现在您不必再担心了Person,因为这是在其他地方维护的。因此,尝试将其他聚合根表示为相关 AR 中的值对象。

于 2013-03-05T04:33:17.527 回答
1

如何创建一个单独的验证器类来进行验证?然后,每当您需要对特定实体进行验证时,您可以将该工作传递给验证器,然后验证器将返回损坏的规则。这将允许您不仅在 Person 存储库中重用验证器,还可以在其他可能添加 parson 作为工作单元的一部分的区域中重用验证器。这种技术已在几个业务对象框架中使用,我已经看到这些框架保持验证独立于其他主要业务逻辑,并且还允许更轻松的测试。 这是一个关于这个主题的好帖子。

于 2013-03-04T14:55:17.017 回答
1

建立正确的关系方向非常重要。

一个好的方法通常是消除实体类中的集合属性。在您的特定情况下,这些是Customer.UsersPerson.Properties。让我们专注于Customer.Users。创建一个Users属性,我们表明用户列表对于Customer实体来说是必不可少的。但随后我们看到了一个矛盾

确保验证所有用户(如果在客户对象上设置了任何用户

我们看到客户可以在没有用户列表的情况下存在。现在的问题是User没有Customer. 可能不是(如果是,可能是不同类型的用户)。因此,翻转关系的方向将为实体创建一个Customer属性,User并消除实体Users的集合属性Customer

这将改进您的 POCO 的设计,因为有些人可能会认为集合属性不适合 POCO,尤其是虚拟成员。

所以,我们可以改进设计,但是我们应该如何验证关系本身呢?答案是我们需要一个不同的实体来执行这个任务。这可能是一个CustomerRegistrationJournal例如它可能有一个RegisterCustomer具有两个重载的方法:一个在Customer没有关联用户的情况下采用一个实体,另一个Customer是相关User帐户的列表。此方法将为每个实体调用验证并调用 DAL。这样,它将控制创建两种类型实体的事务。

在这种情况下,应该对 DAL 进行建模,以对User引用 a 的列强制执行外键约束Customer。这样,它将提供弱关系。这意味着加载User不会加载Customer,不会有级联更新或删除。User实体将有一个Idto Customer,与表相同User。这Id对于 a 是必不可少的,UserCustomer 不是. User当然,您可以将其建模为具有整个Customer属性,但这将是不必要的开销恕我直言(即使使用 NHibernate 延迟加载)。

应应用不同的设计策略建模Person.Properties。出于某种原因,Person 的某些属性必须由Property 值对象列表表示。没有实体,值对象就不存在。值对象是其他实体的组件,它们必须与实体一起加载和更新。这是一个强大的一对多关系。这就是需要充分发挥 ORM 潜力的地方。

于 2013-03-04T15:10:39.940 回答