【问题标题】:Switching from Reflection to Expression trees从反射树切换到表达式树
【发布时间】:2015-09-11 16:33:32
【问题描述】:

由于逐行反射相当昂贵,因此我一直在寻找一种更快的替代方法来构建和插入实体。我对这个主题做了一些研究research ,还发现了一些性能comparisons,这似乎表明表达式树是要走的路。我将如何修改以下函数以利用这一点?

    public static void InsertTable(IEnumerable<DataTable> chunkedTable)
    {

        Parallel.ForEach(
            chunkedTable,
            new ParallelOptions
            {
                MaxDegreeOfParallelism = Convert.ToInt32(ConfigurationManager.AppSettings["MaxThreads"])
            },
            chunk =>
            {
                Realty_Records_ProdEntities entities = null;
                try
                {
                    entities = new Realty_Records_ProdEntities();
                    entities.Configuration.AutoDetectChangesEnabled = false;

                    foreach (DataRow dr in chunk.Rows)
                    {
                        var parcelToInsert = new Parcel();

                        foreach (DataColumn c in dr.Table.Columns)
                        {
                            var propertyInfo = parcelToInsert.GetType()
                                .GetProperty(
                                    c.ColumnName,
                                    BindingFlags.SetProperty | BindingFlags.IgnoreCase
                                    | BindingFlags.Public | BindingFlags.Instance);

                            propertyInfo?.SetValue(
                                parcelToInsert,
                                TaxDataFunction.ChangeType(
                                    dr[c.ColumnName],
                                    propertyInfo.PropertyType),
                                null);
                        }
                        entities.Parcels.Add(parcelToInsert);
                    }
                    entities.SaveChanges();
                }
                catch (Exception ex)
                {
                    TaxDataError.AddTaxApplicationLog(
                        TaxDataConstant.CategoryError,
                        ex.Source,
                        ex.Message,
                        ex.StackTrace);
                    throw;
                }
                finally
                {
                    entities?.Dispose();
                }
            });
    }

编辑:

这是我最终实施的解决方案:

    private static readonly ConcurrentDictionary<SetterInfo, Action<object,object>> CachedSetters =
        new ConcurrentDictionary<SetterInfo, Action<object, object>>();

    private static readonly MethodInfo ChangeTypeMethod =
        ((Func<object, Type, object>) TaxDataFunction.ChangeType).Method;

    private static void SetProperty(object obj, string name, object value)
    {
        if (obj == null)
            return;

        var key = new SetterInfo(obj.GetType(), name);

        var setter = CachedSetters.GetOrAdd(key, CreateSetter);

        setter(obj, value);
    }

    private static Action<object, object> CreateSetter(SetterInfo info)
    {
        var propertyInfo = info.Type.GetProperty(info.Name, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);

        if (propertyInfo == null)
            return (s, v) => { };

        var objParameter = Expression.Parameter(typeof(object));
        var valueParameter = Expression.Parameter(typeof(object));

        var changeTypeCall = Expression.Call(ChangeTypeMethod, valueParameter, Expression.Constant(propertyInfo.PropertyType));

        var objCast = Expression.Convert(objParameter, info.Type);
        var valueCast = Expression.Convert(changeTypeCall, propertyInfo.PropertyType);

        var property = Expression.Property(objCast, propertyInfo);

        var assignment = Expression.Assign(property, valueCast);

        var lambda = Expression.Lambda<Action<object, object>>(assignment, objParameter, valueParameter);

        return lambda.Compile();
    }

    private struct SetterInfo
    {
        public Type Type { get; }
        public string Name { get; }

        public SetterInfo(Type type, string name)
        {
            Type = type;
            Name = name;
        }
    }

【问题讨论】:

  • 值得注意的是,编译表达式树会在前期对性能产生重大影响,以换取后期更快的速度。

标签: c# entity-framework reflection expression-trees parallel.foreach


【解决方案1】:
static ConcurrentDictionary<string, Lazy<Action<object, object>>> CachedProperties = 
    new ConcurrentDictionary<string, Lazy<Action<object, object>>>();

static void SetProperty(object obj, string name, object value)
{
    if(obj==null)
        throw new ArgumentNullException("obj");
    Type objType = obj.GetType();
    string key =  objType.FullName + ":" + name;
    Action<object, object> f = 
    CachedProperties.GetOrAdd(key, k => 
        new Lazy<Action<object,object>>(() => {
            PropertyInfo prop = objType.GetProperty(name);
            if(prop==null){
                return (s,v) => {};
            }
            ParameterExpression pobj = 
                Expression.Parameter(typeof(object));
            ParameterExpression pval = 
                Expression.Parameter(typeof(object));
            Expression left = Expression.Property(
                Expression.TypeAs( pobj, objType), prop);
            Expression right = Expression.Convert(pval, prop.PropertyType);

            return Expression
            .Lambda<Action<object, object>>(
                Expression.Assign(left,right), pobj, pval).Compile();
        })).Value;

    f(obj,value);
}

用法....

SetProperty(parcelToInsert, c.ColumnName, dr[c.ColumnName])

【讨论】:

  • 当我把它放到我的解决方案中时,我无法编译它。我尝试删除所有内部组件以查看是否可以解决基本问题(这可能是完全错误的),但出现错误:无法将类型'System.Lazy>' 隐式转换为' System.Func'
  • @jdm5310: new Lazy&lt;Action&lt;object,object&gt;&gt;{ 应该是new Lazy&lt;Action&lt;object,object&gt;&gt;(() =&gt; {
  • 不确定你是否看到了我的编辑,但我最终将事情分开了一点,并完全放弃了 Lazy 层(我不确定与 ConcurrentDictionary 结合起来应该有什么意义)。通过删除它或在我的解决方案和 Akash Kava 的解决方案之间,我是否错过了性能优势?
  • 创建表达式并编译一切都很慢,这就是我使用并发字典重用编译代码的原因。
猜你喜欢
  • 1970-01-01
  • 2011-04-09
  • 1970-01-01
  • 1970-01-01
  • 2010-11-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多