GithubHelp home page GithubHelp logo

Comments (4)

robertmclaws avatar robertmclaws commented on July 30, 2024 1

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.

habbes avatar habbes commented on July 30, 2024

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.

robertmclaws avatar robertmclaws commented on July 30, 2024

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.

habbes avatar habbes commented on July 30, 2024

@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, a JsonWriter 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)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo 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.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.