首先,EF不是我们开发环境的选项,所以请不要“只使用EF”答案......
我认为这是一个相当标准的困境,所以我确信大多数专业人士都应该有这样的方式,我只是没有偶然发现...所以我在这里希望你们都能告诉我什么它是。
假设您有以下数据库表:
tblCompanies
ID
NAME
tblDepartments
ID
COMPANY_ID
NAME
tblEmployees
ID
DEPARTMENT_ID
FIRSTNAME
LASTNAME
...在代码中的Classes中表示这个的最佳方法是什么?
我认为最佳方式是这样的:
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个字段似乎是错误的对我来说)
这些方案是否有最佳实践?尽管我不喜欢只是出于懒惰而将相同数据存储在多个类中的想法,但我也不喜欢只填充其中一个字段的类的实例,因为这是我当时的所有内容。 。
答案 0 :(得分:5)
这是一个常见问题,也是ORM试图解决的问题。根据想要的内容以及约束的内容,确保它不是一件容易的事。
只保留一份信息的基本选项。懒惰地按要求加载数据或者将其全部加载(Greedy load)。否则你必须复制数据。
使用延迟加载,您基本上可以进行设置,以便在导航到属性时调用数据库并获取加载表示您正在访问的属性的实体所需的信息。需要注意的棘手部分是SELECT N + 1问题。当您最终迭代一组父实体并在每个子实体上触发延迟加载时遇到此问题,从而导致对数据库的N + 1次调用以加载一组实体(1)及其子项(N)。
贪婪加载基本上表示加载你需要的所有内容。 ORM(它们工作的地方)很好,因为它们通过LINQ处理许多细节,并创建可以高效和可维护的解决方案,通常还允许您操纵贪婪和延迟加载的使用。
另一个重要问题是多对多关系。您需要确保不进行循环初始化,并获得循环依赖的所有包袱。肯定有更多我错过了。
在我的拙见中,我不太确定最佳实践和实践一样,其中一些很糟糕 - 没有什么是完美的。你可以:
开始滚动您自己的对象关系映射器,允许您删除重复的ID
使用较轻的ORM框架来处理其中一些,以便您摆脱重复的ID
创建专门的查询以加载数据聚合,允许您删除重复的ID (* cough * DDD)
只需像上面提到的那样保留ID 的重复内容,而不必担心在您的域中创建显式关系模型。
这个你根据你的约束选择最好的东西。这是一个很深刻的主题,我的经验是有限的...... 所以我要说的很多盐。
答案 1 :(得分:3)
我认为这种事情没有“最佳实践”手册,当然这取决于你的课程将如何使用。但根据我的个人经验,我最终采用了这种方法:
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),以防止实例化新类以获取已加载的值。 / p>
正如其他人所说,您也可以模拟“延迟加载”,但这需要您付出额外的努力。
答案 2 :(得分:2)
我认为这取决于要求。您是否需要向上遍历(从部门获取公司,从员工获取部门等)。如果你这样做,那么你最好提供一种方法。理想情况下,这将是公司或部门属性,当然你不希望得到你真正不需要的数据,所以你可能会保留一个私人公司ID,并有一个公共getCompany函数查询数据
答案 3 :(得分:1)
我认为这不是一个真正的OOP问题,在你的情况下,你只有一个数据库模型(类中的数据库表示),它不包含任何逻辑,并且所有类都用作结构,这是一种正确的方法将数据库映射到类 - 结构。因此,在您的下一个代表程序逻辑的模块中,您必须将数据库模块映射到包含逻辑的实际类(我的意思是实现它的方法),当然如果您确实需要它们。因此,在我看来,OO问题应该在您的应用程序的逻辑部分。另一方面,您可以查看nhibernate以及如何在其中完成映射,它将为您提供bes数据库模型实现的提示。
答案 4 :(得分:0)
我相信这就是你的类在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?
答案 5 :(得分:0)
还有另一种选择。创建一个'DataController'类来处理对象的加载和“memoization”。 dataController维护[CompanyIDs,Company objects]和[DepartmentIDs,Department objects]的字典。加载新的部门或公司时,您在此DataController字典中保留一条记录。然后,当您实例化新的Department或Employee时,您可以直接设置对父对象的引用,也可以使用Lazy [Company / Department]对象并使用lambda(在构造函数中)设置它,这将保持对象的范围DataController没有直接在对象内引用它。有一点我忘了提到,如果找不到特定的ID,你也可以在查询数据库的字典的getter / get方法中放置逻辑。将所有这些结合在一起可以使您的类(模型)非常干净,同时对于何时/如何加载数据仍然相当灵活。