首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >创建参数等于对象的LINQ表达式

创建参数等于对象的LINQ表达式
EN

Stack Overflow用户
提问于 2013-04-13 01:58:37
回答 3查看 19.4K关注 0票数 23

给定一个原始值age,我知道如何创建这样的表达式:

代码语言:javascript
复制
//assuming: age is an int or some other primitive type
employee => employee.Age == age

通过这样做:

代码语言:javascript
复制
var entityType = typeof(Employee);
var propertyName = "Age";
int age = 30;
var parameter = Expression.Parameter(entityType, "entity");

var lambda = Expression.Lambda(
        Expression.Equal(
            Expression.Property(parameter, propertyName),
            Expression.Constant(age)
        )                    
    , parameter);

这可以很好地工作,除非所讨论的属性和常量不是基元类型。

如果比较是在对象之间进行的,我该如何构造一个相似的表达式?

使用EF,我可以这样写:

代码语言:javascript
复制
Location location = GetCurrentLocation();
employees = DataContext.Employees.Where(e => e.Location == location);

同样适用于的,但如果我尝试创建相同的表达式:

代码语言:javascript
复制
var entityType = typeof(Employee);
var propertyName = "Location";
var location = GetCurrentLocation();
var parameter = Expression.Parameter(entityType, "entity");

var lambda = Expression.Lambda(
        Expression.Equal(
            Expression.Property(parameter, propertyName),
            Expression.Constant(location)
        )                    
    , parameter);

我得到了一个错误,它说:

Unable to create a constant value of type 'Location'. Only primitive types or enumeration types are supported in this context.

我怀疑Expression.Constant()只需要原始类型,所以我需要使用不同的表达式工厂方法。(maype Expression.Object?-我知道那不存在)

有没有办法创建一个比较对象的表达式?为什么EF能够正确地解释它,如果它是一个编译的LINQ语句,但当它是一个表达式时却不能?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2013-04-16 06:53:20

除了在前面的答案中提到的之外。一个更具体的解决方案是这样的:

代码语言:javascript
复制
public static Expression CreateExpression<T>(string propertyName, object valueToCompare)
{
    // get the type of entity
    var entityType = typeof(T);
    // get the type of the value object
    var valueType = valueToCompare.GetType();
    var entityProperty = entityType.GetProperty(propertyName);
    var propertyType = entityProperty.PropertyType;


    // Expression: "entity"
    var parameter = Expression.Parameter(entityType, "entity");

    // check if the property type is a value type
    // only value types work 
    if (propertyType.IsValueType || propertyType.Equals(typeof(string)))
    {
        // Expression: entity.Property == value
        return Expression.Equal(
            Expression.Property(parameter, entityProperty),
            Expression.Constant(valueToCompare)
        );
    }
    // if not, then use the key
    else
    {
        // get the key property
        var keyProperty = propertyType.GetProperties().FirstOrDefault(p => p.GetCustomAttributes(typeof(KeyAttribute), false).Length > 0);

        // Expression: entity.Property.Key == value.Key
        return Expression.Equal(
            Expression.Property(
                Expression.Property(parameter, entityProperty),
                keyProperty
            ),
            Expression.Constant(
                keyProperty.GetValue(valueToCompare),
                keyProperty.PropertyType
            )
        );
    }
}

要点:

  1. 确保检查KeyAttribute)
  2. This代码是否为空,并确保
  3. propertyTypevalueType兼容(它们可能是同一类型,也可能是此处所做的nulls假设)(例如,您确实分配了一个KeyAttribute)
  4. This代码未经过测试,因此它并未完全准备好复制/粘贴。

希望这能有所帮助。

票数 7
EN

Stack Overflow用户

发布于 2013-04-13 02:01:09

您不能这样做,因为EF不知道如何将Location上的相等比较转换为SQL表达式。

但是,如果您知道要比较Location的哪些属性,则可以使用匿名类型进行比较:

代码语言:javascript
复制
var location = GetCurrentLocation();
var locationObj = new { location.LocationName, location.LocationDescription };
employees = DataContext.Employees.Where(e => new { e.Location.LocationName, e.Location.Description } == locationObj);

当然,这相当于:

代码语言:javascript
复制
var location = GetCurrentLocation();
employees = DataContext.Employees.Where(e => e.Location.LocationName == location.Name && 
                                             e.Location.Description == location.Description);
票数 3
EN

Stack Overflow用户

发布于 2013-04-13 03:21:34

运行下面的代码。我想测试一下您的假设,即e => e.Location == location正在编译成可以用Expression.Equal、Expression.Property和Expression.Constant构建的东西。

代码语言:javascript
复制
    class Program {
       static void Main(string[] args) {
          var location = new Location();
          Expression<Func<Employee, bool>> expression = e => e.Location == location;

          var untypedBody = expression.Body;

          //The untyped body is a BinaryExpression
           Debug.Assert(
              typeof(BinaryExpression).IsAssignableFrom(untypedBody.GetType()), 
              "Not Expression.Equal");

           var body = (BinaryExpression)untypedBody;
           var untypedLeft = body.Left;
           var untypedRight = body.Right;

           //The untyped left expression is a MemberExpression
           Debug.Assert(
              typeof(MemberExpression).IsAssignableFrom(untypedLeft.GetType()), 
              "Not Expression.Property");

           ////The untyped right expression is a ConstantExpression
          //Debug.Assert(
          //   typeof(ConstantExpression).IsAssignableFrom(untypedRight.GetType()),                 
          //   "Not Expression.Constant");

          //The untyped right expression is a MemberExpression?
          Debug.Assert(
               typeof(MemberExpression).IsAssignableFrom(untypedRight.GetType())));
    }
}

public class Employee
{
    public Location Location { get; set; }
}

public class Location { }

看起来不是,这是因为正确的表达式不是常量。要查看这一点,请取消注释掉的代码。

我不明白的是为什么正确的表达式是MemberExpression。也许了解linq表达式编译器的人可以比我更深入地了解这一点。

编辑:这可能与lambdas中的闭包有关-一个类是在幕后创建的,其中包含闭合的变量。然后,该位置可能是该类的成员。我不确定这一点,但这是我怀疑的。

This post可能会为这种情况提供更多信息。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/15977908

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档