【问题标题】:Expression Tree Null VisitMember表达式树空访问成员
【发布时间】:2015-02-07 20:26:33
【问题描述】:

我正在将 Expression<T, bool> 转换为 Expression<Y, bool>,其中 T 和 Y 是不同的实体,除了通过 Automapper 映射之外,没有任何关联。本质上,我有一个我的代码使用的模型对象:

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;


        }

问题是,我的实体对象与我的模型对象没有所有相同的属性(例如,Account 对象与 AccountId)。当 Transformer 到达 Model 对象上的 Account 属性时,我得到一个异常(因为我的 Entity 对象上没有匹配的属性)。我无法从 VisitMember 返回 null,也不允许使用 new Expression()。如何处理忽略我的实体对象上不存在的模型对象上的属性?

使用评论中的信息进行更新

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

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

我有这部分工作(将mS =&gt; mS.Account.AccountId == 1234 更改为mE =&gt; mE.AccountId == 1234)。我现在遇到的问题是,在完成 AccountId 属性之后,以 Account 作为节点调用了 VisitMember。由于我的 Entity.Store 对象中没有 Account,我得到了异常。

【问题讨论】:

  • 问题的链接不能正常工作,我想是this one =)
  • 对不起这位大师,我更新了链接。
  • 你如何忽略A在谓词x =&gt; x.A == 1中不存在的事实?
  • 我试图做 x=> x.Account.AccountId==... 我找到了 AccountId 的正确属性,但随后它试图找到 Account(不存在)
  • 所以,为了更清楚一点,我使用 Automapper 从 Models.Store 映射到 Entity.Store。我的 entity.Store 只有一个 AccountId(因为我不想复制所有的帐户数据),但我的 Models.Store 需要整个帐户对象(我可以通过查询 Accounts 集合获得)。 Automapper 基本上将我的 Account 对象转换为我实体上的 AccountId。因此,当我搜索 x => x.Account.AccountId == abcd1234(其中 x 是 models.Store)时,我需要将表达式转换为 x => x.AccountId(其中 x 是 Entity.Store)。 ...

标签: c# linq lambda expression-trees


【解决方案1】:

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

给定以下表达式 mS =&gt; 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;
        }

【讨论】:

  • 谢谢 Shlomo,我会试一试,看看结果如何。感谢您为我指明正确的方向。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-02-06
  • 1970-01-01
  • 1970-01-01
  • 2022-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多