C# DDD:类枚举实体
我有以下DB模型: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
**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();