I am converting an Expression<T, bool>
to an Expression<Y, bool>
where T and Y are different entities not related in any way other than through an Automapper mapping. Essentially, I have a Model object that my code uses:
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; }
}
that I am mapping to an entity object to store in my mongo database:
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; }
}
I used the answer in this question to work out how to convert between expressions (Question), and that got me 90% there. I was able to modify the code to grab the AutoMapper mappings between my Model store and my entity store and grab the destination property from from the source property:
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;
}
The problem is, my entity object doesn't have all of the same properties as my Model object (Account object versus AccountId for example). When the Transformer gets to the Account property on the Model object, I get an Exception (because there is no matching property on my Entity object). I cannot return null from VisitMember, and new Expression() is not allowed either. How can I handle ignoring properties on my Model object that do not exist on my Entity object?
Updating with info from Comments
So, to be a little more clear, I am using Automapper to map from a Models.Store to an Entity.Store. My entity.Store only has an AccountId (because I don't want to duplicate all of the account data), but my Models.Store needs the whole account object (which I would get by querying the Accounts collection).
Automapper is bascially converting my Account object to just an AccountId on my entity. Therefore, when I search for x => x.Account.AccountId == abcd1234
(where x is a models.Store), I need my expression to convert to x => x.AccountId == abcd1234
(where x is an Entity.Store).
I have that part working (changing mS => mS.Account.AccountId == 1234
to mE => mE.AccountId == 1234
). The problem I am having now is that after doing the AccountId property, VisitMember is called with Account as the node. Since there is no Account in my Entity.Store object, I get the exception.