使用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...*/
}