有没有C#的DataBinder.Eval的快速版本?

4

我想知道是否存在快速版本的ASP.NET System.Web.UI.DataBinder.Eval()?最好是能编译成Func并进行缓存以供稍后调用,例如:

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

有没有人知道这样的东西是否存在?

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


2
您目前是否因为使用经典的Eval方法而遇到了一些性能问题?如果是,您能否解释一下您的情况,展示您正在使用的代码,并评论一下在执行负载测试时所得到的结果,这些结果导致了您得出这个结论?如果不是,那么您为什么需要它呢? - Darin Dimitrov
因为我非常关注性能,不想在编译的模板中包含低效的执行路径。 - mythz
如果某个东西的效率比等价的强类型转换+属性访问慢300倍,我可以不考虑它的效率。 - mythz
@Darin:Eval使用反射吗? - Gabe
1
@Darin,你的第一条评论充满了我每天在处理.NET文化时所面临的惯性。作为许多NoSQL论坛的成员,我可以说,在任何其他平台上,我都不必证明为什么要维护高性能系统。我很惊讶你是一个长期使用SO的用户,却对使SO如此愉快的主要原因之一毫不知情(http://goo.gl/G85eh http://goo.gl/fm9Zl)。我试图阐述为什么这很重要https://github.com/mythz/ScalingDotNET,但是没有人听得进去,你的点赞是典型而又悲哀的。 - mythz
显示剩余2条评论
3个回答

9
我越看越想说:
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; }
}

抱歉马克,不幸的是字符串只能在运行时知道。 - mythz
@mythz - 注意,我稍微调整了代码以处理一些边缘情况。 - Marc Gravell

3
以下是一些可以帮助你入门的内容:
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;
    }
}

0

虽然我有点晚了,但这是我的解决方案。

我将此私有方法添加到名为FormatWith的字符串扩展类中,以取代DataBinder引用。

#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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接