2011-04-13 25 views
3

我在看ASP.NET的System.Web.UI.DataBinder.Eval()的快速版本是否存在? 理想的東西,編譯成,我可以緩存,然後再調用,就像一個FUNC:C#有DataBinder.Eval的快速​​版本嗎?

Func<object,string> expr = CompileDataBinder(typeof(Model), "model.PocoProperty.Name"); 
string propertyName = expr(model); 

有誰知道,如果這樣一個東西存在?

P.S.我不使用ASP.NET和希望它在普通的C#工作

+2

您目前遇到了一些性能問題,由於經典'Eval'方法?如果是的話,你能否解釋一下你的情況,展示你正在使用的代碼,評論你在執行負載測試時得到的結果,從而得出這個結論?如果不是,你爲什麼需要這個? – 2011-04-13 22:11:23

+0

因爲我真的關心性能,不想在編譯的模板中包含低效的執行路徑。 – mythz 2011-04-13 22:13:46

+0

@mythz,'Eval'並不低效。許多繁忙的交通網站正在使用它。 – 2011-04-13 22:14:21

回答

6

越我看它,我就越想說:

Func<Model,string> expr = model => model.PocoProperty.Name; 

如果你需要它基於字符串,Expression API在那裏相當公平。

class Program 
{ 
    static void Main(string[] args) 
    { 
     Func<object, string> expr = CompileDataBinder(typeof(Model), "PocoProperty.Name"); 

     var model = new Model { PocoProperty = new ModelPoco { Name = "Foo" } }; 

     string propertyName = expr(model); 
    } 
    static Func<object, string> CompileDataBinder(Type type, string expr) 
    { 
     var param = Expression.Parameter(typeof(object)); 
     Expression body = Expression.Convert(param, type); 
     var members = expr.Split('.'); 
     for (int i = 0; i < members.Length;i++) 
     { 
      body = Expression.PropertyOrField(body, members[i]); 
     } 
     var method = typeof(Convert).GetMethod("ToString", BindingFlags.Static | BindingFlags.Public, 
      null, new Type[] { body.Type }, null); 
     if (method == null) 
     { 
      method = typeof(Convert).GetMethod("ToString", BindingFlags.Static | BindingFlags.Public, 
       null, new Type[] { typeof(object)}, null); 
      body = Expression.Call(method, Expression.Convert(body, typeof(object))); 
     } 
     else 
     { 
      body = Expression.Call(method, body); 
     } 

     return Expression.Lambda<Func<object, string>>(body, param).Compile(); 
    } 
} 

class Model 
{ 
    public ModelPoco PocoProperty { get; set; } 
} 
class ModelPoco 
{ 
    public string Name { get; set; } 
} 
+0

嗨馬克,不幸的是,字符串只在運行時才知道。 – mythz 2011-04-14 10:27:23

+0

@mythz - 請參閱更新 – 2011-04-14 10:30:46

+0

@mythz - note我收緊了一些代碼以處理一些邊緣情況 – 2011-04-14 10:34:29

2

這裏的東西應該讓你開始:

using System; 
using System.Diagnostics; 
using System.Linq; 
using System.Linq.Expressions; 


class Person 
{ 
    public int Id { get; set; } 
    public FullName FullName { get; set; } 
} 

class FullName 
{ 
    public string FirstName { get; set; } 
    public string LastName { get; set; } 
} 


class Program 
{ 
    static void Main(string[] args) 
    { 
     Person model = new Person 
     { 
      Id = 123, 
      FullName = new FullName 
      { 
       FirstName = "Duncan", 
       LastName = "Smart", 
      } 
     }; 

     var nameBinder = CompileDataBinder<Person, string>("model.FullName.FirstName"); 
     string fname = nameBinder(model); 
     Debug.Assert(fname == "Duncan"); 

     // Note how here we pretend we don't know TProp type 
     var idBinder = CompileDataBinder<Person, object>("model.Id"); 
     object id = idBinder(model); 
     Debug.Assert(id.Equals(123)); 
    } 

    static Func<TModel, TProp> CompileDataBinder<TModel, TProp>(string expression) 
    { 
     var propNames = expression.Split('.'); 

     var model = Expression.Parameter(typeof(TModel), "model"); 

     Expression body = model; 
     foreach (string propName in propNames.Skip(1)) 
      body = Expression.Property(body, propName); 
     //Debug.WriteLine(prop); 

     if (body.Type != typeof(TProp)) 
      body = Expression.Convert(body, typeof(TProp)); 

     Func<TModel, TProp> func = Expression.Lambda<Func<TModel, TProp>>(body, model).Compile(); 
     //TODO: cache funcs 
     return func; 
    } 
}