« first day (526 days earlier)      last day (2505 days later) » 

A K
9:47 AM
@Uranus Итого, по этой задаче я знаю правильный ответ. Даже два правильных ответа.
Причём один мне уже дали, а на второй я сам дал ссылку
Они оба даже дают одинаковую модель базы.
Единственное, я сейчас не понимаю, почему EF делает два отдельных столбца CarID и Car_ID, когда можно было обойтись одним:
Возможно, это потому что оба рабочих способа используют data annotation, а тут уже нужно чуть подшаманить с fluent API
 
A K
10:22 AM
Когда пойму этот момент -- пойду писать ответ. Длинный, обстоятельный: как и почему нужно делать, а не просто итоговый ответ.
Как тут только что в основном чатике сказали -- "в стиле ВладД" ))
 
 
2 hours later…
A K
11:55 AM
@Uranus Не побудете немного моим утёнком? Вижу, вы немного заинтересовались вопросом, а я сейчас как раз структурирую свои мысли перед написанием ответа, как экстраверту мне проще это делать хоть с воображаемым, но собеседником.
Мы на работе называем эту разновидность утёнка "черепом Йорика": достаточно молчать или изредка угукать ))
Так вот, следите за руками, сейчас будет показана последовательность как идти к правильному ответу, а не шарахаться наобум.
Исходные классы без всяких связок
public class Car
{
[Key]
public int ID { get; set; }

[Required]
public string Title { get; set; }
}
public class CarImage
{
[Key]
public int ID { get; set; }

public string FileName { get; set; }
}
Мы говорили, что связей две.
Ок, делаем первую:
public class CarImage
{
[Key]
public int ID { get; set; }

public string FileName { get; set; }

[Required]
public int CarID { get; set; }

[ForeignKey("CarID")]
public virtual Car Car { get; set; }
}
public class Car
{
[Key]
public int ID { get; set; }

[Required]
public string Title { get; set; }

public virtual ICollection<CarImage> AllImages { get; set; }
}
Это классика жанра один-ко-многим, тут особо нечего добавлять.
Просто повторил, чтобы наглядно было видно, где пустой класс, а где первая связь.
Ок, делаем вторую.
Я приводил в своём вопросе ссылку, повторяю: entityframeworktutorial.net/code-first/…
Т.е. надо над первичным ключом (да-да!) подписать внешний ключ:
public class CarImage
{
[Key]
[ForeignKey("Car")]
public int ID { get; set; }

[Required]
public int CarID { get; set; }

[ForeignKey("CarID")]
public virtual Car Car { get; set; }

public string FileName { get; set; }
}
а кроме того, собственно добавить поле PrimaryImage:
public class Car
{
[Key]
public int ID { get; set; }

[Required]
public string Title { get; set; }

public virtual CarImage PrimaryImage { get; set; }

public virtual ICollection<CarImage> AllImages { get; set; }
}
Мне тут не очень нравится автоматическое название ключа в базе, которое делает EF по умолчанию (добавляет лишнее подчёркивание), поэтому я напишу это вот в таком виде:
public class Car
{
[Key]
public int ID { get; set; }

[Required]
public string Title { get; set; }

public int? PrimaryImageID { get; set; }

[ForeignKey("PrimaryImageID")]
public virtual CarImage PrimaryImage { get; set; }

public virtual ICollection<CarImage> AllImages { get; set; }
}
Собственно, всё -- открываем базу и проверяем:
Что ещё? Помимо моего варианта, в котором всё расписано явно (а явное всегда лучше неявного) можно не вешать [ForeignKey("Car")] на таблицу CarImages -- в этом была прелесть ответа, данного мне на en-so: EF достаточно умён и понимает сам, что нужно делать.
Я правда не захотел это решение не потому что оно неявно что-то проделало (я этого ещё не понимал), а мне очень не понравился дополительный столбец в базе. Сильно не понравился
Я не понимал, зачем нам двойное объявление CarID и Car_ID
И потому пошёл искать "правильный" вариант дальше.
И нашёл на свою голову вариант, когда навешивал [Required] на виртуальное свойство. С ним "компилировалось", даже структура базы была похожей на правду -- но возникали проблемы со вставкой в базу. Код ошибки я вам скидывал, а суть была такая (если смотреть в отладчике), что при записи в CarImage он вставлял в CarID значение первичного ключа картинки (скажем, 1853) и это же значение пытался вставить в первичный ключ CarImage.ID -- хотя должен был вставить автоинтерментом единицу.
Этот required я скопировал из совершенно неподходящих ответов на en-so, где описывались отношения one-to-one.
Зато с другой стороны это дало повод более глубоко копнуть вопрос и разделить всё по отдельным подвопросам.
 
A K
12:39 PM
До сих пор я уверен, что всё сделал правильно. Однако...
Проблемы начинаются со вставкой записей в таблицу.
Мы падаем, если пишем простой код вставки новой картинки:
var car1 = new Car { PrimaryImage = null, PrimaryImageID = null, Title = "Mercedes" };
this.Car.Add(car1);
this.SaveChanges();

var carImage1 = new CarImage { CarID = car1.ID, Car = car1, FileName = "asdf1" };
this.CarImage.Add(carImage1);
this.SaveChanges();
A dependent property in a ReferentialConstraint is mapped to a store-generated column. Column: 'ID'.
При этом мы не можем использовать "двухходовку" и сначала вставить картинку без привязки к машине, чтобы потом указать в машине заглавную картинку:
var car1 = new Car { PrimaryImage = null, PrimaryImageID = null, Title = "Mercedes" };
this.Car.Add(car1);
this.SaveChanges();

var carImage1 = new CarImage { CarID = null, Car = null, FileName = "asdf1" };
this.CarImage.Add(carImage1);
this.SaveChanges();

car1.PrimaryImage = carImage1;
car1.PrimaryImageID = carImage1.ID;
carImage1.Car = car1;
carImage1.Car.ID = car1.ID;
this.SaveChanges();

this.Car.ToList().Dump();

this.CarImage.ToList().Dump();
Что вообще очевидно: CarID не может быть пуст, картинка обязана быть привязана к какой-то конкретной машине
вторая проблема -- невозможно почистить таблицу Car от прежних экспериментов, которые я делал в linqpad'е
Хотя таблица CarImages пуста, но нельзя взять и удалить Cars - мешает ограничение внешнего ключа: Cannot truncate table 'base.dbo.Cars' because it is being referenced by a FOREIGN KEY constraint.
Тут уже явно нужно подключать fluent API для того, чтобы пояснить где какие связи находятся
 

« first day (526 days earlier)      last day (2505 days later) »