Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
179 views
in Technique[技术] by (71.8m points)

c# - IQueryable extension method for linq2entities

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.
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

This should work without extending your query provider. It basically breaks it down to exactly what your IsCurrent func is doing.

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

And to use it

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

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...