0

I am using auto generated OData Client, and I am trying to create a generic client class.

I want to get by some predicates.

public T Get<T>(Func<T, bool> predicate)
{
  var entityName = GetEntitySetName<T>();
  var query = _d365Context.CreateQuery<T>(entityName);
  return query.Where(predicate).FirstOrDefault();
}

private static string GetEntitySetName<T>()
{
  var originalEntitySetName = typeof(T).GetCustomAttributes(typeof(EntitySetAttribute), true).Cast<EntitySetAttribute>().SingleOrDefault()?.EntitySet;
  return originalEntitySetName;
}

this code is reading all data and not applying the predicates. What I have already tried.

  • I know query has AddQueryOption but I dont want to use it.
  • I can also use context.EntityName, but it will be hardcoding of entities.

So my question is, is it possible to achieve what I am trying to do? is there any existing library available?

PS - I dont want to use Simple.Odata.Client


One idea is flowing in my mind, to use predicate and parse it somehow and use it in AddQueryOption (this method takes string name and object value). but I don't have much experience with Func, so not sure if it can be done.

Manish
  • 1,139
  • 7
  • 21

2 Answers2

1

Using Func makes your query to switch to IEnumerable so it will need to fetch all data. OData needs to work with IQueryable to be able to translate the query (the same goes for LINQ-based ORMs like EF Core). From OData docs (assuming you are using it, if not the same rules apply to other IQueryable providers):

Since the DataServiceQuery<TElement> class implements the IQueryable<T> interface, the OData client library is able to translate Linq queries against entity sets into URIs executed against a data service resource.

So you need to use expression trees instead of simple funcs:

public T Get<T>(Expression<Func<T, bool>> predicate)
{
  var entityName = GetEntitySetName<T>();
  var query = _d365Context.CreateQuery<T>(entityName);
  return query.Where(predicate).FirstOrDefault();
}

Read more:

  1. What is the difference between IQueryable and IEnumerable?
  2. Expression trees
Guru Stron
  • 102,774
  • 10
  • 95
  • 132
0

You should wrap your Func<T, bool> into expression:

public T Get<T>(Expression<Func<T, bool>> predicate)
{
  var entityName = GetEntitySetName<T>();
  var query = _d365Context.CreateQuery<T>(entityName);
  return query.Where(predicate).FirstOrDefault();
}

The reason is that a query cannot be constructed in such way to be executed on the DB side from the delegate. However if you wrap it to an expression tree, it can be analyzed and converted properly.

The same rule is working for the OData protocol - if you want to get the filtered data directly from the source, you should use IQueryable<T> constructed from expression instead of IEnumerable<T> converted from delegate.

Ivan Vydrin
  • 273
  • 7