LINQ to Entities的IQueryable扩展方法

6
我正在尝试实现一个扩展方法,该方法将与linq2entities一起使用。 我最初认为,如果我的扩展方法采用并返回IQueryable,并且只要我的表达式仅使用受支持的方法,那么它将正常工作。 但是我遇到了很多问题,所以作为最后的手段,我复制了一个我知道已经可以工作的.NET扩展方法(FirstOrDefault)并简单地重命名它。 看起来它将根据从方法返回的表达式而不是方法本身的名称来评估“无法转换为存储表达式”的验证。
var prs = db.People.Where(p => p.PersonKey == 15).Select(p =>
  new
  {
    id = p.PersonKey,
    name1 = p.PersonHistories.AsQueryable().AsOf().Name
  } 
).ToList();

我的扩展方法,它只是一个复制了FirstOrDefault并更名的方法:

public static TSource AsOf<TSource>(this IQueryable<TSource> source)
{
  return source.Provider.Execute<TSource>(Expression.Call(null, ((MethodInfo)MethodBase.GetCurrentMethod()).MakeGenericMethod(new Type[] { typeof(TSource) }), new Expression[] { source.Expression }));
}

错误:

LINQ to Entities does not recognize the method 
'Models.PersonHistory AsOf[PersonHistory](System.Linq.IQueryable`1[Models.PersonHistory])' 
method, and this method cannot be translated into a store expression.

我该如何实现一个在Linq2Entities中受支持的IQueryable扩展方法?
我真正想要的是AsOf(source, DateTime asOf)能够像这样做:source.FirstOrDefault<IHistory>(s => s.EndDate > asOf && asOf >= s.StartDate ),但我不确定如何实现以便Linq2Entities支持它。
LINQKIT:使用linqkit,我得出了以下代码,并希望能将其重构为更可重用的形式:
Expression<Func<PersonHistory, bool>> IsCurrent = (p) => p.Ends > DateTime.Now && p.Starts <= DateTime.Now;
var query = db.PersonHistories.Where(IsCurrent);
  1. 使用全局声明表达式,而不是局部变量。
  2. 添加一个 DateTime asOf 参数,而不是使用 .Now 硬编码。
  3. 如果可能的话,将其改为扩展方法(这与 #1 类似,只是扩展方法更理想)。

4
但是,ObjectQueryProvider 如何知道如何将您的方法转换为 SQL 呢?Queryable 扩展方法只是将方法交给当前查询提供程序来执行。因此,您需要扩展查询提供程序(如果可能的话)或在一个下雨的下午编写自己的查询提供程序 :)。 - Gert Arnold
如果有人能提供一个扩展查询提供程序的例子,我愿意尝试一下。回答你的问题,我认为它不会尝试翻译我的方法(名字有什么关系呢?),而是翻译表达式,因为表达式不会使用linq to entities中不熟悉的内容。 - AaronLS
显然,我通过谷歌搜索找到了很多东西,但这就是我浪费一天时间尝试让它工作的原因,只有意识到所有IQueryable扩展的示例都无法与linq2entities配合使用......所以任何指导将不胜感激。 - AaronLS
你可能想要看一下re-linq,它旨在摆脱IQueryable的世界,进入一个实际可组合的抽象语法树(表达式是不可变的)。CodeProject上有一个示例链接,希望能为您指明正确的方向。 - SPFiredrake
3个回答

4

这应该可以在不扩展查询提供程序的情况下工作。它基本上将其分解为您的IsCurrent函数正在执行的操作。

public static class IQueryableExtensions
{
    public static IQueryable<T> IsCurrent<T>(this IQueryable<T> query,
        Expression<Func<T, DateTime?>> expressionEnd,
        Expression<Func<T, DateTime?>> expressionStart,
        DateTime asOf) where T : class
    {
        // Lambdas being sent in
        ParameterExpression paramEnd = expressionEnd.Parameters.Single();
        ParameterExpression paramStart = expressionStart.Parameters.Single();

        // GT Comparison
        BinaryExpression expressionGT = ExpressionGT(expressionEnd.Body, asOf);

        // LT Comparison
        BinaryExpression expressionLT = ExpressionLE(expressionStart.Body, asOf);

        query = query.Where(Expression.Lambda<Func<T, bool>>(expressionGT, paramEnd))
                     .Where(Expression.Lambda<Func<T, bool>>(expressionLT, paramStart));

        return query;
    }

    private static BinaryExpression ExpressionLE(Expression body, DateTime value)
    {
        return Expression.LessThanOrEqual(body, Expression.Constant(value, typeof(DateTime)));
    }

    private static BinaryExpression ExpressionGT(Expression body, DateTime value)
    {
        return Expression.GreaterThan(body, Expression.Constant(value, typeof(DateTime)));
    }
}

并且使用它

var query = db.PersonHistories.IsCurrent( p => p.Ends, 
                                          p => p.Starts, 
                                          DateTime.Now );

这段代码在大多数情况下都能正常工作,但在像我举的例子(问题中的第一个代码块)这样嵌套查询的地方会出现“Linq2Entities不识别方法”的错误。 LinqKit之所以能够正常工作,是因为他们实现了自己的ExpressionVisitors,这本质上就是Gert所说的我需要做的事情,只是代码非常复杂难以理解。 - AaronLS
EF不支持这个功能,除非你调整生成SQL的Linq解析器。你可以深入了解LinqKit的内部机制,但这有点复杂。如果你想在子查询中进行内联限制,恐怕你需要分两步完成或手写代码实现。 - VulgarBinary
@Steve 那个.Where会在多个地方重复出现,所以我想将该逻辑封装成扩展方法。然后下一步是将它抽象化,使其能够操作IHistory而不是PersonHistory,这样任何实现了IHistory的对象都可以轻松地被切片到某个时点。我已经找到了一个我认为可行的解决方案:http://pastebin.com/4fMjaCMV - AaronLS
@Steve 实际上发生的是访问者在我的扩展方法上调用.Invoke,然后返回IQueryable,该IQueryable替换了表达式树中的扩展方法调用。这就是我尝试使用LinqKit代码实现的内容,但无法正确调用Invoke。我正在解决一些问题,并将发布完整的解决方案。 - AaronLS
@AaronLS 是的,这是相当多的工作!希望你能搞定。我写了几个实现ExpressionVisitor的类,但我无法弄清楚我所做的如何适用于你的问题。 - Steve Mallory
显示剩余2条评论

3

我看到你在另一个问题的回答中评论了我的答案,所以我想在这里也回复一下。我对代码进行了一些修改和改进(支持编译查询和自定义扩展方法来表达替换)。

这可能作为一个答案:

/// <summary>
/// Type helpers
/// </summary>
internal static class TypeSystem
{
    private static Type FindIEnumerable(Type seqType)
    {
        Type type;
        if (seqType == null || seqType == typeof(string) || seqType == typeof(byte[]))
        {
            return null;
        }
        else
        {
            if (!seqType.IsArray)
            {
                if (seqType.IsGenericType)
                {
                    Type[] genericArguments = seqType.GetGenericArguments();
                    int num = 0;
                    while (num < (int)genericArguments.Length)
                    {
                        Type type1 = genericArguments[num];
                        Type[] typeArray = new Type[1];
                        typeArray[0] = type1;
                        Type type2 = typeof(IEnumerable<>).MakeGenericType(typeArray);
                        if (!type2.IsAssignableFrom(seqType))
                        {
                            num++;
                        }
                        else
                        {
                            type = type2;
                            return type;
                        }
                    }
                }
                Type[] interfaces = seqType.GetInterfaces();
                if (interfaces != null && (int)interfaces.Length > 0)
                {
                    Type[] typeArray1 = interfaces;
                    int num1 = 0;
                    while (num1 < (int)typeArray1.Length)
                    {
                        Type type3 = typeArray1[num1];
                        Type type4 = TypeSystem.FindIEnumerable(type3);
                        if (type4 == null)
                        {
                            num1++;
                        }
                        else
                        {
                            type = type4;
                            return type;
                        }
                    }
                }
                if (!(seqType.BaseType != null) || !(seqType.BaseType != typeof(object)))
                {
                    return null;
                }
                else
                {
                    return TypeSystem.FindIEnumerable(seqType.BaseType);
                }
            }
            else
            {
                Type[] elementType = new Type[1];
                elementType[0] = seqType.GetElementType();
                return typeof(IEnumerable<>).MakeGenericType(elementType);
            }
        }
    }

    internal static Type GetElementType(Type seqType)
    {
        Type type = TypeSystem.FindIEnumerable(seqType);
        if (type != null)
        {
            return type.GetGenericArguments()[0];
        }
        else
        {
            return seqType;
        }
    }
}

/// <summary>
/// Marks an extension as compatible for custom linq expression expansion
/// Optionally if you can not write the extension method to fit your needs, you can provide a 
/// expression id constant for a registered expression.
/// </summary>
[AttributeUsage(AttributeTargets.Method, AllowMultiple= false, Inherited = false)]
class ExpandableQueryMethodAttribute :
    Attribute
{
    public ExpandableQueryMethodAttribute()
    {
    }
    public ExpandableQueryMethodAttribute(string expressionId)
    {
        _expressionId = expressionId;
    }

    private string _expressionId;
    public LambdaExpression TranslationExpression
    {
        get
        {
            return _expressionId != null ? QueryMethodTranslationExpressions.GetRegistered(_expressionId) : null;
        }
    }
}

/// <summary>
/// Used to register expressions for extension method to expression substitutions
/// </summary>
static class QueryMethodTranslationExpressions
{
    private static Dictionary<string, LambdaExpression> expressionList = new Dictionary<string, LambdaExpression>();

    /// <summary>
    /// Register expression
    /// </summary>
    /// <typeparam name="TFunc">type of expression delegate</typeparam>
    /// <param name="id">id constant for use with ExpandableQueryMethodAttribute</param>
    /// <param name="expr">expression</param>
    public static void RegisterExpression<TFunc>(string id, Expression<TFunc> expr)
    {
        expressionList.Add(id, expr);
    }

    public static LambdaExpression GetRegistered(string id)
    {
        //Extensions;
        return expressionList[id];
    }
}

static class Extensions
{
    /// <summary>
    /// Use on object sets before using custom extension methods, except inside compiled queries
    /// </summary>
    public static IQueryable<T> AsExtendable<T>(this IQueryable<T> source)
    {
        if (source is ExtendableQuery<T>)
        {
            return (ExtendableQuery<T>)source;
        }

        return new ExtendableQueryProvider(source.Provider).CreateQuery<T>(source.Expression);
    }
}

/// <summary>
/// Provides PlaceHolderQuery
/// 
/// No other functionality
/// </summary>
public class PlaceHolderQueryProvider : IQueryProvider
{
    public PlaceHolderQueryProvider()
    {
    }

    public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
    {
        return new PlaceHolderQuery<TElement>(this, expression);
    }

    public IQueryable CreateQuery(Expression expression)
    {
        Type elementType = TypeSystem.GetElementType(expression.Type);
        try
        {
            return (IQueryable)Activator.CreateInstance(typeof(PlaceHolderQuery<>).MakeGenericType(elementType), new object[] { this, expression });
        }
        catch (System.Reflection.TargetInvocationException tie)
        {
            throw tie.InnerException;
        }
    }

    public TResult Execute<TResult>(Expression expression)
    {
        throw new NotImplementedException();
    }

    public object Execute(Expression expression)
    {
        throw new NotImplementedException();
    }
}

/// <summary>
/// Does nothing
/// 
/// Acts only as a holder for expression
/// </summary>
public class PlaceHolderQuery<T> : IQueryable<T>, IOrderedQueryable<T>
{

    private Expression _expression;
    private PlaceHolderQueryProvider _provider;

    public PlaceHolderQuery(PlaceHolderQueryProvider provider, Expression expression)
    {
        _provider = provider;
        _expression = expression;
    }

    public IEnumerator<T> GetEnumerator()
    {
        throw new NotImplementedException();
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        throw new NotImplementedException();
    }

    public Type ElementType
    {
        get
        {
            return typeof(T);
        }
    }

    public Expression Expression
    {
        get
        {
            return _expression;
        }
    }

    public IQueryProvider Provider
    {
        get
        {
            return _provider;
        }
    }
}

/// <summary>
/// Walks the expression tree and invokes custom extension methods ( to expand them ) or substitutes them with custom expressions
/// </summary>
class ExtendableVisitor : ExpressionVisitor
{
    class ExpandingVisitor : ExpressionVisitor
    {
        private Dictionary<ParameterExpression, Expression> _substitutionDictionary;

        public ExpandingVisitor(Dictionary<ParameterExpression, Expression> subDict)
        {
            _substitutionDictionary = subDict;
        }

        protected override Expression VisitParameter(ParameterExpression node)
        {
            if (_substitutionDictionary != null && _substitutionDictionary.ContainsKey(node))
                return _substitutionDictionary[node];
            else
                return base.VisitParameter(node);
        }
    }

    IQueryProvider _provider;

    internal ExtendableVisitor()
    {
        _provider = new PlaceHolderQueryProvider();
    }

    protected override Expression VisitMethodCall(MethodCallExpression node)
    {
        ExpandableQueryMethodAttribute attrib = (ExpandableQueryMethodAttribute)node.Method.GetCustomAttributes(typeof(ExpandableQueryMethodAttribute), false).FirstOrDefault();

        if (attrib != null && node.Method.IsStatic)
        {

            if (attrib.TranslationExpression != null && attrib.TranslationExpression.Parameters.Count == node.Arguments.Count)
            {
                Dictionary<ParameterExpression, Expression> subDict = new Dictionary<ParameterExpression,Expression>();

                for (int i = 0; i < attrib.TranslationExpression.Parameters.Count; i++)
                {
                    subDict.Add(attrib.TranslationExpression.Parameters[i], node.Arguments[i]);
                }

                ExpandingVisitor expander = new ExpandingVisitor(subDict);

                Expression exp = expander.Visit(attrib.TranslationExpression.Body);

                return exp;
            }
            else if (typeof(IQueryable).IsAssignableFrom(node.Method.ReturnType))
            {
                object[] args = new object[node.Arguments.Count];
                args[0] = _provider.CreateQuery(node.Arguments[0]);

                for (int i = 1; i < node.Arguments.Count; i++)
                {
                    Expression arg = node.Arguments[i];
                    args[i] = (arg.NodeType == ExpressionType.Constant) ? ((ConstantExpression)arg).Value : arg;
                }

                Expression exp = ((IQueryable)node.Method.Invoke(null, args)).Expression;

                return exp;
            }
        }            

        return base.VisitMethodCall(node);
    }
}

/// <summary>
/// Used for query compilation
/// 
/// If custom extension methods are used, the existing CompileQuery functions do not work, so I had to write this.
/// </summary>
static class CompiledExtendableQuery
{
    public static Func<TContext, TResult>
               Compile<TContext, TResult>(
       Expression<Func<TContext, TResult>> expr) where TContext : ObjectContext
    {
        return System.Data.Objects.CompiledQuery.Compile(expr.Update(new ExtendableVisitor().Visit(expr.Body), expr.Parameters));
    }

    public static Func<TContext, TArg0, TResult>
               Compile<TContext, TArg0, TResult>(
       Expression<Func<TContext, TArg0, TResult>> expr) where TContext : ObjectContext
    {
        return System.Data.Objects.CompiledQuery.Compile(expr.Update(new ExtendableVisitor().Visit(expr.Body), expr.Parameters));
    }

    public static Func<TContext, TArg0, TArg1, TResult>
               Compile<TContext, TArg0, TArg1, TResult>
      (Expression<Func<TContext, TArg0, TArg1, TResult>> expr) where TContext : ObjectContext
    {
        return System.Data.Objects.CompiledQuery.Compile(expr.Update(new ExtendableVisitor().Visit(expr.Body), expr.Parameters));
    }

    public static Func<TContext, TArg0, TArg1, TArg2, TResult>
               Compile<TContext, TArg0, TArg1, TArg2, TResult>(
       Expression<Func<TContext, TArg0, TArg1, TArg2, TResult>> expr) where TContext : ObjectContext
    {
        return System.Data.Objects.CompiledQuery.Compile(expr.Update(new ExtendableVisitor().Visit(expr.Body), expr.Parameters));
    }

    public static Func<TContext, TArg0, TArg1, TArg2, TArg3, TResult>
               Compile<TContext, TArg0, TArg1, TArg2, TArg3, TResult>(
       Expression<Func<TContext, TArg0, TArg1, TArg2, TArg3, TResult>> expr) where TContext : ObjectContext
    {
        return System.Data.Objects.CompiledQuery.Compile(expr.Update(new ExtendableVisitor().Visit(expr.Body), expr.Parameters));
    }

    public static Func<TContext, TArg0, TArg1, TArg2, TArg3, TArg4, TResult>
               Compile<TContext, TArg0, TArg1, TArg2, TArg3, TArg4, TResult>(
       Expression<Func<TContext, TArg0, TArg1, TArg2, TArg3, TArg4, TResult>> expr) where TContext : ObjectContext
    {
        return System.Data.Objects.CompiledQuery.Compile(expr.Update(new ExtendableVisitor().Visit(expr.Body), expr.Parameters));
    }

    public static Func<TContext, TArg0, TArg1, TArg2, TArg3, TArg4, TArg5, TResult>
               Compile<TContext, TArg0, TArg1, TArg2, TArg3, TArg4, TArg5, TResult>(
       Expression<Func<TContext, TArg0, TArg1, TArg2, TArg3, TArg4, TArg5, TResult>> expr) where TContext : ObjectContext
    {
        return System.Data.Objects.CompiledQuery.Compile(expr.Update(new ExtendableVisitor().Visit(expr.Body), expr.Parameters));
    }

    public static Func<TContext, TArg0, TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TResult>
               Compile<TContext, TArg0, TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TResult>(
       Expression<Func<TContext, TArg0, TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TResult>> expr) where TContext : ObjectContext
    {
        return System.Data.Objects.CompiledQuery.Compile(expr.Update(new ExtendableVisitor().Visit(expr.Body), expr.Parameters));
    }

    public static Func<TContext, TArg0, TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TResult>
               Compile<TContext, TArg0, TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TResult>(
       Expression<Func<TContext, TArg0, TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TResult>> expr) where TContext : ObjectContext
    {
        return System.Data.Objects.CompiledQuery.Compile(expr.Update(new ExtendableVisitor().Visit(expr.Body), expr.Parameters));
    }

    public static Func<TContext, TArg0, TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TArg8, TResult>
               Compile<TContext, TArg0, TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TArg8, TResult>(
       Expression<Func<TContext, TArg0, TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TArg8, TResult>> expr) where TContext : ObjectContext
    {
        return System.Data.Objects.CompiledQuery.Compile(expr.Update(new ExtendableVisitor().Visit(expr.Body), expr.Parameters));
    }

    public static Func<TContext, TArg0, TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TArg8, TArg9, TResult>
               Compile<TContext, TArg0, TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TArg8, TArg9, TResult>(
       Expression<Func<TContext, TArg0, TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TArg8, TArg9, TResult>> expr) where TContext : ObjectContext
    {
        return System.Data.Objects.CompiledQuery.Compile(expr.Update(new ExtendableVisitor().Visit(expr.Body), expr.Parameters));
    }

    public static Func<TContext, TArg0, TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TArg8, TArg9, TArg10, TResult>
               Compile<TContext, TArg0, TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TArg8, TArg9, TArg10, TResult>(
       Expression<Func<TContext, TArg0, TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TArg8, TArg9, TArg10, TResult>> expr) where TContext : ObjectContext
    {
        return System.Data.Objects.CompiledQuery.Compile(expr.Update(new ExtendableVisitor().Visit(expr.Body), expr.Parameters));
    }

    public static Func<TContext, TArg0, TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TArg8, TArg9, TArg10, TArg11, TResult>
               Compile<TContext, TArg0, TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TArg8, TArg9, TArg10, TArg11, TResult>(
       Expression<Func<TContext, TArg0, TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TArg8, TArg9, TArg10, TArg11, TResult>> expr) where TContext : ObjectContext
    {
        return System.Data.Objects.CompiledQuery.Compile(expr.Update(new ExtendableVisitor().Visit(expr.Body), expr.Parameters));
    }

    public static Func<TContext, TArg0, TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TArg8, TArg9, TArg10, TArg11, TArg12, TResult>
               Compile<TContext, TArg0, TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TArg8, TArg9, TArg10, TArg11, TArg12, TResult>(
       Expression<Func<TContext, TArg0, TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TArg8, TArg9, TArg10, TArg11, TArg12, TResult>> expr) where TContext : ObjectContext
    {
        return System.Data.Objects.CompiledQuery.Compile(expr.Update(new ExtendableVisitor().Visit(expr.Body), expr.Parameters));
    }

    public static Func<TContext, TArg0, TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TArg8, TArg9, TArg10, TArg11, TArg12, TArg13, TResult>
               Compile<TContext, TArg0, TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TArg8, TArg9, TArg10, TArg11, TArg12, TArg13, TResult>(
       Expression<Func<TContext, TArg0, TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TArg8, TArg9, TArg10, TArg11, TArg12, TArg13, TResult>> expr) where TContext : ObjectContext
    {
        return System.Data.Objects.CompiledQuery.Compile(expr.Update(new ExtendableVisitor().Visit(expr.Body), expr.Parameters));
    }

    public static Func<TContext, TArg0, TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TArg8, TArg9, TArg10, TArg11, TArg12, TArg13, TArg14, TResult>
               Compile<TContext, TArg0, TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TArg8, TArg9, TArg10, TArg11, TArg12, TArg13, TArg14, TResult>(
       Expression<Func<TContext, TArg0, TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TArg8, TArg9, TArg10, TArg11, TArg12, TArg13, TArg14, TResult>> expr) where TContext : ObjectContext
    {
        return System.Data.Objects.CompiledQuery.Compile(expr.Update(new ExtendableVisitor().Visit(expr.Body), expr.Parameters));
    }

}

/// <summary>
/// The query as it becomes when AsExtendable is called on it.
/// </summary>
class ExtendableQuery<T> : IQueryable<T>, IOrderedQueryable<T>
{
    ExtendableQueryProvider _provider;
    Expression _expression;

    public ExtendableQuery(ExtendableQueryProvider provider, Expression expression)
    {
        _provider = provider;
        _expression = expression;
    }

    public IEnumerator<T> GetEnumerator()
    {
        return _provider.ExecuteQuery<T>(_expression).GetEnumerator();
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

    public Type ElementType
    {
        get {
            return typeof(T);
        }
    }

    public Expression Expression
    {
        get {
            return _expression;    
        }
    }

    public IQueryProvider Provider
    {
        get {
            return _provider;
        }
    }


}

class ExtendableQueryProvider : IQueryProvider
{
    IQueryProvider _underlyingQueryProvider;

    private ExtendableQueryProvider()
    {
    }

    internal ExtendableQueryProvider(IQueryProvider underlyingQueryProvider)
    {
        _underlyingQueryProvider = underlyingQueryProvider;
    }

    public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
    {
        return new ExtendableQuery<TElement>(this, expression);
    }

    public IQueryable CreateQuery(Expression expression)
    {
        Type elementType = TypeSystem.GetElementType(expression.Type);
        try
        {
            return (IQueryable)Activator.CreateInstance(typeof(ExtendableQuery<>).MakeGenericType(elementType), new object[] { this, expression });
        }
        catch (System.Reflection.TargetInvocationException tie)
        {
            throw tie.InnerException;
        }
    }

    internal IEnumerable<T> ExecuteQuery<T>(Expression expression)
    {
        return _underlyingQueryProvider.CreateQuery<T>(Visit(expression)).AsEnumerable();
    }

    public TResult Execute<TResult>(Expression expression)
    {
        return _underlyingQueryProvider.Execute<TResult>(Visit(expression));
    }

    public object Execute(Expression expression)
    {
        return _underlyingQueryProvider.Execute(Visit(expression));
    }

    private Expression Visit(Expression exp)
    {
        ExtendableVisitor vstr = new ExtendableVisitor();
        Expression visitedExp = vstr.Visit(exp);

        return visitedExp;
    }
}

很抱歉我的回复比较简短,因为现在是深夜,我必须赶快回复并继续工作。

我很乐意回答你的任何问题。


你是否使用过返回单个项目的扩展方法,相当于.First或.Single?我正在努力调整您的代码以执行此操作,以便方法可以提供其表达式,但是然后我发现了TranslationExpression,我认为这正是我所需要的。问题是,我不知道要传递什么来注册表达式,因为我拥有的表达式取决于某些参数是否存在:http://pastebin.com/CpY9Sg65 - AaronLS
这是我尝试过的,但我收到了错误消息“类型为'System.Linq.Expressions.MethodCallExpression'的表达式不能用作返回类型'PropertyHistory'”:http://pastebin.com/BzQWqJRY - AaronLS
算了,我把它想得太复杂了。这个可以: QueryMethodTranslationExpressions.RegisterExpression<Func<IQueryable<PropertyHistory>, PropertyHistory>>("1", source => source.FirstOrDefault(xp => xp.Ends > DateTime.Now && xp.Starts <= DateTime.Now) ); - AaronLS

1

不要使用例如表达式。

Expression<Func<PersonHistory, bool>> IsCurrent = (p)
    => p.Ends > DateTime.Now && p.Starts <= DateTime.Now;

var query = db.PersonHistories.Where(IsCurrent);

您可以定义扩展方法,例如:

public static IsCurrent Func<
    IQueryable<PersonHistory>, DateTime, IQueryable<PersonHistory>
    >()
{
    return (IQueryable<PersonHistory> query, DateTime referenceDate) =>
        query.Where(p.Ends > referenceDate && p.Starts <= referenceDate);
}

使用方法如下:

var query = IsCurrent();

var results = query(context.PeoplesHistory, referenceDate);

或者:

var results = query(previousResults, referenceDate);

或者,另一种选择:

public static IsCurrent Func<IQueryable<PersonHistory>, IQueryable<PersonHistory>>(
    DateTime referenceDate)
{
    return (IQueryable<PersonHistory> query) =>
        query.Where(p.Ends > referenceDate && p.Starts <= referenceDate);
}

var query = IsCurrent(refernceDate);

var results = query(context.PeoplesHistory);

这样你就不需要一个用于构建表达式的框架。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接