Expression Conversion

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP





.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;







up vote
6
down vote

favorite












Using Entity Framework we have a number of entities, which we want to hide from the rest of the calling code to remove the direct dependency on the database. We're doing so by using DTOs, which for the most part are identical to the entities. We also have an abstract generic provider and persister pattern which handle the conversion between the dtos and the entities, and all of the database interaction. The general loading method of the base abstract provider uses the following signature:



protected virtual IEnumerable<TDto> Load(Expression<Func<TEntity, bool>> filter)


So any provider that extends the abstract can look like this:



public IEnumerable<FooDto> GetSomeFilteredData(IEnumerable<string> identifiers)

return base.Load(entity => identifiers.Contains(entity.SomeProperty));



Therefore hiding the entities from the calling code.



This pattern works well for us, but I have been investigating how to manipulate expressions so that I could put the following in the base provider:



public virtual IEnumerable<TDto> Load(Expression<Func<TDto, bool>> filter)


The base provider would then convert the Expression<Func<TDto, bool>> into the equivalent entity expression, and load the data as before, therefore removing the need to write tedious methods and give us more control in the calling code.



The end result has been the following ExpressionConverter



public class ExpressionConverter<Dto, Entity>
where Dto : class, new() where Entity : class, new()

private MappedConverter<Dto, Entity> converter;

public ExpressionConverter(MappedConverter<Dto, Entity> converter)

this.converter = converter;


public Expression<Func<Entity, bool>> ConvertExpr(Expression<Func<Dto, bool>> expr)

ParameterExpression entityParam = Expression.Parameter(typeof(Entity), "e");
Expression entityExpression = this.ConvertExpression(expr.Body, entityParam);

var result = Expression.Lambda<Func<Entity, bool>>(entityExpression, entityParam);
return result;


private Expression ConvertExpression(Expression expression, ParameterExpression entityParam)

if (expression is BinaryExpression binary)

return this.ConvertBinaryExpression(binary, entityParam);

if (expression is MemberExpression member)

return this.ConvertMemberExpression(member, entityParam);

if (expression is MethodCallExpression method)

return this.ConvertMethodCallExpression(method, entityParam);

return expression;


private Expression ConvertBinaryExpression(BinaryExpression binary, ParameterExpression param)

Expression left = this.ConvertExpression(binary.Left, param);
Expression right = this.ConvertExpression(binary.Right, param);
ExpressionType nodeType = binary.NodeType;
return this.CombineExpressions(left, right, nodeType);


private Expression ConvertMemberExpression(Expression expression, ParameterExpression entityParam)

if (this.IsParseableMemberExpression(expression))

MemberExpression memberExpr = expression as MemberExpression;
var value = Expression.Lambda(memberExpr).Compile().DynamicInvoke();
return Expression.Constant(value);

else if (this.IsConvertibleMember(expression))

MemberInfo dtoMember = (expression as MemberExpression).Member;
Mapping<Dto, Entity> mapping = this.converter.GetMappingFromMemberName<Dto>(dtoMember.Name);
MemberExpression entityMemberExpr = Expression.MakeMemberAccess(entityParam, mapping.EntityProperty);
return entityMemberExpr;

return expression;


private Expression ConvertMethodCallExpression(MethodCallExpression expression, ParameterExpression entityParam)

Expression objectExpr = this.ConvertMemberExpression(expression.Object, entityParam);
IEnumerable<Expression> argumentExpressions = expression.Arguments.Select(x => this.ConvertMemberExpression(x, entityParam));
MethodInfo method = expression.Method;

Expression result = Expression.Call(objectExpr, method, argumentExpressions);
return result;


private bool IsConvertibleMember(Expression expression)

return expression.NodeType == ExpressionType.MemberAccess;


private bool IsParseableMemberExpression(Expression expression)

if (expression is MemberExpression memberExpression)

return false;


private bool IsDateTimeExpression(MemberExpression expression)
Now)");


private Expression CombineExpressions(Expression left, Expression right, ExpressionType type)

switch (type)

case ExpressionType.And: return Expression.And(left, right);
case ExpressionType.AndAlso: return Expression.AndAlso(left, right);
case ExpressionType.Equal: return Expression.Equal(left, right);
case ExpressionType.ExclusiveOr: return Expression.ExclusiveOr(left, right);
case ExpressionType.GreaterThan: return Expression.GreaterThan(left, right);
case ExpressionType.GreaterThanOrEqual: return Expression.GreaterThanOrEqual(left, right);
case ExpressionType.LessThan: return Expression.LessThan(left, right);
case ExpressionType.LessThanOrEqual: return Expression.LessThanOrEqual(left, right);
case ExpressionType.NotEqual: return Expression.NotEqual(left, right);
case ExpressionType.Or: return Expression.Or(left, right);
case ExpressionType.OrElse: return Expression.OrElse(left, right);
default:
throw new Exception($"Unsupported expression type: type.ToString()");





Where MappedConverter<Dto, Entity> is the following:



public class MappedConverter<Dto, Entity> where Dto : class, new() where Entity : class, new()

public List<Mapping<Dto, Entity>> Mappings get; set;

public MappedConverter(params Mapping<Dto, Entity> maps)

this.Mappings = maps.ToList();


public Mapping<Dto, Entity> GetMappingFromMemberName<T>(string name)

if (typeof(T) == typeof(Dto))

return this.Mappings.SingleOrDefault(x => x.DtoProperty.Name == name);

else if (typeof(T) == typeof(Entity))

return this.Mappings.SingleOrDefault(x => x.EntityProperty.Name == name);

throw new Exception($"Cannot get mapping for typeof(T).Name from MappedConverter<typeof(Dto).Name, typeof(Entity).Name>");




And Mapping<Dto, Entity> is the following:



public class Mapping<Dto, Entity>

public PropertyInfo DtoProperty get;
public PropertyInfo EntityProperty get;



Notes:



The conversion only has to handle Expressions of the form Expression<Func<Dto, bool>>



I am specifically looking for ways to improve the IsParseableMemberExpression and IsDateTimeExpression methods



IsParseableMemberExpression uses Regex to see if the expression is representing a variable in the full expression. e.g. variable in x => x.Value == variable







share|improve this question















  • 1




    I agree that having to reference TEntity is compromising your otherwise quite extensive separation of layers. However, the issue in only using TDto is that there is no guarantee that its related TEntity has a similar property with which the expression would work. I'm not saying it can't technically be done, but is the effort required worth the payoff? It's counterintuitive to want direct control over the entity properties you're forcibly trying to abstract. You're essentially asking for zero type safety for your filters, which seems like it can become a source of future issues.
    – Flater
    May 2 at 11:26











  • @Flater I understand the concern about type safety but that is something we manage at the data layer when creating the mapped converters. In our experience the effort creating this has already been worth the payoff because of the volume of new DTOs and Entities we are having to add, which are largely simple objects parsed from csv files, and any querying is relatively straightforward. For larger or more complex objects we are still using the old pattern to help with safety
    – JChristen
    May 2 at 12:43






  • 1




    That makes sense now that you're building the intermediate layer. But suppose you're done with that, and a different developer is going to write the layer that consumes your layer. Is it going to be obvious to them when to use which pattern at what time? Are they going to need to be kept in the loop of what has(n't) been implemented in your layer? Because they'd presumably have to decide between patterns based on knowledge of whether the underlying entity is the same as the dto. Which means they need to know about the entity. Which means it's not actually separated by your layer.
    – Flater
    May 2 at 12:51







  • 1




    The point I'm getting at is that when your separation is incomplete (as evidenced by your "for complex objects we are using the old pattern"), then you've created a need for consuming developers to understand your intermediary system while also still knowing the underlying basics. (There is a fairly relevant XKCD for this). Half of a separation is actually worse than no separation at all, since half of a separation would require both knowledge of the separated and unseparated parts.
    – Flater
    May 2 at 12:54










  • And whilst I completely understand where you're coming from, questions about the overall architecture of our system fall out of scope of this specific code review. Undoubtedly it's worthy of a separate discussion on a different question, but it's not relevant to the code review at hand.
    – JChristen
    May 3 at 6:27
















up vote
6
down vote

favorite












Using Entity Framework we have a number of entities, which we want to hide from the rest of the calling code to remove the direct dependency on the database. We're doing so by using DTOs, which for the most part are identical to the entities. We also have an abstract generic provider and persister pattern which handle the conversion between the dtos and the entities, and all of the database interaction. The general loading method of the base abstract provider uses the following signature:



protected virtual IEnumerable<TDto> Load(Expression<Func<TEntity, bool>> filter)


So any provider that extends the abstract can look like this:



public IEnumerable<FooDto> GetSomeFilteredData(IEnumerable<string> identifiers)

return base.Load(entity => identifiers.Contains(entity.SomeProperty));



Therefore hiding the entities from the calling code.



This pattern works well for us, but I have been investigating how to manipulate expressions so that I could put the following in the base provider:



public virtual IEnumerable<TDto> Load(Expression<Func<TDto, bool>> filter)


The base provider would then convert the Expression<Func<TDto, bool>> into the equivalent entity expression, and load the data as before, therefore removing the need to write tedious methods and give us more control in the calling code.



The end result has been the following ExpressionConverter



public class ExpressionConverter<Dto, Entity>
where Dto : class, new() where Entity : class, new()

private MappedConverter<Dto, Entity> converter;

public ExpressionConverter(MappedConverter<Dto, Entity> converter)

this.converter = converter;


public Expression<Func<Entity, bool>> ConvertExpr(Expression<Func<Dto, bool>> expr)

ParameterExpression entityParam = Expression.Parameter(typeof(Entity), "e");
Expression entityExpression = this.ConvertExpression(expr.Body, entityParam);

var result = Expression.Lambda<Func<Entity, bool>>(entityExpression, entityParam);
return result;


private Expression ConvertExpression(Expression expression, ParameterExpression entityParam)

if (expression is BinaryExpression binary)

return this.ConvertBinaryExpression(binary, entityParam);

if (expression is MemberExpression member)

return this.ConvertMemberExpression(member, entityParam);

if (expression is MethodCallExpression method)

return this.ConvertMethodCallExpression(method, entityParam);

return expression;


private Expression ConvertBinaryExpression(BinaryExpression binary, ParameterExpression param)

Expression left = this.ConvertExpression(binary.Left, param);
Expression right = this.ConvertExpression(binary.Right, param);
ExpressionType nodeType = binary.NodeType;
return this.CombineExpressions(left, right, nodeType);


private Expression ConvertMemberExpression(Expression expression, ParameterExpression entityParam)

if (this.IsParseableMemberExpression(expression))

MemberExpression memberExpr = expression as MemberExpression;
var value = Expression.Lambda(memberExpr).Compile().DynamicInvoke();
return Expression.Constant(value);

else if (this.IsConvertibleMember(expression))

MemberInfo dtoMember = (expression as MemberExpression).Member;
Mapping<Dto, Entity> mapping = this.converter.GetMappingFromMemberName<Dto>(dtoMember.Name);
MemberExpression entityMemberExpr = Expression.MakeMemberAccess(entityParam, mapping.EntityProperty);
return entityMemberExpr;

return expression;


private Expression ConvertMethodCallExpression(MethodCallExpression expression, ParameterExpression entityParam)

Expression objectExpr = this.ConvertMemberExpression(expression.Object, entityParam);
IEnumerable<Expression> argumentExpressions = expression.Arguments.Select(x => this.ConvertMemberExpression(x, entityParam));
MethodInfo method = expression.Method;

Expression result = Expression.Call(objectExpr, method, argumentExpressions);
return result;


private bool IsConvertibleMember(Expression expression)

return expression.NodeType == ExpressionType.MemberAccess;


private bool IsParseableMemberExpression(Expression expression)

if (expression is MemberExpression memberExpression)

return false;


private bool IsDateTimeExpression(MemberExpression expression)
Now)");


private Expression CombineExpressions(Expression left, Expression right, ExpressionType type)

switch (type)

case ExpressionType.And: return Expression.And(left, right);
case ExpressionType.AndAlso: return Expression.AndAlso(left, right);
case ExpressionType.Equal: return Expression.Equal(left, right);
case ExpressionType.ExclusiveOr: return Expression.ExclusiveOr(left, right);
case ExpressionType.GreaterThan: return Expression.GreaterThan(left, right);
case ExpressionType.GreaterThanOrEqual: return Expression.GreaterThanOrEqual(left, right);
case ExpressionType.LessThan: return Expression.LessThan(left, right);
case ExpressionType.LessThanOrEqual: return Expression.LessThanOrEqual(left, right);
case ExpressionType.NotEqual: return Expression.NotEqual(left, right);
case ExpressionType.Or: return Expression.Or(left, right);
case ExpressionType.OrElse: return Expression.OrElse(left, right);
default:
throw new Exception($"Unsupported expression type: type.ToString()");





Where MappedConverter<Dto, Entity> is the following:



public class MappedConverter<Dto, Entity> where Dto : class, new() where Entity : class, new()

public List<Mapping<Dto, Entity>> Mappings get; set;

public MappedConverter(params Mapping<Dto, Entity> maps)

this.Mappings = maps.ToList();


public Mapping<Dto, Entity> GetMappingFromMemberName<T>(string name)

if (typeof(T) == typeof(Dto))

return this.Mappings.SingleOrDefault(x => x.DtoProperty.Name == name);

else if (typeof(T) == typeof(Entity))

return this.Mappings.SingleOrDefault(x => x.EntityProperty.Name == name);

throw new Exception($"Cannot get mapping for typeof(T).Name from MappedConverter<typeof(Dto).Name, typeof(Entity).Name>");




And Mapping<Dto, Entity> is the following:



public class Mapping<Dto, Entity>

public PropertyInfo DtoProperty get;
public PropertyInfo EntityProperty get;



Notes:



The conversion only has to handle Expressions of the form Expression<Func<Dto, bool>>



I am specifically looking for ways to improve the IsParseableMemberExpression and IsDateTimeExpression methods



IsParseableMemberExpression uses Regex to see if the expression is representing a variable in the full expression. e.g. variable in x => x.Value == variable







share|improve this question















  • 1




    I agree that having to reference TEntity is compromising your otherwise quite extensive separation of layers. However, the issue in only using TDto is that there is no guarantee that its related TEntity has a similar property with which the expression would work. I'm not saying it can't technically be done, but is the effort required worth the payoff? It's counterintuitive to want direct control over the entity properties you're forcibly trying to abstract. You're essentially asking for zero type safety for your filters, which seems like it can become a source of future issues.
    – Flater
    May 2 at 11:26











  • @Flater I understand the concern about type safety but that is something we manage at the data layer when creating the mapped converters. In our experience the effort creating this has already been worth the payoff because of the volume of new DTOs and Entities we are having to add, which are largely simple objects parsed from csv files, and any querying is relatively straightforward. For larger or more complex objects we are still using the old pattern to help with safety
    – JChristen
    May 2 at 12:43






  • 1




    That makes sense now that you're building the intermediate layer. But suppose you're done with that, and a different developer is going to write the layer that consumes your layer. Is it going to be obvious to them when to use which pattern at what time? Are they going to need to be kept in the loop of what has(n't) been implemented in your layer? Because they'd presumably have to decide between patterns based on knowledge of whether the underlying entity is the same as the dto. Which means they need to know about the entity. Which means it's not actually separated by your layer.
    – Flater
    May 2 at 12:51







  • 1




    The point I'm getting at is that when your separation is incomplete (as evidenced by your "for complex objects we are using the old pattern"), then you've created a need for consuming developers to understand your intermediary system while also still knowing the underlying basics. (There is a fairly relevant XKCD for this). Half of a separation is actually worse than no separation at all, since half of a separation would require both knowledge of the separated and unseparated parts.
    – Flater
    May 2 at 12:54










  • And whilst I completely understand where you're coming from, questions about the overall architecture of our system fall out of scope of this specific code review. Undoubtedly it's worthy of a separate discussion on a different question, but it's not relevant to the code review at hand.
    – JChristen
    May 3 at 6:27












up vote
6
down vote

favorite









up vote
6
down vote

favorite











Using Entity Framework we have a number of entities, which we want to hide from the rest of the calling code to remove the direct dependency on the database. We're doing so by using DTOs, which for the most part are identical to the entities. We also have an abstract generic provider and persister pattern which handle the conversion between the dtos and the entities, and all of the database interaction. The general loading method of the base abstract provider uses the following signature:



protected virtual IEnumerable<TDto> Load(Expression<Func<TEntity, bool>> filter)


So any provider that extends the abstract can look like this:



public IEnumerable<FooDto> GetSomeFilteredData(IEnumerable<string> identifiers)

return base.Load(entity => identifiers.Contains(entity.SomeProperty));



Therefore hiding the entities from the calling code.



This pattern works well for us, but I have been investigating how to manipulate expressions so that I could put the following in the base provider:



public virtual IEnumerable<TDto> Load(Expression<Func<TDto, bool>> filter)


The base provider would then convert the Expression<Func<TDto, bool>> into the equivalent entity expression, and load the data as before, therefore removing the need to write tedious methods and give us more control in the calling code.



The end result has been the following ExpressionConverter



public class ExpressionConverter<Dto, Entity>
where Dto : class, new() where Entity : class, new()

private MappedConverter<Dto, Entity> converter;

public ExpressionConverter(MappedConverter<Dto, Entity> converter)

this.converter = converter;


public Expression<Func<Entity, bool>> ConvertExpr(Expression<Func<Dto, bool>> expr)

ParameterExpression entityParam = Expression.Parameter(typeof(Entity), "e");
Expression entityExpression = this.ConvertExpression(expr.Body, entityParam);

var result = Expression.Lambda<Func<Entity, bool>>(entityExpression, entityParam);
return result;


private Expression ConvertExpression(Expression expression, ParameterExpression entityParam)

if (expression is BinaryExpression binary)

return this.ConvertBinaryExpression(binary, entityParam);

if (expression is MemberExpression member)

return this.ConvertMemberExpression(member, entityParam);

if (expression is MethodCallExpression method)

return this.ConvertMethodCallExpression(method, entityParam);

return expression;


private Expression ConvertBinaryExpression(BinaryExpression binary, ParameterExpression param)

Expression left = this.ConvertExpression(binary.Left, param);
Expression right = this.ConvertExpression(binary.Right, param);
ExpressionType nodeType = binary.NodeType;
return this.CombineExpressions(left, right, nodeType);


private Expression ConvertMemberExpression(Expression expression, ParameterExpression entityParam)

if (this.IsParseableMemberExpression(expression))

MemberExpression memberExpr = expression as MemberExpression;
var value = Expression.Lambda(memberExpr).Compile().DynamicInvoke();
return Expression.Constant(value);

else if (this.IsConvertibleMember(expression))

MemberInfo dtoMember = (expression as MemberExpression).Member;
Mapping<Dto, Entity> mapping = this.converter.GetMappingFromMemberName<Dto>(dtoMember.Name);
MemberExpression entityMemberExpr = Expression.MakeMemberAccess(entityParam, mapping.EntityProperty);
return entityMemberExpr;

return expression;


private Expression ConvertMethodCallExpression(MethodCallExpression expression, ParameterExpression entityParam)

Expression objectExpr = this.ConvertMemberExpression(expression.Object, entityParam);
IEnumerable<Expression> argumentExpressions = expression.Arguments.Select(x => this.ConvertMemberExpression(x, entityParam));
MethodInfo method = expression.Method;

Expression result = Expression.Call(objectExpr, method, argumentExpressions);
return result;


private bool IsConvertibleMember(Expression expression)

return expression.NodeType == ExpressionType.MemberAccess;


private bool IsParseableMemberExpression(Expression expression)

if (expression is MemberExpression memberExpression)

return false;


private bool IsDateTimeExpression(MemberExpression expression)
Now)");


private Expression CombineExpressions(Expression left, Expression right, ExpressionType type)

switch (type)

case ExpressionType.And: return Expression.And(left, right);
case ExpressionType.AndAlso: return Expression.AndAlso(left, right);
case ExpressionType.Equal: return Expression.Equal(left, right);
case ExpressionType.ExclusiveOr: return Expression.ExclusiveOr(left, right);
case ExpressionType.GreaterThan: return Expression.GreaterThan(left, right);
case ExpressionType.GreaterThanOrEqual: return Expression.GreaterThanOrEqual(left, right);
case ExpressionType.LessThan: return Expression.LessThan(left, right);
case ExpressionType.LessThanOrEqual: return Expression.LessThanOrEqual(left, right);
case ExpressionType.NotEqual: return Expression.NotEqual(left, right);
case ExpressionType.Or: return Expression.Or(left, right);
case ExpressionType.OrElse: return Expression.OrElse(left, right);
default:
throw new Exception($"Unsupported expression type: type.ToString()");





Where MappedConverter<Dto, Entity> is the following:



public class MappedConverter<Dto, Entity> where Dto : class, new() where Entity : class, new()

public List<Mapping<Dto, Entity>> Mappings get; set;

public MappedConverter(params Mapping<Dto, Entity> maps)

this.Mappings = maps.ToList();


public Mapping<Dto, Entity> GetMappingFromMemberName<T>(string name)

if (typeof(T) == typeof(Dto))

return this.Mappings.SingleOrDefault(x => x.DtoProperty.Name == name);

else if (typeof(T) == typeof(Entity))

return this.Mappings.SingleOrDefault(x => x.EntityProperty.Name == name);

throw new Exception($"Cannot get mapping for typeof(T).Name from MappedConverter<typeof(Dto).Name, typeof(Entity).Name>");




And Mapping<Dto, Entity> is the following:



public class Mapping<Dto, Entity>

public PropertyInfo DtoProperty get;
public PropertyInfo EntityProperty get;



Notes:



The conversion only has to handle Expressions of the form Expression<Func<Dto, bool>>



I am specifically looking for ways to improve the IsParseableMemberExpression and IsDateTimeExpression methods



IsParseableMemberExpression uses Regex to see if the expression is representing a variable in the full expression. e.g. variable in x => x.Value == variable







share|improve this question











Using Entity Framework we have a number of entities, which we want to hide from the rest of the calling code to remove the direct dependency on the database. We're doing so by using DTOs, which for the most part are identical to the entities. We also have an abstract generic provider and persister pattern which handle the conversion between the dtos and the entities, and all of the database interaction. The general loading method of the base abstract provider uses the following signature:



protected virtual IEnumerable<TDto> Load(Expression<Func<TEntity, bool>> filter)


So any provider that extends the abstract can look like this:



public IEnumerable<FooDto> GetSomeFilteredData(IEnumerable<string> identifiers)

return base.Load(entity => identifiers.Contains(entity.SomeProperty));



Therefore hiding the entities from the calling code.



This pattern works well for us, but I have been investigating how to manipulate expressions so that I could put the following in the base provider:



public virtual IEnumerable<TDto> Load(Expression<Func<TDto, bool>> filter)


The base provider would then convert the Expression<Func<TDto, bool>> into the equivalent entity expression, and load the data as before, therefore removing the need to write tedious methods and give us more control in the calling code.



The end result has been the following ExpressionConverter



public class ExpressionConverter<Dto, Entity>
where Dto : class, new() where Entity : class, new()

private MappedConverter<Dto, Entity> converter;

public ExpressionConverter(MappedConverter<Dto, Entity> converter)

this.converter = converter;


public Expression<Func<Entity, bool>> ConvertExpr(Expression<Func<Dto, bool>> expr)

ParameterExpression entityParam = Expression.Parameter(typeof(Entity), "e");
Expression entityExpression = this.ConvertExpression(expr.Body, entityParam);

var result = Expression.Lambda<Func<Entity, bool>>(entityExpression, entityParam);
return result;


private Expression ConvertExpression(Expression expression, ParameterExpression entityParam)

if (expression is BinaryExpression binary)

return this.ConvertBinaryExpression(binary, entityParam);

if (expression is MemberExpression member)

return this.ConvertMemberExpression(member, entityParam);

if (expression is MethodCallExpression method)

return this.ConvertMethodCallExpression(method, entityParam);

return expression;


private Expression ConvertBinaryExpression(BinaryExpression binary, ParameterExpression param)

Expression left = this.ConvertExpression(binary.Left, param);
Expression right = this.ConvertExpression(binary.Right, param);
ExpressionType nodeType = binary.NodeType;
return this.CombineExpressions(left, right, nodeType);


private Expression ConvertMemberExpression(Expression expression, ParameterExpression entityParam)

if (this.IsParseableMemberExpression(expression))

MemberExpression memberExpr = expression as MemberExpression;
var value = Expression.Lambda(memberExpr).Compile().DynamicInvoke();
return Expression.Constant(value);

else if (this.IsConvertibleMember(expression))

MemberInfo dtoMember = (expression as MemberExpression).Member;
Mapping<Dto, Entity> mapping = this.converter.GetMappingFromMemberName<Dto>(dtoMember.Name);
MemberExpression entityMemberExpr = Expression.MakeMemberAccess(entityParam, mapping.EntityProperty);
return entityMemberExpr;

return expression;


private Expression ConvertMethodCallExpression(MethodCallExpression expression, ParameterExpression entityParam)

Expression objectExpr = this.ConvertMemberExpression(expression.Object, entityParam);
IEnumerable<Expression> argumentExpressions = expression.Arguments.Select(x => this.ConvertMemberExpression(x, entityParam));
MethodInfo method = expression.Method;

Expression result = Expression.Call(objectExpr, method, argumentExpressions);
return result;


private bool IsConvertibleMember(Expression expression)

return expression.NodeType == ExpressionType.MemberAccess;


private bool IsParseableMemberExpression(Expression expression)

if (expression is MemberExpression memberExpression)

return false;


private bool IsDateTimeExpression(MemberExpression expression)
Now)");


private Expression CombineExpressions(Expression left, Expression right, ExpressionType type)

switch (type)

case ExpressionType.And: return Expression.And(left, right);
case ExpressionType.AndAlso: return Expression.AndAlso(left, right);
case ExpressionType.Equal: return Expression.Equal(left, right);
case ExpressionType.ExclusiveOr: return Expression.ExclusiveOr(left, right);
case ExpressionType.GreaterThan: return Expression.GreaterThan(left, right);
case ExpressionType.GreaterThanOrEqual: return Expression.GreaterThanOrEqual(left, right);
case ExpressionType.LessThan: return Expression.LessThan(left, right);
case ExpressionType.LessThanOrEqual: return Expression.LessThanOrEqual(left, right);
case ExpressionType.NotEqual: return Expression.NotEqual(left, right);
case ExpressionType.Or: return Expression.Or(left, right);
case ExpressionType.OrElse: return Expression.OrElse(left, right);
default:
throw new Exception($"Unsupported expression type: type.ToString()");





Where MappedConverter<Dto, Entity> is the following:



public class MappedConverter<Dto, Entity> where Dto : class, new() where Entity : class, new()

public List<Mapping<Dto, Entity>> Mappings get; set;

public MappedConverter(params Mapping<Dto, Entity> maps)

this.Mappings = maps.ToList();


public Mapping<Dto, Entity> GetMappingFromMemberName<T>(string name)

if (typeof(T) == typeof(Dto))

return this.Mappings.SingleOrDefault(x => x.DtoProperty.Name == name);

else if (typeof(T) == typeof(Entity))

return this.Mappings.SingleOrDefault(x => x.EntityProperty.Name == name);

throw new Exception($"Cannot get mapping for typeof(T).Name from MappedConverter<typeof(Dto).Name, typeof(Entity).Name>");




And Mapping<Dto, Entity> is the following:



public class Mapping<Dto, Entity>

public PropertyInfo DtoProperty get;
public PropertyInfo EntityProperty get;



Notes:



The conversion only has to handle Expressions of the form Expression<Func<Dto, bool>>



I am specifically looking for ways to improve the IsParseableMemberExpression and IsDateTimeExpression methods



IsParseableMemberExpression uses Regex to see if the expression is representing a variable in the full expression. e.g. variable in x => x.Value == variable









share|improve this question










share|improve this question




share|improve this question









asked May 2 at 9:13









JChristen

311




311







  • 1




    I agree that having to reference TEntity is compromising your otherwise quite extensive separation of layers. However, the issue in only using TDto is that there is no guarantee that its related TEntity has a similar property with which the expression would work. I'm not saying it can't technically be done, but is the effort required worth the payoff? It's counterintuitive to want direct control over the entity properties you're forcibly trying to abstract. You're essentially asking for zero type safety for your filters, which seems like it can become a source of future issues.
    – Flater
    May 2 at 11:26











  • @Flater I understand the concern about type safety but that is something we manage at the data layer when creating the mapped converters. In our experience the effort creating this has already been worth the payoff because of the volume of new DTOs and Entities we are having to add, which are largely simple objects parsed from csv files, and any querying is relatively straightforward. For larger or more complex objects we are still using the old pattern to help with safety
    – JChristen
    May 2 at 12:43






  • 1




    That makes sense now that you're building the intermediate layer. But suppose you're done with that, and a different developer is going to write the layer that consumes your layer. Is it going to be obvious to them when to use which pattern at what time? Are they going to need to be kept in the loop of what has(n't) been implemented in your layer? Because they'd presumably have to decide between patterns based on knowledge of whether the underlying entity is the same as the dto. Which means they need to know about the entity. Which means it's not actually separated by your layer.
    – Flater
    May 2 at 12:51







  • 1




    The point I'm getting at is that when your separation is incomplete (as evidenced by your "for complex objects we are using the old pattern"), then you've created a need for consuming developers to understand your intermediary system while also still knowing the underlying basics. (There is a fairly relevant XKCD for this). Half of a separation is actually worse than no separation at all, since half of a separation would require both knowledge of the separated and unseparated parts.
    – Flater
    May 2 at 12:54










  • And whilst I completely understand where you're coming from, questions about the overall architecture of our system fall out of scope of this specific code review. Undoubtedly it's worthy of a separate discussion on a different question, but it's not relevant to the code review at hand.
    – JChristen
    May 3 at 6:27












  • 1




    I agree that having to reference TEntity is compromising your otherwise quite extensive separation of layers. However, the issue in only using TDto is that there is no guarantee that its related TEntity has a similar property with which the expression would work. I'm not saying it can't technically be done, but is the effort required worth the payoff? It's counterintuitive to want direct control over the entity properties you're forcibly trying to abstract. You're essentially asking for zero type safety for your filters, which seems like it can become a source of future issues.
    – Flater
    May 2 at 11:26











  • @Flater I understand the concern about type safety but that is something we manage at the data layer when creating the mapped converters. In our experience the effort creating this has already been worth the payoff because of the volume of new DTOs and Entities we are having to add, which are largely simple objects parsed from csv files, and any querying is relatively straightforward. For larger or more complex objects we are still using the old pattern to help with safety
    – JChristen
    May 2 at 12:43






  • 1




    That makes sense now that you're building the intermediate layer. But suppose you're done with that, and a different developer is going to write the layer that consumes your layer. Is it going to be obvious to them when to use which pattern at what time? Are they going to need to be kept in the loop of what has(n't) been implemented in your layer? Because they'd presumably have to decide between patterns based on knowledge of whether the underlying entity is the same as the dto. Which means they need to know about the entity. Which means it's not actually separated by your layer.
    – Flater
    May 2 at 12:51







  • 1




    The point I'm getting at is that when your separation is incomplete (as evidenced by your "for complex objects we are using the old pattern"), then you've created a need for consuming developers to understand your intermediary system while also still knowing the underlying basics. (There is a fairly relevant XKCD for this). Half of a separation is actually worse than no separation at all, since half of a separation would require both knowledge of the separated and unseparated parts.
    – Flater
    May 2 at 12:54










  • And whilst I completely understand where you're coming from, questions about the overall architecture of our system fall out of scope of this specific code review. Undoubtedly it's worthy of a separate discussion on a different question, but it's not relevant to the code review at hand.
    – JChristen
    May 3 at 6:27







1




1




I agree that having to reference TEntity is compromising your otherwise quite extensive separation of layers. However, the issue in only using TDto is that there is no guarantee that its related TEntity has a similar property with which the expression would work. I'm not saying it can't technically be done, but is the effort required worth the payoff? It's counterintuitive to want direct control over the entity properties you're forcibly trying to abstract. You're essentially asking for zero type safety for your filters, which seems like it can become a source of future issues.
– Flater
May 2 at 11:26





I agree that having to reference TEntity is compromising your otherwise quite extensive separation of layers. However, the issue in only using TDto is that there is no guarantee that its related TEntity has a similar property with which the expression would work. I'm not saying it can't technically be done, but is the effort required worth the payoff? It's counterintuitive to want direct control over the entity properties you're forcibly trying to abstract. You're essentially asking for zero type safety for your filters, which seems like it can become a source of future issues.
– Flater
May 2 at 11:26













@Flater I understand the concern about type safety but that is something we manage at the data layer when creating the mapped converters. In our experience the effort creating this has already been worth the payoff because of the volume of new DTOs and Entities we are having to add, which are largely simple objects parsed from csv files, and any querying is relatively straightforward. For larger or more complex objects we are still using the old pattern to help with safety
– JChristen
May 2 at 12:43




@Flater I understand the concern about type safety but that is something we manage at the data layer when creating the mapped converters. In our experience the effort creating this has already been worth the payoff because of the volume of new DTOs and Entities we are having to add, which are largely simple objects parsed from csv files, and any querying is relatively straightforward. For larger or more complex objects we are still using the old pattern to help with safety
– JChristen
May 2 at 12:43




1




1




That makes sense now that you're building the intermediate layer. But suppose you're done with that, and a different developer is going to write the layer that consumes your layer. Is it going to be obvious to them when to use which pattern at what time? Are they going to need to be kept in the loop of what has(n't) been implemented in your layer? Because they'd presumably have to decide between patterns based on knowledge of whether the underlying entity is the same as the dto. Which means they need to know about the entity. Which means it's not actually separated by your layer.
– Flater
May 2 at 12:51





That makes sense now that you're building the intermediate layer. But suppose you're done with that, and a different developer is going to write the layer that consumes your layer. Is it going to be obvious to them when to use which pattern at what time? Are they going to need to be kept in the loop of what has(n't) been implemented in your layer? Because they'd presumably have to decide between patterns based on knowledge of whether the underlying entity is the same as the dto. Which means they need to know about the entity. Which means it's not actually separated by your layer.
– Flater
May 2 at 12:51





1




1




The point I'm getting at is that when your separation is incomplete (as evidenced by your "for complex objects we are using the old pattern"), then you've created a need for consuming developers to understand your intermediary system while also still knowing the underlying basics. (There is a fairly relevant XKCD for this). Half of a separation is actually worse than no separation at all, since half of a separation would require both knowledge of the separated and unseparated parts.
– Flater
May 2 at 12:54




The point I'm getting at is that when your separation is incomplete (as evidenced by your "for complex objects we are using the old pattern"), then you've created a need for consuming developers to understand your intermediary system while also still knowing the underlying basics. (There is a fairly relevant XKCD for this). Half of a separation is actually worse than no separation at all, since half of a separation would require both knowledge of the separated and unseparated parts.
– Flater
May 2 at 12:54












And whilst I completely understand where you're coming from, questions about the overall architecture of our system fall out of scope of this specific code review. Undoubtedly it's worthy of a separate discussion on a different question, but it's not relevant to the code review at hand.
– JChristen
May 3 at 6:27




And whilst I completely understand where you're coming from, questions about the overall architecture of our system fall out of scope of this specific code review. Undoubtedly it's worthy of a separate discussion on a different question, but it's not relevant to the code review at hand.
– JChristen
May 3 at 6:27















active

oldest

votes











Your Answer




StackExchange.ifUsing("editor", function ()
return StackExchange.using("mathjaxEditing", function ()
StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix)
StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
);
);
, "mathjax-editing");

StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");

StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "196"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);

else
createEditor();

);

function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
convertImagesToLinks: false,
noModals: false,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);



);








 

draft saved


draft discarded


















StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f193435%2fexpression-conversion%23new-answer', 'question_page');

);

Post as a guest



































active

oldest

votes













active

oldest

votes









active

oldest

votes






active

oldest

votes










 

draft saved


draft discarded


























 


draft saved


draft discarded














StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f193435%2fexpression-conversion%23new-answer', 'question_page');

);

Post as a guest













































































Popular posts from this blog

Greedy Best First Search implementation in Rust

Function to Return a JSON Like Objects Using VBA Collections and Arrays

C++11 CLH Lock Implementation