2012-07-13 106 views
8

类似:Convert a string to Linq.Expressions or use a string as Selector?如何将一个字符串转换为linq表达式?

类似的一个之一:Passing a Linq expression as a string?

用相同的回答另一个问题:How to create dynamic lambda based Linq expression from a string in C#?

原因,询问一些东西,有这么多的类似的问题:

接受的在这些类似的问题中回答是不可接受的,因为它们都参考了一个4年前的图书馆(假设它是由代码大师Scott Gu编写的)为旧框架(.net 3.5)编写的,并且不提供作为答案,除了链接以外,什么都不愿意。

有一种方法可以在不包含整个库的情况下在代码中执行此操作。

下面是这种情况的一些示例代码:

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

注意,这并不打算把整个字符串转换成表达式,比如

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

但取而代之的是试图只使用字符串作为访问

query = x => x.STRING; 
+0

你有没有排除了做简单的反射让你的'Func'? – 2012-07-13 23:41:11

+0

@PaulPhillips - Reflection将产生传入的类T中属性的属性类型或名称。当连接到实体框架存储库时,类T仅作为类型的引用传递。从存储库('repo.getList()')返回所有类T的列表。反射如何帮助包含linq表达式中类T的属性? – 2012-07-13 23:45:47

+0

您的示例使它看起来像是在执行linq-to-objects,在这种情况下,您可以反射性地获取该属性的值。但既然你在另一个环境下工作,我不确定它会起作用。我发布了我的尝试,所以你可以检查 – 2012-07-13 23:47:09

回答

15

这里是一个表达式树的尝试。我仍然不知道这是否适用于Entity框架,但我认为这是值得一试的。

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

这样称呼它:

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

如果你能在地方的只是一个Func使用Expression<Func<T,int>>,则只是删除调用Compile(和变化MakeGetter签名)。


编辑: 在评论,TravisJ问他怎么可以这样使用它:w => "text" + w.classTypeId

有几种方法可以做到这一点,但为便于阅读,我会建议先引入一个局部变量,像这样:

var getId = MakeGetter<T>(classTypeId); 

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

重点是,getter只是一个函数,你可以像你一样使用它。阅读Func<T,int>这样的:int DoSomething(T instance)

+0

我能够得到这个在linqPad工作 - 这里是代码https://gist.github.com/3108507 – Hogan 2012-07-14 00:44:43

+0

@Paul - 感谢您的一些工作代码和示例。我也能够得到这个工作。我没有改变签名,因为它会干扰更多的线路。我确实有一个问题,我需要做什么才能以这种方式使用'MakeGetter':'w =>“text”+ MakeGetter (classTypeId)'?类似于'w =>“text”+ w.classTypeId'。 – 2012-07-15 19:23:08

+0

@TravisJ查看我的编辑 – 2012-07-16 02:53:04

1

我还没有试过这个,不知道它是否会工作,但你可以使用类似:

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

+0

这看起来很有趣,至少可以编译。仍然有更多的测试要做。 – 2012-07-13 23:57:01

2

这是给你的扩展方法与我的测试代码(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); 
    } 
} 

结果:

results

+0

这在你的例子中有效,但是,我在这里看不到与linq的集成。请参阅Paul对linq集成的回答。 – 2012-07-15 19:24:19

+0

我想,他的作品在一个类型的矿上工作的对象... 6之一。因为没有模板需求,所以我认为我的表现会更好。 – Hogan 2012-07-15 19:30:59