【问题标题】:Performance difference with MemberInit Expression与 MemberInit 表达式的性能差异
【发布时间】:2009-07-07 16:43:56
【问题描述】:

我正在解决与 Question 222511 类似的问题区别。以下是部分代码:

// Method A:
// This work good, is fast and returns an un-executed query...
DataContext.LoanNote.Join<LoanNote, Customer, int, LoanNote>(
    DataContext.Customers, loanNote => loanNote.PrimaryCustomerNumber, customer => customer.CustomerNumber,
(LoanNote loanNote, Customer customer) => new LoanNote()
{
    AccountFeeBillAmount = loanNote.AccountFeeBillAmount,
    AccountOpenDate = loanNote.AccountOpenDate,
    // This goes on and on...
    PrimaryCustomer = customer
});

// Method B:
// This on the other hand is a lot slower and I am not sure why...
var resultSelector = BuildJoinResultSelector<LoanNote, Customer, LoanNote("PrimaryCustomer").Compile();

DataContext.LoanNote.Join<LoanNote, Customer, int, LoanNote>(
        DataContext.Customers, loanNote => loanNote.PrimaryCustomerNumber, customer => customer.CustomerNumber, resultSelector);


// The build MemberInitExpression method...
private static Expression<Func<TOuter, TInner, TResult>> BuildJoinResultSelector<TOuter, TInner, TResult>(string propertyName) where TResult : class
        {
            var result = default(Expression<Func<TOuter, TInner, TResult>>);
            var resultType = typeof(TResult);
            var outerType = typeof(TOuter);
            var innerType = typeof(TInner);
            var outer = Expression.Parameter(outerType, "outer");
            var inner = Expression.Parameter(innerType, "inner");
            var bindings = new List<MemberBinding>();

            foreach (var property in resultType.GetProperties())
            {
                if (property.CanRead == false)
                {
                    continue;
                }
                else if (property.CanWrite == false)
                {
                    continue;
                }
                else if (property.Name == propertyName)
                {
                    var condition = Expression.Condition(Expression.Equal(inner, Expression.Constant(null)), Expression.New(innerType), inner);

                    bindings.Add(Expression.Bind(property, condition));
                }
                else
                {
                    bindings.Add(Expression.Bind(property, Expression.Property(outer, property)));
                }
            }

            var memberInit = Expression.MemberInit(Expression.New(resultType), bindings);

            result = Expression.Lambda<Func<TOuter, TInner, TResult>>(memberInit, outer, inner);

            return result;
        }

【问题讨论】:

  • 您的标题中有错字(执行nce)
  • 你能贴出方法A的全部代码吗?
  • 这只是 Property Init 的 ex:Balance = loanNote.Balance、APR = loanNote.APR 等...
  • 如果你能发布一个完整的程序,那真的很有帮助。我们可以编译和使用的东西。尽可能简单。

标签: c# linq performance lambda


【解决方案1】:

第二种方法执行起来会更慢,因为它使用反射(GetProperties 调用)。

如果你多次调用它,你可以像这样缓存GetProperties的结果:

static class PropertiesCache<T> { 
    public static readonly PropertyInfo[] Properties = typeof(T).GetProperties();
}

这将对每种类型仅调用一次GetProperties;像这样使用:

foreach (var property in PropertiesCache<TResult>.Properties) {
    if(!property.CanRead || !property.CanWrite) continue;

    //...
}

编辑:

您还可以使用 LINQ 查询替换整个循环,如下所示:

var memberInit = Expression.MemberInit(Expression.New(typeof(TResult)),
    from property in PropertiesCache<TResult>.Properties
    where property.CanRead && property.CanWrite
    select Expression.Bind(property, property.Name == propertyName ?
        Expression.Coalesce(inner, Expression.New(innerType))
        : Expression.Property(outer, property)
        )
    );

【讨论】:

  • 我尝试了您的建议,但没有任何区别...我已逐步执行代码,并且 BuildJoinResultSelector 仅被调用一次,因此 GetProperties 仅被调用一次。所以它应该对它有很大的影响。不过还是谢谢...
  • 在构建或执行表达式时是否出现缓慢?
  • 我刚刚检查了生成的SQL,它是完全一样的。所以不是 SQL 查询需要更长的时间。
猜你喜欢
  • 1970-01-01
  • 2014-06-29
  • 2015-06-10
  • 1970-01-01
  • 1970-01-01
  • 2021-12-08
  • 1970-01-01
  • 2010-10-03
  • 2011-01-04
相关资源
最近更新 更多