我正在将Expression<T, bool>
转换为Expression<Y, bool>
,其中T和Y是通过Automapper映射以外的任何方式不相关的不同实体。基本上,我有一个我的代码使用的Model对象:
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()。如何处理我的Model对象上忽略我的Entity对象上不存在的属性?
使用评论信息进行更新
所以,为了更清楚一点,我使用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 == 1234
更改为mE => mE.AccountId == 1234
)。我现在遇到的问题是,在执行AccountId属性后,调用VisitMember,并将Account作为节点。由于我的Entity.Store对象中没有Account,因此我得到了异常。
答案 0 :(得分:1)
在没有可测试/可运行代码的情况下测试解决方案相当困难。但这是一个猜测
鉴于以下表达式mS => mS.Account.AccountId == 1234
并希望转换MemberExpressions
,您将获得以下调用:
VisitMember(mS.Account.AccountId
VisitMember(mS.Account)
您想将第二个转换为mE.AccountId
。这涉及两个转换:一,将属性访问从(EntityType).(AccountType).AccountId
更改为(MongoStoreType).AccountId
,还更改基础对象。如果您已经在ExpressionVisitor
的其他方法(可能是VisitParameter
和VisitLambda
)处理参数转换,那么您就可以了。然后你只需要跳过查看父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;
}