对于linq2entities IQueryable的扩展方法 [英] IQueryable extension method for linq2entities

查看:397
本文介绍了对于linq2entities IQueryable的扩展方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想实现的扩展方法,将与linq2entities工作。我最初假设,如果我的扩展方法了,返回一个IQueryable,只要我的前妻pression只利用了支持的方法,那么它会正常工作。我有很多的麻烦,所以作为最后手段,我抄,我知道工作(FirstOrDefault),并简单地更名为现有的.NET扩展方法。这似乎像它会评估不能被翻译成店前pression的基础上的前pression验证从方法,该方法本身不是名字。

返回

  VAR PRS = db.People.Where(P => p.PersonKey == 15)。选择(P =>
  新
  {
    ID = p.PersonKey,
    名1 = p.PersonH​​istories.AsQueryable()。ASOF()。名称
  }
).ToList();

我的扩展方法,这仅仅是一个FirstOrDefault的副本,我改名为:

 公共静态TSource ASOF< TSource>(这IQueryable的< TSource>源)
{
  返回source.Provider.Execute< TSource>(防爆pression.Call(NULL,((MethodInfo的)MethodBase.GetCurrentMethod())MakeGenericMethod(新类型[] {typeof运算(TSource)}),新的前$ P $。 pssion [] {source.Ex pression}));
}

错误:

  LINQ到实体无​​法识别方法
'Models.PersonH​​istory ASOF [PersonH​​istory](System.Linq.IQueryable`1 [Models.PersonH​​istory])'
法,这种方法不能被翻译成店前pression。

我如何实施在Linq2Entities支持一个IQueryable扩展方法?

我真正想要的ASOF(源,日期ASOF)做的是soemthing像 source.FirstOrDefault< IHistory>(S => s.EndDate> ASOF和放大器;&安培; ASOF> = s.StartDate),但我不知道这样,它是由linq2entities支持如何做到这一点。

LINQKIT:这是我想出用linqkit,我希望以某种方式我可以因素纳入更多的东西可重用这样的:

 防爆pression<&Func键LT; PersonH​​istory,布尔>> IsCurrent =(P)=> p.Ends> DateTime.Now&功放;&安培; p.Starts< = DateTime.Now;
VAR的查询= db.PersonH​​istories.Where(IsCurrent);


  1. 有一个更全局声明前pression,而不是本地
    变量。

  2. 添加一个DateTime ASOF参数,而不必辛苦。现在codeD。

  3. 如果可能,适应它变成一个扩展方法(这种
    是一样的#1,只是一个扩展方法是理想的。


解决方案

我看到你谈到我对另一个问题的答案,所以我想我会在这里也答复。我做了一些修改和改进到code(支持编译查询和自定义扩展方法前pression取代)。

这可以作为一个答案:

  ///<总结>
///类型帮手
///< /总结>
内部静态类类型系统
{
    私有静态类型FindIEnumerable(类型seqType)
    {
        输入型;
        如果(seqType == NULL || seqType == typeof运算(字符串)|| seqType == typeof运算(字节[]))
        {
            返回null;
        }
        其他
        {
            如果(!seqType.IsArray)
            {
                如果(seqType.IsGenericType)
                {
                    键入[] = genericArguments seqType.GetGenericArguments();
                    INT NUM = 0;
                    而(NUM≤(INT)genericArguments.Length)
                    {
                        键入TYPE1 = genericArguments [NUM]
                        键入[] = typeArray新型[1];
                        typeArray [0] = 1型;
                        类型TYPE2 = typeof运算(IEnumerable的<>)MakeGenericType(typeArray)。
                        如果(!type2.IsAssignableFrom(seqType))
                        {
                            NUM ++;
                        }
                        其他
                        {
                            键入= 2型;
                            返回类型;
                        }
                    }
                }
                键入[] =接口seqType.GetInterfaces();
                如果(接口=空&放大器;!及(INT)interfaces.Length大于0)
                {
                    键入[] = typeArray1接口;
                    INT NUM1 = 0;
                    而(NUM1≤(INT)typeArray1.Length)
                    {
                        类型3型= ty​​peArray1 [NUM1]
                        键入TYPE4 = TypeSystem.FindIEnumerable(3型);
                        如果(TYPE4 == NULL)
                        {
                            NUM1 ++;
                        }
                        其他
                        {
                            键入= TYPE4;
                            返回类型;
                        }
                    }
                }
                如果(!(seqType.BaseType!= NULL)||!(seqType.BaseType!= typeof运算(对象)))
                {
                    返回null;
                }
                其他
                {
                    返回TypeSystem.FindIEnumerable(seqType.BaseType);
                }
            }
            其他
            {
                键入[] =元素类型新型[1];
                的ElementType [0] = seqType.GetElementType();
                返回的typeof(IEnumerable的<>)MakeGenericType(元素类型)。
            }
        }
    }    内部静态类型GetElementType(类型seqType)
    {
        类型type = TypeSystem.FindIEnumerable(seqType);
        如果(型!= NULL)
        {
            返回type.GetGenericArguments()[0];
        }
        其他
        {
            返回seqType;
        }
    }
}///<总结>
///标志着一个扩展作为自定义LINQ前pression扩展兼容
///可选,如果你不能写扩展方法来满足您的需求,您可以提供一个
///前pression ID常量注册前pression。
///< /总结>
[AttributeUsage(AttributeTargets.Method,的AllowMultiple = FALSE,继承= FALSE)]
类ExpandableQueryMethodAttribute:
    属性
{
    公共ExpandableQueryMethodAttribute()
    {
    }
    公共ExpandableQueryMethodAttribute(字符串前pressionId)
    {
        _ex pressionId =前pressionId;
    }    私人字符串_ex pressionId;
    公共LambdaEx pression TranslationEx pression
    {
        得到
        {
            返回_ex pressionId!= NULL? QueryMethodTranslationEx pressions.GetRegistered(_ex pressionId):空;
        }
    }
}///<总结>
///用于为扩展方法前pression换人注册前pressions
///< /总结>
静态类QueryMethodTranslationEx pressions
{
    私有静态字典<字符串,LambdaEx pression>前pressionList =新词典<字符串,LambdaEx pression>();    ///<总结>
    ///注册前pression
    ///< /总结>
    ///< typeparam NAME =TFunc>前pression委托&LT型; / typeparam>
    ///< PARAM NAME =ID>对于ExpandableQueryMethodAttribute&LT使用ID常量; /参数>
    ///< PARAM NAME =expr的>前pression< /参数>
    公共静态无效RegisterEx pression< TFunc>(字符串ID,防爆pression< TFunc>表达式)
    {
        前pressionList.Add(ID,表达式);
    }    公共静态LambdaEx pression GetRegistered(字符串ID)
    {
        //扩展;
        返回前pressionList [ID]
    }
}静态类扩展
{
    ///<总结>
    ///使用自定义扩展方法之前,对象集使用,但里面编译查询
    ///< /总结>
    公共静态的IQueryable< T> AsExtendable< T>(这IQueryable的< T>源)
    {
        如果(来源ExtendableQuery< T>)
        {
            返回(ExtendableQuery< T>)来源;
        }        返回新ExtendableQueryProvider(source.Provider).CreateQuery< T>(source.Ex pression);
    }
}///<总结>
///提供PlaceHolderQuery
///
///没有其他功能
///< /总结>
公共类PlaceHolderQueryProvider:IQueryProvider
{
    公共PlaceHolderQueryProvider()
    {
    }    公众的IQueryable< TElement> &的createQuery LT; TElement>(防爆pression前pression)
    {
        返回新PlaceHolderQuery< TElement>(这一点,当然pression);
    }    公众的IQueryable的createQuery(前pression前pression)
    {
        键入的ElementType = TypeSystem.GetElementType(如pression.Type);
        尝试
        {
            回报(IQueryable的)Activator.CreateInstance(typeof运算(PlaceHolderQuery<方式>)MakeGenericType(元素类型),新的对象[] {这一点,前pression});
        }
        赶上(System.Reflection.TargetInvocationException并列)
        {
            扔tie.InnerException;
        }
    }    公共TResult执行< TResult>(防爆pression前pression)
    {
        抛出新NotImplementedException();
    }    公共对象执行(前pression前pression)
    {
        抛出新NotImplementedException();
    }
}///<总结>
/// 什么也没做
///
///只作为前pression持有人
///< /总结>
公共类PlaceHolderQuery< T> :IQueryable的<吨>中IOrderedQueryable< T>
{    私人防爆pression _ex pression;
    私人PlaceHolderQueryProvider _provider;    公共PlaceHolderQuery(PlaceHolderQueryProvider提供商,防爆pression前pression)
    {
        _provider =供应商;
        _ex pression =前pression;
    }    公众的IEnumerator< T>的GetEnumerator()
    {
        抛出新NotImplementedException();
    }    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        抛出新NotImplementedException();
    }    公共类型的ElementType
    {
        得到
        {
            返回的typeof(T);
        }
    }    公共防爆pression前pression
    {
        得到
        {
            返回_ex pression;
        }
    }    公共IQueryProvider提供商
    {
        得到
        {
            返回_provider;
        }
    }
}///<总结>
///自助游恩pression树,并调用自定义扩展的方法(将其展开),或者通过自定义前pressions替换它们
///< /总结>
类ExtendableVisitor:防爆pressionVisitor
{
    类ExpandingVisitor:防爆pressionVisitor
    {
        私人字典< ParameterEx pression,防爆pression> _substitutionDictionary;        公共ExpandingVisitor(词典< ParameterEx pression,防爆pression> subDict)
        {
            _substitutionDictionary = subDict;
        }        保护覆盖防爆pression VisitParameter(ParameterEx pression节点)
        {
            如果(_substitutionDictionary = NULL&放大器;!&安培; _substitutionDictionary.ContainsKey(节点))
                返回_substitutionDictionary [节点]
            其他
                返回base.VisitParameter(节点);
        }
    }    IQueryProvider _provider;    内部ExtendableVisitor()
    {
        _provider =新PlaceHolderQueryProvider();
    }    保护覆盖防爆pression VisitMethodCall(MethodCallEx pression节点)
    {
        ExpandableQueryMethodAttribute ATTRIB = (ExpandableQueryMethodAttribute)node.Method.GetCustomAttributes(typeof(ExpandableQueryMethodAttribute),假).FirstOrDefault();        如果(ATTRIB = NULL&放大器;!&安培; node.Method.IsStatic)
        {            如果(attrib.TranslationEx pression = NULL&放大器;!&安培; attrib.TranslationEx pression.Parameters.Count == node.Arguments.Count)
            {
                字典< ParameterEx pression,防爆pression> subDict =新词典< ParameterEx pression,防爆pression>();                的for(int i = 0; I< attrib.TranslationEx pression.Parameters.Count;我++)
                {
                    subDict.Add(attrib.TranslationEx pression.Parameters [I]中,node.Arguments [I]);
                }                ExpandingVisitor扩展=新ExpandingVisitor(subDict);                防爆pression EXP = expander.Visit(attrib.TranslationEx pression.Body);                返回EXP;
            }
            否则,如果(typeof运算(IQueryable的).IsAssignableFrom(node.Method.ReturnType))
            {
                对象[] ARGS =新对象[node.Arguments.Count]
                ARGS [0] = _provider.CreateQuery(node.Arguments [0]);                的for(int i = 1; I< node.Arguments.Count;我++)
                {
                    防爆pression ARG = node.Arguments [I]
                    ARGS [I] =(arg.NodeType ==前pressionType.Constant)? ((ConstantEx pression)ARG).value的:ARG;
                }                防爆pression EXP =((IQueryable的)node.Method.Invoke(NULL,参数))前pression。                返回EXP;
            }
        }        返回base.VisitMethodCall(节点);
    }
}///<总结>
///用于查询编译
///
///如果使用自定义扩展的方法,现有的CompileQuery功能不工作,所以我不得不写这一点。
///< /总结>
静态类CompiledExtendableQuery
{
    公共静态Func键< TContext,TResult>
               编译< TContext,TResult>(
       防爆pression<&Func键LT; TContext,TResult>>表达式),其中TContext:ObjectContext的
    {
        返回System.Data.Objects.CompiledQuery.Compile(expr.Update(新ExtendableVisitor()访问(expr.Body),expr.Parameters));
    }    公共静态Func键< TContext,TArg0,TResult>
               编译< TContext,TArg0,TResult>(
       防爆pression<&Func键LT; TContext,TArg0,TResult>>表达式),其中TContext:ObjectContext的
    {
        返回System.Data.Objects.CompiledQuery.Compile(expr.Update(新ExtendableVisitor()访问(expr.Body),expr.Parameters));
    }    公共静态Func键< TContext,TArg0,TArg1,TResult>
               编译< TContext,TArg0,TArg1,TResult>
      (防爆pression<&Func键LT; TContext,TArg0,TArg1,TResult>>表达式),其中TContext:ObjectContext的
    {
        返回System.Data.Objects.CompiledQuery.Compile(expr.Update(新ExtendableVisitor()访问(expr.Body),expr.Parameters));
    }    公共静态Func键< TContext,TArg0,TArg1,TArg2,TResult>
               编译< TContext,TArg0,TArg1,TArg2,TResult>(
       防爆pression<&Func键LT; TContext,TArg0,TArg1,TArg2,TResult>>表达式),其中TContext:ObjectContext的
    {
        返回System.Data.Objects.CompiledQuery.Compile(expr.Update(新ExtendableVisitor()访问(expr.Body),expr.Parameters));
    }    公共静态Func键< TContext,TArg0,TArg1,TArg2,TAr​​g3,TResult>
               编译< TContext,TArg0,TArg1,TArg2,TAr​​g3,TResult>(
       防爆pression<&Func键LT; TContext,TArg0,TArg1,TArg2,TAr​​g3,TResult>>表达式),其中TContext:ObjectContext的
    {
        返回System.Data.Objects.CompiledQuery.Compile(expr.Update(新ExtendableVisitor()访问(expr.Body),expr.Parameters));
    }    公共静态Func键< TContext,TArg0,TArg1,TArg2,TAr​​g3,TArg4,TResult>
               编译< TContext,TArg0,TArg1,TArg2,TAr​​g3,TArg4,TResult>(
       防爆pression<&Func键LT; TContext,TArg0,TArg1,TArg2,TAr​​g3,TArg4,TResult>>表达式),其中TContext:ObjectContext的
    {
        返回System.Data.Objects.CompiledQuery.Compile(expr.Update(新ExtendableVisitor()访问(expr.Body),expr.Parameters));
    }    公共静态Func键< TContext,TArg0,TArg1,TArg2,TAr​​g3,TArg4,TArg5,TResult>
               编译< TContext,TArg0,TArg1,TArg2,TAr​​g3,TArg4,TArg5,TResult>(
       防爆pression<&Func键LT; TContext,TArg0,TArg1,TArg2,TAr​​g3,TArg4,TArg5,TResult>>表达式),其中TContext:ObjectContext的
    {
        返回System.Data.Objects.CompiledQuery.Compile(expr.Update(新ExtendableVisitor()访问(expr.Body),expr.Parameters));
    }    公共静态Func键< TContext,TArg0,TArg1,TArg2,TAr​​g3,TArg4,TArg5,TArg6,TResult>
               编译< TContext,TArg0,TArg1,TArg2,TAr​​g3,TArg4,TArg5,TArg6,TResult>(
       防爆pression<&Func键LT; TContext,TArg0,TArg1,TArg2,TAr​​g3,TArg4,TArg5,TArg6,TResult>>表达式),其中TContext:ObjectContext的
    {
        返回System.Data.Objects.CompiledQuery.Compile(expr.Update(新ExtendableVisitor()访问(expr.Body),expr.Parameters));
    }    公共静态Func键< TContext,TArg0,TArg1,TArg2,TAr​​g3,TArg4,TArg5,TArg6,TArg7,TResult>
               编译< TContext,TArg0,TArg1,TArg2,TAr​​g3,TArg4,TArg5,TArg6,TArg7,TResult>(
       防爆pression<&Func键LT; TContext,TArg0,TArg1,TArg2,TAr​​g3,TArg4,TArg5,TArg6,TArg7,TResult>>表达式),其中TContext:ObjectContext的
    {
        返回System.Data.Objects.CompiledQuery.Compile(expr.Update(新ExtendableVisitor()访问(expr.Body),expr.Parameters));
    }    公共静态Func键< TContext,TArg0,TArg1,TArg2,TAr​​g3,TArg4,TArg5,TArg6,TArg7,TArg8,TResult>
               编译< TContext,TArg0,TArg1,TArg2,TAr​​g3,TArg4,TArg5,TArg6,TArg7,TArg8,TResult>(
       防爆pression<&Func键LT; TContext,TArg0,TArg1,TArg2,TAr​​g3,TArg4,TArg5,TArg6,TArg7,TArg8,TResult>>表达式),其中TContext:ObjectContext的
    {
        返回System.Data.Objects.CompiledQuery.Compile(expr.Update(新ExtendableVisitor()访问(expr.Body),expr.Parameters));
    }    公共静态Func键< TContext,TArg0,TArg1,TArg2,TAr​​g3,TArg4,TArg5,TArg6,TArg7,TArg8,TArg9,TResult>
               编译< TContext,TArg0,TArg1,TArg2,TAr​​g3,TArg4,TArg5,TArg6,TArg7,TArg8,TArg9,TResult>(
       防爆pression<&Func键LT; TContext,TArg0,TArg1,TArg2,TAr​​g3,TArg4,TArg5,TArg6,TArg7,TArg8,TArg9,TResult>>表达式),其中TContext:ObjectContext的
    {
        返回System.Data.Objects.CompiledQuery.Compile(expr.Update(新ExtendableVisitor()访问(expr.Body),expr.Parameters));
    }    公共静态Func键< TContext,TArg0,TArg1,TArg2,TAr​​g3,TArg4,TArg5,TArg6,TArg7,TArg8,TArg9,TArg10,TResult>
               编译< TContext,TArg0,TArg1,TArg2,TAr​​g3,TArg4,TArg5,TArg6,TArg7,TArg8,TArg9,TArg10,TResult>(
       防爆pression<&Func键LT; TContext,TArg0,TArg1,TArg2,TAr​​g3,TArg4,TArg5,TArg6,TArg7,TArg8,TArg9,TArg10,TResult>>表达式),其中TContext:ObjectContext的
    {
        返回System.Data.Objects.CompiledQuery.Compile(expr.Update(新ExtendableVisitor()访问(expr.Body),expr.Parameters));
    }    公共静态Func键< TContext,TArg0,TArg1,TArg2,TAr​​g3,TArg4,TArg5,TArg6,TArg7,TArg8,TArg9,TArg10,TArg11,TResult>
               编译< TContext,TArg0,TArg1,TArg2,TAr​​g3,TArg4,TArg5,TArg6,TArg7,TArg8,TArg9,TArg10,TArg11,TResult>(
       防爆pression<&Func键LT; TContext,TArg0,TArg1,TArg2,TAr​​g3,TArg4,TArg5,TArg6,TArg7,TArg8,TArg9,TArg10,TArg11,TResult>>表达式),其中TContext:ObjectContext的
    {
        返回System.Data.Objects.CompiledQuery.Compile(expr.Update(新ExtendableVisitor()访问(expr.Body),expr.Parameters));
    }    公共静态Func键< TContext,TArg0,TArg1,TArg2,TAr​​g3,TArg4,TArg5,TArg6,TArg7,TArg8,TArg9,TArg10,TArg11,TArg12,TResult>
               编译< TContext,TArg0,TArg1,TArg2,TAr​​g3,TArg4,TArg5,TArg6,TArg7,TArg8,TArg9,TArg10,TArg11,TArg12,TResult>(
       防爆pression<&Func键LT; TContext,TArg0,TArg1,TArg2,TAr​​g3,TArg4,TArg5,TArg6,TArg7,TArg8,TArg9,TArg10,TArg11,TArg12,TResult>>表达式),其中TContext:ObjectContext的
    {
        返回System.Data.Objects.CompiledQuery.Compile(expr.Update(新ExtendableVisitor()访问(expr.Body),expr.Parameters));
    }    公共静态Func键< TContext,TArg0,TArg1,TArg2,TAr​​g3,TArg4,TArg5,TArg6,TArg7,TArg8,TArg9,TArg10,TArg11,TArg12,TAr​​g13,TResult>
               编译< TContext,TArg0,TArg1,TArg2,TAr​​g3,TArg4,TArg5,TArg6,TArg7,TArg8,TArg9,TArg10,TArg11,TArg12,TAr​​g13,TResult>(
       防爆pression<&Func键LT; TContext,TArg0,TArg1,TArg2,TAr​​g3,TArg4,TArg5,TArg6,TArg7,TArg8,TArg9,TArg10,TArg11,TArg12,TAr​​g13,TResult>>表达式),其中TContext:ObjectContext的
    {
        返回System.Data.Objects.CompiledQuery.Compile(expr.Update(新ExtendableVisitor()访问(expr.Body),expr.Parameters));
    }    公共静态Func键< TContext,TArg0,TArg1,TArg2,TAr​​g3,TArg4,TArg5,TArg6,TArg7,TArg8,TArg9,TArg10,TArg11,TArg12,TAr​​g13,TArg14,TResult>
               编译< TContext,TArg0,TArg1,TArg2,TAr​​g3,TArg4,TArg5,TArg6,TArg7,TArg8,TArg9,TArg10,TArg11,TArg12,TAr​​g13,TArg14,TResult>(
       防爆pression< Func键< TContext,TArg0,TArg1,TArg2,TAr​​g3,TArg4,TArg5,TArg6,TArg7,TArg8,TArg9,TArg10,TArg11,TArg12,TAr​​g13,TArg14,TResult>>表达式),其中TContext:ObjectContext的
    {
        返回System.Data.Objects.CompiledQuery.Compile(expr.Update(新ExtendableVisitor()访问(expr.Body),expr.Parameters));
    }}///<总结>
///查询,因为它成为当AsExtendable被调用就可以了。
///< /总结>
类ExtendableQuery< T> :IQueryable的<吨>中IOrderedQueryable< T>
{
    ExtendableQueryProvider _provider;
    防爆pression _ex pression;    公共ExtendableQuery(ExtendableQueryProvider提供商,防爆pression前pression)
    {
        _provider =供应商;
        _ex pression =前pression;
    }    公众的IEnumerator< T>的GetEnumerator()
    {
        返回_provider.ExecuteQuery< T>(_前pression).GetEnumerator();
    }    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        返回的GetEnumerator();
    }    公共类型的ElementType
    {
        获得{
            返回的typeof(T);
        }
    }    公共防爆pression前pression
    {
        获得{
            返回_ex pression;
        }
    }    公共IQueryProvider提供商
    {
        获得{
            返回_provider;
        }
    }
}类ExtendableQueryProvider:IQueryProvider
{
    IQueryProvider _underlyingQueryProvider;    私人ExtendableQueryProvider()
    {
    }    内部ExtendableQueryProvider(IQueryProvider underlyingQueryProvider)
    {
        _underlyingQueryProvider = underlyingQueryProvider;
    }    公众的IQueryable< TElement> &的createQuery LT; TElement>(防爆pression前pression)
    {
        返回新ExtendableQuery< TElement>(这一点,当然pression);
    }    公众的IQueryable的createQuery(前pression前pression)
    {
        键入的ElementType = TypeSystem.GetElementType(如pression.Type);
        尝试
        {
            回报(IQueryable的)Activator.CreateInstance(typeof运算(ExtendableQuery<方式>)MakeGenericType(元素类型),新的对象[] {这一点,前pression});
        }
        赶上(System.Reflection.TargetInvocationException并列)
        {
            扔tie.InnerException;
        }
    }    内部的IEnumerable< T>的executeQuery< T>(防爆pression前pression)
    {
        返回_underlyingQueryProvider.CreateQuery< T>(参观(如pression))AsEnumerable();
    }    公共TResult执行< TResult>(防爆pression前pression)
    {
        返回_underlyingQueryProvider.Execute< TResult>(参观(如pression));
    }    公共对象执行(前pression前pression)
    {
        返回_underlyingQueryProvider.Execute(访问(如pression));
    }    私人防爆pression访问(防爆pression EXP)
    {
        ExtendableVisitor VSTR =新ExtendableVisitor();
        防爆pression visitedExp = vstr.Visit(EXP);        返回visitedExp;
    }
}

对不起,我的答复的简洁,它是这里的半夜,我得赶紧因为有工作要做。

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

I am trying to implement an extension method that will work with linq2entities. I had initially assumed that if my extension method took and returned an IQueryable, and as long as my expression only made use of supported methods, then it would work fine. I was having alot of trouble, so as a last resort I copied an existing .NET extension method that I knew to work(FirstOrDefault) and simply renamed it. It would seem like it would evaluate the "cannot be translated into a store expression" validation based on the expression returned from the method, not the name of the method itself.

var prs = db.People.Where(p => p.PersonKey == 15).Select(p =>
  new
  {
    id = p.PersonKey,
    name1 = p.PersonHistories.AsQueryable().AsOf().Name
  } 
).ToList();

My extension method, which is just a copy of FirstOrDefault that I renamed:

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 }));
}

Error:

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.

How do I implement an IQueryable extension method that is supported in Linq2Entities?

What I really want AsOf(source, DateTime asOf) to do is soemthing like source.FirstOrDefault<IHistory>(s => s.EndDate > asOf && asOf >= s.StartDate ), but I'm not sure how to accomplish this so that it is supported by linq2entities.

LINQKIT: This is what I've come up with using linqkit and I'm hoping somehow I can factor this into something more reusable:

Expression<Func<PersonHistory, bool>> IsCurrent = (p) => p.Ends > DateTime.Now && p.Starts <= DateTime.Now;
var query = db.PersonHistories.Where(IsCurrent);

  1. Have a more globally declared expression, instead of a local variable.
  2. Add a DateTime asOf parameter, instead of having .Now hard coded.
  3. And if possible, adapt it into an extension method(this sort of is the same as #1, just that an extension method is ideal.

解决方案

I saw you commented on my answer on another question, so i thought i'd reply here also. I've made some modifications and improvements to the code ( support for compiled queries and custom extension method to expression substitutions ).

This might serve as an answer:

/// <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;
    }
}

Sorry for the brevity of my reply, it's the middle of the night here and i've got to hurry as there is work to be done.

I'll be happy to answer any questions you might have.

这篇关于对于linq2entities IQueryable的扩展方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆