使用 lambda 表达式将反射 属性 传递给 .HasKey()
Pass reflected property to .HasKey() using lambda expression
我想要一个灵活的 Entity DbContext,以便我可以将我的数据库优先模式放在一个嵌套的 class 中。
如果每个 table 都有一个名为 Id 的列作为主键,则下面的这个 poc 实际上有效。
但是如果主键不同,我需要指定主键。
我可以使用 Entity 的 [Key]
属性来指定密钥,它确实有效。
但我不想这样做,因为我想将实体代码与模式定义分开。
我宁愿编写自定义属性,并编写一些逻辑来检查自定义属性的列。
通常我会使用 entityTypeConfiguration.HasKey(MyTable => myTable.MyKey);
来设置所需的 属性 作为主键。
但是我不知道如何编写正确的 Lambda 表达式来设置所需的 属性,因为我使用的是泛型和反射。
这是我的代码:
using MySql.Data.Entity;
using System;
using System.Data.Entity;
using System.Data.Entity.ModelConfiguration.Conventions;
using System.Linq;
class MySchema // my database
{
public class MyTable // a table inside mySchema
{
public int MyKey { get; set; } // a column inside myTable
public string MyValue { get; set; } // another column inside myTable
}
}
static class Program
{
static void Main()
{
var myTable = new EfDataContext<MySchema>().Set<MySchema.MyTable>();
Console.Write(myTable.Where(a => a.MyKey == 1).First().MyValue);
}
}
[DbConfigurationType(typeof(MySqlEFConfiguration))] // using mysql
public class EfDataContext<Schema> : DbContext
{
public EfDataContext() : base("db") { } // use connection string "db"
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>(); // let's assume my naming is like in the database
foreach (Type table in typeof(Schema).GetNestedTypes())
{
// type table is now a variable, so we cannot use it as a generic like: this.createEntity<table>(modelBuilder);
this.GetType().GetMethod("CreateEntity").MakeGenericMethod(table).Invoke(this, new object[] { modelBuilder });
}
}
public void CreateEntity<Table>(DbModelBuilder modelBuilder) where Table : class
{
var entityTypeConfiguration = modelBuilder.Entity<Table>();
// So far, so good!
// But if there is no column named "Id", I need to tell EF which column is the primary key.
// If I weren't using a generic type, I would that column MyKey is the primary key of table MyTable like this:
// entityTypeConfiguration.HasKey(row => row.MyKey);
// But since now I am using a generic type as the table, and propertyInfo appears to be the primary key, what do I say?
foreach (var propertyInfo in typeof(Table).GetProperties())
{
if (propertyInfo.DeclaringType.Name == "MyTable" && propertyInfo.Name == "MyKey") // just a silly example for now
{
// this throws an InvalidOperationException: The properties expression 'row => value(EfDataContext`1+<>c__DisplayClass2_0`1[MySchema,MySchema+MyTable]).propertyInfo' is not valid. The expression should represent a property: C#: 't => t.MyProperty'
entityTypeConfiguration.HasKey(row => propertyInfo);
}
}
}
}
您可以创建以下扩展方法
public static class ConfigurationExtensions
{
public static EntityTypeConfiguration<T> HasKey<T>(this EntityTypeConfiguration<T> config, PropertyInfo property) where T : class
{
//entity type
ParameterExpression parameter = Expression.Parameter(typeof(T));
//entity.key
Expression prop = Expression.Property(parameter, property.Name);
//entity => entity.key
var lambda = Expression.Lambda(prop, parameter);
MethodInfo hasKeyMethod = typeof(EntityTypeConfiguration<T>)
.GetMethods(BindingFlags.Public | BindingFlags.Instance)
.Where(m => m.Name == nameof(EntityTypeConfiguration<T>.HasKey)
&& m.GetParameters().Count() == 1)
.First()
.MakeGenericMethod(property.PropertyType);
return (EntityTypeConfiguration<T>)hasKeyMethod.Invoke(config, new object[] { lambda });
}
}
并使用它
entityTypeConfiguration.HasKey(propertyInfo);
我想要一个灵活的 Entity DbContext,以便我可以将我的数据库优先模式放在一个嵌套的 class 中。 如果每个 table 都有一个名为 Id 的列作为主键,则下面的这个 poc 实际上有效。 但是如果主键不同,我需要指定主键。
我可以使用 Entity 的 [Key]
属性来指定密钥,它确实有效。
但我不想这样做,因为我想将实体代码与模式定义分开。
我宁愿编写自定义属性,并编写一些逻辑来检查自定义属性的列。
通常我会使用 entityTypeConfiguration.HasKey(MyTable => myTable.MyKey);
来设置所需的 属性 作为主键。
但是我不知道如何编写正确的 Lambda 表达式来设置所需的 属性,因为我使用的是泛型和反射。
这是我的代码:
using MySql.Data.Entity;
using System;
using System.Data.Entity;
using System.Data.Entity.ModelConfiguration.Conventions;
using System.Linq;
class MySchema // my database
{
public class MyTable // a table inside mySchema
{
public int MyKey { get; set; } // a column inside myTable
public string MyValue { get; set; } // another column inside myTable
}
}
static class Program
{
static void Main()
{
var myTable = new EfDataContext<MySchema>().Set<MySchema.MyTable>();
Console.Write(myTable.Where(a => a.MyKey == 1).First().MyValue);
}
}
[DbConfigurationType(typeof(MySqlEFConfiguration))] // using mysql
public class EfDataContext<Schema> : DbContext
{
public EfDataContext() : base("db") { } // use connection string "db"
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>(); // let's assume my naming is like in the database
foreach (Type table in typeof(Schema).GetNestedTypes())
{
// type table is now a variable, so we cannot use it as a generic like: this.createEntity<table>(modelBuilder);
this.GetType().GetMethod("CreateEntity").MakeGenericMethod(table).Invoke(this, new object[] { modelBuilder });
}
}
public void CreateEntity<Table>(DbModelBuilder modelBuilder) where Table : class
{
var entityTypeConfiguration = modelBuilder.Entity<Table>();
// So far, so good!
// But if there is no column named "Id", I need to tell EF which column is the primary key.
// If I weren't using a generic type, I would that column MyKey is the primary key of table MyTable like this:
// entityTypeConfiguration.HasKey(row => row.MyKey);
// But since now I am using a generic type as the table, and propertyInfo appears to be the primary key, what do I say?
foreach (var propertyInfo in typeof(Table).GetProperties())
{
if (propertyInfo.DeclaringType.Name == "MyTable" && propertyInfo.Name == "MyKey") // just a silly example for now
{
// this throws an InvalidOperationException: The properties expression 'row => value(EfDataContext`1+<>c__DisplayClass2_0`1[MySchema,MySchema+MyTable]).propertyInfo' is not valid. The expression should represent a property: C#: 't => t.MyProperty'
entityTypeConfiguration.HasKey(row => propertyInfo);
}
}
}
}
您可以创建以下扩展方法
public static class ConfigurationExtensions
{
public static EntityTypeConfiguration<T> HasKey<T>(this EntityTypeConfiguration<T> config, PropertyInfo property) where T : class
{
//entity type
ParameterExpression parameter = Expression.Parameter(typeof(T));
//entity.key
Expression prop = Expression.Property(parameter, property.Name);
//entity => entity.key
var lambda = Expression.Lambda(prop, parameter);
MethodInfo hasKeyMethod = typeof(EntityTypeConfiguration<T>)
.GetMethods(BindingFlags.Public | BindingFlags.Instance)
.Where(m => m.Name == nameof(EntityTypeConfiguration<T>.HasKey)
&& m.GetParameters().Count() == 1)
.First()
.MakeGenericMethod(property.PropertyType);
return (EntityTypeConfiguration<T>)hasKeyMethod.Invoke(config, new object[] { lambda });
}
}
并使用它
entityTypeConfiguration.HasKey(propertyInfo);