Comments (4)
This is just one of MANY situations where we need to totally re-architect how DI is working.
I don't want this to come across mean when I say it, but previous OData teams basically invented their own way to do DI and it's permeated the codebase. Don't even get me started on Restier's "InnerService" nested services design. That was a nightmare to undo.
I will point out that passing in service containers to constructors is a MAJOR DI anti-pattern.
What should be happening is that these objects should be declaring what individual services they need in the constructor, and DI should be automagically making them available to the class. If more than one instance of an injected service is needed, you can pass in an enumerable of that type and get the right one from there.
We REALLY need to get away from passing around IServiceProvider + resolving services from there and let the runtime (like ASP.NET Core) do the dirty work of creating and injecting scoped services.
I would highly recommend everyone reading this article: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-8.0
from odata.net.
I think passing individual services is definitely much better than passing the service container. But passing the to the constructor seems more intuitive than attaching it to some obscure interface that needs to be attached to some specific, impossible-to-predict, class.
from odata.net.
I understand that mentality, but the end result is not any different and they are both equally bad. It's still not immediately and clearly apparent what other services the class needs to function. You are just swapping one obfuscation for another.
from odata.net.
@robertmclaws I would argue that there are not equally bad, but one slightly worse than the other by virtue of adding another layer of obfuscation that's less intuitive.
However, to your point about fixing the overall problem instead of fixing the margins, I would like to consider more concretely what that would look like, say for ODataMessageWriter
for example. We should also consider making things intuitive for people using OData.NET directly, outside of ASP.NET Core OData.
From a cursory look at the ODataMessageWriter
, here are the services that it currently gets from DI
ODataMessageWriterSettings
(this is also passed as a constructor argument, the DI container is only used a fallback)- The
IEdmModel
(this is also passed to the constructor, DI container only used as fallback and when the DI fallback is not available,EdmCoreModel.Instance
is used) ODataMediaTypeResolver
(when DI not provided, a default static instance is used)IJsonWriterFactory
(when DI not provided, aJsonWriter
instance is constructed)
It also uses an IODataPayloadUriConverter
, it expects the request message to implement the interface. I think this should be also be passed down as a standalone service.
This list is not exhaustive, I might have missed some, especially since the ODataMessageInfo.Container
is passed down to other components.
Let's say we refactor the constructor to something like the following:
ODataMessageWriter(IODataRequestMessage, ODataMessageWriterSettings, IEdmModel, IJsonWriterFactory = null, IODataPayloadUriConverter = null, ODataMediaTypeResolver = null, anyOtherOptionalService = null, ...)
Now I'm wondering whether we should register the writer automatically in the AddDefaultODataServices
extension method we have something like:
// ...
serviceCollection.AddScoped(writerSettings);
serviceCollection.AddScoped<ODataMessageWriter>(sp => {
return new ODataMessageWriter(
sp.GetRequiredService<IODataRequestMessage>(),
sp.GetRequiredService<ODataMessageWriterSettings>(),
sp.GetRequiredService<IEdmModel>(),
....
);
The challenge with this approach is that we use the ODataMessageWriter
can take either IODataRequestMessage
or IODataResponseMessage
depending on whether it's writing a response (server-side) or request (client-side). Maybe we could refactor these interfaces to avoid this distinction. Would keyed services be suitable here? Hmm, not sure. The idea of having of reserving one internal key for requests and another for responses doesn't feel right. Maybe we should refactor the constructor or IODataRequest/ResponseMessage
interface such that this issue shouldn't exist. But there could still be some ODataMessageWriter inputs that don't make sense as DI services?
Another approach would be not to automatically inject ODataMessageWriter
in the service container. But in ASP.NET Core OData library, inside the ODataOutputFormatter
we would fetch the various services from the DI container and construct a new ODataMessageWriter
from those services, without having to pass the DI container itself to ODataMessageWriter
.
from odata.net.
Related Issues (20)
- ODataException is thrown when getting a worksheet range via Microsoft.Graph API HOT 7
- Nav prop on complex type returned from a collection
- Mitigate ArgumentOutOfRangeException when the typename in context URL is not fully qualified HOT 2
- Querying by primary key throws exception if element doesn't exist HOT 1
- EdmLib successfully parses type names with unbalanced parens HOT 1
- Issue with data conversion errors. HOT 5
- Remove `ODataMessageInfo` from the DI container
- ODL 8 breaking changes tracking
- re: CustomUriFunctions should not be static or irreversible
- Filter on nullable DateOnly seems not supported HOT 2
- Set license expression on nuget packages
- ODataException is thrown when getting a worksheet table rows via Microsoft.Graph API HOT 2
- Exception thrown when trying to create EdmTargetPath from string path that contains type casts HOT 1
- WritesJsonElementCorrectly() test fails Intermittently in the release pipeline HOT 2
- Scale facet value has wrong scaling
- Unhandled Exception: System.TypeInitializationException: The type initializer for 'Microsoft.OData.Edm.Vocabularies.V1.CoreVocabularyModel' threw an exception. ---> System.BadImageFormatException: Could not load file or assembly 'System.Reflection, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies. Reference assemblies should not be loaded for execution. They can only be loaded in the Reflection-only loader context. (Exception from HRESULT: 0x80131058) ---> System.BadImageFormatException: Cannot load a reference assembly for execution.
- NullReference exception generated when using FilterClause.ApplyTo method with open properties in the filter query
- Microsoft.OData.Cli 0.3.0 enable-tracking no longer working
- Client - inefficient query generated from projection with only expansion HOT 3
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from odata.net.