aspnet / DependencyInjection

[Archived] Contains common DI abstractions that ASP.NET Core and Entity Framework Core use. Project moved to https://github.com/aspnet/Extensions
Apache License 2.0
873 stars 318 forks source link

Documentation of use #296

Closed angelsix closed 9 years ago

angelsix commented 9 years ago

In my new project I'm using all the dotnet 5 core stuff, and want to use this dependecy injection instead of NInject. However I can find no documetation at all and the Mvc extension helpers don't really provide me much details of whats going on.

As far as I know the IServiceCollection is the IoC Container.

However:

  1. In ASP.Net it just gets injected itself into the Configure calls. Where do I get the same IServiceCollection in my class libraries?
  2. What is the equivelant of Kernel.Bind for binding an interface to a:
    • Type
    • Singleton instance
    • Declared instance
  3. How do you handle scopes in general such as Request, Transient, Thread and custom scopes.

Right now when the app starts using NInject I would of done: Kernel.Bind().ToSelf().InSingletonScope();

The Kernel is just a = new StandardKernel() and is a static reference throughout the entire app.

Then i just use Kernel.Get to retrieve them anwhere.

How do I do the equivalent with IServiceCollection?

khellang commented 9 years ago

Related to #294 re. static reference.

davidfowl commented 9 years ago

This post is old but is a pretty good start: http://blogs.msdn.com/b/webdev/archive/2014/06/17/dependency-injection-in-asp-net-vnext.aspx

khellang commented 9 years ago

As far as I know the IServiceCollection is the IoC Container.

This is not really the case. You register stuff using the IServiceCollection and at some point, the collection is used to create an IServiceProvider. The IServiceProvider is the "container" and is what you use to resolve instances.

  1. What is the equivelant of Kernel.Bind for binding an interface to a:
    • Type
    • Singleton instance
    • Declared instance

Have you looked at the (extension) members of the IServiceCollection interface?

There's a bunch of overloads for

The main difference from Ninject is that you don't register services using an explicit scope. Instead, you just register the service as "scoped" and let the consumer control the scope. This allows you do do nesting of scopes:

// container is an IServiceProvider instance
var scopeFactory = container.GetService<IServiceScopeFactory>();

using (var scope = scopeFactory.CreateScope())
{
    var scopedService = scope.GetService<IScopedService>();

    using (var innerScope = scopeFactory.CreateScope())
    {
        // Is not same instance as scopedService
        var innerScopedService = innerScope.GetService<IScopedService>();
    }

    // Same instance as scopedService
    var anotherScopedService = scope.GetService<IScopedService>();
}

When hosting a web application using Microsoft.AspNet.Hosting, it will automatically take care of creating a "request scope" for you.

Right now when the app starts using NInject I would of done: Kernel.Bind().ToSelf().InSingletonScope();

The Kernel is just a = new StandardKernel() and is a static reference throughout the entire app.

Then i just use Kernel.Get to retrieve them anwhere.

How do I do the equivalent with IServiceCollection?

As far as I know, the IServiceProvider is automatically registered, so you don't need the first step. See #294 for examples on how to access the provider in a static context. You should also be able to inject IServiceProvider if you want to do some dynamic resolution, i.e. provider.GetService(type), otherwise I'd recommend you use regular constructor injection.

angelsix commented 9 years ago

Great thanks that should be all I need to use it. Great explanation.

The one issue I get now is I cannot add a reference to Microsoft.Framework.DependecyInjection. In the web project its there because Microsoft.AspNet.Mvc depends on it so its pulled that way, but in my class libraries adding this fails to find anything in the drop-down of types or explicitly specifying the version.

{
  "version": "1.0.0-*",
  "description": "Class Library",
  "tags": [ "" ],
  "projectUrl": "",
  "licenseUrl": "",

  "dependencies": {
    "Microsoft.Framework.DependencyInjection": "1.0.0-beta7"
  },

  "frameworks": {
    "dnxcore50": {
      "dependencies": {
        "Microsoft.CSharp": "4.0.1-beta-23225",
        "System.Collections": "4.0.11-beta-23225",
        "System.Linq": "4.0.1-beta-23225",
        "System.Runtime": "4.0.21-beta-23225",
        "System.Threading": "4.0.11-beta-23225"
      }
    }
  }
}
davidfowl commented 9 years ago

What happens when you do the above?

angelsix commented 9 years ago

Seems like a bug in VS. I have the beta 7 tools installed but the VS Restore packages still seems flakely. I run dnu restore from command and it worked fine.

angelsix commented 9 years ago

Ok I need to "bind" stuff in the class library to the IServiceProvider. It seems the IServiceCollection is what you need to bind... whats the best way to bind stuff to the IoCContainer from within a class library.

davidfowl commented 9 years ago

I don't understand the question.

angelsix commented 9 years ago

So in the class library which now has access to the IServiceProvider (by passing it in from the Mvc Configure method - app.ApplicationServices), I can get services anywhere in the application.

However, in that same class library I would like to add items to the IoCContainer. Say I have a class MyClass that I want in the IoCContainer with interface IMyClass.

From Mvc thats fine, inside Configure I could do app.UseServices and add it there using the IServiceCollection. But how do I do that when I dont have access to the IServiceCollection in the class library?

All of the..

There's a bunch of overloads for

AddTransient - Registers a service that will resolve a new instance every time (same as Ninject)
AddScoped - Registers a "scoped" service that will resolve the same instance within the same scope
AddSingleton - Registers a singleton service that will resolve the same instance every time (same as Ninject)
AddInstance - Registers an existing instance as a singleton (same as Ninject)

Are in IServiceCollection in Mvc, not in the IServiceProvider that the class library has access too.

davidfowl commented 9 years ago

There are no statics if that's what you're getting at. Just pass the state that you need around at the appropriate times.

angelsix commented 9 years ago

I mean to "Bind" like NInject would of, so ServiceProvider.Get actually finds the thing you put into the IoCContainer. I cannot bind anything anywhere in the application except in Mvc's Configure method, so the class libraries cannot add their own things to the container.

I could just pass the IServiceCollection into the class libraries to bind when called by Mvc, but as I havent used this DI before and not sure of its inner workings I'm wondreing if thats correct.

It is pretty much the most basic requirement I would of thought of DI. I want to bind something in the application so any other part of the application can retrieve it. That binding should not be limited to the Mvc applications Configure method surely.

davidfowl commented 9 years ago

@angelsix when running in ASP.NET 5 you can't change services after the container is built. It's readonly. You can create as many containers as you like on your own. If you to participate in configuring the application container then you need to do all of it from ConfigureServices.

angelsix commented 9 years ago

Ok in that case that makes sense why it is restricted.

So I have 2 options -

  1. pass in the collection inside ConfigureServices to the class libraries to do their binding then, which would work.

Or 2. Create my own service collection and use that for dependecies. I dont think I'll do this but for future reference would that be a simple case of creating a new ServiceCollection, and then to get the provider calling collection.BuildServiceProvider?

davidfowl commented 9 years ago

pass in the collection inside ConfigureServices to the class libraries to do their binding then, which would work.

Yes, that's what you should do.

Or 2. Create my own service collection and use that for dependecies. I dont think I'll do this but for future reference would that be a simple case of creating a new ServiceCollection, and then to get the provider calling collection.BuildServiceProvider?

Yes, but who would use your service collection?

angelsix commented 9 years ago

For 2, I havent put a lot of thought into it but I was thinking a static class that lives in the core class library, that all the rest of the app uses to bind to and to get services from. So the service collection would live in there, and be used to "bind", and when services are to be retrieved the provider is used.

I wont be using that anyway but I just wanted to know for better understanding.

davidfowl commented 9 years ago

The ASP.NET 5 application is handed a starting service collection to append its services to, if you want the class library to have its own service collection it would be separate from what the web application sees.