有没有一种方法可以在lambda表达式树中使用“dynamic”
本文关键字:表达式 dynamic lambda 一种 方法 有没有 | 更新日期: 2023-09-27 18:11:23
首先,规范。我们使用MVC5、.NET 4.5.1和实体框架6.1。
在我们的MVC5业务应用程序中,我们有很多重复的CRUD代码。我的工作是"自动化"大部分内容,这意味着将其提取到基类中并使其可重用。现在,我有了控制器、视图模型和EF6实体模型的基类。
所有EF6实体继承的抽象基类:
public abstract class BaseEntity<TSubclass>
where TSubclass : BaseEntity<TSubclass>
{
public abstract Expression<Func<TSubclass, object>> UpdateCriterion();
}
CCD_ 1方法用于数据库上下文的CCD_ 2方法。我有一个用于子类的泛型参数,因为UpdateCriterion
需要返回使用确切子类类型的lambda表达式,而不是接口或基类。实现这个抽象基类的一个极其简化的子类如下所示:
public class Worker : BaseEntity<Worker>
{
public int ID { get; set; }
public int Name { get; set; }
public override Expression<Func<Worker, object>> UpdateCriterion()
{
return worker => worker.ID;
}
}
之后,在我的基本控制器的SaveOrUpdate
操作中,我会有这样的代码:
public ActionResult Save(TViewModel viewModel)
{
if (ModelState.IsValid)
{
var entityModel = viewModel.ConstructEntityModel();
db.Set<TEntityModel>().AddOrUpdate<TEntityModel>(entityModel.UpdateCriterion(), entityModel);
db.SaveChanges();
}
}
正因为如此,基本控制器的子类不需要像以前那样自己实现Save
方法。现在,所有这些都有效,尽管语法很时髦(我的意思是,class BaseEntity<TSubclass> where TSubclass : BaseEntity<TSubclass>
,真的吗?(。
我的问题来了。对于大多数实体来说,字段ID
是关键,但对于一些实体来说不是,所以我不能用超类实现进行正确的概括。所以目前,每个实体子类都实现了自己的UpdateCriterion
。但是,由于对于大多数(90%+(实体,e => e.ID
是正确的实现,所以我有很多重复。所以我想把实体基类重写成这样:
public abstract class BaseEntity<TSubclass>
where TSubclass : BaseEntity<TSubclass>
{
public virtual Expression<Func<TSubclass, object>> UpdateCriterion()
{
return entity => ((dynamic)entity).ID;
}
}
其目的是提供使用ID作为键的默认实现,并允许子类在使用不同键时覆盖它。我不能使用带有ID字段的接口或基类,因为不是所有实体都有。我想用UpdateCriterion
0来拉出ID
字段,但我得到了以下错误:Error: An expression tree may not contain a dynamic operation
。
那么,你知道怎么做吗?反射在UpdateCriterion
基础上工作吗?
否,不能在Linq-to-Entities查询中使用dynamic。但是您可以在运行时构建Lambda表达式。
public virtual Expression<Func<TSubclass, object>> UpdateCriterion()
{
var param = Expression.Parameter(typeof(TSubclass));
var body = Expression.Convert(Expression.Property(param, "ID"), typeof(object));
return Expression.Lambda<Func<TSubclass, object>>(body, param);
}
如果TSubclass类型没有ID属性Expression。property(param,"ID"(将引发异常。
此外,您可以使用实体模型中的MetadataWorkspace来获取TSubclass的主键列。
如果定义BaseEntity类,可以添加一个返回实际ID属性的虚拟只读属性。I belive EF将只读属性视为"计算的"属性,因此它们不会存储到数据库中。
public abstract class BaseEntity<TSubclass> where TSubclass : BaseEntity<TSubclass>
{
public abstract object ID { get; }
public virtual Expression<Func<TSubclass, object>> UpdateCriterion()
{
return entity => entity.ID;
}
}
public partial class Foo : BaseEntity<Foo>
{
public Int32 FooId { get; set; }
public override object ID { get { return FooId; } }
}
只是一个想法-我只尝试在LinqPad中编译并检查对UpdateCriterion的调用的值。:(
看看这个答案。它使用反射来获取ID属性。我认为它解决了你的问题:
public static object GetPropValue(object src, string propName)
{
return src.GetType().GetProperty(propName).GetValue(src, null);
}
然后用替换lambda表达式
return entity => GetPropValue(entity, "ID");
我还没有测试过,因为我没有完全可以测试它的代码。如果它有效,请告诉我们。