C# DDD:类枚举实体

C# DDD:类枚举实体,c#,.net,design-patterns,domain-driven-design,C#,.net,Design Patterns,Domain Driven Design,我有以下DB模型: **Person table** ID | Name | StateId ------------------------------ 1 Joe 1 2 Peter 1 3 John 2 **State table** ID | Desc ------------------------------ 1 Working 2

我有以下DB模型:

**Person table**
ID    |    Name    | StateId
------------------------------
1          Joe       1
2          Peter     1
3          John      2

**State table**
ID    |    Desc
------------------------------
1          Working
2          Vacation
域模型将(简化):

该状态可用于域逻辑,例如:

if(person.State == State.Working)
    // some logic
根据我的理解,状态就像一个用于域逻辑检查的值对象。但它也需要出现在DB模型中,以表示干净的ERM

因此,国家可以扩展到:

public class State
{
    private int id;
    public string Name { get; set; }

    public static State New {get {return new State([hardCodedIdHere?], [hardCodeNameHere?]);}}
}
但使用这种方法,州的名称将硬编码到域中

你知道我的意思吗?这样的事情有没有一个标准的方法?从我的角度来看,我正在尝试使用一个对象(从ERM设计的角度来看是持久化的)作为我的域中的一种值对象。你觉得怎么样

问题更新: 也许我的问题不够清楚

我需要知道的是,如何使用存储在域逻辑中的数据库中的实体(如State示例)。避免以下情况:

  if(person.State.Id == State.Working.Id)
      // some logic


通常的做法是在枚举中包含值为0的“未知”元素。如果你真的想,你可以这样做,并将其用于新的状态


但您所描述的是业务逻辑。。。创建新对象后设置状态应该发生在业务逻辑层,而不是类本身内部。

在我看来,域层必须与DB模型/ERM设计分离。我很难理解你对国家班的最后建议。IMHO这对于建立一种通用语言(DDD的主要目的之一)来说不是一件好事

我会选择更简单的设计。状态属于Person类。我会把它包括在课堂上

public class Person
{
    public int Id { get; }
    public string Name { get; set; }
    public PersonState State { get; set; }
}
国家本身似乎有定义的价值观(我假设一个人在你的环境中是一名雇员),这些价值观不会经常改变。因此,我将它建模为enum,并将其视为数据类型

enum Days {Working, Vacation};

在我看来,这是一个简单易懂的设计。到ERM设计的映射属于持久层中的IMHO。在那里,枚举必须映射到状态表的键。这可以使用方面来保持原始域模型的干净。

您提出的结构似乎很好。(术语离题:由于
State
有一个ID,所以它不是一个值对象,而是一个实体。)

枚举是一种代码气味,所以不要尝试这样做。使用模式将行为移动到状态对象中更加面向对象

而不是必须写作

if (person.State == State.Working)
    // do something...
在所有代码中,这将允许您编写

person.State.DoSomething();

这更简洁,并允许您在需要时添加新状态。

您希望创建一个工厂方法,根据存储的值实例化所需的适当状态类

差不多

public  static State GetStateByID( StateEnum value)
{
   if(value.Invalid)
        throw new Exception();

switch(value)
   case State.Working
        return new WorkingState();
   case State.somethingelse
         return new somethingelseState();
   case State.something
         return new somethingState();
   case State.whatever
         return new whateverState();

}
使用枚举时,请始终尝试将0用作无效。在这种情况下,枚举是一种值类型,而未赋值的int始终为0

将工厂(如此)与状态模式结合使用是很常见的


因此,当您从数据库中读取存储的整数值时,您可以将int转换为enum,并使用它调用工厂以获取适当的状态对象。

A发现了一些我怀疑与您的问题相关的有用链接,特别是Jimmy Bogard关于.

的讨论,我个人认为针对IDs编程是错误的。相反,我将把你的表格修改如下:

   **State table**
ID    |    Desc               | IsWorking  | IsVacation
-----------------------------------------------------------
1          Working                True         False
2          Vacation               False        True
然后,我将使用这些属性来做出业务决策,例如:

    public void MakeDecisionOnState(State state)
    {
        if (state.IsVacation)
            DoSomething();
        if (state.IsWorking)
            DoSomethingElse();
    }
或者更聪明地使用factory模式基于以下属性创建正确的实例:

    public abstract class State
    {
        public Guid Id { get; set; }

        public string Description { get; set; }

        public abstract void DoSomething();

    }

    public class WorkingState : State
    {
        public override void DoSomething()
        {
            //Do something specific for the working state
        }
    }

    public class VacationState : State
    {
        public override void DoSomething()
        {
            //Do something specific for the vacation state
        } 
    }

    public class StateFactory
    {
        public static State CreateState(IDataRecord record)
        {
            if (record.GetBoolean(2))
                return new WorkingState { Id = record.GetGuid(0), Description = record.GetString(1) };
            if (record.GetBoolean(3))
                return new VacationState { Id = record.GetGuid(0), Description = record.GetString(1) };

            throw new Exception("Data is screwed");
        }
    }
现在您已经消除了if/switch语句,您的代码可以是:

state.DoSomething();

我这样做的原因是,这些类型的实体通常可以由客户进行配置,即他们可能不希望某些状态在系统中处于活动状态,或者他们可能希望将其命名为其他状态。通过对属性进行编程,客户可以随意删除/编辑记录,即使该过程生成了新的ID,也不会影响系统,他们只需要设置属性。

由于您的状态在数据库中有ID,因此可以将其视为实体(值对象由其属性标识,而不是ID标识)。也许你应该删除它们并将值直接存储在相应的db表中?问题与如何实现状态模式有关,这是一个给定的问题,询问者正在使用状态模式。@Brian Leahy:我没有看到问题中提到的状态模式……他使用的是从db值水合而来的状态对象。他正在绘制州工作和州休假的地图。你的回答解释了一点状态模式,但不是如何进行水合作用。。。他的问题是关于水合作用,而不是模式本身的使用。。。偶然发现了一个升级版本。也可以作为NuGet软件包提供!-美好的我仍然不知道如果持久化枚举,如何将枚举与数据库中的记录同步,特别是ID值
    public abstract class State
    {
        public Guid Id { get; set; }

        public string Description { get; set; }

        public abstract void DoSomething();

    }

    public class WorkingState : State
    {
        public override void DoSomething()
        {
            //Do something specific for the working state
        }
    }

    public class VacationState : State
    {
        public override void DoSomething()
        {
            //Do something specific for the vacation state
        } 
    }

    public class StateFactory
    {
        public static State CreateState(IDataRecord record)
        {
            if (record.GetBoolean(2))
                return new WorkingState { Id = record.GetGuid(0), Description = record.GetString(1) };
            if (record.GetBoolean(3))
                return new VacationState { Id = record.GetGuid(0), Description = record.GetString(1) };

            throw new Exception("Data is screwed");
        }
    }
state.DoSomething();