存储库模式以及如何管理实体之间的依赖关系

时间:2012-04-11 21:28:28

标签: c# database repository design-patterns

假设我有一个包含员工和部门的数据库。我创建了一个简单的对象模型来处理这个问题:

class Employee
{
   decimal Salary;
   Department Department;
}

class Department
{     
    decimal TotalSalaries; //calculated as the sum of all employees salaries
                           //stored in database, not just a C# property
}

和..

class Employees
{
    void Create(Employee e);
    void Update(Employee e);    
    void Delete(Employee e);
    ...
}

class Departments
{
    ...
}

如果我想更新一名员工,我只会这样做:

Employee emp = Employees.GetById(1);
emp.Salary += 100; //salary increase
Employees.Update(emp);

我希望每次更新其中一名员工TotalSalaries时自动重新计算Department Salary

我可以做以下事情:

void Update(Employee e)
{
     ... //
     ... // sql stuff to update employee fields.

     Departments.ReCalculateTotalSalaries(e.Department.Id);
}

但如果我这样做,我想我打破了单一责任原则。为什么员工更新方法需要关注Departments?这是一个非常简单的例子,但如果我在实体之间有很多依赖关系,代码就会变得非常混乱。

我考虑过其他方法:

1)为Employee创建一个服务类:EmployeeService。然后在此类中,定义方法Update()以更新员工。该方法将更新员工和重新计算部门总数(当然通过调用存储库方法)。

void Update(Employee e)
{
     Employees.Update(e);
     Departments.ReCalculateTotalSalaries(e.Department.Id);
}

问题:如果我的数据模型中有很多依赖项,我几乎要“加倍”我的模型层(类数x2)。另外:我怎样才能确定控制器(或视图)总是会直接调用服务方法而不是存储库?


2)在Employees存储库中有一个事件OnSalaryChange(),从Update()方法调用。存储库Departments将订阅它,并在需要时执行必要的操作(换句话说:call ReCalculateTotalSalaries())。然后,属于Departments DepartmentsEmployees存储库的内容不必关心其他实体。

问题:当工资发生变化时(如果有多个订阅者),可能会很难跟踪(通过阅读代码或调试模式)。如果需要以特定顺序调用订户,它也可能成为一个问题。

3 个答案:

答案 0 :(得分:1)

您应该有理由在数据库中使用计算字段(性能?)。 我尽量避免这种情况。

原因更新不是唯一的情况:如果您删除了员工,则还必须重新计算。

但这可能很危险(出于一个原因,你有一天用数据库中的脚本工作,忘记在更新员工工资后手动更新你的部门表吗?)

所以

  • 您可以在数据库中创建一个触发器(如果您先拥有代码,则可以始终使用SMO保留“代码”逻辑)

  • 您可以创建数据库视图

  • 您可以在您的部门实体中拥有一个雇员ICollection(或IList,或......),并获得一个总薪水作为“简单获取财产”,它总结了该职位的工资......

答案 1 :(得分:1)

在您的情况下,是的,您打破了单一的责任原则。

尊重原则,这一行

Departements.ReCalculateTotalSalaries(e.Department.Id);

应该是

e.Department.ReCalculateTotalSalaries();

为此,您必须在数据库和数据库模式中使用两个实体之间的关系。

编辑:请注意,ReCalculateTotalSalaries应修改属性,但不应将其自身保存在数据库中。因此,因为它不会触及数据库本身,所以它应该放在你的部门实体而不是你的部门库中

EDIT2 :当您致电Employees.Update(yourEmployee)时,它还会保存您的theEmployee.Department。这就是这个的魔力!无需为每个实体调用一个Update()

答案 2 :(得分:0)

为什么不将构造函数注入(或其他类型)其他存储库到员工存储库中?

public EmployeeRepository(DepartmentRepository department){}

...然后将_department用于任何你需要的东西。这是一种干净的方式。您无法避免在代码中进行复杂的交互。

...或者使用一个'监督'存储库,它将两者结合起来 - 并为两者注入了引用 - 并首先执行Employee事务,然后调用另一个?也许这更像是你的想法     MasterRepository(员工员工,部门部门)......     SaveEmployee()     {         //先做员工,然后......         //呼叫部门     }

希望这会有所帮助。

编辑:我不会参与这些事件 - 至少对于存储库来说,有原因,但首先是它对Db和数据的位置并不是很好。我不明白的第一个选项可能与我的主回购相似。