2012-05-31 12 views
6

Estoy tratando de implementar un método de extensión que funcione con linq2entities. Inicialmente, había asumido que si mi método de extensión tomaba y devolvía un IQueryable, y siempre que mi expresión solo hiciera uso de métodos compatibles, funcionaría bien. Estaba teniendo muchos problemas, así que como último recurso, copié un método de extensión de .NET existente que sabía que iba a funcionar (FirstOrDefault) y simplemente lo renombré. Parecería que evaluaría la validación de "no se puede traducir a una expresión de tienda" en función de la expresión devuelta por el método, no el nombre del método en sí.Método de extensión IQueryable para linq2entities

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

Mi método de extensión, que es sólo una copia de FirstOrDefault que Retitulé:

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. 

¿Cómo se implementa un método de extensión IQueryable que se apoya en Linq2Entidades?

Lo que realmente quiero que haga AsOf (source, DateTime asOf) es algo así como source.FirstOrDefault<IHistory>(s => s.EndDate > asOf && asOf >= s.StartDate), pero no estoy seguro de cómo lograr esto para que sea compatible con linq2entities.

LINQKIT: Esto es lo que he llegado con el uso de linqkit y espero que de alguna manera me puedo factor esto en algo más reutilizable:

Expression<Func<PersonHistory, bool>> IsCurrent = (p) => p.Ends > DateTime.Now && p.Starts <= DateTime.Now; 
var query = db.PersonHistories.Where(IsCurrent); 
  1. tienen una expresión más declarada a nivel mundial, en lugar de una variable local .
  2. Agregue un parámetro DateTime asOf, en lugar de tener .Now hard codificado.
  3. Y si es posible, adaptar en un método de extensión (este tipo de es lo mismo que # 1, sólo que un método de extensión es ideal.
+4

Pero, ¿cómo sabe ObjectQueryProvider cómo traducir su método en SQL? Un método de extensión consultable solo entrega el método al proveedor de consultas actual, que realmente lo ejecuta. Por lo tanto, deberá ampliar el proveedor de consultas (si es posible) o escribir el suyo en una tarde lluviosa :). –

+0

Bueno, si alguien tiene un ejemplo de cómo podría extender el proveedor de consultas, estaría dispuesto a intentarlo. Para responder a su pregunta, asumí que no trataría de traducir mi método (¿qué hay en un nombre?), Sino que traduciría la expresión, que sabría cómo hacer porque la expresión no usaría nada desconocido para linq para las entidades. – AaronLS

+0

Obviamente encuentro muchas cosas al buscar en Google, pero así es como terminé perdiendo un día tratando de hacer que esto funcione solo para reallizar todos los ejemplos de extensiones IQueryable que no funcionan con linq2entities ... Así que cualquier orientación sería apreciada. – AaronLS

Respuesta

3

Vi que comentábamos mi respuesta a otra pregunta, así que pensé en responder aquí también. Realicé algunas modificaciones y mejoras en el código (soporte para consultas compiladas y método de extensión personalizado para sustituciones de expresiones).

Esto podría servir como una respuesta:

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

Lo siento por la brevedad de mi respuesta, que es la mitad de la noche aquí y tengo que darse prisa ya que hay trabajo por hacer.

Estaremos encantados de responder cualquier pregunta que pueda tener.

+0

¿Ha utilizado esto con un método de extensión que devuelve un solo elemento, el equivalente de .Primero o .Single? Estaba trabajando para adaptar tu código para hacer esto, para que un método pueda proporcionar su expresión, pero luego encontré el TranslationExpression, que creo que es exactamente lo que necesito.El problema es que no sé qué pasar para registrar la expresión, ya que la expresión que tengo depende de que ciertos parámetros estén presentes: http://pastebin.com/CpY9Sg65 – AaronLS

+0

P.S. Esto es lo que he intentado, pero aparece el error "Expresión de tipo 'System.Linq.Expressions.MethodCallExpression' no se puede utilizar para el tipo de retorno 'PropertyHistory'": http://pastebin.com/BzQWqJRY – AaronLS

+0

No importa, yo estaba haciéndolo más complicado de lo necesario. esto funciona: 'QueryMethodTranslationExpressions.RegisterExpression , PropertyHistory >> (" 1 ", source => source.FirstOrDefault (xp => xp.Ends> DateTime.Now && xp.Starts <= DateTime. Ahora) ); ' – AaronLS

2

Esto debería funcionar sin extender su proveedor de consulta. Básicamente lo descompone a exactamente lo que está haciendo su IsCurrent func.

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

Y para usarlo

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

Esto funciona, excepto en lugares como mi ejemplo (el primer bloque de código en cuestión) donde está anidado en una consulta, donde da el linq2entities no reconoce el error de método. La única razón por la que LinqKit funciona es porque han implementado sus propios ExpressionVisitors, que es esencialmente lo que Gert estaba diciendo que tengo que hacer, es un código realmente complicado de entender. – AaronLS

+0

EF no es compatible con esto sin ajustar su analizador de Linq que genera el SQL. Puedes cavar en las entrañas de LinqKit pero es un poco complicado. Me temo que si desea hacer limitaciones en línea en una sub selección, tendrá que hacerlo en 2 pasos o escribirlo con la mano larga. – VulgarBinary

+0

Esto puede deberse al linaje de EF w.r.t. Linq a SQL. Me encontré con un problema similar donde terminé teniendo que repetir una consulta interna compleja una y otra vez porque el motor simplemente no podía leer los metadatos obvios (para los humanos, al menos) para emitir la consulta correcta. – bluevector

1

En lugar de usar expresiones, p.

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

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

Se pueden definir los métodos de extensión, tales como:

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

El uso de esta manera:

var query = IsCurrent(); 

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

O:

var results = query(previousResults, referenceDate); 

O, alternativamente:

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

De esta manera no necesita un marco para construir expresiones.

Cuestiones relacionadas