-
Notifications
You must be signed in to change notification settings - Fork 135
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Model building documentation does not show correct DI registration #435
Comments
@advancedrei sure, I can assign the issue to you. And one comments on the model builder, both example of customized model builder are correct depending on the needs. Consider several examples,
I just invite you as project member, after you accept, then I can assign issue to you. |
@chinadragon0515 Just so you know, the issue is not related to model builders at all. The issue is related to the DI framework in general. If you call If you call Consider that the existing docs look like my first sample. When I pasted that into my API (and changed the types) it didn't work. When I looked at the TripPin sample and did it that way, it worked. |
@advancedrei I think @chinadragon0515 's point is that for certain services it's necessary to register a custom instance before calling base.ConfigureApi. Of course it's not true for every service, and of course for certain services (those overridden in base.ConfigureApi) it's totally wrong. |
Oh, I gotcha. Sorry, I misread your explanation, @chinadragon0515 :). In all the examples I've ever seen DI used in (and in all the ones I use myself), any service registrations across all custom components get called at the very beginning of application spin-up (often in a DependencyConfig.cs that gets called before WebApiConfig.cs). In this case, overriding ConfigureApi in each class might not be the best option, because it's going to force the developer to understand your order of operations, and that shouldn't be necessary. It might be better to direct users to add all of their RESTier customizations before calling If you'd like, I can put together a sample of what this would look like. :) |
@advancedrei sure, we'd like to see your sample.
Consider these requirements and also smooth for user move from basic usage (no his API class at all) to some customized (add just after base call) to most advanced customization (control all the services his wants to register), we have the design we have now. If there is a better design, of course, we want to adopt and we welcome and appreciate contributions. |
@advancedrei do you have chance to work on this issue or you want us to take over this issue? thanks |
I have not had a chance to look at it yet, I've been bogged down with troubleshooting why installing the .NET Core RTM on my machine breaks RESTier (whether the assemblies are installed in your app, or not). As soon as I can figure that out, I'll get back to this. In the meantime, I have been diving into it more, and calling Thanks for reminding me, I'll get to it ASAP. :) |
@advancedrei OK, I see, thanks. |
I've taken some time to create a self-contained sample application using RESTier 0.6. You can find it here: https://github.com/robertmclaws/RESTier-DI-Sample. In the comments across the various files are my comments on why certain parts are incorrect, and how to fix them. If you have any questions, please let me know, and I'm happy to explain. Thanks! |
@robertmclaws I take a quick look at the sample code, I see several comments that the sample service can not be complied. I double check the sample services, it works well. The difference is sample services refer to RESTier-1.0.0-beta version, it has some changes on the Api part. Also RESTier 1.0.0 is based on Odata .net lib 7.x and Web Api OData 6.x, both of them has adopted DI. Before ConfigureApi method is not static method, and in ApiBase, when we get ApiContext, it will set Api instance for DI. Also it depends on ODL 6.x and WAO 5.x, none of them has DI supported. From my view, the logic here is updated, and it is more clean and simple now. ConfigureApi is static method now, and this allows us does not need any Api instance before DI container is built, it also makes Api class as a completely DI service which means user can make any service referred by Api as DI service. Can you help to update the sample you give to me and let us know whether these changes ease your concerns? |
Why are the currently-shipping samples, that you are referring to in your documentation to cover for the user's inability to understand the docs, linking to code that is not shipping yet? Why isn't 1.0 work for the samples being done on another branch? Come on guys, this is basic stuff. Regardless, I copied the code from https://github.com/OData/ODataSamples/blob/master/RESTier/SpatialSample/Spatial/Api/SpatialApi.cs for the |
1.0.0-beta was released on Sep. 5, refer to nuget to download https://www.nuget.org/packages/Microsoft.Restier/1.0.0-beta And for sample services, it is always upgraded to newest version to help user migrate to newest status. For the DI part, I see in your code, you expect end user to create a container, add service, then pass the resolver to the service. We will discuss this, but from my view,
|
My apologies on the 1.0 code, it was not available on NuGet when I built the sample. Just to be clear, I do not expect them to manage the container. The Autofac code is in there to show you what people using existing DI techniques already have to do, and how they expect to use it. I showed code in the sample of how to do it with the Microsoft DI framework (which also allows you to plug in your own container framework if you desire). Check Startup.cs. Static method overrides are not dependency injection. You are inventing something else entirely. If that is the direction you're going to go with, then DI is totally not necessary, and you should not be introducing that level of complexity. I point all of this out because you're not even using the DI container the way ASP.NET Core uses it... So you are going to cause all kinds of confusion moving forward. I'm just trying to save you from the headaches now. |
This works because many Restier services (including IModelBuilder) is chained. They're chained so that consuming side could optionally override or partially override those services. As you can see in EF ModelProducer class there's InnerModelBuilder, which will be YOURMODELBUILDER in this case. |
This is not how you are supposed to use Dependency Injection. I understand that the code functions. The way you are doing it is counter to the way everyone else does it. I don't know how else to explain it to you guys, and you guys can explain it all you want... It's still wrong. You register services at the very beginning... Not inside the class where you are doing the work. |
Months ago I proposed a design, I think maybe that's what you want, in that design service registration is (at least could possibly be) outside of API classes. That design if rejected, I think the main reason is it's easier to understand if everything is put together, especially for those who aren't very familiar with DI. |
I know RESTier is supposed to be simple, but in trying to make the API easy to implement, you're creating an anti-pattern that caters to the lowest common denominator. In trying to reduce friction for DI newbies, you're increasing friction for people that know what they're doing. You should not be teaching people who don't know DI how to use it improperly. It's much better to do it right and have people grow; supporting that growth with clean, simple instructions on how to set it up. Registering a custom ModelBuilder, for example, should be as simple as it is here: https://github.com/robertmclaws/RESTier-DI-Sample/blob/master/RESTier%20DI%20Sample/Startup.cs. Just add your own ModelBuilder instance before you tell RESTier to register the rest of its services. The What's happening now is that you're taking Constructor Injection and implementing it the way someone who doesn't understand DI would use it. It's only going to confuse people. I promise the way I'm suggesting is far simpler, and it also happens to be architecturally correct. But you don't have to take just my word for it. I think you should take the design to the ASP.NET Core team and have them review it. Because I think they will agree with me that the design should be in keeping with what the rest of Microsoft is doing with DI, especially if 1.1 will need to have ASP.NET Core support. |
thanks for all great comments. For restier, we want user to create service in a simplest way, minimum code effort. In order to use DI,
As ASP .Net does not have method like ConfigureService, also there is no default container, we can not do in the way you suggest. When we support ASP. Net core, we will consider the way you suggest. If you suggest something like DependencyConfig in your sample, from my view, it is too complex and we also request user provides the container, need to work out how to get service from user's container, how to manage the service's scope and so on. If you have any suggestion for non ASP .NET core implement (which current RESTier based on), help to let us know, thanks. |
There are so many flaws with this logic that I don't even know where to start. Just because you're not on ASP.NET Core yet doesn't mean you code yourself into a corner. You took a dependency on the DI framework that ASP.NET Core uses (which you probably should not have done yet), and you're not using it properly. Seriously, I don't understand why you guys keep explaining to me how DI works. You're ALREADY using DI in RESTier. I'm talking about how to register things WITH YOUR OWN STUFF, not tying into another DI container. I simply showed the Autofac stuff to show you how DI registration is supposed to work, using an outside framework example. Look, It's already annoying enough that, if I'm already using a DI container in my app, that you're making me use two different DI containers. But, I imagine you didn't think about that use case either. I feel like you're too deep into the architecture to get how someone who is just getting started with your platform would use it, and something is getting lost in translation when I try to explain it. I updated the sample to show you how it should work, with no other DI containers involved except the one you are already using. Look at Startup.cs. Then check the UnusedSampleController to see my comments on all the code that is now unnecessary, if you do it right. |
Do you have time to have a call? We can discuss in the call to understand each other better, then see what we can improve. |
I really have to agree that this is an absolutely disastrous implementation of dependency injection. Issues: 1: Cannot replace IContainerBuilderFactory, must reimplement MapRestierRoute(...), and many of the dependencies of this call require internal class access, so those also have to be reimplemented.
|
@robertmclaws Do you plan to address this "dependency injection" madness? |
Let me chime in here. I tried replacing the DI container with Autofac, and eventually succeeded, but it took me a week and a lot of reflection to call inner api's and work around quirks. This must be the worst implementation of DI in a library (which is debatable in the first place, libraries should not require DI, let alone a specific container, just be DI friendly). I realize that some of the issues raised are actually in the OData libraries itself, not in Restier, but I thought splitting this in a few comments across all repositories does not make it any clearer. Some observations, not necessarily a comprehensive list, just to illustrate my point:
And this is just off the top of my head, I've probably forgotten a few problems I encountered last week. Long story short: I will revert the code that I've written. Nice that I was able to pull it off, but this will be a future source of problems and maintenance hell that I cannot pass on to other members of my team. Now that the rant's over, I'm more than willing to invest some time to sort some of these issues out and creating a few PRs that will help moving forward, but only if any of it is truly appreciated. I've been reading issues 2 to 3 years old with signals that Restier will be picked up by the Odata team members and will get the love it so desperately needs. It would be a waste of my time when next week a PR will be merged that addresses all these issues, updates Restier to the latest Odata and DI, and supports .NET and EF core. @mikepizzo sorry for the rant, but is there any interest in some progress? |
@jspuij I forked it only to fix a couple issues that were preventing my code from working. Honestly, I have so many branches in my fork to try to submit PRs that it's been hard to keep track. My hope is that @robward-ms and team will be able to get to Restier in a few weeks, and hopefully we can iron out this, as well as A NUMBER of other architectural issues with the platform. I'm now using Restier extensively at a US state government org that I contract for, and I'm personally invested in getting the thing to 1.0 quality. HTH! |
@robertmclaws Thanks for the response and your efforts so far. We only started using Restier recently, I suspected from the issues that it wasn't the only thing we'd encounter. The thing I'm working on is a large project as well though, and writing a few hundred OData controllers ourselves or generating them does not seem like an attractive option either. I'll see what I can do. |
So, there are a couple things that will make your life easier.
HTH! |
- Reduce the number of allocations in the app by making the DefaultSubmitHandler instance-based and completing service lookups in the constructor rather than on every request. An initial pass at tackling the architectural issues with #628. Also starts to address the DI architectural concerns in #435 via #629, although those are a long way off from solving properly.
Willing to help. There is a serious amount of DI madness in OData already though. What's the goal? To at least not make it any worse or to completely remove the dependency on Microsoft.Extensions.DependencyInjection and to have every class correctly announce it's dependencies, i.e. no Service Locator Anitpattern. To do it right, Odata.net needs to be fixed as well. |
- Reduce the number of allocations in the app by making the DefaultSubmitHandler instance-based and completing service lookups in the constructor rather than on every request. An initial pass at tackling the architectural issues with #628. Also starts to address the DI architectural concerns in #435 via #629, although those are a long way off from solving properly.
In several places throughout the documentation, there are examples of how to use
protected override IServiceCollection ConfigureApi()
to add model services. Each one of those examples shows the wrong order for registration. The docs typically show this:However, that will not register the service in the stack. If you try to debug a service registered this way, your breakpoints will not be hit. But the actual order is this:
If you could please assign this issue to me, I will make sure a complete explanation of how and why the DI functions this way, along with corrected examples, are added to the new docs.
This is related to Issue #432.
The text was updated successfully, but these errors were encountered: