Ассоциация Entity Framework с фильтром - PullRequest
2 голосов
/ 12 октября 2011

У меня есть следующая модель в моей модели:

Patient

Vendor

Organization

каждому из этих объектов нужны адреса.

Адрес в основном выглядит следующим образом

Address
  AddressTypeId // with Navigation Property/Association to AddressType
  EntityKey // indicates the PK Id of the entity this address is for

AddressType
  EntityId // indicates the entity type this address type corresponds to (Patient or Vendor)
  // This should be on the AddressType, not the Address, since we need a way of knowing what kind of AddressTypes are available to create for new addresses for Patients, Vendors, and Organizations
  //...that is Patients support AddressType X, Vendors support AddressType Y, etc.

Я хочу создать ассоциацию для Patient, Vendor и Organization в свойстве EntityKey для Address - каждое с ограничением фильтра, согласно которому AddressType.EntityId адреса является соответствующим EntityId для этого объекта (1 для пациента, 2 для поставщика, 3 для адреса).

Каков наилучший способ сделать это? Большинство ORM на рынке поддерживают такой сценарий .... и это, безусловно, очень распространенный сценарий.

ПРИМЕЧАНИЕ. Я не хочу создавать производные объекты типа PatientAddress / PatientAddressType, VendorAddress / VendorAddressType и OrganizationAddress / OrganizationAddress. Это сильно загромождает модель и делает ее в основном непостижимой.

Сейчас я решаю эту проблему, делая явные объединения в моих запросах LINQ:

const int patientTypeEntityId = 1;
var query = from p in repository.Patients
              let addresses = repository.Addresses.Where(a => 
                  a.EntityKey == p.Id & a.AddressType.EntityId == patientTypeEntityId)
              select new { Patient = p, Addresses = a }

но я не хочу продолжать это делать.

1 Ответ

1 голос
/ 13 октября 2011

Если я правильно понимаю, вы хотите иметь коллекцию адресов в ваших Patient, Vendor и т. Д. ...

public class Patient
{
    public int Id { get; set; }
    public ICollection<Address> Addresses { get; set; }
}

public class Vendor
{
    public int Id { get; set; }
    public ICollection<Address> Addresses { get; set; }
}

public class Address
{
    public int Id { get; set; }
    //public int EntityKey { get; set; }
    public AddressType AddressType { get; set; }
}

... и как-то сказать EF, что Patient.Addresses только получаетзаполнены адресами типа «Пациент».

Я думаю, что это невозможно по нескольким причинам:

  • Если вы не выставите внешний ключ в Address (там нет EntityKey свойства) вы должны указать EF ключ в отображении (в противном случае он создаст / примет два разных столбца FK):

    modelBuilder.Entity<Patient>()
        .HasMany(p => p.PVAddresses)
        .WithRequired()
        .Map(a => a.MapKey("EntityKey"));
    
    modelBuilder.Entity<Vendor>()
        .HasMany(p => p.PVAddresses)
        .WithRequired()
        .Map(a => a.MapKey("EntityKey"));
    

Это выдаетисключение из-за дублирования столбца «EntityKey» для двух разных отношений.

  • Следующее, что мы могли бы попытаться, это показать внешний ключ как свойство в Address (свойство EntityKey есть), а затем используйте это отображение:

    modelBuilder.Entity<Patient>()
        .HasMany(p => p.PVAddresses)
        .WithRequired()
        .HasForeignKey(a => a.EntityKey);
    
    modelBuilder.Entity<Vendor>()
        .HasMany(p => p.PVAddresses)
        .WithRequired()
        .HasForeignKey(a => a.EntityKey);
    

Это (удивительно) не вызывает исключения, но создает два ограничения FK в базе данных между Patient - Address и Vendor - Address с тем же столбцом FK EntityKey.Я думаю, что для вашей модели это не имеет смысла, поскольку для этого потребуется наличие Patient и a Vendor с одним и тем же PK, если у вас есть адрес с некоторым значением EntityKey.Таким образом, вам придется удалить эти ограничения FK в БД вручную (что мне очень неприятно).

  • И последнее, что вы не можете указать фильтр для ленивых и энергичныхзагрузка навигационных свойств.Коллекция Addresses всегда будет заполняться адресами, которые имеют тот же EntityKey, что и PK Patient или Vendor соответственно.Вы можете применить фильтр с явной загрузкой:

    var patient = context.Patients.Single(p => p.Id == 1);
    context.Entry(patient).Collection(p => p.Addresses).Query()
        .Where(a => a.Addresstype.EntityId == patientTypeEntityId)
        .Load();
    

Но вы должны убедиться, что вы никогда не используете ленивую или энергичную загрузку для коллекции Addresses.Таким образом, это не совсем решение, и мы должны немедленно об этом забыть.

Для меня самым уродливым является то, что вы не можете иметь ограничения FK для EntityKey.Другими словами: БД позволяет иметь EntityKey = 1 без ссылки Patient или Vendor с этим PK (потому что каким-то образом пациент 1 и поставщик 1 были удалены, например).

Длятолько по этой причине я предпочел бы решение, показанное @Akash, - кроме того факта, что это, пожалуй, единственное работающее и чистое решение с EF вообще.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...