【问题标题】:Class linking best practices in C#C# 中的类链接最佳实践
【发布时间】:2012-03-18 08:26:30
【问题描述】:

首先,EF 不是我们开发环境的一个选项,所以请不要回答“只使用 EF”...

我认为这是一个非常标准的两难境地,所以我敢肯定,大多数专业人士都会有一种我没有偶然发现的方法......所以我在这里希望你们能告诉我什么是的。

假设您有以下数据库表:

tblCompanies 
ID 
NAME

tblDepartments 
ID 
COMPANY_ID 
NAME

tblEmployees
ID 
DEPARTMENT_ID 
FIRSTNAME 
LASTNAME

...在代码中的类中表示这一点的最佳方式是什么?

我认为最好的方式是这样的:

public class Company
{
     public int ID { get; set; }
     public string Name { get; set; }
     public List<Department> Departments { get; set; }
}

public class Department
{
     public int ID { get; set; }
     public string Name { get; set; }
     public List<Employee> Employees { get; set; }
}

public class Employee
{
     public int ID { get; set; }
     public string FirstName { get; set;}
     public string LastName { get; set; }
}

我相信这是解决此问题的“OOP 正确方法”。然而,似乎总是会发生这样的事情:

public class Department
{
     public int ID { get; set; }
     public string Name { get; set; }
     public int CompanyID { get; set; }
     public List<Employee> Employees { get; set; }
}

...主要是因为当您从数据库中仅提取一个部门时,您只会拥有公司 ID,而不是完全填充公司类实例所需的所有其他属性。

(我在这里使用了一个非常普通的示例,但我在当前项目中实际处理的示例有 3 个字段用于将数据链接在一起,因此在多个类中具有相同 3 个字段的想法似乎是错误的对我来说)

这些场景是否有最佳实践?尽管我不喜欢仅仅出于懒惰而将相同的数据存储在多个类中,但我也不喜欢返回一个只填充一个字段的类的实例,因为这就是我当时所拥有的一切.

【问题讨论】:

  • 我相信你的“最佳方式”示例,你想要一个 Company DepartmentCompany {get; set;} 吗?
  • 在您的一对多关系中,看起来您只代表“多”部分。例如,Department 有员工,但Employee 没有DepartmentEmployee 应该有一个 Department 属性,以便您可以轻松获取 Employee's Department。

标签: c# oop class coding-style


【解决方案1】:

还有另一种选择。创建一个“DataController”类来处理对象的加载和“记忆”。 dataController 维护 [CompanyIDs, Company objects] 和 [DepartmentIDs, Department objects] 的字典。当您加载一个新的部门或公司时,您会在此 DataController 字典中保留一条记录。然后,当您实例化一个新的部门或员工时,您可以直接设置对父对象的引用,或者您可以使用 Lazy[Company/Department] 对象并使用 lambda(在构造函数中)设置它,这将保持范围没有直接在对象内部引用的 DataController。我忘了提一件事,如果找不到特定 ID,您还可以在查询数据库的字典的 getter/get 方法中放置逻辑。将所有这些结合使用可以让您的类(模型)非常干净,同时在加载数据的时间/方式方面仍然相当灵活。

【讨论】:

    【解决方案2】:

    我相信这就是您的类在 NHibernate 中的样子:

    public class Company
    {
         public int ID { get; set; }
         public string Name { get; set; }
         public IList<Department> Departments { get; set; }
    }
    
    public class Department
    {
         public int ID { get; set; }
         public string Name { get; set; }
         public Company Company { get; set; }
         public IList<Employee> Employees { get; set; }
    }
    
    public class Employee
    {
         public int ID { get; set; }
         public string FirstName { get; set;}
         public string LastName { get; set; }
         public Department Department { get; set; }
    }
    

    请注意,除了您已经指定的内容之外,还有一种方法可以从员工导航到部门以及从部门导航到公司。

    NHibernate 具有各种功能,可以让它正常工作。它工作得非常非常好。主要技巧是允许延迟加载的运行时代理对象。此外,NHibernate 支持许多不同的方式来急切和延迟加载,这正是您想要的方式。

    当然,您可以在不使用 NHibernate 或类似 ORM 的情况下获得这些相同的功能,但为什么不使用功能丰富的主流技术而不是手动编码您自己的功能较差的自定义 ORM?

    【讨论】:

    • 我没有研究过 NHibernate,但我研究了 EF,但我找不到在我办公室的编程环境中实现该技术的有效方法。我过去问过一个问题,基本上是关于“我们有办法实现 EF 吗?”我真的没有得到关于我们将如何在我们的办公室实施它的答案。在 asp.net 论坛上也是如此。
    • 就个人而言,在许多情况下,数据库的结构不是由应用程序(或实用程序)开发人员设计的,数据库中的数据结构也不容易映射到 ORM。
    【解决方案3】:

    这是一个常见问题,也是 ORM 试图解决的问题。确保这不是一件容易的事,这取决于您的想要是什么以及您的约束是什么。

    只有两个基本选项可以保留一份信息副本。根据请求延迟加载数据或从头开始加载(贪婪加载)。否则你必须复制数据。

    通过延迟加载,您基本上进行了设置,以便在导航到属性时调用数据库并获取加载表示您正在访问的属性的实体所需的信息。与此有关的棘手部分是 SELECT N + 1 问题。当您最终迭代一组父实体并在每个子实体上触发延迟加载时,您会遇到此问题,从而导致 N+1 次调用数据库以加载一组实体 (1) 及其子实体 (N)。

    贪婪加载基本上是说加载您需要开始的所有内容。 ORM(它们工作的地方)很好,因为它们通过 LINQ 处理许多细节,并创建通常具有高性能和可维护性的解决方案,同时还允许您操纵贪婪和延迟加载的使用。

    另一个重要的问题是多对多关系。您需要确保没有循环初始化,并获得循环依赖的所有包袱。肯定还有很多我错过了。

    在我的拙见中,我不太确定是否存在 最佳实践,因为存在一些不好的 实践 - 没有什么是完美的。你可以:

    1. 开始滚动您自己的对象关系映射器让您摆脱重复的 ID

    2. 使用更轻量级的 ORM 框架来处理其中的一些问题让您摆脱重复的 ID

    3. 创建专门的查询以加载数据聚合让您摆脱重复的 ID(* 咳嗽 * DDD)

    4. 只需像上面提到的那样保留 ID 的重复,不必担心在您的域中创建显式关系模型。

    这个是根据你的限制选择最好的。这是一个深奥的话题,我的经验有限......所以请用大量的盐来理解我所说的

    【讨论】:

    • 非常好的答案......如果我可以选择共同答案,我也会选择这个。
    【解决方案4】:

    我认为没有针对此类事情的“最佳实践”手册,这当然取决于您的课程将如何使用。但根据我的个人经验,我最终采用了这种方法:

    public class Company
    {
       public int ID { get; set; }
       public string Name { get; set; }
    
       public IEnumerable<Department> GetDepartments()
       {
          // Get departments here
       }
    }
    
    public class Department
    {
       public int ID { get; set; }
       public string Name { get; set; }
       protected int CompanyID { get; set; }
    
       private Company _Company;
       public Company Company
       {
          get
          {
             // Get company here
          } 
       }
    
       public IEnumberable<Employee> GetEmployees()
       {
          // Get employees here
       }
    }
    
    public class Employee
    {
       public int ID { get; set; }
       public string Name { get; set; }
       protected int DepartmentID { get; set; }
    
       private Department _Department;
       public Department Department
       {
          get
          {
             // Get department here
          } 
       }
    
       public IEnumberable<Employee> GetEmployees()
       {
          // Get employees here
       }
    }
    

    在某些情况下,我将我的类的一些“导航”属性公开为public(如 CompanyID 和 DepartmentID),以防止实例化新类以获取已加载的值。

    正如其他人所指出的,您也可以模拟“延迟加载”,但这需要您付出一些额外的努力。

    【讨论】:

    • 我已经做了一些类似于延迟加载变体的事情......这可能真的是最好的方法。我只是不确定这是否是大多数人采用的标准解决方案,但话说回来,这种方法可能与 ORM 的做法最相似,而且这些似乎是当前的时尚,因此可以解释这一点。
    【解决方案5】:

    我相信这不是一个真正的 OOP 问题,在您的情况下,您只有一个数据库模型(类中的数据库表示),它不包含任何逻辑并且所有类都用作结构,这是正确的方法将您的数据库映射到类 - 结构。因此,在下一个代表程序逻辑的模块中,如果你真的需要它们,你必须将数据库模块映射到包含逻辑的真实类(我的意思是实现它的方法)。所以在我看来,OO 问题应该在你的应用程序的逻辑部分。另一方面,您可以查看 nhibernate 以及其中的映射如何完成,它将为您提供有关 bes 数据库模型实现的提示。

    【讨论】:

      【解决方案6】:

      我认为这取决于要求。您是否需要向上遍历(从部门获取公司,从员工获取部门等)。如果你这样做了,那么你最好提供一种方法来做到这一点。理想情况下,这类似于 Company 或 Department 属性,当然您不会想要获取您并不真正需要的数据,因此您可能会保留一个私有公司 ID 并拥有一个公共 getCompany 函数来查询数据.

      【讨论】:

      • 出于某种原因,我什至从未考虑过隐藏 ID。当然,他们必须使用“getCompany”来做任何事情……即使只是为了获取 ID。如果 Company 是在考虑延迟加载的情况下构建的,那么它将是两全其美,因为 Department.Company.ID 可以在没有额外工作的情况下检索,并且 Department.Company.Name 也可以检索,但构建了一个 data-dip -在。对于如何存储和隐藏以供将来使用仍然是一个好主意。
      • 真的,无论哪种方式,您都在访问数据库。您可以考虑您所说的比延迟加载更急切的加载。在您加载它时,甚至没有人要求它(并且可能永远不会)。另一方面,如果您在被要求之前不加载,那么当他们想要数据时您会得到延迟。权衡是数据库请求与函数处理时间。如果您愿意接受 db 的打击,那么您最好采用异步方式(因为您还不需要数据)。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-11-08
      • 2012-10-07
      • 2010-12-16
      • 1970-01-01
      • 2014-06-21
      • 1970-01-01
      相关资源
      最近更新 更多