使用Entity Framework之类的ORM时如何处理原始的痴迷?
How to deal with primitive obsession when using ORM like Entity Framework?
我明白创造整体价值的好处 classes 来封装特定领域的想法。
但是当class/entity持久化到数据库中时,我该如何处理呢?
例如:
class User
{
Email Email { get; set;}
Address Address { get; set;}
PhoneNumber CellPhone { get; set;}
PhoneNumber HomePhone { get; set;}
}
SQL table 以保留此数据将包含标准类型的列 (nvarchar
);
如果 ORM 从中生成 class,它将看起来像
class User
{
string Email { get; set;}
string Address { get; set;}
string CellPhone { get; set;}
string HomePhone { get; set;}
}
那么,问题是,允许使用整个值并消除对原始类型的痴迷,但仍使用原始类型存储它的模式是什么?以及如何使 ORM 与它一起正常工作?
Primitive Obsession 是与域层相关的主题,与持久层无关。当你的域中有 User class 时,Email 应该表示为值对象,但是对于持久层,你不应该使用你的域对象,你需要有不同的 User class,用 Email 属性 作为原始数据,然后使用 Memento Pattern 或像 AutoMapper 这样的自动地图库来填充你的持久性用户 class 并存储它。所有这些工作都应该由您的 Generic/Specific 存储库 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; }
}
Repository/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 class 上重载转换运算符,您可以简化转换,这样您就可以将其转换为字符串而不必担心太多。
entity framework 设计器允许您设置从数据库列映射的属性的名称和可见性,因此您可以将 "primitive" 属性设置为 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...*/
}
我明白创造整体价值的好处 classes 来封装特定领域的想法。
但是当class/entity持久化到数据库中时,我该如何处理呢?
例如:
class User
{
Email Email { get; set;}
Address Address { get; set;}
PhoneNumber CellPhone { get; set;}
PhoneNumber HomePhone { get; set;}
}
SQL table 以保留此数据将包含标准类型的列 (nvarchar
);
如果 ORM 从中生成 class,它将看起来像
class User
{
string Email { get; set;}
string Address { get; set;}
string CellPhone { get; set;}
string HomePhone { get; set;}
}
那么,问题是,允许使用整个值并消除对原始类型的痴迷,但仍使用原始类型存储它的模式是什么?以及如何使 ORM 与它一起正常工作?
Primitive Obsession 是与域层相关的主题,与持久层无关。当你的域中有 User class 时,Email 应该表示为值对象,但是对于持久层,你不应该使用你的域对象,你需要有不同的 User class,用 Email 属性 作为原始数据,然后使用 Memento Pattern 或像 AutoMapper 这样的自动地图库来填充你的持久性用户 class 并存储它。所有这些工作都应该由您的 Generic/Specific 存储库 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; }
}
Repository/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 class 上重载转换运算符,您可以简化转换,这样您就可以将其转换为字符串而不必担心太多。
entity framework 设计器允许您设置从数据库列映射的属性的名称和可见性,因此您可以将 "primitive" 属性设置为 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...*/
}