Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/angular/32.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 领域模型和相关数据(贫血领域模型)_C#_Architecture_Asp.net Core_Domain Driven Design_Entity Framework Core - Fatal编程技术网

C# 领域模型和相关数据(贫血领域模型)

C# 领域模型和相关数据(贫血领域模型),c#,architecture,asp.net-core,domain-driven-design,entity-framework-core,C#,Architecture,Asp.net Core,Domain Driven Design,Entity Framework Core,我目前正在使用EntityFrameworkCore使用ASP.NETCore1.0。我对数据库中的数据进行了一些复杂的计算,我不知道如何使用依赖注入构建一个合适的体系结构,而不构建一个贫乏的域模型() (简化)示例: 我拥有以下实体: public class Project { public int Id {get;set;} public string Name {get;set;} } public class TimeEntry { publi

我目前正在使用EntityFrameworkCore使用ASP.NETCore1.0。我对数据库中的数据进行了一些复杂的计算,我不知道如何使用依赖注入构建一个合适的体系结构,而不构建一个贫乏的域模型()

(简化)示例:

我拥有以下实体:

public class Project {
    public int Id {get;set;}
    public string Name {get;set;}        
}

public class TimeEntry
{
    public int Id {get;set;}
    public DateTime Date {get;set;}
    public int DurationMinutes {get;set;}
    public int ProjectId {get;set;}
    public Project Project {get;set;}        
}

public class Employee {
    public int Id {get;set;}
    public string Name {get;set;}
    public List<TimeEntry> TimeEntries {get;set;}
}
如果我想得到时间表,我需要注入EmployeeService并调用
GetMonthlyTimeSheet

因此-我的服务中有很多GetThis()和GetThis()方法,尽管这些方法完全适合
Employee
类本身

我想要实现的是:

public class Employee {
    public int Id {get;set;}
    public string Name {get;set;}
    public List<TimeEntry> TimeEntries {get;set;}

    public List<CalculatedMonth> GetMonthlyTimeSheet() {            
        var result = new List<CalculatedMonth>();

        //complex calculation using TimeEntries etc here

        return result;
    }
}

public IActionResult GetTimeSheets(int employeeId) {
    var employee = _employeeRepository.Get(employeeId);
    return employee.GetTimeSheets();
}
公共类员工{
公共int Id{get;set;}
公共字符串名称{get;set;}
公共列表时间项{get;set;}
公共列表GetMonthlyTimeSheet(){
var result=新列表();
//此处使用时间项等进行复杂计算
返回结果;
}
}
公共IActionResult GetTimeSheets(int employeeId){
var employee=_employeeRepository.Get(employeeId);
return employee.GetTimeSheets();
}
…但为此,我需要确保从数据库填充TimeEntries列表(EF Core不支持延迟加载)。我不想在每个请求中都包含(x=>y)所有内容,因为有时我只需要员工的姓名而不需要时间条目,这会影响应用程序的性能

有人能告诉我如何正确地设计这个吗

编辑: 一种可能性(根据第一个答案的评论)是:

public class Employee {
    public int Id {get;set;}
    public string Name {get;set;}
    public List<TimeEntry> TimeEntries {get;set;}

    public List<CalculatedMonth> GetMonthlyTimeSheet() { 
        if (TimeEntries == null)
          throw new PleaseIncludePropertyException(nameof(TimeEntries));

        var result = new List<CalculatedMonth>();

        //complex calculation using TimeEntries etc here

        return result;
    }
}

public class EmployeeService {
    private DbContext _db;
    public EmployeeService(DbContext db) {
        _db = db;
    }

    public Employee GetEmployeeWithoutData(int employeeId) {
        return _db.Employee.Single();
    }

    public Employee GetEmployeeWithData(int employeeId) {
        return _db.Employee.Include(x=>x.TimeEntry).ThenInclude(x=>x.Project).Single();
    }
}

public IActionResult GetTimeSheets(int employeeId) {
    var employee = _employeeService.GetEmployeeWithData(employeeId);
    return employee.GetTimeSheets();
}
公共类员工{
公共int Id{get;set;}
公共字符串名称{get;set;}
公共列表时间项{get;set;}
公共列表GetMonthlyTimeSheet(){
if(TimeEntries==null)
抛出新的PleaseIncludePropertyException(nameof(TimeEntries));
var result=新列表();
//此处使用时间项等进行复杂计算
返回结果;
}
}
公营雇员服务{
私有DbContext_db;
公共雇员服务(DbContext db){
_db=db;
}
公共雇员GetEmployeeWithout数据(int employeeId){
return _db.Employee.Single();
}
公共雇员GetEmployeeWithData(int employeeId){
return _db.Employee.Include(x=>x.TimeEntry).然后Include(x=>x.Project).Single();
}
}
公共IActionResult GetTimeSheets(int employeeId){
var employee=_employeeService.GetEmployeeWithData(employeeId);
return employee.GetTimeSheets();
}

如果我正确理解了您的问题,您可以使用一个技巧,将服务注入到您的实体中,以帮助它完成工作,例如:

public class Employee()
{
    public object GetTimeSheets(ICalculatorHelper helper)
    {
    }
}
然后在保存员工的服务中,您将在构造函数中获得该服务,并将其传递给employee类进行计算。该服务可以是一个门面,例如获取所有数据并执行初始化或任何您真正需要的操作

对于时间条目,您可以使用如下函数获取它们:

private GetTimeEntries(ICalculationHelper helper)
{
   if (_entries == null)
   {
       _entries = helper.GetTimeEntries();
   }
   return _entries;
}
如果这种模式适合您,这当然取决于您的缓存策略等等


就我个人而言,我发现使用贫乏的类和服务中的大量业务逻辑相当容易。我确实在对象中添加了一些,例如,根据FirstName和LastName计算FullName。通常是不涉及其他服务的东西。尽管这是一个偏好问题。

不要试图解决聚合的查询问题。您的聚合旨在处理命令并保护不变量。它们围绕一组数据形成一致性边界

员工
对象是否负责保护员工时间表的完整性?如果没有,则此数据不属于
Employee

延迟加载对于CRUD模型来说可能很好,但在我们设计聚合时通常被认为是一种反模式,因为这些聚合应该尽可能小和内聚

您是否根据时间表的计算结果做出业务决策?有什么不变量需要保护吗?如果决策是针对过时的时间表数据做出的,这有关系吗?如果这些问题的答案是,那么您的计算实际上只是一个查询


在服务对象中放置查询是可以的。这些服务对象甚至可能位于域模型之外(例如,在应用程序层),但没有严格的规则可遵循。此外,您可以选择加载一些聚合以访问处理计算所需的数据,但通常最好直接进入数据库。这允许更好地分离读写(CQR)。

避免进入贫血域模型的第一步是使setter
私有化,并通过参数验证创建适当的对象构造函数。此外,当使用EF时,如果您的
公共
构造函数不是如我所建议的那样无参数的,您将至少需要一个
受保护的
构造函数。我认为这是考虑如何对您的模型进行单元测试的工作。IMHO认为,使代码可测试通常是解决设计问题及其解决方案的一种很好的方法。例如,鉴于上述设计,您是否高兴能够测试(单元或集成)您的
EmployeeService
?他的问题是数据可能加载到对象中,也可能不加载到对象中。@HristoYankov:这有助于他不必担心如何获取数据,这是
ICalculatorHelper
(或其他命名服务)的任务,他可以将计算的逻辑保存在模型中(如果它特定于模型)或者在计算器帮助程序中,如果它已退出
private GetTimeEntries(ICalculationHelper helper)
{
   if (_entries == null)
   {
       _entries = helper.GetTimeEntries();
   }
   return _entries;
}