使用 EF 核心将 table 行映射到 DDD 值对象

Map table row to DDD value object woith EF core

场景

我的微服务域模型中有两个聚合:User 和 UserNotification。

public class User : Aggreagete
{
    public int Id { get; private set; }
}

public class UserNotification : Aggreagete
{
    public int Id { get; private set; }

    public int UserId { get; private set; }

    public bool EnableSms { get; private set; }

    public bool EnableEmail { get; private set; }

    public NotificationKind NotificationKind { get; private  set; }

    public UserModel User { get;private set }
}

UserValueObject 需要从与用户聚合相同的 table 进行映射。我使用 UserValueObject 因为从 Notification bounded Context 它需要是 immutable.

我有以下 EF 映射:

    public void Configure(EntityTypeBuilder<User> builder)
    {
        builder.ToTable("Users", SCHEME);

        builder.HasKey(o => o.Id);
        builder.Property(o => o.Id);

        builder.HasMany<UserNotification>()
            .WithOne()
            .HasForeignKey(entry => entry.UserId)
            .OnDelete(DeleteBehavior.Cascade);
    }

    public void Configure(EntityTypeBuilder<UserNotification> builder)
    {
        builder.ToTable("UserNotifications", SCHEME);

        builder.HasKey(o => o.Id);
        builder.Property(o => o.Id);

        builder.Property(e => e.UserId).IsRequired();
        builder.Property(e => e.EnableSms).IsRequired();
        builder.Property(e => e.EnableEmail).IsRequired();
        builder.Property(e => e.NotificationKind).IsRequired()
            .HasConversion(
                v => v.ToString(),
                strValue => Enum.Parse<NotificationKind>(strValue)
            );
        builder.OwnsOne(e => e.User).ToTable("Users");
    }

问题

我在 "Add Migration"

期间收到错误消息
Cannot use table 'dbo.Users' for entity type 'User' since it is being used for entity type 'UserModel' and there is no relationship between their primary keys.

用户模型是:

public class UserModel : ValueObject
{
    public int Id { get; private set; }
    public string Email { get; private set; }
    public string Phone { get; private set; }
    public string Name { get; private set; }
}

问题

我想从数据库 table "Users" 映射 UserModel.Id,但是电子邮件、Phone 和名称应该使用对身份的 HTTP 请求注入到存储库中 服务。是否可以使用 EF 核心进行此类操作? 我发现用拥有的实体不可能实现这一点,因为用户 table 也用于映射其他聚合。

由于名称冲突,您得到了正确的异常。

对于 User 实体,您要求 EF 使用 Users table:

builder.ToTable("Users", SCHEME);

但您还要求它使用相同的 table 来存储 UserNotification 实体的值对象:

builder.OwnsOne(e => e.User).ToTable("Users");

如果您的计划是 "reuse" Users 聚合持久性以神奇地将价值项目映射到它,那么我可以肯定地说这不是一个好主意。

值对象是类型化的状态片段。它们属于实体,由实体管理。你必须在需要时更新VO的内容。

您也不需要将用户信息保存在聚合中,除非您在通知聚合的任何方法中使用它。如果您需要能够查询和加入 - 只需在查询中执行即可。不要使用 EF 进行查询,而是编写带有内部联接的普通 SQL 来获取您需要的信息。查查 CQRS,Jimmy Bogard 提倡使用 EF 作为写入端,前段时间使用普通 SQL 查询作为读取端,这完全有道理。