Is there a fast version of DataBinder.Eval for C#?
I'm looking to see if a fast version of ASP.NET's System.Web.UI.DataBinder.Eval() exists? Ideally so开发者_StackOverflow社区mething that compiles to a Func that I can cache and call later, like:
Func<object,string> expr = CompileDataBinder(typeof(Model), "model.PocoProperty.Name");
string propertyName = expr(model);
Does anyone know if such a beast exists?
P.S. I'm not using ASP.NET and would like it to work in normal C#
The more I look at it, the more I want to say:
Func<Model,string> expr = model => model.PocoProperty.Name;
if you need it based on a string, the Expression
API is pretty fair there.
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; }
}
Here's something that should get you started:
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;
}
}
I'm a little late to the game, but this was my solution.
I added this private method to a string Extension class called FormatWith Removing the DataBinder reference in favor for this method.
#nullable enable
private static object? Eval(object source, string valueName)
{
MemberInfo[] miList = source.GetType().GetMember(valueName);
foreach (var mi in miList)
{
if (mi is PropertyInfo)
{
PropertyInfo? pi = mi as PropertyInfo;
if (pi != null && pi.CanRead)
{
return pi.GetValue(source);
}
}
else if (mi is FieldInfo)
{
FieldInfo? fi = mi as FieldInfo;
if (fi != null)
{
return fi.GetValue(source);
}
}
}
return null;
}
#nullable disable
精彩评论