I have set up a multi targetting (net4.5.2/netstandard2) class library allowing to consume one of our enterprise OData services. To access this OData service we use a proxy class generated with the OData v4 Client Code Generator (v7.5.0)
Unfortunately, when trying to use my library in a Netcoreapp2.1 application I encounter an issue as soon as I try to enumerate a collection.
Container.MyDataSet.ToList();
produces the following exception :
"System.NotSupportedException : This target framework does not enable you to directly enumerate over a data service query. This is because enumeration automatically sends a synchronous request to the data service. Because this framework only supports asynchronous operations, you must instead call the BeginExecute and EndExecute methods to obtain a query result that supports enumeration."
I do not encounter this issue when using this same multitarget library in a .Net 4.5.2 application.
Having a look at the Microsoft.OData.Client v7.5.0 source code, this behaviour seems to be by design with specific handling of the .Net Core case.
Did I miss something ?
The following code prevents the issue, but it is barely usable :
var query = (DataServiceQuery<MyData>)Container.MyDataSet;
var taskFactory = new TaskFactory<IEnumerable<MyData>>();
var t = taskFactory.FromAsync(query.BeginExecute(null, null), data => query.EndExecute(data));
t.ConfigureAwait(false);
IEnumerable<MyData> result = t.Result;
How can I use an OData IQueryable in .Net Core application without adding specific code ?
As mentioned in the error message, the platform only supports asynchronous fetches. Even after you use that, you will likely need to enumerate over the results multiple times -- everytime you perform a ToList()
, FirstOrDefault()
or other similar System.Generics.Collections
operations, you are essentially getting the Enumerator
of the collection and enumerating over it.
I adopted this solution: immediately after I fetch enumerable results from the OData libraries I enumerate over them and put them in another enumerable container (Dictionary<string, MyAwesomeResult>
in this case) instantiated by me.
var resultsQuery = this.oDataClient.MyAwesomeResults
.AddQueryOption("$filter", "Name eq 'MyAwesomeName'")
.AddQueryOption("$top", "5")
.AddQueryOption("$skip", "2");
IEnumerable<MyAwesomeResult> resultsRaw = await
resultsQuery.ExecuteAsync();
var results = new Dictionary<string, MyAwesomeResult>();`
foreach (var resultRaw in resultsRaw)
{
results.Add(resultRaw.Key, resultRaw);
}
Then I use the container I instantiated -- I no longer need to enumerate again over the enumerable returned by
DataServiceQuery<MyAwesomeResult>.ExecuteAsync
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With