【问题标题】:EF4.1 Code First Complex Type as primary keyEF4.1 Code First 复杂类型作为主键
【发布时间】:2011-04-02 10:23:26
【问题描述】:

我目前正在尝试使用 Entity Framework 4.1 的 RC 及其代码优先方法为我的域对象实现存储库。 现在我有一个域实体“Voyage”,它有一个封装在“VoyageNumber”类型中的唯一标识符

public class VoyageNumber
{
    private readonly string number;

    public VoyageNumber(string number)
    {
        Validate.NotNull(number, "VoyageNumber is required");

        this.number = number;
    }

    public string Id
    {
        get { return number; }
    }

现在我在 DbContext 的配置中执行此操作时遇到异常:

modelBuilder.Entity<Voyage>().HasKey<VoyageNumber>(k => k.VoyageNumber);

属性“VoyageNumber”不能 用作实体的关键属性 'Domain.Model.Voyages.Voyage' 因为 属性类型不是有效的键 类型。只有标量类型、字符串和 byte[] 是支持的键类型。

当我尝试这个时:

modelBuilder.Entity<Voyage>().HasKey<string>(k => k.VoyageNumber.Id);

属性表达式'k => k.VoyageNumber.Id' 无效。这 表达式应该代表一个 属性:C#:'t => t.MyProperty'

我真的必须丢弃我的 VoyageNumber 并将其替换为原始类型吗?

【问题讨论】:

  • 使用这个时的异常信息是什么:modelBuilder.Entity().HasKey(k => k.VoyageNumber);
  • 用异常消息更新了问题。
  • 在 EF 6.1.3 中仍然如此

标签: .net entity-framework domain-driven-design primary-key ef-code-first


【解决方案1】:

这是限制。关键成员只能是实体中直接的标量属性。复杂类型表示为不支持的复杂属性。

【讨论】:

  • 可惜了,那我估计还是先放弃Code吧,因为我不愿意放弃主键的封装。
  • 我不清楚。这是实体框架的局限性。不仅限于代码优先。
  • 在 EF6 中仍然如此吗?我在原始问题中记录了相同的错误,所以我认为没有任何改变,但希望添加了一些我缺少的配置选项。
【解决方案2】:

我们可以通过以下方式解决它。希望它有帮助。

public class TestPaperResultId: ValueObject
{
    public TestPaperResultId(string testPaperId, string userId)
    {
        TestPaperId = testPaperId;
        UserId = userId;
    }

    protected TestPaperResultId() { }

    public string TestPaperId { get; protected set; }
    public string UserId { get; protected set; }

    public override string ToString()
    {
        return $"{TestPaperId}_{UserId}";
    }
}

public class TestPaperResult : AggregateRoot
{
    private TestPaperResultId _id;

    public TestPaperResultId Id
    {
        get => _id ?? (_id = new TestPaperResultId(TestPaperId, UserId));
        protected set
        {
            TestPaperId = value.TestPaperId;
            UserId = value.UserId;
            _id = value;
        }
    }

    public string TestPaperId { get; protected set; }

    public string UserId { get; protected set; }

    protected TestPaperResult() { }

    public TestPaperResult(TestPaperResultId id,
                           decimal fullmarks)
    {
        Id = id;
        Fullmarks = fullmarks;
    }
}

在 dbContext 中:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    base.OnModelCreating(modelBuilder);

    modelBuilder.Entity<TestPaperResult>()
                .Ignore(t => t.Id)
                .HasKey(t => new {t.TestPaperId, t.UserId});
 }

在存储库中:

 public Task<TestPaperResult> FindTestPaperResultAsync(TestPaperResultId id)
 {
     return GetByKeyAsync<TestPaperResult>(id.TestPaperId, id.UserId);
 }

【讨论】:

    【解决方案3】:

    对于隔离类,您可以通过向 DbContext 添加一个“get”方法来执行只读解决方法,该方法执行 SqlQuery&lt;&gt; 并将表映射到内部的类(老式方式)。

    我在这里设计了一个最小的测试用例:https://github.com/timabell/ef-complex-pk

    例如

    public class TestDbContext : DbContext
    {
    
        public IEnumerable<UberWidget> GetUberWidgets()
        {
            return Database.SqlQuery<WidgetSqlDto>("select WidgetId, Name from Widgets")
                .Select(dto => new UberWidget
                {
                    UberWidgetId = new IdWrap { IdWrapId = dto.WidgetId },
                    Name = dto.Name
                });
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2016-01-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-08-05
      • 2011-06-15
      相关资源
      最近更新 更多