Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/321.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

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/dart/3.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# DDD和实体框架类_C#_Entity Framework_Model_Domain Driven Design - Fatal编程技术网

C# DDD和实体框架类

C# DDD和实体框架类,c#,entity-framework,model,domain-driven-design,C#,Entity Framework,Model,Domain Driven Design,我已经阅读了许多关于DDD的文章,并且理解到,我应该在基础设施级别使用我的域模型类,因此,我应该使用与实体框架基础设施相同的类,并使用它们生成表(代码优先方法)等。但是我的域模型可以完全不同于关系数据库模型 为什么我不能再创建一个模型,即infrastructure model来创建关系数据库模型,而不将域模型与EF类混合使用?考虑以下简单示例: 域模型 public class Customer { public Customer(IRegistrar registrar) {

我已经阅读了许多关于DDD的文章,并且理解到,我应该在基础设施级别使用我的域模型类,因此,我应该使用与实体框架基础设施相同的类,并使用它们生成表(代码优先方法)等。但是我的域模型可以完全不同于关系数据库模型


为什么我不能再创建一个模型,即infrastructure model来创建关系数据库模型,而不将域模型与EF类混合使用?

考虑以下简单示例:

域模型

public class Customer
{
    public Customer(IRegistrar registrar)
    {
        this.registrar = registrar;
    }

    public int Age
    {
        get
        {
            // Just for this example. This will not work for all locals etc but beyond the point here.
            var today = DateTime.Today;
            return today.Year - this.DateOfBirth.Year;
        }
    }

    public DateTime DateOfBirth { get; set; }

    public int Register()
    {
        if (this.Age < 18)
        {
            throw new InvalidOperationException("You must be at least 18 years old");
        }

        int id = this.registrar.Register(this);

        return id;
    }
}

public interface IRegistrar 
{
    public int Register(Customer customer);
}
  • 现在开发者可以忘记在两个不同的地方检查年龄

  • 代码也在两个不同的地方。如果有bug,我们需要记住在两个不同的地方修复它
  • 如果公司在法定年龄为21岁的地方开始运营,我们需要找到所有地方并添加此规则
  • 如果我们与BA讨论规则,那么我们需要查看所有应用程序并找到规则
  • 在上述情况下,我们只有一条规则:年龄必须大于18岁。如果我们有更多的规则和更多的课程呢?你可以看到这将走向何方


    EF型号

    public class Customer
    {
        public Customer(IRegistrar registrar)
        {
            this.registrar = registrar;
        }
    
        public int Age
        {
            get
            {
                // Just for this example. This will not work for all locals etc but beyond the point here.
                var today = DateTime.Today;
                return today.Year - this.DateOfBirth.Year;
            }
        }
    
        public DateTime DateOfBirth { get; set; }
    
        public int Register()
        {
            if (this.Age < 18)
            {
                throw new InvalidOperationException("You must be at least 18 years old");
            }
    
            int id = this.registrar.Register(this);
    
            return id;
        }
    }
    
    public interface IRegistrar 
    {
        public int Register(Customer customer);
    }
    
    您的EF模型可能如下所示:

    public int Register(Customer customer)
    {
        var today = DateTime.Today;
        var age = today.Year - this.DateOfBirth.Year;
        if (age < 18)
        {
            // Return a SOAP fault or some other error
        }
    
        int id = this.registrar.Register(customer);
    
        // The rest of code
    }  
    
    public class Customer
    {
        public int Id { get; set; }
        public DateTime DateOfBirth { get; set; }  
    
        // It may have a foreign key etc.    
    }
    
    public class Customer
    {
        // Or instead of Domain.Customer, it may be a CustomerDto which is used
        // to transfer data from one layer or tier to another.
        // But you get the point.
        public Customer(Domain.Customer customer)
        {
            this.DateOfBirth = customer.DateOfBirth;
            this.Age = customer.Age;
            if (this.DateOfBirth.DayOfYear == DateTime.Today.DayOfYear)
            {
                this.Greeting = "Happy Birthday!!!";
            }
        }
        public int Age { get; set; }
    
        [Required(ErrorMessage = "Date of birth is required.")]
        [Display(Name = "Data of birth")]
        public DateTime DateOfBirth { get; set; }
    
        public string Greeting { get; set; }
    }
    

    应用层模型

    public class Customer
    {
        public Customer(IRegistrar registrar)
        {
            this.registrar = registrar;
        }
    
        public int Age
        {
            get
            {
                // Just for this example. This will not work for all locals etc but beyond the point here.
                var today = DateTime.Today;
                return today.Year - this.DateOfBirth.Year;
            }
        }
    
        public DateTime DateOfBirth { get; set; }
    
        public int Register()
        {
            if (this.Age < 18)
            {
                throw new InvalidOperationException("You must be at least 18 years old");
            }
    
            int id = this.registrar.Register(this);
    
            return id;
        }
    }
    
    public interface IRegistrar 
    {
        public int Register(Customer customer);
    }
    
    您的MVC视图模型可能如下所示:

    public int Register(Customer customer)
    {
        var today = DateTime.Today;
        var age = today.Year - this.DateOfBirth.Year;
        if (age < 18)
        {
            // Return a SOAP fault or some other error
        }
    
        int id = this.registrar.Register(customer);
    
        // The rest of code
    }  
    
    public class Customer
    {
        public int Id { get; set; }
        public DateTime DateOfBirth { get; set; }  
    
        // It may have a foreign key etc.    
    }
    
    public class Customer
    {
        // Or instead of Domain.Customer, it may be a CustomerDto which is used
        // to transfer data from one layer or tier to another.
        // But you get the point.
        public Customer(Domain.Customer customer)
        {
            this.DateOfBirth = customer.DateOfBirth;
            this.Age = customer.Age;
            if (this.DateOfBirth.DayOfYear == DateTime.Today.DayOfYear)
            {
                this.Greeting = "Happy Birthday!!!";
            }
        }
        public int Age { get; set; }
    
        [Required(ErrorMessage = "Date of birth is required.")]
        [Display(Name = "Data of birth")]
        public DateTime DateOfBirth { get; set; }
    
        public string Greeting { get; set; }
    }
    
    这里有一个问题:您见过多少带有
    Display
    属性的EF模型?我将让您决定EF模型是否应该关注它在UI中的显示方式。只是假设我的EF模型将显示在UI中是错误的。也许我们班上唯一的消费者是另一个web服务。我不认为EF模型中应该有
    显示
    ,但有些可能不同意我的观点;你打电话

    stackoverflow上有很多问题,人们问有时需要PropertyX,有时不需要,我怎么做?如果您没有在EF模型上添加
    Required
    属性并在视图中使用EF模型,那么您就不会有这个问题。视图将有一个模型,其中PropertyX是必填字段。该模型将使用
    Required
    属性装饰PropertyX,而不需要PropertyX的视图的另一个模型将不会使用
    Required
    属性装饰属性


    视图模型

    然后,您可以为WPF应用程序的客户提供一个viewmodel,也可以为前端提供一个javascript viewmodel(KnockoutJS viewmodel)


    结论和问题的答案

    总之,您可以拥有不同于实体模型的域模型。您的域模型应该不知道数据库。如果由于规范化而决定从一个表中删除一列,并将其放入自己的表中,则实体模型将受到影响。您的域模型不应受到影响

    我在网上读到过一些争论,比如“这个设计花的时间太长了,我只是想快速推出一些东西,并把它交给客户,然后得到报酬”。如果你不是在设计一个需要维护和添加功能的产品,但是你只是在为你的客户设计一个快速的小网站,那么不要使用这种方法。没有一种设计适用于所有情况。需要注意的是,您的设计应该明智地选择,并考虑到未来

    此外,从实体模型到域到MVC模型的转换也不需要手动完成。有一些库可以很容易地为您做到这一点,例如


    但我必须承认,网上有大量的例子,在许多应用程序中也有使用,实体模型在整个应用程序中使用,规则在任何地方都用大量的if语句实现

    这与DDD的关系

    当我读到你的问题时,我发现一些吸引眼球的东西。这是:

    我已经阅读了许多关于DDD的文章,并且了解到,我应该在基础设施级别使用我的域模型类,因此,我应该使用与实体框架基础设施相同的类,并使用它们生成表(代码优先方法)

    老实说,DDD知识的最好来源仍然是蓝皮书。我知道,我知道,它很厚,很难读。可以看看弗农蒸馏的DDD。结论应该是,DDD并不是真正处理持久性,而是更深入地了解领域,更好地理解领域专家。当然,它没有提到ORM

    域模型持久性

    域模型通常由具有状态和行为的对象(如果我们谈论面向对象的模型)组成。模型将有一个或多个实体,可能是一些值对象。在许多情况下,每个有界上下文只有一个实体。实体以集合的形式分组,这些集合一起变化,形成事务边界。这意味着聚合中的每个更改都是一个事务,无论此更改涉及多少个实体。每个聚合都有一个且只有一个实体,即聚合根,它公开了公共方法,供其他人使用整个聚合

    因此,您的存储库应注意:

    • 在一个事务中为新对象和更新对象持久化整个聚合(无论有多少个实体)
    • 通过其标识(聚合根
      Id
      property)从持久性存储中获取整个聚合
    您当然需要一些查询,但只要不修改域模型状态,他们就可以查询所需的方式。许多人将查询方法添加到存储库中,但这取决于您。我将使用
    DbContext
    扩展方法将它们实现为一个单独的静态类

    相互不匹配的型号

    你提到你的坚持