#inheritance #mapping #entity-framework-4.1
#наследование #сопоставление #entity-framework-4.1
Вопрос:
Я получаю Invalid column name 'Discriminator'
при сохранении записи. (Сначала код, EF4.1)
У меня есть объект, который я хочу отслеживать через EF:
public class Audit
public virtual string p1
public virtual string p2
у меня есть специализированный UserAudit, который не добавляет новых виртуальных свойств, он просто настраивает базу, готовую к аудиту
public class UserAudit : Audit
public UserAudit() { p1 = someval; }
И конфигурация:
public class AuditConfiguration : EntityTypeConfiguration<Audit>
{
public AuditConfiguration()
{
ToTable("_AUDIT");
HasKey(c => c.Id);
Property(c => c.Id).HasColumnName("AUDIT_ID");
}
}
И репозиторий:
public class AuditRepository : IAuditRepository
{
public void LogAudit(Audit audit)
{
using (var db = new AuditContext())
{
db.Audits.Add(audit);
db.SaveChanges();
}
}
}
Что мне нужно сделать, чтобы сообщить EF, чтобы игнорировать / обрабатывать специализацию правильно, когда я repo.LogAudit( userAudit );
?
Ответ №1:
Из сообщения об исключении Invalid column name 'Discriminator'
я бы сделал вывод, что вы не разрешили EF 4.1 создавать таблицу базы данных, _AUDIT
потому что в противном случае EF создал бы столбец, вызываемый Discriminator
в таблицу. Возможно, у вас есть существующая таблица базы данных без такого столбца. Когда EF пытается сохранить объект, он хочет сохранить значение, представляющее конкретный тип, который вы сохраняете, в столбце дискриминатора, но столбец не существует. Отсюда и исключение.
Редактировать
Итак, вам нужен столбец дискриминатора. Вы можете определить свой собственный столбец дискриминатора следующим образом:
public class AuditConfiguration : EntityTypeConfiguration<Audit>
{
public AuditConfiguration()
{
ToTable("_AUDIT");
HasKey(c => c.Id);
Property(c => c.Id).HasColumnName("AUDIT_ID");
Map<Audit>(m => m.Requires("Type").HasValue<byte>(0).IsRequired());
Map<UserAudit>(m => m.Requires("Type").HasValue<byte>(1).IsRequired());
}
}
При этом в tinyint
таблице использовался бы не обнуляемый Type
столбец _AUDIT
, который имеет значение 0
для объектов базового Audit
типа и значение 1
для объектов производного UserAudit
типа.
Комментарии:
1. Это не то, что я ищу. Мне не нужно было добавлять тип в мою таблицу — действительно, я не могу. Я хочу, чтобы EF игнорировал UserAudit — у него просто есть методы, которые записывают в базовые свойства. Не дискриминируйте, игнорируйте!
2. Хорошо, итак, у меня действительно есть столбец «TypeOfAction». Как мне различать несколько значений?
3. @BobTodd: Вы не можете игнорировать
UserAudit
отображение EF, а затем добавить UserAudit кAudits
набору. Вы могли бы создать только вспомогательный метод, который извлекаетAudit
изUserAudit
(в основном путем копирования всех свойств), а затем сохранить этотAudit
объект. Вероятно, вам также понадобится метод для созданияUserAudit
изAudit
объекта, потому что при извлечении объекта из базы данных EF всегда будет создавать экземплярAudit
, а неUserAudit
an. Другим обходным путем может быть созданиеAudit
элемента вUserAudit
(композиция вместо наследования), который позволяет избежать копирования свойств.4. @BobTodd: «… различать по нескольким значениям …» Вы имеете в виду, что один и тот же тип может иметь разные значения в этом столбце? Если да, я боюсь, что это невозможно. Но я не уверен. Вам лучше задать это как отдельный вопрос, чтобы получить обоснованный ответ. Мне тоже было бы интересно, есть ли способ.
5. Ооо! Оо! ModelBuilder. Игнорировать(new [] {typeof (UserAudit)});
Ответ №2:
@Slauma абсолютно прав, и вы должны использовать его решение. Я просто добавляю объяснение, почему это происходит.
Наследование в сущностях должно быть смоделировано в базе данных, потому что, когда вы загружаете сущность из базы данных, EF должен знать, должна ли она материализоваться Audit
или UserAudit
экземпляр. По умолчанию EF использует нечто, называемое наследованием таблицы для каждой иерархии, где базовый тип и все подтипы хранятся в одной таблице. Для поддержки такого сценария EF ожидает дополнительный столбец в таблице, вызываемой дискриминатором по умолчанию. Этот столбец будет использоваться для различения хранимых экземпляров — по умолчанию он включает имена типов.
Ответ №3:
В моем случае модель, отличная от EF, была унаследована от модели EF. Вместо наследования я скопировал все свойства в модель, отличную от EF, и это было решением для меня.
public class ApplicationContext : DbContext
{
public DbSet<Person> Persons { get; set; }
}
public class Person
{
public int PersonId { get; set; }
public string Name { get; set; }
}
Перед:
public class PersonWithExtraInfo:Person
{
public string ExtraInfo { get; set; }
}
После:
public class PersonWithExtraInfo
{
public int PersonId { get; set; }
public string Name { get; set; }
public string ExtraInfo { get; set; }
}