Entity Framework TPH, изменяющий столбец дискриминатора в абстрактном базовом классе и сценарий с пустым унаследованным классом
У меня есть стол:
Groups
- Id
- Name
- Type
И эта таблица представляет собой группу продуктов, клиентов или поставщиков. Чтобы сгруппировать продукты, мне нужно использовать "P" в столбце "Тип", чтобы сгруппировать клиентов и поставщиков, мне нужно использовать "C" и "S" соответственно.
Интересно, смогу ли я использовать ТПХ здесь. Подобно созданию абстрактного класса Group со всеми полями, кроме Type, затем созданию пустых подклассов ProductGroup, ClientGroup и SupplierGroup, а затем настройке столбца Type в качестве дискриминатора со значениями "P", "C" и "S" для сопоставления их соответствующим классы.
Я пытаюсь сделать это, но кажется, что EF не угадывает мое намерение правильно, когда класс Group является абстрактным или когда унаследованные классы пусты.
Мои занятия похожи на следующие:
abstract class Group {
public int Id { get; set; }
public string Name { get; set; }
}
class ProductGroup : Group
{}
// I did not create the other two classes yet.
Моя конфигурация в DbContext выглядит следующим образом:
public class DataContext : DbContext
{
public DbSet<ProductGroup> ProductGroups { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new GroupConfiguration());
}
}
class GroupConfiguration : EntityTypeConfiguration<Group>
{
public GroupConfiguration()
{
Map<ProductGroup>(m =>
{
m.Requires("Type").HasValue("P");
});
}
}
Я думаю, что это должно работать, но используя приведенный выше код, datacontext.ProductGroups вернет все группы, а не только группы продуктов.
Я проверил и обнаружил, что если мой класс группы конкретен, код будет работать, как я ожидаю, группы в datacontext.ProductGroups будут только группами продуктов.
Я также проверил и обнаружил, что если я сохраню свой класс Group абстрактным, но переместу поле "Имя" для класса ProductGroup, код также будет работать так, как я ожидаю.
Интересно, это ошибка или я не на том пути.
Я пришел к этой идее, потому что мой начальник хочет иметь конечную точку "/productgroups" в нашем веб-интерфейсе, и я обнаружил, что было бы интересно иметь определенный класс группы продуктов, который будет использоваться в системе, вместо использования универсальной группы и должен фильтровать .Where(e => e.Type == "P")
везде. Я думаю, что последний подход менее понятен при работе с объектами, передаваемыми вокруг.
Кто-нибудь знает, как это решить? Нужно ли мне превращать бетон в класс моей группы? Мне бы не понравилось, что класс Group, кажется, нужен, чтобы быть абстрактным, так как на практике у меня есть только группы с заполненным полем Type.
3 ответа
Чего вам не хватает, так это того, что наследование EF основано на одном полиморфном DbSet
в то время как различные стратегии (TPH, TPT и TPC) управляют физической моделью хранения в базе данных.
С этим, как говорится, вам нужно следующее в вашем DbContext
(удалить ProductGroups
что ты так далеко положил)
public DbSet<Group> Groups { get; set; }
Все операции CRUD должны пройти через это DbSet
, Если вам нужны конкретные потомки, вы должны использовать OfType
метод. Обратите внимание, что это может создать больше проблем, чем использование одного класса с явным Type
свойство, так как EF не предоставляет вам никакого доступа к столбцу дискриминатора, поэтому тщательно подумайте, прежде чем использовать его только для этой цели.
Не уверен, что это проблема, но я помещаю свои сопоставления в метод OnModelCreating объекта DbContext:
// Define DisplayLocation subclasses using discriminator column
modelBuilder.Entity<AbstractDisplayLocation>()
.Map<VirtualDisplayLocation>(m => m.Requires("Virtual").HasValue(true))
.Map<PdbLocation>(m => m.Requires("Virtual").HasValue(false));
Я обнаружил проблему.
Я использовал тип Group где-то еще в коде. Когда я удалил все ссылки на тип группы, это сработало!
Спасибо за все ответы, хотя