POCO - 展平或使用复杂类型

时间:2014-02-04 18:41:37

标签: c# asp.net-mvc poco cqrs complextype

我在ASP.Net MVC项目中使用POCO作为我的模型类。到目前为止,这种方法运行良好,但在大多数情况下,这些POCO包含简单类型。我现在有一种情况,我正在考虑使用一些复杂的类型,我不确定这种方法的好处。由于我需要将单个数据库表拆分为多个POCO,因此我的情况更加复杂。以下示例将解释。我有一张约40个字段的员工桌。我不想总是使用它的所有属性传递这个大对象,所以我创建了3个POCO类,它们表示数据的逻辑分组,如下所示:

public class EmployeeProfile
{
    public int EmployeeID { get; set; }
    public string FirstName { get; set; }        
    public string LastName { get; set; }
    public string Gender { get; set; }
    etc ...    
}

public class EmployeeContact
{
    public Address Address { get; set; }
    public PhoneNumber Phone { get; set; }        
    public string Email { get; set; }
}

public class EmploymentInfo
{
    public decimal Salary { get; set; }       
    public string Occupation { get; set; }
    etc ....
}

到目前为止一直很好,但我有时也需要整个员工表作为单个对象,这是我不确定的地方。我可以通过将其他类型放入其中来创建员工类:

public class EmployeeDetail
{
    public EmployeeProfile EmployeeProfile { get; set; }
    public EmployeeContact EmployeeContact { get; set; }
    public EmploymentInfo EmploymentInfo { get; set; }        

}

或者我可以将三个POCO的所有属性(包括其中包含的复杂类型)复制到EmployeeDetail类中,使其平坦,具有如下简单属性:

public class EmployeeDetail
{
    public int EmployeeID { get; set; }
    public string FirstName { get; set; }        
    public string LastName { get; set; }
    public string Gender { get; set; }
    etc ...        
    public Address Address { get; set; }
    public PhoneNumber Phone { get; set; }        
    public string Email { get; set; }
    public decimal Salary { get; set; }       
    public string Occupation { get; set; }
    etc ....

}

除了包含复杂类型的版本需要更短的时间来创建POCO之外,使用一种方法比另一种方法有什么好处?我认为整个观点是面向对象的,但我不确定在这种情况下,“嵌套”这些对象是否有任何好处。在我看来,将会有更多工作访问嵌套对象并在我的视图模型和域模型之间来回转换它们。

编辑:我想指出我正在考虑的子模型并不特定于特定视图。它们很可能在整个应用程序中使用多个位置。这是一个HR /员工福利应用程序。我需要这些类可重用,否则我只是将它们放在我已经使用的ViewModel中。

3 个答案:

答案 0 :(得分:0)

您实际上想要做的是根据将使用它们的观点将您的业务概念Employee划分为3个部分。因此,您希望将其拆分为EmployeeProfile,以便在与其关联的视图中显示档案管理等。

通过这样做,您可以将业务域与您的视图域混合 - 这是您应该避免的。

如果存在逻辑域拆分,拆分域模型是一个很好的解决方案+很可能需要单独使用某些部分。因此,一个很好的例子可能是(尽管不是必须在每种情况下都是如此)为Address的{​​{1}}定义一个单独的域模型。

如果我穿着你会怎么做:

  • 我会留下一个域模型Employee,而不会将其拆分为与视图相关的部分。
  • 我会根据某个视图中需要的Employee模型数据的哪一部分创建单独的视图模型。
  • 我想如果我可以将Employee模型拆分成一些与域相关的子模型(例如将地址与Employee分开)。

从性能角度来看,上述解决方案也可以带来好处。向数据库发送几个额外的字段并不是一件很麻烦的事情,而在需要整个子模型的情况下加入子模型的需要可能在某些情况下对您的性能有害(特别是如果有数千名员工)。

更重要的是:从维护角度来看,我也建议使用上述解决方案。当从域的角度来看不太合适时使用太多的嵌套对象会引入一些额外的挑战,例如: CRUD操作(例如,在保存/更新整个Employee等时需要提供提交/回滚机制)。

答案 1 :(得分:0)

@bejger有一个很好的答案,但我想补充一点,以便发表评论。

划分数据是一个坏主意。如果你的表包含40个属性或400个属性并不重要,那么查询单个表总是最好的服务而不是连接。现在,我刚才所说的有一些警告。应该打破可以重用的数据的逻辑分组。像地址这样的东西更有意义地被组成一个需要这些信息的类而不是一遍又一遍地重复。

但是,有了合成,你有两个选择:

  1. 复杂类型:复杂类型允许您抽象出要在多个类上使用的一组属性,但在一天结束时,每个表都会获得自己的副本那些属性。这样做的好处是您不需要使用连接来访问数据,但它有一个限制,即您不能包含外键或其他导航属性。

  2. 外键:使用外键可以让一个表保存此属性组的所有实例。同样,缺点是需要加入。如果您可以预见与只是这些属性的交互,或者您需要一对多的集合,这实际上只是合适的选项。使用地址示例,如果您希望能够在不担心员工关联的情况下提取所有地址,或者您希望能够为员工分配可变数量的地址,那么这是一个可行的选择。

  3. 除此之外,不要担心你的桌子大小。即使是包含大量列的表仍然比连接更有效,如果您只需要这些列的子集,您只需选择所需的那些(您可以使用Select方法在LINQ中执行此操作。实体框架将在查询数据库时表示尊重。)

答案 2 :(得分:0)

最终,我没有将我的域模型类拆分为子组件,而是采用了一种完全不同的方法,并选择将我的Read模型与我的域模型分离,或者将R分离为CUD。我这样做的原因是因为我的应用程序的65-75%是关于数据的读取和报告,而不是创建,更新或删除它。

所以在这种情况下,我会将我的域实体作为没有组合的完整Employee模型:

public class Employee
{
    public int EmployeeID { get; set; }
    public string FirstName { get; set; }        
    public string LastName { get; set; }
    public string Gender { get; set; }
    etc ...        
    public Address Address { get; set; }
    public PhoneNumber Phone { get; set; }        
    public string Email { get; set; }
    public decimal Salary { get; set; }       
    public string Occupation { get; set; }
    etc ....
}

除了阅读方面的这一点,我可能会举例如下:

public class EmployeeRoster
{
    public int EmployeeID { get;set;}
    public string FullName { get;set; }   
    public decimal Salary { get;set; }       
    public string Occupation { get;set;}
    public DateTime DateOfHire { get;set;}
}

以及我在阅读方面需要的任何其他对象。这听起来很像CQRS(Command Query Responsibility Segregation),我已经了解CQRS一段时间了,但我在CQRS上看到的资源令人失望。例如,通常建议使用不同的数据库进行读操作和写操作。虽然我理解为什么人们可能想要这样做(表现),但就我的目的来说,这一点是过度的。此外,CQRS上的大多数资源都建议将读取查询直接映射到视图模型。我有一些问题。一,在许多情况下,我需要重用这些读取模型。像EmployeeRoster这样的东西在我的应用程序中的多个地方用于不同的目的。此外,在某些情况下,我需要在读取对象被视图模型使用之前对其应用业务逻辑。

总结一下,此时我确信我需要一个单独的阅读模型,但我并不一定相信正确实施的CQRS是要走的路。所以我现在回答了我自己的问题,但是没有接受这个作为我的答案,因为我认为其他有更多阅读模型和/或CQRS经验的人可以提供比我更好的答案。