有没有一种方法可以在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字段的接口或基类,因为不是所有实体都有。我想用UpdateCriterion0来拉出ID字段,但我得到了以下错误:Error: An expression tree may not contain a dynamic operation

那么,你知道怎么做吗?反射在UpdateCriterion基础上工作吗?

有没有一种方法可以在lambda表达式树中使用“dynamic”

否,不能在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");

我还没有测试过,因为我没有完全可以测试它的代码。如果它有效,请告诉我们。