HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpMinor

Something like a LINQ provider

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
linqlikeprovidersomething

Problem

Ok, before you ask: yes, I need to do this. Sort of.

I'm wrapping a 3rd-party API for data access, and I can't use an ORM, so I'm implementing this kind of thing:

public interface IRepository where TEntity : class, new()
{
    /// 
    /// Projects all entities that match specified predicate into a  instance.
    /// 
    /// A function expression that returns true for all entities to return.
    /// 
    IEnumerable Select(Expression> filter);

    /// 
    /// Projects the single that matches specified predicate into a  instance.
    /// 
    /// Thrown when predicate matches more than a single result.
    /// A function expression that returns true for the only entity to return.
    /// 
    TEntity Single(Expression> filter);

    /// 
    /// Updates the underlying  for the specified entity.
    /// 
    /// The existing entity with the modified property values.
    void Update(TEntity entity);

    /// 
    /// Deletes the specified entity from the underlying .
    /// 
    /// The existing entity to remove.
    void Delete(TEntity entity);

    /// 
    /// Inserts a new entity into the underlying .
    /// 
    /// A non-existing entity to create in the system.
    void Insert(TEntity entity);
}


Notice the Expression> filter parameter of the Single and Select methods? That's so I can write this:

using (var repository = new PurchaseOrderRepository())
{
    var po = repository.Single(x => x.Number == "123456");
    //...
}


Instead of this:

_headerView.Browse("PONUMBER = \"123456\"", true);


So, this ToFilterExpression extension method allows me to nicely wrap this stringly-typed API with my own strongly-typed API, and hide all the nastiness behind a familiar IRepository abstraction.

Here's the extension method in question:

```
public static string ToFilterExpression(this Expression> expression)
where TEntity : class, new()
{
if (expression == null)
{
return string.Empty;
}

var lambdaExpres

Solution

I wouldn't worry too much about the amount of things that aren't supported (yet/if ever) - it's impossible to cover everything in a scenario like this. One thing I would suggest is that you throw exceptions so the caller knows they're doing something unexpected:

protected override Expression VisitMethodCall(MethodCallExpression m)
{
    if (m.Method.DeclaringType == typeof(string))
    {
        // might work better as a switch with a default case. 
        if (m.Method.Name == "StartsWith")
        {
            // ...
        }
        if (m.Method.Name == "EndsWith")
        {
            // ...
        }
        if (m.Method.Name == "Contains")
        {
            // ...
        }
        throw new NotSupportedException("A meaningful error message");
    }

    throw new NotSupportedException("A meaningful error message");
}


"LIKE" should be a well named constant.

String.Format or string interpolation is nicer than concatenation:

var value = expressionTreeHelpers.GetValueFromExpression(m.Arguments[0]);
_filter += $"{property.FieldName} LIKE \"%{value}\"";


I'm afraid that's about the limit of what I can suggest at the moment. It seems like a good approach to me but I'm not exactly an expert at this kind of thing! I would suggest putting some search string validation/escaping in as it's generally a good idea to be cautious.

Code Snippets

protected override Expression VisitMethodCall(MethodCallExpression m)
{
    if (m.Method.DeclaringType == typeof(string))
    {
        // might work better as a switch with a default case. 
        if (m.Method.Name == "StartsWith")
        {
            // ...
        }
        if (m.Method.Name == "EndsWith")
        {
            // ...
        }
        if (m.Method.Name == "Contains")
        {
            // ...
        }
        throw new NotSupportedException("A meaningful error message");
    }

    throw new NotSupportedException("A meaningful error message");
}
var value = expressionTreeHelpers.GetValueFromExpression(m.Arguments[0]);
_filter += $"{property.FieldName} LIKE \"%{value}\"";

Context

StackExchange Code Review Q#118939, answer score: 5

Revisions (0)

No revisions yet.