EF4.1 Code First Complex Тип в качестве первичного ключа

В настоящее время я пытаюсь реализовать репозитории для своих объектов домена с помощью RC Entity Framework 4.1 и его первого подхода к коду. Теперь у меня есть объект домена "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 'недействителен. выражение должно представлять собой свойство: С#: 't = > t.MyProperty'

Неужели мне нужно уничтожить мой VoyageNumber и заменить его примитивным типом?

Ответ 1

Это ограничение. Ключевыми элементами могут быть только скалярные свойства непосредственно в сущности. Сложный тип представлен как комплексное свойство, которое не поддерживается.

Ответ 2

Для изолированного класса вы можете сделать обходной путь только для чтения, добавив метод "get" к вашему DbContext, который выполняет SqlQuery<>, и отображает таблицу внутри класса (старомодно).

Я разработал минимальный тестовый сценарий здесь: 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
            });
    }
}

Ответ 3

Мы можем решить это с помощью ниже. Надеюсь, это полезно.

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);
 }