Как использовать свойства интерфейса с помощью CodeFirst

У меня есть следующие объекты:

public interface IMyEntity { [Key] int Id { get; set; } IMyDetail MyDetail { get; set; } ICollection CollectionOfReferences { get; set; } } public interface IMyDetail { [Key] int Id { get; set; } int IntValue { get; set; } } public class MyEntity : IMyEntity { [Key] public virtual int Id { get; set; } public virtual IMyDetail MyDetail { get; set; } public virtual ICollection CollectionOfReferences { get; set; } } public class MyDetail : IMyDetail { [Key] public virtual int Id { get; set; } public virtual int IntValue { get; set; } } 

Я хочу использовать EF CodeFirst для доступа к базе данных и создания схемы базы данных. Но CodeFirst не позволяет использовать типы интерфейсов для отношений между объектами. Поэтому он не создает связи между MyEntity и MyDetail. Я не могу изменить интерфейсы, поэтому я не могу изменить тип свойства в MyDetail вместо IMyDetail. Но я знаю, что клиент этой модели будет использовать только одну реализацию каждого интерфейса.

Я нашел обходное решение для свойств типа IMyDetail. Я могу создать свойство типа MyDetail и явно реализовать свойство интерфейса:

  private MyDetail _myDetail; public virtual MyDetail MyDetail { get { return this._myDetail; } set { this._myDetail = value; } } IMyDetail IMyEntity.MyDetail { get { return this._myDetail; } set { this._myDetail = (MyDetail)value; } } 

Он работает нормально. Но это решение не работает с ICollection потому что я не могу отдать его ICollection .

Есть ли решения для этого?

Недостаточное решение состоит в том, чтобы просто объединить эти интерфейсы, которые вы хотите сохранить в базовых classах, и разбивать базовые объекты на подclassы. EF действительно поддерживает это, и если вы идете с таблицей на иерархию (по умолчанию), вы можете отсортировать все базовые объекты подclassа с помощью общего свойства, используя обычный запрос LINQ из EF вместо того, чтобы обладать лукавством и делать что-то вроде write raw SQL или получить несколько списков в памяти и отсортировать объединение без помощи БД, как и с решением Cel интерфейсов и адаптеров.

Вы также можете использовать дочерние / родительские типы интерфейсов как generics, так что когда разработчик использует конкретные classы в Db, они могут в основном использовать ваши интерфейсы, но скажите EF использовать конкретные classы:

 public interface IParent where TChild : IChild { ICollection Children { get; set; } 

Кто-то может создавать свои classы Db, например:

 public class Parent : IParent . . . 

Но все равно используйте их как:

 IParent parents = db.Parents.Include(p => p.Children).ToArray(); 

Поскольку общий признак выделен, общий ковариант и, следовательно, может принимать все, что соответствует ограничениям общего характера, включая приведенные выше типы дерева типов для интерфейса IChild.

Тем не менее, если вы действительно хотите сохранить интерфейсы, правильным ответом является, вероятно, использование NHibernate: как сопоставить интерфейс в nhibernate?

И некоторые кодеры рекомендуют вам поддерживать интерфейсы на объектах в любой ORM, ограниченной несколькими общими свойствами, или рисковать неправильным использованием: программирование на интерфейсы при сопоставлении с Fluent NHibernate

Обходной путь заключается в создании специальной реализации для каждого интерфейса, который вы хотите использовать с Entity Framework, используя шаблон адаптера:

Обертка для каждого интерфейса

 // Entity Framework will recognize this because it is a concrete type public class SecondLevelDomainRep: ISecondLevelDomain { private readonly ISecondLevelDomain _adaptee; // For persisting into database public SecondLevelDomainRep(ISecondLevelDomain adaptee) { _adaptee = adaptee; } // For retrieving data out of database public SecondLevelDomainRep() { // Mapping to desired implementation _adaptee = new SecondLevelDomain(); } public ISecondLevelDomain Adaptee { get { return _adaptee; } } public string Id { get { return _adaptee.Id; } set { _adaptee.Id = value; } } // ... whatever other members the interface defines } 

Пример сохранения и загрузки

  // Repositor is your DbContext public void SubmitDomain(ISecondLevelDomain secondLevelDomain) { Repositor.SecondLevelDomainReps.Add(new SecondLevelDomainRep(secondLevelDomain)); Repositor.SaveChanges(); } public IList RetrieveDomains() { return Repositor.SecondLevelDomainReps.Select(i => i.Adaptee).ToList(); } 

Использование навигационных свойств / внешних ключей / родительских дочерних сопоставлений

Для более сложных интерфейсов / classов вы можете получить InvalidOperationException – см. « Конфликтующие изменения с первым первым ключом кода в Entity Framework» для реализации, которая работает с такими иерархиями объектов

После нескольких бессонных ночей я считаю, что нашел решение этой проблемы. Я тестировал этот подход (немного) и, похоже, работает, но ему, вероятно, нужны еще несколько глазных яблок, чтобы разорвать его на части и объяснить, почему этот подход может сломаться. Я использовал FluentAPI для настройки атрибутов базы данных вместо украшения свойств classа сущности. Я удалил виртуальный атрибут из членов classа сущности (я предпочитаю использовать явные включения вместо того, чтобы возвращаться к ленивой загрузке для дочерних объектов). Я также немного переименовал classы classов и свойства, чтобы он стал яснее для меня. Я предполагаю, что вы пытаетесь выразить отношения «один ко многим» между сущностью и ее деталями. Вы пытаетесь реализовать интерфейсы для своих объектов в уровне хранилища, чтобы верхние уровни были агностическими для classов сущностей. Более высокие уровни знают только интерфейсы, а не сами сущности …

 public interface IMyEntity { int EntityId { get; set; } //children ICollection Details { get; set; } } public interface IMyDetailEntity { int DetailEntityId { get; set; } int EntityId { get; set; } int IntValue { get; set; } //parent IEntity Entity { get; set; } } public class MyEntity : IMyEntity { public int EntityId { get; set; } private ICollection _Details; public ICollection Details { get { if (_Details == null) { return null; } return _Details.Select(x => (MyDetailEntity) x).ToList(); } set { _Details = value.Select(x => (IMyDetailEntity) x).ToList(); } } ICollection IMyEntity.Details { get { return _Details; } set { _Details = value; } } } public class MyDetailEntity : IMyDetailEntity { public int DetailEntityId { get; set; } public int EntityId { get; set; } public int IntValue { get; set; } private IMyEntity _Entity; public MyEntity Entity { get { return (Entity)_Entity; } set { _Entity = (Entity)value; } } IEntity IMyDetailEntity.Entity { get { return _Entity; } set { _Entity = value; } } } 

У меня была эта проблема и на некоторых моделях, а не на других, и я попытался использовать принятый ответ. Затем я углубился в то, что было по-другому на моделях, которые работали.

Исправлено было изменение от использования ICollection, чтобы использовать IEnumerable, и проблемы ушли, пока.

Это устранило необходимость использования приведенного ниже кода в принятом ответе:

 public interface IParent where TChild : IChild { ICollection Children { get; set; } 

и это стало

 public interface IParent { IEnumerable Children { get; set; } 

что намного проще.

У меня была та же проблема, и я нашел решение, подобное Нафану, но вы можете сделать еще один шаг и IAddress.Extensions свойствам одинаковые (здесь Extensions и IAddress.Extensions ), явно определяя интерфейс:

 public interface IAddress { string Address { get; set; } IEnumerable Extensions { get; set; } } public interface IAddressExtension { string Key { get; set; } string Value { set; } } [Table("AddressExtensions")] public class AddressExtension : IAddressExtension { [Key] public string Id { get; set; } public string Key { get; set; } public string Value { get; set; } } [Table("Addresses")] public class Address : IAddress { [Key] public string Id { get; set; } public string Address { get; set; } public IEnumerable Extensions { get; set; } [NotMapped] IEnumerable IAddress.Extensions { get { return Extensions; } set { Extensions = value as IEnumerable; } } } 

Code First игнорирует свойство interface и использует конкретный class, в то время как вы можете получить доступ к этому classу в качестве интерфейса IAddress .

  • сочетание двух полей должно быть уникальным в первом подходе кода Entity Framework. как это было бы?
  • Entity Framework: «Заявление об обновлении, вставке или удалении влияет на неожиданное количество строк (0)».
  • Давайте будем гением компьютера.