Ответ 1
Используйте атрибут [Column ( "GarageId" )] для свойства GarageId как для класса автомобиля, так и для мотоцикла.
У меня есть Garage
, который содержит Cars
и Motorcycles
. Автомобили и мотоциклы Vehicles
. Вот они:
public class Garage
{
public int Id { get; set; }
public virtual List<Car> Cars { get; set; }
public virtual List<Motorcycle> Motorcycles { get; set; }
public Garage()
{
Cars = new List<Car>();
Motorcycles = new List<Motorcycle>();
}
}
public abstract class Vehicle
{
public int Id { get; set; }
public string Make { get; set; }
public string Model { get; set; }
}
public class Car : Vehicle
{
public int GarageId { get; set; }
public virtual Garage Garage { get; set; }
// some more properties here...
}
public class Motorcycle : Vehicle
{
public int GarageId { get; set; }
public virtual Garage Garage { get; set; }
// some more properties here...
}
Почему у автомобилей и мотоциклов есть свойство GarageId и Garage? Если я подталкиваю эти свойства до суперкласса Vehicle, EF жалуется и говорит, что свойства навигации должны находиться в конкретных классах.
Перемещение, здесь мой DbContext:
public class DataContext : DbContext
{
public DbSet<Garage> Garages { get; set; }
public DbSet<Vehicle> Vehicles { get; set; }
public DbSet<Car> Cars { get; set; }
public DbSet<Motorcycle> Motorcycles { get; set; }
public DataContext()
: base("GarageExample")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>();
}
}
И вот короткая программа для игры с моими игрушками:
class Program
{
static void Main(string[] args)
{
Database.SetInitializer<DataContext>(new DropCreateDatabaseAlways<DataContext>());
using (var db = new DataContext())
{
var car1 = new Car { Make = "Subaru", Model = "Legacy" };
var car2 = new Car { Make = "Porche", Model = "911" };
var bike1 = new Motorcycle { Make = "Suzuki", Model = "GS500" };
var bike2 = new Motorcycle { Make = "Kawasaki", Model = "Ninja" };
var garage = new Garage();
garage.Cars.Add(car1);
garage.Cars.Add(car2);
garage.Motorcycles.Add(bike1);
garage.Motorcycles.Add(bike2);
db.Garages.Add(garage);
db.SaveChanges();
}
}
}
Программа запускается и создает следующую таблицу Транспортные средства:
Id Make Model GarageId GarageId1 Discriminator
1 Subaru Legacy 1 null Car
2 Porche 911 1 null Car
3 Suzuki GS500 null 1 Motorcycle
4 Kawasaki Ninja null 1 Motorcycle
Как у автомобилей, так и у мотоциклов, обладающих собственными свойствами GarageId и Garage, кажется, что каждый подкласс создает свой собственный внешний ключ для гаража. Как я могу сказать EF (с помощью свободного Api, если возможно), что Car.Garage и Motorcycle.Garage - это одно и то же, и должен использовать тот же столбец?
Это Транспортные средства, которые я хочу, конечно:
Id Make Model GarageId Discriminator
1 Subaru Legacy 1 Car
2 Porche 911 1 Car
3 Suzuki GS500 1 Motorcycle
4 Kawasaki Ninja 1 Motorcycle
Используйте атрибут [Column ( "GarageId" )] для свойства GarageId как для класса автомобиля, так и для мотоцикла.
Единственный способ, которым я знаю, получить один столбец внешнего ключа и схему базы данных, которую вы хотите, - отказаться от коллекций навигации для производного типа в Garage
и вместо этого использовать одну коллекцию для базового типа:
public class Garage
{
public int Id { get; set; }
public virtual List<Vehicle> Vehicles { get; set; }
public Garage()
{
Vehicles = new List<Vehicle>();
}
}
public abstract class Vehicle
{
public int Id { get; set; }
public string Make { get; set; }
public string Model { get; set; }
public int GarageId { get; set; }
public virtual Garage Garage { get; set; }
}
public class Car : Vehicle
{
// some more properties here...
}
public class Motorcycle : Vehicle
{
// some more properties here...
}
Конечно, вы теряете фильтр удобного типа с ленивой или нетерпеливой загрузкой, когда хотите загружать Car
или Motorcycle
из Garage
, и вам нужно либо загрузить все Vehicle
в Garage
или использовать проекции или явную загрузку для загрузки производных типов.
По-моему, это совершенно верно, что вы пытаетесь сделать, но каким-то образом оно не поддерживается с помощью Entity Framework, или сопоставление с столбцами FK не было реализовано таким образом, что этот сценарий может поддерживаться.
public class Garage
{
public int Id { get; set; }
public virtual List<Car> Cars { get; set; }
public virtual List<Motorcycle> Motorcycles { get; set; }
public Garage()
{
Cars = new List<Car>();
Motorcycles = new List<Motorcycle>();
}
}
public abstract class Vehicle
{
public int Id { get; set; }
public int GarageId { get; set; }
public string Make { get; set; }
public string Model { get; set; }
}
public class Car : Vehicle
{
[ForeignKey("GarageId")]
public virtual Garage Garage { get; set; }
// some more properties here...
}
public class Motorcycle : Vehicle
{
[ForeignKey("GarageId")]
public virtual Garage Garage { get; set; }
// some more properties here...
}
Вы еще посмотрели на это?
Отображение наследования на основе иерархии таблиц (TPH)
В сценарии сопоставления TPH все типы в иерархии наследования сопоставлены с одной таблицей. Столбец дискриминатора используется для идентификации тип каждой строки. При создании вашей модели с помощью Code First TPH стратегия по умолчанию для типов, которые участвуют в наследовании иерархия. По умолчанию столбец дискриминатора добавляется в таблицу с именем "Дискриминатор" и именем типа CLR каждого типа в иерархия используется для значений дискриминатора. Вы можете изменить по умолчанию с использованием свободного API.
modelBuilder.Entity<Course>()
.Map<Course>(m => m.Requires("Type").HasValue("Course"))
.Map<OnsiteCourse>(m => m.Requires("Type").HasValue("OnsiteCourse"));
Прямо от здесь.