首页 > 解决方案 > 调用 EntityTypeBuilder.HasKey 通过反射获得复合键

问题描述

我正在尝试将“历史”表添加到 EFCore,并让它大部分工作。对于我的模型中在类上具有 [Historyable] 属性的每个 POCO,我使用反射生成一个新类,该类除了原始类中的属性之外还具有三个附加属性 - ChangeDate、ChangeType 和 ChangeUserID。

当我需要为新生成的 History 类型设置主键时,我的问题就出现了。在手写代码中,添加主键看起来像:

modelBuilder.Entity<EquipmentPortProfile>().HasKey(k => new { k.Name, k.PortType, k.CardType })
modelBuilder.Entity<EquipmentPortProfileHistory>().HasKey(k => new { k.Name, k.PortType, k.CardType, k.ChangeDate })  // add ChangeDate to keep it unique

其中 EquipmentPortProfile 是我可以手动调用 HasKey() 的原始 [Historable] POCO,EquipmentPortProfileHistory 是我需要通过反射调用 HasKey() 的新生成类型。

我有以下代码,它添加了模型的新 History 类型,但我不太清楚如何使用我构建的 MemberExpressions 列表来一般地调用 HasKey() 。

var type = tbuilder.CreateType();  // create the new History type
var entityMethod = typeof(ModelBuilder).GetMethods().First(f => f.Name == "Entity");
var ent = entityMethod.MakeGenericMethod(type).Invoke(modelBuilder, new object[] { });  // add the new History type to the model
var haskey = ent.GetType().GetMethods().FirstOrDefault(m => m.Name == "HasKey");
var param = Expression.Parameter(type, "k");  List<MemberExpression> args = new List<MemberExpression>();
var pk = historable.FindPrimaryKey();
foreach (var prop in pk.Properties)  // we add in the same key properties as the table that we are tracking
    args.Add(Expression.PropertyOrField(param, prop.Name));

args.Add(Expression.PropertyOrField(param, "ChangeDate"));  // and then also add in ChangeDate as part of the PK to keep it unique

标签: c#linqlambdareflectionentity-framework-core

解决方案


最简单的是使用HasKey重载接受params string[] propertyNames参数。

也不需要Entity<TEntity>通过反射调用泛型方法,您可以简单地使用非泛型实体方法接受Type type参数,例如像这样

var type = tbuilder.CreateType();  // create the new History type
var pk = historable.FindPrimaryKey();

var pkPropertyNames = pk.Properties.Select(p => p.Name).Append("ChangeDate").ToArray();
modelBuilder.Entity(type).HasKey(pkPropertyNames);

推荐阅读