使用防爆pression< Func键< T,X>> Linq中包含扩展 [英] use Expression<Func<T,X>> in Linq contains extension

查看:256
本文介绍了使用防爆pression< Func键< T,X>> Linq中包含扩展的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

使用下面的例子我想用我的防爆pression 在我的包含方法,让它使用<$ C $查询传递到SQL服务器C> EF 。

Using the following example i would like to use my Expression inside my Contains method, having it pass the query onto sql server using the EF.

如何建立这件事才能正常工作?

How can i build this up to work correctly?

void Main()
{

    IQueryable<Person> qry = GetQueryableItemsFromDB();
    var filtered = qry.Filter(p=>p.CompanyId);

}

public static class Ext
{
    public static IQueryable<T> Filter<T>(this IQueryable<T> items, Expression<Func<T, int>> resolveCompanyIdExpression)
    {      
        IEnumerable<int> validComps = GetCompanyIdsFromDataBase();        
        var exp = Expression.Lambda<Func<T, bool>>(          
            Expression.Call(typeof(Queryable),"Contains", new[] { typeof(Company) },
            Expression.Constant(validComps),
            resolveCompanyIdExpression.Body),
            resolveCompanyIdExpression.Parameters[0]);
        return items.Where(exp);  
    }  

    public static IQueryable<T> Filter<T>(this IQueryable<T> items, Expression<Func<T, IEnumerable<int>>> resolveCompanyIdExpression)
    {      
        IEnumerable<int> validComps = GetCompanyIdsFromDataBase();        
        //No Idea what to do here ?
    }  
}

public class Person
{
    public int CompanyId {get;set;}
}

我知道我可以通过整个predicate,但我只希望用户提供如何从有关实体解决的公司。

I know i could pass in the entire predicate but i only want the user to supply how to resolve the Company from the entity in question.

更新

我决定来解决companyId而不是整个公司实体,我可以得到ID列表中的内存和IM不是大惊小怪,如果这是IQueryable的,或只是一个普通的阵列/ IEnumerable的

I have decided to resolve the companyId rather than the entire company entity, i can get the list of ids in memory and im not fussed if that is IQueryable or just a plain array/IEnumerable

但我得到一些奇怪的错误:

However i get some strange errors :

这是异常的执行过程中发生   Extent.Select(O => 0)。凡(P =>(p.Hide   = FALSE))。其中(P =>(p.Archived = FALSE))。其中(项目=> System.Int32的[]。载有(item.Development.CompanyId))。看到   的InnerException更多的细节。

An exception occured during the execution of ' Extent.Select(o => o).Where(p => (p.Hide = False)).Where(p => (p.Archived = False)).Where(item => System.Int32[].Contains(item.Development.CompanyId))'. See InnerException for more details.

内的例外是

参数EX pression是无效的。

Argument expression is not valid

更新2

我已经编辑了code,以反映我真的很想喜欢做的​​事,没有找到一个解决方案,这多少运气。

I have edited the code to reflect what i would really like like to do, not having much luck on finding a solution to this.

推荐答案

如果我理解正确,你想要的是EX pression组成:

If I'm understanding correctly, what you want is expression composition:

public static IQueryable<T> Filter<T>(IQueryable<T> query, Expression<Func<T, int>> getCompanyId) {
    IEnumerable<int> validCompanyIds = GetCompanyIdsFromDatabase();
    Expression<Func<int, bool>> filterByCompanyId = id => validCompanyIds.Contains(id);

    // these generics will actually be inferred, I've just written them to be explicit
    Expression<Func<T, bool>> composed = filterByCompanyId.Compose<T, int, bool>(getCompanyId);
    return query.Where(composed);
}

下面是对EX pression撰写()扩展方法的实现:

Below is the implementation of the Compose() extension method on expression:

    /// <summary>
    /// Composes two lambda expressions f(y) and g(x), returning a new expression representing f(g(x)).
    /// This is useful for constructing expressions to pass to functions like Where(). If given x => x.Id and id => ids.Contains(id),
    /// for example, you can create the expression x => ids.Contains(x.Id), which could be passed to Where() for an IQueryable of x's type
    /// </summary>
    /// <typeparam name="TIn">The input of g</typeparam>
    /// <typeparam name="TIntermediate">The output of g and the input of f</typeparam>
    /// <typeparam name="TOut">The output of f</typeparam>
    /// <param name="f">The outer function</param>
    /// <param name="g">The inner function</param>
    /// <returns>A new lambda expression</returns>
    public static Expression<Func<TIn, TOut>> Compose<TIn, TIntermediate, TOut>(this Expression<Func<TIntermediate, TOut>> f, Expression<Func<TIn, TIntermediate>> g)
    {
        // The implementation used here gets around EF's inability to process Invoke expressions. Rather than invoking f with the output of g, we
        // effectively "inline" g by replacing all instances of f's parameter with g's body and creating a new lambda with the rebound body of f and
        // the parameters of g
        var map = f.Parameters.ToDictionary(p => p, p => g.Body);            
        var reboundBody = ParameterRebinder.ReplaceParameters(map, f.Body);
        var lambda = Expression.Lambda<Func<TIn, TOut>>(reboundBody, g.Parameters);
        return lambda;
    }

public class ParameterRebinder : ExpressionVisitor
        { 
            private readonly Dictionary<ParameterExpression, Expression> Map;

            public ParameterRebinder(Dictionary<ParameterExpression, Expression> map)
            {
                this.Map = map ?? new Dictionary<ParameterExpression, Expression>();
            }

            public static Expression ReplaceParameters(Dictionary<ParameterExpression, Expression> map, Expression exp)
            {
                return new ParameterRebinder(map).Visit(exp);
            } 

            protected override Expression VisitParameter(ParameterExpression node)
            {
                Expression replacement;
                if (this.Map.TryGetValue(node, out replacement))
                {
                    return this.Visit(replacement);
                }
                return base.VisitParameter(node);
            }
        }

这篇关于使用防爆pression&LT; Func键&LT; T,X&GT;&GT; Linq中包含扩展的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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