我确实理解创建整个价值类来封装特定域名的好处。
但是当类/实体持久存储在数据库中时,如何处理它呢?
例如:
class User
{
Email Email { get; set;}
Address Address { get; set;}
PhoneNumber CellPhone { get; set;}
PhoneNumber HomePhone { get; set;}
}
要保留此数据的SQL表将包含标准类型(nvarchar
);
如果ORM从中生成类,它将看起来像
class User
{
string Email { get; set;}
string Address { get; set;}
string CellPhone { get; set;}
string HomePhone { get; set;}
}
所以,问题是,什么是允许使用整个值并消除原始迷恋的模式,但仍然使用原始类型存储它?你如何使ORM与它一起正常工作?
答案 0 :(得分:3)
Primitive Obsession是与域层相关的主题,而不是与持久层相关的主题。当你的域中有User类时,Email应该表示为Value Object,但对于持久层,你不应该使用你的域对象,你需要有不同的User类,Email属性作为原始,然后使用Memento Pattern或自动映射库,如AutoMapper,用于填充持久性用户类并存储它。所有这些工作都应该由您的Generic / Specific Repository Class完成,因为存储库应该是关于持久性技术的抽象消费者。
Domain / User.cs
public class User {
//code omitted for brevity
public EmailAddress Email { get; set; } //or protected set?
}
Persistence / User.cs
public class User {
//code omitted for brevity
public string Email { get; set; }
}
存储库/ UserRepository.cs
public class UserRepository {
//code omitted for brevity
public void Save(Domain.User user) {
//Pseudo-code
//1) Validate Domain.User
//2) Convert Domain.User to Persistence.User
//3) Persist Persistence.User
}
}
最后一条评论:如果您在EmailAddress类上使用重载转换运算符,则可以简化转换,因此您可以将其转换为字符串而不必过于担心它。
答案 1 :(得分:2)
实体框架设计器允许您设置从数据库列映射的属性的名称和可见性,因此您可以设置"基元"属性到private
,然后声明执行转换的包装器属性。
粗略的例子:
// Class generated by entity framework designer
partial class User
{
// Entity framework designer can be told to declare private properties
// with custom "Db" names
private string DbEmail { get; set; } // Maps to Email column
private string DbAddress { get; set; } // Maps to Address column
private string DbCellPhone { get; set; } // Maps to CellPhone column
private string DbHomePhone { get; set; } // Maps to HomePhone column
}
// Class declared in User.cs
partial class User
{
public Email Email
{
get { return new Email(DbEmail); }
set { DbEmail = value.ToString(); }
}
public Address Address
{
get { return new Address(DbAddress); }
set { DbAddress = value.ToString(); }
}
/* and so on...*/
}