具有Entity Framework Core 2.0的多个子实体中的相同属性名称

时间:2017-09-06 09:25:49

标签: c# entity-framework entity-framework-core .net-core-2.0

我目前正在使用下面的简单模型。这非常简单:我们拥有资源,可以是RoomEmptyOffice(...)或Service

RoomEmptyOffice可以有容量,但不是Service

public abstract class Resource : Entity
{
    public string Name { get; set; }
}

public class Room : Resource
{
    public int Capacity { get; set; }
}

public class EmptyOffice : Resource
{
    public int Capacity { get; set; }
}

public class Service : Resource
{ }

要从我的SQL视图中获取数据,我使用映射:

builder.Entity<Resource>(m =>
{
    m.ToTable("resource", "facility");
    m.HasKey(x => x.Id);
    m.Property(x => x.Id)
        .HasColumnName("ResourceId");
    m.Property(x => x.Type)
        .HasColumnName("ResourceTypeId");

    m.HasDiscriminator(x => x.Type)
        .HasValue<Room>(ResourceType.Room)
        .HasValue<EmptyOffice>(ResourceType.EmptyOffice)
        .HasValue<Service>(ResourceType.Service);
});

builder.Entity<Room>();
builder.Entity<EmptyOffice>();
builder.Entity<Service>();

当我运行我的代码时,EF Core会引发以下异常:

  

System.Data.SqlClient.SqlException:'无效的列名'Room_Capacity'。'

如果我将Capacity属性重命名为Room_Capacity,它可以正常运行,但这很糟糕。

如何强制EF Core 2.0定位每个子实体的容量属性?

谢谢 的Sebastien

3 个答案:

答案 0 :(得分:1)

您不能这样做,因为EF Core中唯一可用的继承模式是每个类层次结构的表。如果使用接口而不是基类,则可以,但每个实体将映射到不同的表。使用[NotMapped]标记要排除的任何属性,或使用代码标记Ignore。

答案 1 :(得分:1)

这对我有用:

builder.Entity<Room>().Property(a => a.Capacity).HasColumnName("Capacity");

builder.Entity<EmptyRoom>().Property(a => a.Capacity).HasColumnName("Capacity");

答案 2 :(得分:1)

我在项目的下一个代码中做了使其更通用的方式。

        private static void FindAndConfigureBackgroundJobResultTypes(ModelBuilder modelBuilder)
        {
            var backgroundJobResultTypes = typeof(BackgroundJobResult).Assembly.GetTypes().Where(x => x.IsSubclassOf(typeof(BackgroundJobResult))).ToList();

            var sameTypeAndNameProperties = backgroundJobResultTypes
                .SelectMany(x => x.GetProperties())
                .GroupBy(d => new {d.Name, d.PropertyType})
                .Select(grp => new
                {
                    PropertyType = grp.Key.PropertyType,
                    PropertyName = grp.Key.Name,
                    Count = grp.Count()
                })
                .Where(x => x.Count > 1).ToList();


            foreach (var backgroundJobResultType in backgroundJobResultTypes)
            {

                //Set base type , instead of exposing this type by DbSet
                modelBuilder.Entity(backgroundJobResultType).HasBaseType(typeof(BackgroundJobResult));

                //Map properties with the same name and type into one column, EF Core by default will create separate column for each type, and make it really strange way. 
                foreach (var propertyInfo in backgroundJobResultType.GetProperties())
                {
                    if (sameTypeAndNameProperties.Any(x => x.PropertyType == propertyInfo.PropertyType && x.PropertyName == propertyInfo.Name))
                    {
                        modelBuilder.Entity(backgroundJobResultType).Property(propertyInfo.PropertyType, propertyInfo.Name).HasColumnName(propertyInfo.Name);
                    }
                }
            }
        }