Open HuntJason opened 1 year ago
Using the Convention Model (ODataQueryOptions) as opposed to the Attribute Model ([EnableQueryAttribute]) does not route the command to the controller.
In my opinion, it is preferable to use the Convention Model as the QueryOptions can be applied to an Entity Framework query; returning fewer records between the database and the controller before returning it to the calling client as opposed to returning ALL records from the database and then filtering them in the service prior to returning them to the client.
Steps to replicate: When running the ODataRoutingSample and opening the Swagger UI, execute the api/Accounts endpoint. Putting a breakpoint within the controller method does not get hit.
In my opinion, it is preferable to use the Convention Model as the QueryOptions can be applied to an Entity Framework query; returning fewer records between the database and the controller before returning it to the calling client as opposed to returning ALL records from the database and then filtering them in the service prior to returning them to the client.
I think you are confused there. There is zero difference between conventional and attribute routes when it comes to how OData operates: you can get the exact same feature-set no matter how your routes are being discovered.
Can you please elaborate on what you mean exactly that lead you to believe the QueryOptions
wouldn't be able to be applied at the database level if not using conventional model?
Or maybe you are talking about EDM-less flow vs EDM flow? Even then however, you can still apply the query options to the database regardless of approach, so if you have an example you wanna share that would be great.
Steps to replicate: When running the ODataRoutingSample and opening the Swagger UI, execute the api/Accounts endpoint. Putting a breakpoint within the controller method does not get hit.
Would be nice if you could update the issue with all the necessary information.
Using the Convention Model (ODataQueryOptions) as opposed to the Attribute Model ([EnableQueryAttribute]) does not route the command to the controller.
Just to make sure: are you using ODataQueryOptions
, or ODataQueryOptions<T>
? The terminology you are using "convention model" vs "attribute model" is very misleading, as it immediately reminds people of conventional routing vs attribute routing, which is completely different.
Personally, I am using ODataQueryOptions
Personally, I am using ODataQueryOptions, the same as in the referenced AccountsController from the ODataRoutingSample.
The sample is using ODataQueryOptions<Account>
:
Can you share your controller code along with the request you are trying to make?
In my opinion, it is preferable to use the Convention Model as the QueryOptions can be applied to an Entity Framework query; returning fewer records between the database and the controller before returning it to the calling client as opposed to returning ALL records from the database and then filtering them in the service prior to returning them to the client.
I think you are confused there. There is zero difference between conventional and attribute routes when it comes to how OData operates: you can get the exact same feature-set no matter how your routes are being discovered.
Can you please elaborate on what you mean exactly that lead you to believe the
QueryOptions
wouldn't be able to be applied at the database level if not using conventional model?Or maybe you are talking about EDM-less flow vs EDM flow? Even then however, you can still apply the query options to the database regardless of approach, so if you have an example you wanna share that would be great.
Steps to replicate: When running the ODataRoutingSample and opening the Swagger UI, execute the api/Accounts endpoint. Putting a breakpoint within the controller method does not get hit.
Would be nice if you could update the issue with all the necessary information.
I have never seen a sample using [EnableQuery] where the queryoptions can be applied to the databasecontext. I have only ever seen using ODataQueryOptions.
That is exactly the sample. If you navigate to the swaggerui and try and to execute the method, it will not work.
There is no reason for you t
Two points: 1) You can use NSwagStudio to generate the client code for your API from the swagger endpoint, when you have that long "garbage URL", you cannot make the call properly, thus you end up having to hand-write every call to your API? This certainly not optimal if you are trying to write a client package for consuming client applications to use but, as noted, may be the only option (though it worked before... so....).
2) Regarding using the [EnableQuery] attribute and applying to the data context, how do you get access to the query options to then pass to your CQRS command handler? I have never seen a sample that shows how to address this using EnableQuery. That being said, it could be either my naivity or my misunderstanding of what's going on.
It has been my experience that the query on the database that, regardless if you are using CQRS or not, the query, as viewed in SQL Profiler, using EnableQuery ends up being "Select ", returning all records. The memory consumption of the service host thread goes up as all the records are brought back from the database, then the CPU usage goes up as the generated LINQ query is applied to the returned results. When using the ODataQueryOptions
Again, this might be me misunderstanding but it has been my experience.
Two points:
- You can use NSwagStudio to generate the client code for your API from the swagger endpoint, when you have that long "garbage URL", you cannot make the call properly, thus you end up having to hand-write every call to your API? This certainly not optimal if you are trying to write a client package for consuming client applications to use but, as noted, may be the only option (though it worked before... so....).
To be clear, I'm in no way saying that what is happening there is correct or good. It is bad that OData doesn't properly integrate with Swashbuckle/OpenAPI and this is a fairly well-known gap at this point.
Now, one thing I'll say about OData, is that you should prefer using OData Connected Service to create a proxy client which relies on OData metadata, instead of relying on the OpenAPI document. You can find the official extension here:
It uses the standard "Connected Services" flow from Visual Studio to automatically generate a client-side proxy class that uses DataServiceContext
class which you can query against using LINQ.
- Regarding using the [EnableQuery] attribute and applying to the data context, how do you get access to the query options to then pass to your CQRS command handler? I have never seen a sample that shows how to address this using EnableQuery. That being said, it could be either my naivity or my misunderstanding of what's going on.
As I said, it you do need to explicitly pass the values around into your own abstractions, then yes: using an explicit ODataQueryOptions
object is the way to go. I was just saying that, usually, it is simpler to just return an IQueryable
from your abstraction and let that be used by [EnableQuery]
automatically. As long as you are not materializing the results upfront, it will still apply all the query options at the database level.
It has been my experience that the query on the database that, regardless if you are using CQRS or not, the query, as viewed in SQL Profiler, using EnableQuery ends up being "Select *", returning all records. The memory consumption of the service host thread goes up as all the records are brought back from the database, then the CPU usage goes up as the generated LINQ query is applied to the returned results.
That would be incorrect. [EnableQuery]
will only work in-memory if you materialize the results before returning from the controller (say, you call .ToListAsync
yourself, or return .AsEnumerable
, etc). As long as you do not materialize the results, and expose them using the IQueryable
interface, all query options will be directly translated to SQL and you will not get any memory or CPU utilization spikes due to in-memory processing.
When using the ODataQueryOptions, the parameters are applied directly to the datacontext (queryOptions.ApplyTo()), resulting in a "SELECT * WHERE" query where any filter options are then applied directly to the query.
[EnableQuery]
also performs the same ApplyTo
call when it detects an IQueryable
return type, meaning the end-result is identical in that case.
Again, it might be me not knowing how to use the tool, but the tooling you recommended (ODataConnectedService), appears not to work properly in Visual Studio 2022. No client code is generated, even when it correctly accepts the https://localhost:5001/odata/$metadata endpoint. Does the same "gap" exist between the OData project and the ODataConnectedService project as between the OData project and the Swagger/OpenAPI project?
Again, it might be me not knowing how to use the tool, but the tooling you recommended (ODataConnectedService), appears not to work properly in Visual Studio 2022. No client code is generated, even when it correctly accepts the https://localhost:5001/odata/$metadata endpoint.
I've used it in our project here for a real-world scenario in Visual Studio 2022 so I know it works (the extension was updated to work with VS2022 recently). You might want to send a bug report in the ODataConnectedService repo if you are experiencing problems.
Here is the repo for the tool:
Does the same "gap" exist between the OData project and the ODataConnectedService project as between the OData project and the Swagger/OpenAPI project?
Apologies but I don't quite get what you are asking here. The connected service project is handled by the same team that handles this library IIRC. I was not aware of any gaps with it until you mentioned the problem above.
Assemblies affected Which assemblies and versions are known to be affected e.g. ASP.NET Core OData 8.x
Describe the bug A clear and concise description of what the bug is.
Reproduce steps The simplest set of steps to reproduce the issue. If possible, reference a commit that demonstrates the issue.
Data Model Please share your Data model, for example, your C# class.
EDM (CSDL) Model Please share your Edm model, for example, CSDL file. You can send
$metadata
to get a CSDL XML content.Request/Response Please share your request Uri, head or the request body Please share your response head, body.
Expected behavior A clear and concise description of what you expected to happen.
Screenshots If applicable, add screenshots to help explain your problem.
Additional context Please share your call stack or any error message Add any other context about the problem here.