使用 EF Core 隐式强制转换实体



假设我有以下实体

public abstract class BaseEntity {
public Guid Id { get;set; }
public string Prop1 { get;set; }
public long Prop2 { get;set; }
public byte Type_Id { get;set; }
}
public class Type1 : BaseEntity { }
public class Type2 : BaseEntity { }
public class Type3 : BaseEntity {
public long? Prop3 { get;set; }
}

以及以下上下文映射:

builder.ToTable("Entities").HasDiscriminator(a => a.Type_Id)
.HasValue<Type1>((byte)Types.Type1)
.HasValue<Type2>((byte)Types.Type2)
.HasValue<Type3>((byte)Types.Type3);
// in DbContext
public DbSet<BaseEntity> Entities { get; set; }

我想从数据库创建获取IQueryable(所有记录(类型1和类型2将在Prop3中为空, 我执行以下操作:

public DbSet<BaseEntity> DBSet { get;set; }
private static readonly MethodInfo FromSqlMethodInfo = typeof(RelationalQueryableExtensions).GetTypeInfo().GetDeclaredMethods("FromSql").Single(mi => mi.GetParameters().Length == 3);
public IQueryable<Type3> GetEntities(IEnumerable<Guid> ids) {
RawSqlString sql = @"select [Id]
,[Prop1]
,[Prop2]
,[Prop3]
,[Type_Id]
from [dbo].[Entities] where Id in (select item from @Ids)";
var ids = new SqlParameter("@Ids", SqlDbType.Structured);
ids.TypeName = typeof(Guid).Name.ToLowerInvariant() + "_item_list";
ids.Direction = ParameterDirection.Input;
ids.Value = CreateItemList(tpIds);
var param = new object[] { ids };
var conversion = from s in DBSet select (Type3)s;
var result = conversion.Provider.CreateQuery<Type3>(Expression.Call(null, FromSqlMethodInfo.MakeGenericMethod(typeof(Type3)), conversion.Expression,
Expression.Constant(sql), Expression.Constant(param)));
return result;
}

var query = GetEntities(someIds);
var result = query.OrderBy(m => m.Type_Id).Skip(skip).Take(take).ToList();

并且查询成功执行,但是当调用 ToLIst 时,会抛出异常: 无法将类型"Type1"的对象转换为类型"Type3",这是意料之中的,因为我们没有告诉它应该如何转换......所以问题是:EF Core可以完成这样的技巧吗?

好的,所以现在唯一可能的解决方案是再创建一个具有所有必需属性的实体,

public class NewEntity {
public Guid Id { get;set; }
public string Prop1 { get;set; }
public long Prop2 { get;set; }
public byte Type_Id { get;set; }
public long? Prop3 { get;set; }
}

将新的数据库集添加到上下文

public DbSet<NewEntity> NewEntities { get; set; }

将其映射到旧表并正确更新上下文配置, 以避免生成冗余列(在这种情况下,EF 可能会 尝试添加名称为 NewEntity_Prop1、NewEntity_Prop2 等的新列。 以及外键和索引

public override void Configure(EntityTypeBuilder<NewEntity> builder) {
builder.ToTable("Entities");
builder.Property(m => m.Prop1).HasColumnName("Prop1");
builder.Property(m => m.Prop2).HasColumnName("Prop2");
builder.Property(m => m.Type_Id).HasColumnName("Type_Id");
builder.HasOne<BaseEntity>().WithOne().HasForeignKey<NewEntity>(e => e.Id);
}

或创建视图

public DbQuery<NewEntity> NewEntities { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder) {
modelBuilder.Query<NewEntity>().ToView("NewEntities");
base.OnModelCreating(modelBuilder);
}

可以使用隐式重载强制转换运算符。这是一个链接来帮助你。

最新更新