EF6: расщепление таблицы не работает
Я пытаюсь создать базу данных EF6, в которой две таблицы, адреса и посещения, имеют одинаковые значения с первичными ключами. Посещения, концептуально, являются продолжением Адреса. Я делю таблицы, потому что большинство записей в Адресе не требуют полей, содержащихся в Посещениях.
Я использую код первый подход. Вот соответствующий код для адресов:
public class Address
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
[ForeignKey( "ID" )]
public virtual Visit Visit { get; set; }
и для посещений:
public class Visit
{
[Key]
[DatabaseGenerated( DatabaseGeneratedOption.Identity )]
public int ID { get; set; }
[ForeignKey("ID")]
public virtual Address Address { get; set; }
Основываясь на моих исследованиях, мне также нужно было включить в метод OnModelCreating моего datacontext следующее:
modelBuilder.Entity<Visit>()
.HasOptional( v => v.Address )
.WithRequired();
К сожалению, это не работает. Я могу корректно обновить базу данных, исключив вызовы скаффолдинга для удаления первичного индекса из адресов (возможно, потому что код добавления-миграции считает, что первичный ключ является "просто" полем внешнего ключа). Но когда я запускаю приложение, я получаю следующую ошибку:
Неверное имя столбца 'Address_ID'. Неверное имя столбца 'Address_ID'.
Из моего ограниченного опыта работы с EF6 это выглядит как-то глубоко внутри фреймворка, где он ожидает, что будут поля с именем 'Address_ID', возможно, в таблице посещений (на основе структуры имен 'table name'_'field name', которую я видел для другие неявно добавленные поля).
Возможно ли то, что я пытаюсь сделать? Если так, что я пропускаю в конфигурации?
Дополнительная информация
Испытывая предлагаемое решение Bubi, которое, к сожалению, до сих пор генерирует ту же ошибку, я мог устранить код OnModelCreating и по-прежнему генерировать функциональный код миграции.
разрешение
В конце концов я сделал то, что должен был сделать ранее, то есть изучил фактический код T-SQL, сгенерированный взрывающимся запросом. Оказывается, проблема была не в связи между визитом и адресом, а в совершенно разных отношениях, связанных с другой таблицей. По-видимому, где-то по пути я что-то сделал, чтобы EF подумал, что другая таблица (Избиратели) имеет поле внешнего ключа Address_ID. На самом деле отношение адрес / избиратель должно было быть и изначально было связано с полем Voter.AddressID.
Вместо того, чтобы пытаться развернуть большое количество миграций, я решил уничтожить базу данных, выбросить миграции и начать с нуля. После воссоздания базы данных - но по предложению Буби - я перезагрузил данные из резервной копии и, вуаля, вернулся в бизнес.
Для полноты, вот код, который мне пришлось вставить в вызов метода OnModelCreating, чтобы отношения Address/Visit работали правильно:
modelBuilder.Entity<Visit>()
.HasRequired( v => v.Address )
.WithRequiredDependent( a => a.Visit );
modelBuilder.Entity<Address>()
.HasRequired( a => a.Visit )
.WithRequiredPrincipal( v => v.Address );
Меня немного смущает, почему я должен использовать HasRequired, чтобы иметь возможность использовать WithRequiredPrincipal/WithRequiredDependent, поскольку не каждая запись в таблице адресов имеет запись в таблице посещений. Это может показаться "необязательным", а не "обязательным". Но, похоже, это работает, и, возможно, "требуемая" часть является внутренней только для модели базы данных EF, а не для самой базы данных.
2 ответа
В модели есть 2 проблемы:
- Только один из ключей может иметь автонумерацию, другой должен получить тот же Id (независимо от EF).
- Проблема с отображением.
Эта модель должна работать.
public class Address
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Description { get; set; }
public virtual Visit Visit { get; set; }
}
public class Visit
{
public Visit()
{
Address = new Address();
}
[Key]
[ForeignKey("Address")]
public int Id { get; set; }
public string Description { get; set; }
public virtual Address Address { get; set; }
}
Пример использования
var visit = new Visit
{
Description = "Visit",
Address = {Description = "AddressDescription"}
};
db.Visits.Add(visit);
db.SaveChanges();
В дополнение к тому, что упомянул Буби, ваш modelBuilder
утверждение противоречит модели в том, что оно не упоминает Address.Visit
как обратное свойство. Таким образом, он думает, что свойство представляет собой отдельное отношение и пытается создать Address_ID
столбец для этих отношений.
Тебе нужно иметь
modelBuilder.Entity<Visit>()
// from your description sounds like every Visit needs an Address
.HasRequired(v => v.Address )
// need to mention the inverse property here if you have one
.WithOptional(a => a.Visit);
... или просто полностью удалите оператор, так как вы уже используете атрибуты, и EF сможет разобраться с ним по соглашению.