Skip to content

.NET standard 2.1: C# expression translation between database and service layers

License

Notifications You must be signed in to change notification settings

newdigate/ExpressionMapper

Repository files navigation

ExpressionMapper

  • a C# expression translator for expressions between database/service/controller layers
  • this is a work-in-progress, please feel free to contribute.

install

> dotnet add package Newdigate.ExpressionMapper

why ?

this allows services to be agnostic to data types. A bit like AutoMapper but specifically for predicate expressions. A service class depending on data query/command/subscription providers shouldnt need to be coupled to any platform specific data-types.

for instance, the controller defined below does not need to be coupled to a specific entity, just a RecordType service entity class and can therefor be agnostic to database implementation

[ApiController]
[Route("[controller]")]
public class RecordTypeController : ControllerBase
{
    private readonly IUniqueKeyQueryProvider<RecordType,string> _queryByUniqueKeyProvider;
    
    public RecordTypeController(IUniqueKeyQueryProvider<RecordType, string> queryByUniqueKeyProvider)
    {
        _queryByUniqueKeyProvider = queryByUniqueKeyProvider;
    }

    [HttpGet(Name = "GetRecordType")]
    public RecordType? GetRecordType()
    {
        RecordType? result = _queryByUniqueKeyProvider.Get("6224ff2fdc13b1955a4c2db8");
        return result;
    }
}

TL/DR

  • given a predicate expression for a service class named StudentRow:
Expression<Func<StudentRecord, bool>>    
    ent_predicate = 
        (StudentRecord e) => (e.Id == "10");
  • map to a predicate expression for a data type named StudentRecordRow
Expression<Func<StudentRecordRow, bool>> 
  mapped_dto_predicate = 
    _expressionMapper
        .GetMappedExpression( ent_predicate );
  • StudentRecord and StudentRecordRow are not related in a class heirachy, but do share certain property names, but not types
    private class StudentRecord
    {
        public string? Id { get; set; }
        public string? Name { get; set; }
    }

    private class StudentRecordRow
    {
        public ObjectId? Id { get; set; }
        public string? Name { get; set; }
    }
  • Properties in the expression are currently only matched on name.
  • Properties which match by name but not by type can be mapped by providing a conversion expression.
    private static Expression ConvertConstStringToObjectIdExpression(Type leftType, Type rightType, ConstantExpression constantExpression)
    {
        if (leftType == typeof(string?) && rightType == typeof(ObjectId?))
        {
            return (string? s) => new ObjectId(s);
        }
        return null;
    }
    
    private readonly IPredicateMapper<StudentRecord, StudentRecordRow> _expressionMapper =
        new PredicateMapper<StudentRecord, StudentRecordRow>(ConvertConstStringToObjectIdExpression);

Releases

No releases published

Packages

No packages published

Languages