[CSHARP-4548] IFindFluent.Projection fails with ExpressionNotSupportedException after upgrading to 2.19.0 Created: 25/Feb/23 Updated: 27/Oct/23 Resolved: 22/Mar/23 |
|
| Status: | Closed |
| Project: | C# Driver |
| Component/s: | None |
| Affects Version/s: | None |
| Fix Version/s: | None |
| Type: | Bug | Priority: | Major - P3 |
| Reporter: | GDar N/A | Assignee: | Robert Stam |
| Resolution: | Works as Designed | Votes: | 0 |
| Labels: | None | ||
| Remaining Estimate: | Not Specified | ||
| Time Spent: | Not Specified | ||
| Original Estimate: | Not Specified | ||
| Issue Links: |
|
||||||||||||||||||||
| Documentation Changes Summary: | 1. What would you like to communicate to the user about this feature? |
||||||||||||||||||||
| Description |
| Comments |
| Comment by Robert Stam [ 22/Mar/23 ] | |||||||||||||||||||||||
|
`IAsyncEnumerable` isn't implemented because it hasn't always existed and still doesn't exist in all our targeted frameworks. We definitely plan to implement it some day. You don't have to use `ToList` to use client side projections. The examples above separated the "find" part from the "client side projection" part but they could be written as a single chain of calls, none of which actually call the database until the final enumerable is enumerated:
The role of calling `ToEnumerable()` here is to separate the database operation (the `IQueryable` part) from the client side projection (the `IEnumerable` part). None of the code above calls the database. The database would be called when enumerating find, for example by executing:
but of course that requires enough memory to hold the entire result in memory at once. You could also iterate through the results using:
It's a little harder to incorporate client side projection with async processing. You would have to factor out the client side processing to be separate from fetching the database results. It would look something like the following:
| |||||||||||||||||||||||
| Comment by GDar N/A [ 17/Mar/23 ] | |||||||||||||||||||||||
|
Thank you for the clarification. So, as this change is intentional, can we get the ability to do a client-side projection with minimal overhead? For now, all the ways to do it is to, either use a non-async IEnumerable .ToList which will block the thread, or use ToListAsync which will allocate extra memory. Of course, there is a way to traverse a Cursor (which strangely doesn't implement the IAsyncEnumerable interface, but it's another topic.) and accumulate projected documents in your list, but it's still a step back in terms of optimizations, because when it's done inside the driver, it has a better context of the data, like it's total count, etc to do it in the best way possible. | |||||||||||||||||||||||
| Comment by Robert Stam [ 27/Feb/23 ] | |||||||||||||||||||||||
|
Thank you for reporting this issue. As explained in There are several techniques for a temporary data object to contain the data being retrieved from the server: BsonDocument, anonymous class, explicit C# class, Tuple and ValueTupe would be the most common. BsonDocument, anonymous classes and explicit C# classes work right now. Tuple works already if you call the constructor instead of the `Tuple.Create` method. I've created ValueTuple will be supported either calling the constructor or calling `ValueTuple.Create` once I'm writing test cases to demonstrate how all the above approaches would work. Here's the first one that you could use right now without waiting for a new release:
Here's a similar example using Tuple (calling the constructor instead of the Create method):
|