2015-02-07 54 views
2

我将Expression<T, bool>转换为Expression<Y, bool>其中T和Y是不同于除了通过自动映射器映射以外的任何方式相关的不同实体。从本质上讲,我有我的代码使用一个模型对象:表达式树空访问成员

public class Store 
{ 
    public string StoreId { get; set; } 

    public string Name { get; set; } 

    public List<Phone> Phones { get; set; } 

    public Address Address { get; set; } 

    public Account Account { get; set; } 

    public Status Status { get; set; } 
} 

,我映射到实体对象存储在我的Mongo的数据库:

public class Store : MongoEntity 
{ 

    public string AccountId { get; set; } 

    public string Name { get; set; } 

    public List<string> UserIds { get; set; } 

    public List<Phone> PhoneNumbers { get; set; } 

    public Address Address { get; set; } 
} 

public abstract class MongoEntity : IMongoEntity 
{ 
    [BsonId] 
    public ObjectId Id { get; set; } 

    public Status Status { get; set; } 
} 

我以前的答案在这个问题上的工作了解如何在表达式(Question)之间进行转换,并让我在那里获得了90%。我能够从源属性修改代码抢我的模型店,我店的实体之间的AutoMapper映射,抓住目标属性:

private Expression<Func<TNewTarget, bool>> TransformPredicateLambda<TOldTarget, TNewTarget>(
Expression<Func<TOldTarget, bool>> predicate) 
    { 
     var lambda = (LambdaExpression)predicate; 
     if (lambda == null) 
     { 
      throw new NotSupportedException(); 
     } 

     //Modified here to get automapper mappings 
     var maps = Mapper.FindTypeMapFor<TOldTarget, TNewTarget>(); 
     var mutator = new ExpressionTargetTypeMutator(t => typeof(TNewTarget), maps); 
     var explorer = new ExpressionTreeExplorer(); 
     var converted = mutator.Visit(predicate.Body); 

     return Expression.Lambda<Func<TNewTarget, bool>>(
      converted, 
      lambda.Name, 
      lambda.TailCall, 
      explorer.Explore(converted).OfType<ParameterExpression>()); 
    } 

     protected override Expression VisitMember(MemberExpression node) 
     { 
      var dataContractType = node.Member.ReflectedType; 
      var activeRecordType = _typeConverter(dataContractType); 

      PropertyMap prop = null; 
      foreach (var propertyMap in _maps) 
      { 
       var source = propertyMap.SourceMember; 
       var dest = propertyMap.DestinationProperty; 

       if (source != null && source.Name == node.Member.Name) 
       { 
        prop = propertyMap; 
       } 
      } 
      if (prop == null) 
      { 
       return base.VisitMember(node); 
      } 

      var propertyName = prop.DestinationProperty.Name; 

      var property = activeRecordType.GetProperty(propertyName); 

      var converted = Expression.MakeMemberAccess(
        base.Visit(node.Expression), 
        property 
        ); 

       return converted; 


     } 

的问题是,我的实体对象不具有所有与我的Model对象具有相同的属性(例如Account对象和AccountId)。当Transformer获得Model对象的Account属性时,我得到一个Exception(因为我的Entity对象没有匹配的属性)。我无法从VisitMember返回null,并且也不允许使用新的Expression()。我该如何处理忽略我的模型对象上不存在于我的实体对象上的属性?有信息更新,从评论

所以

,是一个更加清楚一点,我使用Automapper从Models.Store到Entity.Store映射。我的实体.Store只有一个AccountId(因为我不想复制所有帐户数据),但我的Models.Store需要整个帐户对象(我将通过查询Accounts集合来获取)。

Automapper基本上是将我的Account对象转换为我实体上的AccountId。因此,当我搜索x => x.Account.AccountId == abcd1234(其中x是models.Store)时,我需要将我的表达式转换为x => x.AccountId == abcd1234(其中x是一个Entity.Store)。

我有那部分工作(更换mS => mS.Account.AccountId == 1234mE => mE.AccountId == 1234)。我现在遇到的问题是,在完成AccountId属性后,VisitMember将以Account作为节点进行调用。由于在我的Entity.Store对象中没有Account,所以我得到异常。

+1

链接的问题是不能正常工作,我想这是[这一个](http://stackoverflow.com/questions/2797261/mutating-the-expression-tree-of-a-predicate-to-target-another-type)=) – 2015-02-07 21:27:01

+0

对不起,大师,我更新了链接。 – Mike 2015-02-07 21:45:07

+1

你会如何忽略“A”在谓词'x => x.A == 1'中不存在的事实? – usr 2015-02-07 22:06:58

回答

1

很难测试没有可测试/可运行代码的解决方案。但这里有一个猜测

考虑下面的表达式mS => mS.Account.AccountId == 1234,展望改造MemberExpressions,你会得到以下呼叫:

  1. VisitMember(mS.Account.AccountId
  2. VisitMember(mS.Account)

您要变换的第二一入mE.AccountId。这包括两个转换:一,将属性访问从(EntityType).(AccountType).AccountId更改为(MongoStoreType).AccountId,并更改基础对象。如果您已经在处理ExpressionVisitor的其他方法(可能是VisitParameterVisitLambda)的其他方法中处理了参数转换,那么您将在那里很好。然后你只需要跳过看父MemberAccess,并直接跳转到祖父:

 var converted = Expression.MakeMemberAccess(
       base.Visit(node.Expression), 
       property 
       ); 

     return converted; 

变得像这样:

 var parentMember = node.Expression as MemberExpression; 

     if (parentMember != null) 
     { 
      var grandparent = parentMember.Expression; 

      var converted = Expression.MakeMemberAccess(
        base.Visit(grandparent), 
        property 
        ); 

      return converted; 
     } 
     else 
     { 
      var converted = Expression.MakeMemberAccess(
        base.Visit(node.Expression), 
        property 
        ); 

      return converted; 
     } 
+0

谢谢Shlomo,我会给这个镜头看看结果如何。感谢您指点我正确的方向。 – Mike 2015-02-10 04:43:08