2011-02-17 28 views
1

我有這種形式的表達:如何在表達式(C#4.0)中展開(內聯)屬性?

Expression<Func<ShowParticipant, bool>> expr = z => z.Show.OrgName == "xyz"; 

我需要轉換/擴大到以下形式:

Expression<Func<ShowParticipant, bool>> expr = z => z.Show.Organization.Name == "xyz"; 

其中OrgName財產上Show實體解析爲Organization.Name。假如我需要在EF中工作,我該如何實現這一點?你能想象在下面顯示類中定義OrgName -

public partial class Show 
{ 
    public string OrgName 
    { 
     get 
     { 
      return this.Organization.Name; 
     } 
     set 
     { 
      this.Organization.Name = value; 
     } 
    } 
} 

感謝您迴應,

阿南德。

+0

是否有一個原因,你不能只寫z.Show.Organization.Name開始? – 2011-02-17 23:36:56

回答

1

表達式樹本身無法做到這一點,因爲表達式樹無法知道調用OrgName屬性在封面下做了什麼。

但是如果你把一個屬性的財產,那麼也許有些工廠可以用屬性路徑替換到property1通話(「ORGNAME」)Organization.Name下面

示例代碼。

static void Main(string[] args) 
    { 
     Company company = new Company { Organization = { Name = "Microsoft" } }; 
     Expression<Func<Company, int>> lambdaExpression = c => c.OrgName.Length; 
     var expanded = Expand(lambdaExpression); 
    } 

    private static Expression<Func<TSource, TResult>> Expand<TSource, TResult>(Expression<Func<TSource, TResult>> lambdaExpression) 
    { 
     Expression expanded = GetExpandedExpression(lambdaExpression.Body); 
     if (Object.ReferenceEquals(lambdaExpression.Body, expanded)) 
     { 
      return lambdaExpression; 
     } 
     return Expression.Lambda<Func<TSource, TResult>>(
      expanded, 
      lambdaExpression.Parameters 
     ); 
    } 

    private static Expression GetExpandedExpression(Expression expression) 
    { 
     Expression expandedContainer; 
     switch (expression.NodeType) 
     { 
      case ExpressionType.MemberAccess: 
       MemberExpression memberExpression = (MemberExpression)expression; 
       if (memberExpression.Expression != null) 
       { 
        expandedContainer = GetExpandedExpression(memberExpression.Expression); 
        PropertyPathAttribute attribute = memberExpression.Member.GetCustomAttributes(typeof(PropertyPathAttribute), false).Cast<PropertyPathAttribute>().FirstOrDefault(); 
        if (attribute != null && !String.IsNullOrEmpty(attribute.Path)) 
        { 
         string[] parts = attribute.Path.Split('.'); 
         expression = expandedContainer; 
         for (int i = 0; i < parts.Length; i++) 
         { 
          string part = parts[i]; 
          expression = Expression.MakeMemberAccess(
           expression, 
           (MemberInfo)expression.Type.GetProperty(part, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) ?? 
            expression.Type.GetField(part, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) 
          ); 
         } 
        } 
        else if (!Object.ReferenceEquals(expandedContainer, memberExpression.Expression)) 
        { 
         return Expression.MakeMemberAccess(
          expandedContainer, 
          memberExpression.Member 
         ); 
        } 
       } 
       break; 
      case ExpressionType.ArrayLength: 
       UnaryExpression unaryExpression = (UnaryExpression)expression; 
       if (!Object.ReferenceEquals(expandedContainer = GetExpandedExpression(unaryExpression.Operand), unaryExpression.Operand)) 
       { 
        return Expression.ArrayLength(expandedContainer); 
       } 
       break; 
     } 
     return expression; 
    } 

    [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)] 
    public sealed class PropertyPathAttribute : Attribute 
    { 
     private readonly string _path; 

     public string Path 
     { 
      get 
      { 
       return this._path; 
      } 
     } 

     public PropertyPathAttribute(string path) 
     { 
      this._path = path; 
     } 
    } 

    public class Organization 
    { 
     public string Name { get; set; } 
    } 

    public class Company 
    { 
     [PropertyPath("Organization.Name")] 
     public string OrgName 
     { 
      get 
      { 
       return this.Organization.Name; 
      } 
      set 
      { 
       this.Organization.Name = value; 
      } 
     } 

     public Organization Organization { get; private set; } 

     public Company() 
     { 
      this.Organization = new Organization(); 
     } 
    } 
0

感謝您的回覆。這完全回答了我的問題。同時,我正在使用ExpressionVisitor提供解決方案。這不是完整的,但我給的方法,我在這裏了 -

public class ExpressionExpander : ExpressionVisitor 
{ 
    private Dictionary<Expression, Expression> parameterMap = new Dictionary<Expression, Expression>(); 

    protected override Expression VisitParameter(ParameterExpression node) 
    { 
     if (parameterMap.ContainsKey(node)) 
      return parameterMap[node]; 

     return node; 
    } 

    protected override Expression VisitMember(MemberExpression node) 
    { 
      var newObj = Visit(node.Expression); 
      if (NeedsInlineExpansion(newObj.Type, node.Member.Name)) 
      { 
       LambdaExpression exp = GetPropertyTransform(); 

       if (parameterMap.ContainsKey(node.Expression)) 
        parameterMap.Add(exp.Parameters[0], node.Expression); 

       var visitedExp = Visit(exp.Body); 
       var memExp = (MemberExpression)visitedExp; 

       parameterMap.Add(node, memExp); 
       return memExp; 
      } 
      else 
      { 
       var newMember = newObj.Type.GetMember(node.Member.Name).First(); 
       var newMemberAccessExpr = Expression.MakeMemberAccess(newObj, newMember); 
       parameterMap.Add(node, newMemberAccessExpr); 
       return newMemberAccessExpr; 
      } 
    } 

    private bool NeedsInlineExpansion(Type type, string coreMemberName) 
    { 
     // Figure out way to determine if the property needs to be flattened out... 
     // may be using attribute on Property 
    } 

    private LambdaExpression GetPropertyTransform() 
    { 
     // this is hardcoded right now, but represents some mechanism of getting a lambda 
     // returned for property in question... 
     Expression<Func<Show, string>> exp = z => z.Organization.Name; 
     var lambda = (LambdaExpression)exp; 
     return lambda; 
    } 
} 

而且主叫方會像下面 -

 Expression<Func<ShowParticipant, bool>> expr1 = z => z.Show.OrgName == "xyz"; 
     ExpressionExpander expander = new ExpressionExpander(); 
     var inlineExpanded = expander.Visit(expr1); 

感謝, 阿南德。