问题
Completely rewriting this question since I understand more now than I did before.
I am attempting to abstract out the conversion of an OData query string directly to .NET expression tree. There appear to be a number of questions and articles on this, but no answers that provide an abstract solution that relies soley on the Microsoft.Data.OData
namespace (ie, all examples rely on WebAPI, Entity Framework, or some other library).
The answer that does the best at providing an abstract solution is here:
https://stackoverflow.com/a/21536369/701346
These two lines got me started:
IEdmModel model = EdmxReader.Parse(new XmlTextReader(/*stream of your $metadata file*/));
IEdmEntityType type = model.FindType("organisation");
After much toil, I've learned that OData requires an EDM in order to generate its own proprietary expression tree model. It's only a model. You have to traverse that model to ultimately generate your own expression tree.
So I've done all of this (sort of). I happened upon this article which showed me how to create a basic EDM without any navigation:
https://blogs.msdn.microsoft.com/alexj/2012/12/06/parsing-filter-and-orderby-using-the-odatauriparser/
Using that, I ended up creating an EDM generator that recursively reflects through a class to build the EDM. The problem is that this is ridiculously complex and there isn't much information online about how to create an EDM dynamically, so it doesn't define any navigation properties and only works with a single entity.
I then created an ODataExpressionVisitor that's modeled after System.Linq.Expressions.ExpressionVisitor
. It works pretty good. It's able to take this OData query string:
var filter = ODataUriParser.ParseFilter(
"(Name eq 'Oxford Mall' or Street eq '123 whatever ln') and Id eq 2",
edmBuilder.Model, edmBuilder.Model.FindType(typeof(CustomerLocation).FullName));
And generate this expression:
(
$CustomerLocation.Name == "Oxford Mall" ||
$CustomerLocation.Street == "123 whatever ln"
) &&
$CustomerLocation.Id == 2
It works, too, because I can compile it to a delagate and pass a CustomerLocation
object into it and it will return the proper true/false. I haven't tested it with EF6 or my other expression-based framework yet, though.
However, I think I am recreating a wheel here. There must be an existing means to 1) generate an convention-based EDM from a class alone and 2) convert the resulting OData expression tree to a .NET expression tree.
回答1:
I hat the same issue a while ago and solved it with the following approach:
- Create
ODataQueryOptions
from an ODATA query string, which can be achieved by constructing it from a URI (I described this in more detail with code examples in Modifying ODataQueryOptions on the fly To convert the various parts of
ODataQueryOptions
likeFilterQueryOption
to anExpression
(actually aMethodCallExpression
) we can useApplyTo
in conjunction with an emptyIQueryable
(convertingOrderByQueryOption
is quite similar to this):public static Expression ToExpression<T>(this FilterQueryOption filterQueryOption) where T: class { IQueryable queryable = Enumerable.Empty<T>().AsQueryable(); queryable = filterQueryOption.ApplyTo(queryable, new OdataQuerySettings()); return queryable.Expression; }
Converting
$skip
and$top
is as easy as usingSkip()
andTake()
.- Depending on the scenario, in order to make this expression usable as a Lambda we might have to use an
ExpressionVisitor
that replaces the argument of theMethodCallExpression
. Creating a Lambda is then only a matter of taking the
Body
and theParameter
and create a newExpression.Lambda
:var expressionLambda = Expression.Lambda<Func<T, bool>> ( visitedMethodCallExpression.Body, Expression.Parameter(typeof(T), "$it") );
For a more complete example see Converting ODataQueryOptions into LINQ Expressions in C# (but this uses EntityFramework in the end, so you might want to skip the rest).
来源:https://stackoverflow.com/questions/40089918/how-to-convert-an-odata-query-string-to-net-expression-tree