patterncsharpMinor
Something like a LINQ provider
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:
Notice the
Instead of this:
So, this
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
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:
"LIKE" should be a well named constant.
String.Format or string interpolation is nicer than concatenation:
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.
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.