2012-07-13 7 views
8

similares: Convert a string to Linq.Expressions or use a string as Selector?cómo convertir una cadena en una expresión linq?

una similar de aquel: Passing a Linq expression as a string?

Otra cuestión con la misma respuesta: How to create dynamic lambda based Linq expression from a string in C#?

razón de hacer algo que no tiene tantas preguntas similares:

El aceptada La respuesta en esas preguntas similares es inaceptable en el sentido de que todas hacen referencia a una biblioteca de hace 4 años (a condición de que haya sido escrita por el maestro de códigos Scott Gu) escrita para un viejo marco (.net 3.5), y no ovide algo más que un enlace como respuesta.

Hay una forma de hacer esto en el código sin incluir una biblioteca completa.

Aquí hay un código de ejemplo de esta situación:

public static void getDynamic<T>(int startingId) where T : class 
    { 
     string classType = typeof(T).ToString(); 
     string classTypeId = classType + "Id"; 
     using (var repo = new Repository<T>()) 
     { 
      Build<T>(
      repo.getList(), 
      b => b.classTypeId //doesn't compile, this is the heart of the issue 
       //How can a string be used in this fashion to access a property in b? 
      ) 
     } 
    } 

    public void Build<T>(
     List<T> items, 
     Func<T, int> value) where T : class 
    { 
     var Values = new List<Item>(); 
     Values = items.Select(f => new Item() 
     { 
      Id = value(f) 
     }).ToList(); 
    } 

    public class Item 
    { 
    public int Id { get; set; } 
    } 

Tenga en cuenta que esto no está tratando de convertir una cadena entera en una expresión como

query = "x => x.id == somevalue"; 

Pero en cambio es tratar de un solo uso la cadena como el acceso

query = x => x.STRING; 
+0

¿Ha descartado hacer simple reflejo de hacer tu 'Func'? –

+0

@PaulPhillips - La reflexión arrojará el tipo de propiedad o el nombre de la propiedad en la clase T que se transfiere. La Clase T se pasa solo como referencia para el tipo cuando se conecta a un Repositorio de Entity Framework. Se devuelve una lista de todas las clases T del repositorio ('repo.getList()'). ¿Cómo ayudaría la reflexión a incluir propiedades de la Clase T en la expresión linq? –

+0

Su ejemplo hizo que parezca que está haciendo linq-to-objects, en cuyo caso podría tomar el valor de la propiedad de forma reflexiva. Pero como trabajas en otro contexto, no estoy seguro de que funcione. Publiqué mi intento para que pudiera verificar –

Respuesta

15

Aquí hay un intento árbol de expresión. Todavía no sé si esto funcionaría con Entity framework, pero creo que vale la pena intentarlo.

Func<T, int> MakeGetter<T>(string propertyName) 
{ 
    ParameterExpression input = Expression.Parameter(typeof(T)); 

    var expr = Expression.Property(input, typeof(T).GetProperty(propertyName)); 

    return Expression.Lambda<Func<T, int>>(expr, input).Compile(); 
} 

llamada así:

Build<T>(repo.getList(), MakeGetter<T>(classTypeId)) 

Si puede utilizar un Expression<Func<T,int>> en lugar de un solo Func, a continuación, sólo quitar la llamada a Compile (y cambiar la firma de MakeGetter).


Editar: En los comentarios, TravisJ le preguntó cómo podía utilizar de esta manera: w => "text" + w.classTypeId

Hay varias maneras de hacer esto, pero para facilitar la lectura, recomendaría la introducción de una variable local en primer lugar, de esta manera:

var getId = MakeGetter<T>(classTypeId); 

return w => "text" + getId(w); 

el punto principal es que el comprador es sólo una función, y se puede usar exactamente como lo haría normalmente.Leer Func<T,int> así: int DoSomething(T instance)

+0

Pude hacer que esto funcionara en linqPad - aquí está el código https://gist.github.com/3108507 – Hogan

+0

@Paul - Gracias por algunos códigos de trabajo y ejemplos. También pude hacer que esto funcione. No cambié la firma porque habría interferido con más linq en la línea. Tenía una pregunta, ¿qué tendría que hacer para usar 'MakeGetter' de esta manera:' w => "text" + MakeGetter (classTypeId) '? Similar a 'w =>" text "+ w.classTypeId'. –

+0

@TravisJ ver mi edición –

1

No he probado esto, y no estoy seguro si funcionaría , Pero podría utilizar algo como:

b => b.GetType().GetProperty(classTypeId).GetValue(b, null);

+0

Esto parece interesante y al menos compila. Todavía tengo más pruebas para hacer. –

2

Aquí es un método de extensión para usted con mi código de prueba (LINQPad):

class test 
{ 
    public string sam { get; set; } 
    public string notsam {get; set; } 
} 

void Main() 
{ 
    var z = new test { sam = "sam", notsam = "alex" }; 

    z.Dump(); 

    z.GetPropertyByString("notsam").Dump(); 

    z.SetPropertyByString("sam","john"); 

    z.Dump(); 
} 

static class Nice 
{ 
    public static void SetPropertyByString(this object x, string p,object value) 
    { 
    x.GetType().GetProperty(p).SetValue(x,value,null); 
    } 

    public static object GetPropertyByString(this object x,string p) 
    { 
    return x.GetType().GetProperty(p).GetValue(x,null); 
    } 
} 

resultados:

results

+0

Esto funciona en su ejemplo, sin embargo, no veo integración con linq aquí. Vea la respuesta de Paul para una integración de linq. –

+0

Supongo que sus trabajos en una mina tipo trabajan en un objeto ... 6 de uno. Creo que el mío sería más productivo ya que no se necesitan plantillas. – Hogan

Cuestiones relacionadas