假设我有一个包含员工和部门的数据库。我创建了一个简单的对象模型来处理这个问题:
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
Departments
,Employees
存储库的内容不必关心其他实体。
问题:当工资发生变化时(如果有多个订阅者),可能会很难跟踪(通过阅读代码或调试模式)。如果需要以特定顺序调用订户,它也可能成为一个问题。
答案 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和数据的位置并不是很好。我不明白的第一个选项可能与我的主回购相似。