如何在运行时创建Func<未知类型, bool>并替换表达式树中的类型?
解决方案:运行时构建泛型表达式与委托
你遇到的核心问题是编译时无法确定泛型类型,需要通过反射和表达式树在运行时动态构建逻辑。下面分步骤拆解实现思路和代码:
1. 核心思路梳理
要实现ForeignKeyProperty.SetValue(model, repository.GetList<ForeignKeyProperty.PropertyType>().Single(x => x.Id == model.Id));这个逻辑,我们需要分三步动态处理:
- 构建针对运行时类型的过滤委托
Func<UnknownType, bool> - 动态调用泛型方法
GetList<UnknownType>()和Single<UnknownType>() - 最终完成属性赋值
2. 具体实现代码
假设你已经拿到了ForeignKeyProperty(PropertyInfo实例)、model(ViewModel实例)和repository(IVenturaRepository实例),可以按以下代码实现:
// 1. 获取运行时才知道的实体类型 Type entityType = ForeignKeyProperty.PropertyType; // 2. 构建过滤表达式:x => x.Id == model.Id // 创建参数表达式(x) ParameterExpression paramExpr = Expression.Parameter(entityType, "x"); // 获取实体的Id属性(假设所有实体都有名为Id的属性,可根据实际调整) PropertyInfo entityIdProp = entityType.GetProperty("Id"); if (entityIdProp == null) throw new InvalidOperationException($"类型{entityType.Name}未找到Id属性"); // 构建x.Id的表达式 Expression leftExpr = Expression.Property(paramExpr, entityIdProp); // 构建model.Id的常量表达式(注意类型匹配) object modelIdValue = typeof(ViewModel).GetProperty("Id").GetValue(model); Expression rightExpr = Expression.Constant(modelIdValue, entityIdProp.PropertyType); // 构建相等表达式 BinaryExpression equalsExpr = Expression.Equal(leftExpr, rightExpr); // 构建Lambda表达式并编译为委托 LambdaExpression filterLambda = Expression.Lambda(equalsExpr, paramExpr); Delegate filterDelegate = filterLambda.Compile(); // 3. 动态调用repository.GetList<T>() MethodInfo getListMethod = typeof(IVenturaRepository) .GetMethod("GetList", Type.EmptyTypes) .MakeGenericMethod(entityType); IEnumerable entityList = (IEnumerable)getListMethod.Invoke(repository, null); // 4. 动态调用Enumerable.Single<T>(source, predicate) MethodInfo singleMethod = typeof(Enumerable) .GetMethods(BindingFlags.Public | BindingFlags.Static) .First(m => m.Name == "Single" && m.GetParameters().Length == 2) .MakeGenericMethod(entityType); object matchedEntity = singleMethod.Invoke(null, new object[] { entityList, filterDelegate }); // 5. 给ForeignKeyProperty赋值 ForeignKeyProperty.SetValue(model, matchedEntity);
3. 针对List<Action<IVenturaRepository, ViewModel>>的适配
因为你的Action是针对ViewModel的,但我们需要在内部处理运行时类型,直接使用现有泛型Action是可行的——在Action内部封装上述动态逻辑即可。比如:
// 假设你在某个上下文中已经获取了ForeignKeyProperty PropertyInfo foreignKeyProp = ...; // 将动态逻辑封装到Action中 Action<IVenturaRepository, ViewModel> action = (repo, model) => { // 把上面的核心实现代码放这里 Type entityType = foreignKeyProp.PropertyType; // ... 其余步骤同上 ... }; // 添加到你的List中 yourActionList.Add(action);
4. 注意事项
- 属性存在性检查:实际代码中要确保
entityType确实包含Id属性,避免空引用异常 - 类型转换:
model.Id的类型要和entityType.Id的类型匹配,如果可能有不一致,需要添加类型转换表达式(比如Expression.Convert) - 方法重载选择:获取
Single方法时,要确保选对带Predicate参数的重载,避免因为重载匹配错误导致异常
内容的提问来源于stack exchange,提问作者Nico Haegens
相关产品推荐
相关产品推荐

