C# Linq到实体查询的动态谓词

C# Linq到实体查询的动态谓词,c#,asp.net-mvc-3,linq,linq-to-entities,C#,Asp.net Mvc 3,Linq,Linq To Entities,以下Linq到实体查询工作正常: var query = repository.Where(r => r.YearProp1.HasValue && r.YearProp1 >= minYear && r.YearProp1 <= maxYear); 这将导致以下错误: LINQ to实体无法识别该方法 'Sy

以下Linq到实体查询工作正常:

var query = repository.Where(r => r.YearProp1.HasValue &&
                                  r.YearProp1 >= minYear &&
                                  r.YearProp1 <= maxYear);
这将导致以下错误:

LINQ to实体无法识别该方法 'System.Nullable'1[System.Int16]fx(RepoEntity)'方法,以及 方法无法转换为存储表达式

我理解为什么会出现错误,但我想知道是否有一种解决方法,它不需要重复十几次代码,只需更改SQL查询所运行的属性


我将在不止一个查询中重用该函数,因此我想我的问题的一般版本是:有没有办法将简单的属性getter lambda函数转换为Linq可以使用的表达式以转换为实体?

谓词本身就是一个过滤器,应该计算为bool(用于确定是否将其包含在结果中)。您可以将您的方法修改为如下所示,它应该可以工作:

public static Expression<Func<RepoEntity, bool>> FitsWithinRange(int minYear, int maxYear)
{
    return w => w.HasValue && w >= minYear && w <= maxYear;
}

您可以做类似的事情(不确定它是否能在linq2实体中“按原样”工作,但如果您有问题,请告诉我)

用法

var query=.NullableShortBetween(1,3).ToList();
作用

public static IQueryable<T> NullableShortBetween<T>(this  IQueryable<T> queryable, short? minValue, short? maxValue) where T: class
        {
            //item (= left part of the lambda)
            var parameterExpression = Expression.Parameter(typeof (T), "item");

            //retrieve all nullable short properties of your entity, to change if you have other criterias to get these "year" properties
            var shortProperties = typeof (T).GetProperties().Where(m => m.CanRead && m.CanWrite && m.PropertyType == typeof(short?));

            foreach (var shortProperty in shortProperties)
            {
                //item (right part of the lambda)
                Expression memberExpression = parameterExpression;
                //item.<PropertyName>
                memberExpression = Expression.Property(memberExpression, shortProperty);
                //item.<PropertyName>.HasValue
                Expression firstPart = Expression.Property(memberExpression, "HasValue");
                //item.<PropertyName> >= minValue
                Expression secondPart = Expression.GreaterThanOrEqual(memberExpression, Expression.Convert(Expression.Constant(minValue), typeof (short?)));
                //item.<PropertyName> <= maxValue
                var thirdPart = Expression.LessThanOrEqual(memberExpression, Expression.Convert(Expression.Constant(maxValue), typeof (short?)));
                //item.<PropertyName>.HasValue && item.<PropertyName> >= minValue
                var result = Expression.And(firstPart, secondPart);
                //item.<PropertyName>.HasValue && item.<PropertyName> >= minValue && item.<PropertyName> <= maxValue
                result = Expression.AndAlso(result, thirdPart);
                //pass the predicate to the queryable
                queryable = queryable.Where(Expression.Lambda<Func<T, bool>>(result, new[] {parameterExpression}));
            }
            return queryable;
        }
公共静态IQueryable NullableShortBetween(此IQueryable可查询,short?minValue,short?maxValue),其中T:class
{
//项目(=lambda的左侧部分)
var parameterExpression=Expression.Parameter(typeof(T),“item”);
//检索实体的所有可为空的短属性,如果您有其他标准来获取这些“年”属性,则要进行更改
var shortProperties=typeof(T).GetProperties()。其中(m=>m.CanRead&&m.CanWrite&&m.PropertyType==typeof(short?);
foreach(shortProperties中的var shortProperty)
{
//项目(lambda的右侧部分)
表达式memberExpression=参数表达式;
//项目。
memberExpression=Expression.Property(memberExpression,shortProperty);
//项..HasValue
expressionfirstpart=Expression.Property(memberExpression,“HasValue”);
//项目>=最小值
表达式第二部分=Expression.GreaterThanOrEqual(memberExpression,Expression.Convert(Expression.Constant(minValue),typeof(short?));
//项目=最小值
var结果=表达式和(第一部分,第二部分);
//item..HasValue&&item.>=minValue&&item.m.CanRead&&m.CanWrite&&m.PropertyType==typeof(short?);
将propertyInfo.GetValue(实例,null)返回为short?;
}
用法


var result=list.Where(item=>item.GetYearValue()!=null&&item.GetYearValue()>=1&&item.GetYearValue()基于Raphaël Althaus的答案,但添加了您最初寻找的通用选择器:

public static class Examples
{
    public static Expression<Func<MyEntity, short?>> SelectPropertyOne()
    {
        return x => x.PropertyOne;
    }

    public static Expression<Func<MyEntity, short?>> SelectPropertyTwo()
    {
        return x => x.PropertyTwo;
    }

    public static Expression<Func<TEntity, bool>> BetweenNullable<TEntity, TNull>(Expression<Func<TEntity, Nullable<TNull>>> selector, Nullable<TNull> minRange, Nullable<TNull> maxRange) where TNull : struct
    {
        var param = Expression.Parameter(typeof(TEntity), "entity");
        var member = Expression.Invoke(selector, param);

        Expression hasValue = Expression.Property(member, "HasValue");
        Expression greaterThanMinRange = Expression.GreaterThanOrEqual(member,
                                             Expression.Convert(Expression.Constant(minRange), typeof(Nullable<TNull>)));
        Expression lessThanMaxRange = Expression.LessThanOrEqual(member,
                                          Expression.Convert(Expression.Constant(maxRange), typeof(Nullable<TNull>)));

        Expression body = Expression.AndAlso(hasValue,
                      Expression.AndAlso(greaterThanMinRange, lessThanMaxRange));

        return Expression.Lambda<Func<TEntity, bool>>(body, param);
    }
}
Expression<Func<MyEntity, short?>> whatToSelect = Examples.SelectPropertyOne;

var query = Context
            .MyEntities
            .Where(Examples.BetweenNullable<MyEntity, short>(whatToSelect, 0, 30));
公共静态类示例
{
公共静态表达式SelectPropertyOne()
{
返回x=>x.PropertyOne;
}
公共静态表达式SelectPropertyTwo()
{
返回x=>x.PropertyTwo;
}
公共静态表达式BetweenNullable(表达式选择器,可为null的minRange,可为null的maxRange),其中TNull:struct
{
var param=表达式参数(typeof(tenty),“entity”);
var member=Expression.Invoke(选择器,参数);
表达式hasValue=Expression.Property(成员,“hasValue”);
表达式greaterThanMinRange=Expression.GreaterThanOrEqual(成员,
Expression.Convert(Expression.Constant(minRange),typeof(Nullable));
表达式lessThanMaxRange=表达式.lessthanRequal(成员,
Expression.Convert(Expression.Constant(maxRange),typeof(Nullable));
表达式体=表达式.AndAlso(hasValue,
表达式.AndAlso(大于最小范围,小于最大范围);
返回表达式.Lambda(body,param);
}
}
可以使用有点像您要查找的原始查询:

public static class Examples
{
    public static Expression<Func<MyEntity, short?>> SelectPropertyOne()
    {
        return x => x.PropertyOne;
    }

    public static Expression<Func<MyEntity, short?>> SelectPropertyTwo()
    {
        return x => x.PropertyTwo;
    }

    public static Expression<Func<TEntity, bool>> BetweenNullable<TEntity, TNull>(Expression<Func<TEntity, Nullable<TNull>>> selector, Nullable<TNull> minRange, Nullable<TNull> maxRange) where TNull : struct
    {
        var param = Expression.Parameter(typeof(TEntity), "entity");
        var member = Expression.Invoke(selector, param);

        Expression hasValue = Expression.Property(member, "HasValue");
        Expression greaterThanMinRange = Expression.GreaterThanOrEqual(member,
                                             Expression.Convert(Expression.Constant(minRange), typeof(Nullable<TNull>)));
        Expression lessThanMaxRange = Expression.LessThanOrEqual(member,
                                          Expression.Convert(Expression.Constant(maxRange), typeof(Nullable<TNull>)));

        Expression body = Expression.AndAlso(hasValue,
                      Expression.AndAlso(greaterThanMinRange, lessThanMaxRange));

        return Expression.Lambda<Func<TEntity, bool>>(body, param);
    }
}
Expression<Func<MyEntity, short?>> whatToSelect = Examples.SelectPropertyOne;

var query = Context
            .MyEntities
            .Where(Examples.BetweenNullable<MyEntity, short>(whatToSelect, 0, 30));
Expression whatToSelect=Examples.SelectPropertyOne;
var query=Context
.髓质
.式中(示例:介于可用(whatToSelect,0,30))之间);

但是我在哪里选择谓词操作的属性呢?哎呀,掩盖了真正的问题,跳到了我认为你的问题所在的地方。你能把你的方法变成
表达式而不是
Func
?原因是:我可以使用
表达式,但我不确定从那里走到哪里。
var result = list.Where(item => item.GetYearValue() != null && item.GetYearValue() >= 1 && item.GetYearValue() <= 3).ToList();
public static class Examples
{
    public static Expression<Func<MyEntity, short?>> SelectPropertyOne()
    {
        return x => x.PropertyOne;
    }

    public static Expression<Func<MyEntity, short?>> SelectPropertyTwo()
    {
        return x => x.PropertyTwo;
    }

    public static Expression<Func<TEntity, bool>> BetweenNullable<TEntity, TNull>(Expression<Func<TEntity, Nullable<TNull>>> selector, Nullable<TNull> minRange, Nullable<TNull> maxRange) where TNull : struct
    {
        var param = Expression.Parameter(typeof(TEntity), "entity");
        var member = Expression.Invoke(selector, param);

        Expression hasValue = Expression.Property(member, "HasValue");
        Expression greaterThanMinRange = Expression.GreaterThanOrEqual(member,
                                             Expression.Convert(Expression.Constant(minRange), typeof(Nullable<TNull>)));
        Expression lessThanMaxRange = Expression.LessThanOrEqual(member,
                                          Expression.Convert(Expression.Constant(maxRange), typeof(Nullable<TNull>)));

        Expression body = Expression.AndAlso(hasValue,
                      Expression.AndAlso(greaterThanMinRange, lessThanMaxRange));

        return Expression.Lambda<Func<TEntity, bool>>(body, param);
    }
}
Expression<Func<MyEntity, short?>> whatToSelect = Examples.SelectPropertyOne;

var query = Context
            .MyEntities
            .Where(Examples.BetweenNullable<MyEntity, short>(whatToSelect, 0, 30));