{"id":13430755,"url":"https://github.com/seesharper/LightInject","last_synced_at":"2025-03-16T06:31:03.908Z","repository":{"id":1632864,"uuid":"2356614","full_name":"seesharper/LightInject","owner":"seesharper","description":"An ultra lightweight IoC container ","archived":false,"fork":false,"pushed_at":"2024-10-15T21:41:56.000Z","size":12096,"stargazers_count":618,"open_issues_count":99,"forks_count":121,"subscribers_count":37,"default_branch":"master","last_synced_at":"2024-10-17T10:14:04.432Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"http://www.lightinject.net","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/seesharper.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":"contributing.md","funding":".github/FUNDING.yml","license":"license.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":["seesharper"]}},"created_at":"2011-09-09T16:32:59.000Z","updated_at":"2024-10-12T19:01:34.000Z","dependencies_parsed_at":"2023-01-11T16:05:04.675Z","dependency_job_id":"f0faca4c-36b8-4747-b03f-1d8afadda515","html_url":"https://github.com/seesharper/LightInject","commit_stats":{"total_commits":831,"total_committers":35,"mean_commits":"23.742857142857144","dds":0.09626955475330923,"last_synced_commit":"476a36a08403644cb981006afc33ce451a06ad29"},"previous_names":[],"tags_count":57,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seesharper%2FLightInject","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seesharper%2FLightInject/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seesharper%2FLightInject/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seesharper%2FLightInject/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/seesharper","download_url":"https://codeload.github.com/seesharper/LightInject/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243762236,"owners_count":20343976,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-07-31T02:00:57.443Z","updated_at":"2025-03-16T06:31:03.886Z","avatar_url":"https://github.com/seesharper.png","language":"C#","readme":"[![AppVeyor](https://img.shields.io/appveyor/ci/seesharper/lightinject.svg?maxAge=2592000)](https://ci.appveyor.com/project/seesharper/lightinject/branch/master)\n[![NuGet](https://img.shields.io/nuget/v/LightInject.svg?maxAge=2592000)](https://www.nuget.org/packages/LightInject)\n[![GitHub tag](https://img.shields.io/github/tag/seesharper/lightinject.svg?maxAge=2592000)](https://github.com/seesharper/LightInject/releases/latest)\n\n\u003ca href=\"https://www.buymeacoffee.com/Y3bqWk1\" target=\"_blank\"\u003e\u003cimg src=\"https://bmc-cdn.nyc3.digitaloceanspaces.com/BMC-button-images/custom_images/orange_img.png\" alt=\"Buy Me A Coffee\" style=\"height: auto !important;width: auto !important;\" \u003e\u003c/a\u003e\n\n## Installing ##\n\n**LightInject** provides two distribution models via NuGet\n\n### Binary ###\n\n\u003cdiv class=\"nuget-badge\" \u003e\n   \u003cp\u003e\n        \u003ccode\u003ePM\u0026gt; Install-Package LightInject\u003c/code\u003e\n   \u003c/p\u003e\n\u003c/div\u003e\n\nThis adds a reference to the LightInject.dll in the target project.\n\n### Source ###\n\n\u003cdiv class=\"nuget-badge\" \u003e\n   \u003cp\u003e\n        \u003ccode\u003ePM\u0026gt; Install-Package LightInject.Source \u003c/code\u003e\n   \u003c/p\u003e\n\u003c/div\u003e\n\nThis will install a single file (LightInject.cs) into the current project.\n\n### Creating a container ###\n\n```c#\nvar container = new LightInject.ServiceContainer();\n```\n\nThe container implements IDisposable and should be disposed after usage has completed. It can also be used inside of a using statement for a constrained scope.\n\n### Default services ###\n\n```c#\npublic interface IFoo {}\npublic class Foo : IFoo {}\n```\n\n---\n\n```c#\ncontainer.Register\u003cIFoo, Foo\u003e();\nvar instance = container.GetInstance\u003cIFoo\u003e();\nAssert.IsInstanceOfType(instance, typeof(Foo));\n```\n\n### Named services ###\n\n```c#\npublic class Foo : IFoo {}\npublic class AnotherFoo : IFoo {}\n```\n\n---\n\n```c#\ncontainer.Register\u003cIFoo, Foo\u003e();\ncontainer.Register\u003cIFoo, AnotherFoo\u003e(\"AnotherFoo\");\nvar instance = container.GetInstance\u003cIFoo\u003e(\"AnotherFoo\");\nAssert.IsInstanceOfType(instance, typeof(AnotherFoo));\n```\n\nIf only one named registration exists, **LightInject** is capable of resolving this as the default service.\n\n```c#\ncontainer.Register\u003cIFoo, AnotherFoo\u003e(\"AnotherFoo\");\nvar instance = container.GetInstance\u003cIFoo\u003e();\nAssert.IsInstanceOfType(instance, typeof(AnotherFoo));\n```\n\n### Unresolved services ###\n\nLightInject can resolve services that are not registered with the container using the *RegisterFallback* method.\n\n```c#\nvar container = new ServiceContainer();\ncontainer.RegisterFallback((type, s) =\u003e true, request =\u003e new Foo());\nvar foo = container.GetInstance\u003cIFoo\u003e();\n```\n\nThe first argument to the *RegisterFallback* method makes it possible to decide if the service can be \"late-resolved\".\nThe second argument is a *ServiceRequest* instance that provides the requested service type and service name.\n\n### IEnumerable\u0026lt;T\u0026gt; ###\n\nWhen we register multiple services with the same service type, **LightInject** is capable of resolving these services as an  [IEnumerable\u0026lt;T\u0026gt;](http://msdn.microsoft.com/en-us/library/9eekhta0.aspx).\n\n```c#\npublic class Foo : IFoo {}\npublic class AnotherFoo : IFoo {}\n```\n\n---\n\n```c#\ncontainer.Register\u003cIFoo, Foo\u003e();\ncontainer.Register\u003cIFoo, AnotherFoo\u003e(\"AnotherFoo\");\nvar instances = container.GetInstance\u003cIEnumerable\u003cIFoo\u003e\u003e();\nAssert.AreEqual(2, instances.Count());\n```\n\nAlternatively using the **GetAllInstances** method.\n\n```c#\nvar instances = container.GetAllInstances\u003cIFoo\u003e();\nAssert.AreEqual(2, instances.Count());\n```\n\nIn addition, **LightInject** supports the following [IEnumerable\u0026lt;T\u0026gt;](http://msdn.microsoft.com/en-us/library/9eekhta0.aspx) sub-types. \n\n* Array\n* ICollection\u0026lt;T\u0026gt;\n* IList\u0026lt;T\u0026gt;\n* IReadOnlyCollection\u0026lt;T\u0026gt; (Net 4.5 and Windows Runtime);\n* IReadOnlyList\u0026lt;T\u0026gt; (Net 4.5 and Windows Runtime)\n\nBy default, **LightInject** will resolve all services that are compatible with the requested element type.\n\n```c#\ncontainer.Register\u003cFoo\u003e();\ncontainer.Register\u003cDerivedFoo\u003e();\nvar instances = container.GetAllInstances\u003cFoo\u003e();\nAssert.AreEqual(2, instances.Count());\n```\n\nThis behavior can be overridden using the **EnableVariance** container option.\n\n```c#\nvar container = new ServiceContainer(new ContainerOptions { EnableVariance = false });\ncontainer.Register\u003cFoo\u003e();\ncontainer.Register\u003cDerivedFoo\u003e();\nvar instances = container.GetAllInstances\u003cFoo\u003e();\nAssert.AreEqual(1, instances.Count());\n```\n\nWe can also selectively decide to apply variance only for certain `IEnumerable\u003cT\u003e` services.\n\n```C#\noptions.VarianceFilter = (enumerableType) =\u003e enumerableType.GetGenericArguments()[0] == typeof(IFoo);\n```\n\n\n\n#### Ordering\n\nSometimes the ordering of the resolved services are important and **LightInject** solves this by ordering services by their service name.\n\n```c#\ncontainer.Register\u003cIFoo, Foo1\u003e(\"A\");\ncontainer.Register\u003cIFoo, Foo2\u003e(\"B\");\ncontainer.Register\u003cIFoo, Foo3\u003e(\"C\");\n\nvar instances = container.GetAllInstances\u003cIFoo\u003e().ToArray();\nAssert.IsType\u003cFoo1\u003e(instances[0]);\nAssert.IsType\u003cFoo2\u003e(instances[1]);\nAssert.IsType\u003cFoo3\u003e(instances[2]);\n```\n\nWe can also register multiple implementations for a given service type using the `RegisterOrdered` method.\n\n```c#\nvar container = CreateContainer();\ncontainer.RegisterOrdered(\n    typeof(IFoo),\n    new[] {typeof(Foo1), typeof(Foo2), typeof(Foo3)},\n    type =\u003e new PerContainerLifetime());\n\nvar instances = container.GetAllInstances\u003cIFoo\u003e().ToArray();\n\nAssert.IsType\u003cFoo1\u003e(instances[0]);\nAssert.IsType\u003cFoo2\u003e(instances[1]);\nAssert.IsType\u003cFoo3\u003e(instances[2]);\n```\n\nThe `RegisterOrdered` method gives each implementation a service name that can be used for ordering when resolving these services. By default the service name is formatted like `001`, `002` and so on. \nIf we need so change this convention, we can do this by passing a format function to the `RegisterOrdered` method.\n\n```c#\ncontainer.RegisterOrdered(\n    typeof(IFoo\u003c\u003e),\n    new[] { typeof(Foo1\u003c\u003e), typeof(Foo2\u003c\u003e), typeof(Foo3\u003c\u003e) },\n    type =\u003e new PerContainerLifetime(), i =\u003e $\"A{i.ToString().PadLeft(3,'0')}\");\n\nvar services = container.AvailableServices.Where(sr =\u003e sr.ServiceType == typeof(IFoo\u003c\u003e))\n    .OrderBy(sr =\u003e sr.ServiceName).ToArray();\nAssert.Equal(\"A001\", services[0].ServiceName);\nAssert.Equal(\"A002\", services[1].ServiceName);\nAssert.Equal(\"A003\", services[2].ServiceName);\n```\n\n### Values ###\n\nRegisters the value as a constant.\n\n```c#\ncontainer.RegisterInstance\u003cstring\u003e(\"SomeValue\");\nvar value = container.GetInstance\u003cstring\u003e();\nAssert.AreEqual(\"SomeValue\", value);\n```\n\n\n\n### Compilation\n\n**LightInject** uses dynamic code compilation either in the form of System.Reflection.Emit or compiled expression trees. When a service is requested from the container, the code needed for creating the service instance is generated and compiled and a delegate for that code is stored for lookup later on so that we only compile it once. These delegates are stored in an AVL tree that ensures maximal performance when looking up a delegate for a given service type. In fact, looking up these delegates is what sets the top performing containers apart. Most high performance container emits approximately the same code, but the approach to storing these delegates may differ. \n\n**LightInject** provides lock-free service lookup meaning that no locks are involved for getting a service instance after its initial generation and compilation. The only time **LightInject** actually creates a lock is when generating the code for a given service.  That does however mean a potential lock contention problem when many concurrent requests asks for services for the first time.\n\n**LightInject** deals with this potential problem by providing an API for compilation typically used when an application starts.\n\nThe following example shows how to compile all registered services.\n\n```c#\ncontainer.Compile();\n```\n\nOne thing to be aware of is that not all services are backed by its own delegate. \n\nConsider the following service:\n\n```c#\npublic class Foo\n{\n    public Foo(Bar bar)\n    {\n        Bar = bar;\n    }\n} \n```\n\nRegistered and resolved like this:\n\n```c#\ncontainer.Register\u003cFoo\u003e();\ncontainer.Register\u003cBar\u003e();\nvar foo = container.GetInstance\u003cFoo\u003e();\n```\n\nIn this case we only create a delegate for resolving `Foo` since that is the only service that is directly requested from the container. The code for creating the `Bar` instance is embedded inside the code for creating the `Foo` instance and hence there is only one delegate created.\n\nWe call `Foo` a root service since it is directly requested from the container.\n\nIn fact lets just have a look at the IL generated for creating the `Foo` instance. \n\n```assembly\nnewobj Void .ctor() // Bar\nnewobj Void .ctor(LightInject.SampleLibrary.IBar) //Foo\n```\n\nWhat happens here is that a new instance of `Bar` is created and pushed onto the stack and then we create the `Foo` instance. This is the code that the delegate for `Foo` points to. \n\nThe reason for such a relatively detailed explanation is to illustrate that we don't always create a delegate for a given service and by simply doing a `container.Compile()` we might create a lot of delegates that is never actually executed.  Probably no big deal as long as we don't have tens of thousands of services, but just something to be aware of.\n\n**LightInject** does not attempt to identify root services as that would be very difficult for various reasons.\n\nWe can instead use a predicate when compiling services up front.\n\n```C#\ncontainer.Compile(sr =\u003e sr.ServiceType == typeof(Foo));\n```\n\n#### Open Generics\n\n**LightInject** cannot compile open generic services since the actual generic arguments are not known at \"compile\" time. \n\nWe can however specify the generic arguments like this:\n\n```c#\ncontainer.Compile\u003cFoo\u003cint\u003e\u003e()\n```\n\n**LightInject** will create a log entry every time a new delegate is created so that information can be used to identify root services that could be compiled up front. In addition to this, a log entry (warning) is also created when trying to compile an open generic service up front.\n\n\n\n## Lifetime ##\n\nThe default behavior in **LightInject** is to treat all objects as transients unless otherwise specified.\n\n```c#\ncontainer.Register\u003cIFoo,Foo\u003e();\nvar firstInstance = container.GetInstance\u003cIFoo\u003e();\nvar secondInstance = container.GetInstance\u003cIFoo\u003e();\nAssert.AreNotSame(firstInstance, secondInstance);\n```\n\n### PerScopeLifetime ###\n\nEnsures that only one instance of a given service can exists within a scope.\nThe container will call the **Dispose** method on all disposable objects created within the scope.\n\n```c#\ncontainer.Register\u003cIFoo,Foo\u003e(new PerScopeLifetime());\nusing(container.BeginScope())\n{    \n    var firstInstance = container.GetInstance\u003cIFoo\u003e();\n    var secondInstance = container.GetInstance\u003cIFoo\u003e();\n    Assert.AreSame(firstInstance, secondInstance);\n}\n```\n\n**Note:** *An **InvalidOperationException** is thrown if a service registered with the **PerScopeLifetime** is requested outside the scope.*\n\n### PerContainerLifetime ###\n\nEnsures that only one instance of a given service can exist within the container.\nThe container will call the Dispose method on all disposable objects when the container itself is disposed.\n\n```c#\nusing(container = new ServiceContainer())\n{\n    container.Register\u003cIFoo,Foo\u003e(new PerContainerLifetime());    \n    var firstInstance = container.GetInstance\u003cIFoo\u003e();\n    var secondInstance = container.GetInstance\u003cIFoo\u003e();\n    Assert.AreSame(firstInstance, secondInstance);\n}\n```\n\n### PerRequestLifeTime ###\n\nA new instance is created for each request and the container calls **Dispose** when the scope ends.\nThis lifetime is used when the conrete class implements **IDisposable**.\n\n```c#\ncontainer.Register\u003cIFoo,Foo\u003e(new PerRequestLifeTime());\nusing(container.BeginScope())\n{        \n    var firstInstance = container.GetInstance\u003cIFoo\u003e();\n    var secondInstance = container.GetInstance\u003cIFoo\u003e();\n    Assert.AreNotSame(firstInstance, secondInstance);\n}    \n```\n\n\u003e**Note:** *An **InvalidOperationException** is thrown if a service registered with the **PerRequestLifeTime** is requested outside the scope.*\n\n\n### Custom lifetime ###\n\nA custom lifetime is created by implementing the **ILifetime** interface\n\n```c#\ninternal interface ILifetime\n{\n    object GetInstance(Func\u003cobject\u003e instanceFactory, Scope currentScope);        \n}\n```\n\nThe following example shows to create a custom lifetime that ensures only one instance per thread.\n\n```c#\npublic class PerThreadLifetime : ILifetime\n{\n    ThreadLocal\u003cobject\u003e instances = new ThreadLocal\u003cobject\u003e();     \n\n    public object GetInstance(Func\u003cobject\u003e instanceFactory, Scope currentScope)\n    {\n        if (instances.value == null)\n        {\n            instances.value = instanceFactory();\n        }\n        return instances.value;\n    }\n}\n```\n\nThat is all it takes to create a custom lifetime, but what about disposable services?\n\n```c#\npublic class PerThreadLifetime : ILifetime\n{\n    ThreadLocal\u003cobject\u003e instances = new ThreadLocal\u003cobject\u003e();     \n\n    public object GetInstance(Func\u003cobject\u003e instanceFactory, Scope currentScope)\n    {            \n        if (instances.value == null)\n        {                \n            object instance = instanceFactory();                \n            IDisposable disposable = instance as IDisposable;                \n            if (disposable != null)\n            {\n                if (currentScope == null)\n                {\n                    throw new InvalidOperationException(\n                        \"Attempt to create an disposable object without a current scope.\");\n                }\n                currentScope.TrackInstance(disposable);\n            }\n\n            instances.value = instance;\n        }\n        return instance.value;\n    }\n}\n```\n\n#### Important ####\n\nA lifetime object controls the lifetime of a single service and can **never** be shared for multiple service registrations.\n\n**Wrong**\n\n```c#\nILifetime lifetime = new PerContainerLifeTime();\ncontainer.Register\u003cIFoo,Foo\u003e(lifetime);\ncontainer.Register\u003cIBar,Bar\u003e(lifetime);\n```\n\n**Right**\n\n```c#\ncontainer.Register\u003cIFoo,Foo\u003e(new PerContainerLifeTime());\ncontainer.Register\u003cIBar,Bar\u003e(new PerContainerLifeTime());\n```\n\nA lifetime object is also shared across threads and that is something we must take into consideration when developing new lifetime implementations.\n\n### Async and Await ###\n\nBy default scopes are managed per thread which means that when the container looks for the current scope, it will look for a scope that is associated with the current thread.\n\nWith the introduction of the async/await pattern chances are that the code that is requesting a service instance is running on another thread.\n\nTo illustrate this lets consider an example that is going to cause an instance to be resolved on another thread.\n\nWe start of by creating an interface that returns a **Task\u0026lt;IBar\u0026gt;**\n\n```c#\npublic interface IAsyncFoo\n{\n    Task\u003cIBar\u003e GetBar();\n}\n```\n\nNext we implement this interface in such a way that the **IBar** instance is requested on another thread.\n\n```c#\npublic class AsyncFoo : IAsyncFoo\n{\n    private readonly Lazy\u003cIBar\u003e lazyBar;\n\n    public AsyncFoo(Lazy\u003cIBar\u003e lazyBar)\n    {\n        this.lazyBar = lazyBar;\n    }\n\n    public async Task\u003cIBar\u003e GetBar()\n    {\n        await Task.Delay(10);\n        return lazyBar.Value; \u003c--This code is executed on another thread (continuation).\n    }\n}\n```\n\nThe we register the dependency (**IBar**) with the **PerScopeLifetime** that is going to cause the container to ask for the current scope so that the instance can be registered with that scope.\n\n```c#\nvar container = new ServiceContainer();\ncontainer.Register\u003cIBar, Bar\u003e(new PerScopeLifetime());\ncontainer.Register\u003cIAsyncFoo, AsyncFoo\u003e();\n\nusing (container.BeginScope())\n{\n    var instance = container.GetInstance\u003cIAsyncFoo\u003e();\n    ExceptionAssert.Throws\u003cAggregateException\u003e(() =\u003e instance.GetBar().Wait());                \n}\n```\n\nThis will throw an exception that states the following:\n\n    Attempt to create a scoped instance without a current scope.  \n\nThe reason that this is happening is that the current scope is associated with the thread that created it and when the continuation executes, we are essentially requesting an instance on another thread.\n\nTo deal with this issue, **LightInject** now supports scopes across the logical [CallContext](http://msdn.microsoft.com/en-us/library/system.runtime.remoting.messaging.callcontext(v=vs.110).aspx).  \n\n```c#\nvar container = new ServiceContainer();\ncontainer.ScopeManagerProvider = new PerLogicalCallContextScopeManagerProvider();\ncontainer.Register\u003cIBar, Bar\u003e(new PerScopeLifetime());\ncontainer.Register\u003cIAsyncFoo, AsyncFoo\u003e();\n\nusing (container.BeginScope())\n{\n    var instance = container.GetInstance\u003cIAsyncFoo\u003e();\n    var bar = instance.GetBar().Result;\n    Assert.IsInstanceOfType(bar, typeof(IBar));\n}\n```\n\n\u003e Note that the **PerLogicalCallContextScopeManagerProvider** is only available when running under .Net 4.5.\n\u003e For more information, please refer to the following [article](http://blog.stephencleary.com/2013/04/implicit-async-context-asynclocal.html) by Stephen Cleary.\n\n\n\n## Scope \n\nThe purpose of the scope is to track the services created within the scope. For instance, the `PerScopeLifetime` uses the scope to ensure that we only create a single service instance even if it requested multiple times.\n\nOne of the most canonical examples would be in a web application where we need to inject `IDbConnection` into different services. Let say that we have an `OrderController` and we need two services to process the order.\n\n```c#\npublic class CustomerService : ICustomerService\n{\n    public CustomerService(IDbConnection dbConnection)\n    {\n    }\n}\n```\n\n```C#\npublic class OrderService : IOrderService\n{\n    public OrderService(IDbConnection dbConnection)\n    {\n    }\n}  \n```\n\n\n\n```C#\npublic class OrderController\n{\n    public OrderController(ICustomerService customerService, IOrderService orderService)\n    {\n    }\n}\n```\n\nAs we can see the `OrderController` depends on both the `CustomerService` and the `OrderService` which are both dependant upon an `IDbConnection`. \n\nBy registering the `IDbConnection` as a scoped service we ensure two things.\n\n- Only a single instance of `IDbConnection` will ever be created inside a scope. \n- The `IDbConnection` instance is disposed when the scope ends.\n\n```c#\ncontainer.RegisterScoped\u003cIDbConnection\u003e(factory =\u003e new ProviderSpecificConnection());\n```\n\nSo when and how do we start these scopes?\n\nShe short answer is that most of the time, we don't. For instance, in AspNetCore, the scopes are started and ended by the AspNetCore infrastructure so we don't have to think about that when developing web application. A scope is started when the web request starts and it ended when the web request ends. It is really that simple, one request equals one scope.\n\nSo in **LightInject**, we register a scoped service using `RegisterScoped` without really thinking about when and how the scopes are started and ended. In a web application this usually means a web request, but for other applications it can mean something else. Maybe for a UI application it means a page/window/form or something similar. \n\nTo start a scope manually we can create scope using the `BeginScope` method\n\n```c#\nusing (container.BeginScope())\n{\n    var dbConnection = container.GetInstance\u003cIDbConnection\u003e();  \n}        \n```\n\n\u003e Note: The `Scope` implement `IDisposable` and should always be wrapped in a using block to ensure its disposal\n\nIn this example we start a new scope and retrieve the service from the container which means that **LightInject** uses the \"current\" scope to resolve the service. This is only supported for backwards compatibility and should be avoided if possible. The recommended approach is to retrieve services directly from the scope.\n\n```c#\nusing (var scope = container.BeginScope())\n{\n    var dbConnection = scope.GetInstance\u003cIDbConnection\u003e();  \n}    \n```\n\nSince we are retrieving the service directly from the scope, the current scope is ignored and we simply use the scope from which the service was requested. This is not only much faster, but it is also a much safer way to deal with scopes.\n\nThis also allows for multiple active scopes.\n\n```C#\nusing (var outerScope = container.BeginScope())\n{    \n    using (var innerScope = container.BeginScope())\n    {\n        var outerDbConnection = outerScope.GetInstance\u003cIDbConnection\u003e();\n        var innerDbConnection = innerScope.GetInstance\u003cIDbConnection\u003e();\n    }  \n}    \n```\n\nIn addition to the `PerScopeLifetime` which ensures disposal and a single instance within a scope, we also have the `PerRequestLifetime`. This lifetime behaves just a transient meaning that we get a new instance for every time it is requested with the only difference to transients being that instances are disposed when the scope ends.\n\n\u003e Note: The `PerRequestLifetime` has NO relation to the notion of a web request. \n\nIf we don't need access to an ambient scope, we can disable this in the `ContainerOptions`\n\n```csharp\nvar container = new ServiceContainer(o =\u003e o.EnableCurrentScope = false);\n```\n\nThis also improves performance ever so slightly as we don't need to maintain a current scope when scopes are started and ended. \n\n### IAsyncDisposable\n\nLightInject also supports [IAsyncDisposable](https://docs.microsoft.com/en-us/dotnet/api/system.iasyncdisposable) meaning that [IAsyncDisposable.DisposeAsync](https://docs.microsoft.com/en-us/dotnet/api/system.iasyncdisposable.disposeasync) will be called if the scope is started with a using-block adding the await `await` keyword.\n\n```csharp\nawait using (var scope = container.BeginScope())\n{\n    asyncDisposable = container.GetInstance\u003cAsyncDisposable\u003e();\n}\n```\n\nThe `Scope` returned from `BeginScope` also implements `IAsyncDisposable` and will call `DisposeAsync` on all scoped services resolved within the `Scope`. \nServices only implementing `IDisposable` will also be disposed the the async scope ends.\n\nIf on the other hand, a service ONLY implements `IAsyncDisposable` and is resolved within a synchronous scope, an exception will be thrown \n\n```csharp\nusing (var scope = container.BeginScope())\n{\n    asyncDisposable = container.GetInstance\u003cAsyncDisposable\u003e();\n}\n```\n\n## Dependencies ##\n\n\n### Constructor Injection ##\n\n```c#\npublic interface IFoo {}        \npublic interface IBar {}\n\npublic class Foo : IFoo\n{\n    public Foo(IBar bar) \n    {\n        Bar = bar;\n    }\n\n    public IBar Bar { get; private set; } \n}\n\npublic class Bar : IBar {}\n```\n\n#### Implicit service registration ####\n\nRegisters a service without specifying any information about how to resolve the constructor dependencies of the implementing type.\n\n```c#\ncontainer.Register\u003cIFoo, Foo\u003e();\ncontainer.Register\u003cIBar, Bar\u003e();\nvar foo = (Foo)container.GetInstance\u003cIFoo\u003e();\nAssert.IsInstanceOfType(foo.Bar, typeof(Bar)); \n```\n\n\u003e Note: In the case where the implementing type(Foo) has more than one constructor, **LightInject** will choose the constructor with the most parameters. \n\nFor fine grained control of the injected constructor dependencies, we can provide a factory that makes it possible to create an instance of a given constructor dependency.\n\n```c#\ncontainer.RegisterConstructorDependency\u003cIBar\u003e((factory, parameterInfo) =\u003e new Bar());\n```\n\nThis tells the container to inject a new **Bar** instance whenever it sees an **IBar** constructor dependency.\n\n\n#### Explicit service registration ####\n\nRegisters a service by providing explicit information about how to create the service instance and how to resolve the constructor dependencies.\n```c#\ncontainer.Register\u003cIBar, Bar\u003e();\ncontainer.Register\u003cIFoo\u003e(factory =\u003e new Foo(factory.GetInstance\u003cIBar\u003e()));\nvar foo = (Foo)container.GetInstance\u003cIFoo\u003e();\nAssert.IsNotNull(foo.Bar);\n```\n\n#### Parameters ####\n\nParameters are used when we want to supply one or more values when the service is resolved.\n\n```c#\npublic class Foo : IFoo\n{\n    public Foo(int value)\n    {\n        Value = value;\n    }\n\n    public int Value { get; private set; }\n}   \n```\n\n---\n\n```c#\ncontainer.Register\u003cint, IFoo\u003e((factory, arg) =\u003e new Foo(arg));\nvar foo = (Foo)container.GetInstance\u003cint, IFoo\u003e(42);\nAssert.AreEqual(42,foo.Value);\n```\n\nWe can also do a combination of supplied values and dependencies.\n\n```c#\npublic class Foo : IFoo\n{\n    public Foo(int value, IBar bar)\n    {\n        Value = value;\n    }\n\n    public int Value { get; private set; }\n    public IBar Bar { get; private set; }\n}    \n```\n\n---\n\n```c#\ncontainer.Register\u003cIBar, Bar\u003e();\ncontainer.Register\u003cint, IFoo\u003e((factory, value) =\u003e new Foo(value, factory.GetInstance\u003cIBar\u003e()));\nvar foo = (Foo)container.GetInstance\u003cint, IFoo\u003e(42);\nAssert.AreEqual(42, foo.Value);\nAssert.IsNotNull(foo.Bar);\n```\n\n#### Optional arguments\n\nLightInject will allow for default values to be used when a constructor dependency cannot be resolved.\n\n```c#\npublic class Foo\n{\n    public Foo(string value = \"42\")\n    {\n        Value = value;\n    }\n\n    public string Value { get; }\n}\n```\n\nWe can still resolve `Foo` even though we have not registered a `string` service. \n\n```c#\nvar container = new ServiceContainer(options =\u003e options.EnableOptionalArguments = true);\ncontainer.Register\u003cFoo\u003e();\nvar instance = container.GetInstance\u003cFoo\u003e();\nAssert.AreEqual(\"42\", instance.Value)\n```\n\n\u003e Note that the use cases for optional dependencies should be rare and are to be used with caution.\n\n\n\n### Property Injection ###\n\n```c#\npublic interface IFoo {}\n\npublic interface IBar {}\n\npublic class Foo : IFoo\n{\n    public IBar Bar { get; set; }\n}\n\npublic class Bar : IBar {}\n```\n\n#### Implicit service registration ####\n\nRegisters the service without specifying any information about how to resolve the property dependencies.\n\n```c#\ncontainer.Register\u003cIFoo, Foo\u003e();\ncontainer.Register\u003cIBar, Bar\u003e();\nvar foo = (Foo)container.GetInstance\u003cIFoo\u003e();\nAssert.IsNotNull(foo.bar);\n```\n\n\u003e**Note:** ***LightInject** considers all read/write properties a dependency, but implements a loose strategy around property dependencies, meaning that it will **NOT** throw an exception in the case of an unresolved property dependency.*          \n\nFor fine grained control of the injected property dependencies, we can provide a factory that makes it possible to create an instance of a given property dependency.\n\n```c#\ncontainer.RegisterPropertyDependency\u003cIBar\u003e((factory, propertyInfo) =\u003e new Bar());\n```\n\nThis tells the container to inject a new **Bar** instance whenever it sees an **IBar** property dependency.\n\n\n#### Explicit service registration ####\n\nRegisters a service by providing explicit information about how to create the service instance and how to resolve the property dependencies.\n\n\n```c#\ncontainer.Register\u003cIBar, Bar\u003e();\ncontainer.Register\u003cIFoo\u003e(factory =\u003e new Foo() {Bar = factory.GetInstance\u003cIBar\u003e()}) \nvar foo = (Foo)container.GetInstance\u003cIFoo\u003e();\nAssert.IsNotNull(foo.bar);\n```\n\n#### Property injection on existing instances. ####\n\nIn the cases where we don't control the creation of the service instance, **LightInject** can inject property dependencies into an existing instance.\n\n```c#\ncontainer.Register\u003cIBar, Bar\u003e();\nvar foo = new Foo();\ncontainer.InjectProperties(foo);\nAssert.IsNotNull(foo);\n```\n\n#### Disabling ProperyInjection\n\nProperty injection is enabled by default in **LightInject**, but it can be disabled like this.\n\n```c#\nvar container = new ServiceContainer(new ContainerOptions { EnablePropertyInjection = false });\n```\n\n\u003e It is actually recommended to turn off property injection unless it is really needed. Backward compatibility is the only reason that this is not the default.\n\n## Initializers ##\n\nUse the **Initialize** method to perform service instance initialization/post-processing.  \n\n```c#\ncontainer.Register\u003cIFoo, FooWithPropertyDependency\u003e();\ncontainer.Initialize(\n    registration =\u003e registration.ServiceType == typeof(IFoo), \n    (factory, instance) =\u003e ((FooWithPropertyDependency)instance).Bar = new Bar());\nvar foo = (FooWithProperyDependency)container.GetInstance\u003cIFoo\u003e();\nAssert.IsInstanceOfType(foo.Bar, typeof(Bar));\n```\n\n## Assembly Scanning ##\n\nLightInject is capable of registering services by looking at the types of a given assembly.\n\n```c#\ncontainer.RegisterAssembly(typeof(IFoo).Assembly)\n```\n\nTo filter out the services to be registered with the container, we can provide a predicate that makes it possible to inspect the service type and the implementing type.\n\n```c#\ncontainer.RegisterAssembly(typeof(IFoo).Assembly, (serviceType, implementingType) =\u003e serviceType.NameSpace == \"SomeNamespace\");\n```\n\nIt is also possible to scan a set assembly files based on a search pattern.\n\n```c#\ncontainer.RegisterAssembly(\"SomeAssemblyName*.dll\");  \n```\nWhen scanning assemblies, **LightInject** will register services using a service name that by default is the implementing type name. This behavior can be changed by specifying a function delegate to provide the name based on the service type and the implementing type.\n\n```c#\ncontainer.RegisterAssembly(typeof(IFoo).Assembly, () =\u003e new PerContainerLifetime(), (serviceType, implementingType) =\u003e serviceType.NameSpace == \"SomeNamespace\", (serviceType, implementingType) =\u003e \"Provide custom service name here\");\n```\n\nWe can also change this behavior globally for all registrations by implementing the **IServiceNameProvider** interface.\n\n```c#\npublic class CustomServiceNameProvider : IServiceNameProvider\n{\n    public string GetServiceName(Type serviceType, Type implementingType)\n    {\n        return \"Provide custom service name here\";  \n    }\n}\n```\n\nTo change the default behavior for all registrations we simply change this dependency on the container before we start scanning assemblies.\n\n```c#\ncontainer.ServiceNameProvider = new CustomServiceNameProvider();\n```\n\n\n\n## Composition Root ##\n\nWhen **LightInject** scans an assembly it will look for an implementation of the **ICompositionRoot** interface.   \n\n```c#\npublic class SampleCompositionRoot : ICompositionRoot\n{               \n    public void Compose(IServiceRegistry serviceRegistry)\n    {     \n        serviceRegistry.Register(typeof(IFoo),typeof(Foo));\n    }\n}\n```\n\nIf one or more implementations of the **ICompositionRoot** interface is found, they will be created and executed.\n\n\u003e**Note:** *Any other services contained within the target assembly that is not registered in the composition root, will **NOT** be registered.*\n\nRather that having a single composition root that basically needs to reference all other assemblies, having multiple composition roots makes it possible to group services naturally together. Another advantage of registering services in a **ICompositionRoot**, is that they can easily be reused in automated tests.   \n\n### Lazy Composition Roots ###\n\n**LightInject** is capable of registering services on a need to have basis. For a large application that has a lot of services, it might not be the best solution to register all these services up front as this could seriously hurt the startup time of our application due to extensive assembly loading.\n\nIf an unregistered service is requested, **LightInject** will scan the assembly where this service is contained.  \n\n### CompositionRootAttribute ###\n\nWhen an assembly is being scanned, **LightInject** will look for implementations of the **ICompositionRoot** interface. For large assemblies that contains many type, this might be an expensive operation. The **CompositionRootAttribute** is an assembly level attribute that simply helps **LightInject** to locate the compostion root.\n\n```c#\n[assembly: CompositionRootType(typeof(SampleCompositionRoot))]\n```\n\n\n### RegisterFrom ###\n\nAllows explicit execution of a composition root.\n\n```c#\ncontainer.RegisterFrom\u003cSampleCompositionRoot\u003e();\n```\n\nAlternatively we can also pass an existing composition root instance.\n\n```c#\ncontainer.RegisterFrom(new SampleCompositionRoot());\n```\n\n## Generics ##\n\n```c#\npublic interface IFoo\u003cT\u003e {};\npublic class Foo\u003cT\u003e : IFoo\u003cT\u003e {};\n```\n\nThe container creates the closed generic type based on the service request.\n\n```c#\ncontainer.Register(typeof(IFoo\u003c\u003e), typeof(Foo\u003c\u003e));\nvar instance = container.GetInstance(typeof(IFoo\u003cint\u003e));\nAssert.IsInstanceOfType(instance, typeof(Foo\u003cint\u003e));\n```\n\n### Constraints ###\n\n**LightInject** enforces generic constrains  \n\n\n## Lazy\u0026lt;T\u0026gt; ##\n\n**LightInject** can resolve a service as an instance of [Lazy\u0026lt;T\u0026gt;](http://msdn.microsoft.com/en-us/library/dd642331.aspx) when we want to postpone resolving the underlying service until it is needed.\n\n```c#\npublic interface IFoo {}\npublic class Foo : IFoo {}\n```\n\n---\n\n```c#\ncontainer.Register\u003cIFoo, Foo\u003e();\nvar lazyFoo = container.GetInstance\u003cLazy\u003cIFoo\u003e\u003e();\nAssert.IsNotNull(lazyFoo.Value);\n```\n\n## Function Factories ##\n\nFunction factories allows services to resolved as a function delegate that in turn is capable of returning the underlying service instance. We can think of this as an alternative to the [Service Locator](http://en.wikipedia.org/wiki/Service_locator_pattern) (anti)pattern.\n\n```c#\npublic interface IFoo {}\npublic class Foo : IFoo {}\n```\n\n---\n\n```c#\ncontainer.Register\u003cIFoo,Foo\u003e();\nvar func = container.GetInstance\u003cFunc\u003cIFoo\u003e\u003e();\nvar foo = func();\nAssert.IsNotNull(foo); \n```\n\n\u003e**Note:** *A function factory is effectively a delegate that redirects back to the corresponding **GetInstance** method on the service container.*\n\n### Named Factories ###\n\nThe container returns a function delegate that represents calling the **GetInstance** method with \"SomeFoo\" as the service name argument.\n\n```c#\ncontainer.Register\u003cIFoo, Foo\u003e(\"SomeFoo\");\nvar func = container.GetInstance\u003cFunc\u003cIFoo\u003e\u003e(\"SomeFoo\");   \nvar foo = func();\nAssert.IsNotNull(foo);\n```\n\n\n### Parameters ###\n\nFunction factories can also take parameters that will be used create the service instance.\n\n```c#\npublic class Foo : IFoo\n{\n    public Foo(int value)\n    {\n        Value = value;\n    }\n\n    public int Value { get; private set; }\n}\n```\n\n---\n\n```c#\ncontainer.Register\u003cint, IFoo\u003e((factory, value) =\u003e new Foo(value));\nvar fooFactory = container.GetInstance\u003cFunc\u003cint, IFoo\u003e\u003e();\nvar foo = (Foo)fooFactory(42); \nAssert.AreEqual(foo.Value, 42);\n```\n\n\u003e**Note** : *The service must be explicitly registered in order for the container to resolve it as a parameterized function factory.*\n\n### IDisposable ###\n\nThe only way to deal with disposable objects when using function factories, is to let the service type inherit from IDisposable.\n\n```c#\npublic interface IFoo : IDisposable {}\npublic class Foo : IFoo {}\n```\n\n---\n\n```c#\ncontainer.Register\u003cIFoo, Foo\u003e();\nvar fooFactory = container.GetInstance\u003cFunc\u003cIFoo\u003e\u003e();\n\nusing(IFoo foo = fooFactory())\n{\n    \n} \u003c--Instance is disposed here          \n```\n\n\u003e**Note:** *Although this is common practice even in the [BCL](http://en.wikipedia.org/wiki/Base_Class_Library), this kind of interfaces are often referred to as [leaky abstractions](http://en.wikipedia.org/wiki/Leaky_abstraction).*\n\n## Typed Factories  ##\n\nA typed factory is a class that wraps the function factory that is used to create the underlying service instance.\nAs opposed to just function factories, typed factories provides better expressiveness to the consumer of the factory.  \n\n```c#\npublic interface IFooFactory\n{\n    IFoo GetFoo();\n}\n```\n\n---\n\n```c#\npublic class FooFactory : IFooFactory\n{\n    private Func\u003cIFoo\u003e createFoo;\n\n    public FooFactory(Func\u003cIFoo\u003e createFoo)\n    {\n        this.createFoo = createFoo;\n    }\n\n    public IFoo GetFoo()\n    {\n        return createFoo();\n    }\n} \n```\n\n---\n\n```c#\ncontainer.Register\u003cIFoo, Foo\u003e();\ncontainer.Register\u003cIFooFactory, FooFactory\u003e(new PerContainerLifetime());\nvar fooFactory = container.GetInstance\u003cIFooFactory\u003e();\nvar foo = fooFactory.GetFoo();\nAssert.IsNotNull(foo);\n```\n\n\u003e**Note:** *Register typed factories with the **PerContainerLifetime** unless a compelling reason exists to choose a different lifetime.*  \n\n### Parameters ###\n\nTypes factories can also wrap a parameterized function factory and allows us to pass arguments.\n\n```c#\npublic class Foo : IFoo\n{\n    public Foo(int value)\n    {\n        Value = value;\n    }\n\n    public int Value { get; private set; }\n}\n\npublic interface IFooFactory\n{\n    IFoo GetFoo(int value);\n} \n```\n\n---\n\n```c#\npublic class FooFactory : IFooFactory\n{\n    private Func\u003cint, IFoo\u003e createFoo;\n\n    public FooFactory(Func\u003cint, IFoo\u003e createFoo)\n    {\n        this.createFoo = createFoo;\n    }\n\n    public IFoo GetFoo(int value)\n    {\n        return createFoo(value);\n    }\n} \n```\n\n---\n\n```c#\ncontainer.Register\u003cint, IFoo\u003e((factory, value) =\u003e new Foo(value));\ncontainer.Register\u003cIFooFactory, FooFactory\u003e(new PerContainerLifetime());\nvar typedFooFactory = container.GetInstance\u003cIFooFactory\u003e();\nvar foo = typedFooFactory.GetFoo(42);\nAssert.AreEqual(foo.Value, 42);\n```\n\n### IDisposable ###\n\nWorking with typed factories gives us the possibility to release disposable services registered as transients without exposing a leaky abstraction.\n\n```c#\npublic interface IFooFactory\n{\n    IFoo GetFoo(int value);\n    void Release(IFoo foo);\n} \n```\n\n---\n\n```c#\npublic class FooFactory : IFooFactory\n{\n    private Func\u003cIFoo\u003e createFoo;\n\n    public FooFactory(Func\u003cIFoo\u003e createFoo)\n    {\n        this.createFoo = createFoo;\n    }\n\n    public IFoo GetFoo(int value)\n    {\n        return createFoo(value);\n    }\n\n    public void Release(IFoo foo)\n    {\n        var disposable = foo as IDisposable;\n        if (disposable != null)\n        {\n            disposable.Dispose();\n        }\n    }\n}    \n```\n\n## Recursive dependency detection ##\n\nA recursive dependency graph is when a service depends directly or indirectly on itself.\n\n```c#\npublic class FooWithRecursiveDependency : IFoo\n{\n    public FooWithRecursiveDependency(IFoo foo)\n    {\n    }\n}\n```\n\nThe following code will throw an **InvalidOperationException** stating that there are existing recursive dependencies. \n\n```c#\ncontainer.Register(typeof(IFoo), typeof(FooWithRecursiveDependency));\ncontainer.GetInstance\u003cIFoo\u003e()\n```\n\n## Internals ##\n\nWhen running under the .Net platform, **LightInject** is capable of creating instances of classes that has the [internal](http://msdn.microsoft.com/en-us/library/7c5ka91b.aspx) modifier. \n\nThe only requirement is that the internal class exposes a public constructor.\n\n```c#\ninternal class InternalFooWithPublicConstructor : IFoo\n{\n    public InternalFooWithPublicConstructor () {}\n}\n```\n\n## Logging ##\n\nSometimes it might be useful to obtain information about what is going on inside the container \nand **LightInject** provides a very simple log abstraction that is used to log information and warnings from within the container.\n\n```csharp\nvar containerOptions = new ContainerOptions();\ncontainerOptions.LogFactory = (type) =\u003e logEntry =\u003e Console.WriteLine(logEntry.Message);\n```\n\n## Unit Testing\n\nSometimes it might be useful to use the service container within our unit tests. LightInject also provides the [LightInject.xUnit](https://www.nuget.org/packages/LightInject.xUnit/) extension that enables dependencies to be injected into test methods. One side effect of using that extension is that it is tightly coupled to xUnit and it we have less control with regards to container instances. \n\nInstead consider this simple base class \n\n```c#\npublic class ContainerFixture : IDisposable\n{\n    public ContainerFixture()\n    {\n        var container = CreateContainer();\n        Configure(container);\n        container.RegisterFrom\u003cCompositionRoot\u003e();\n        ServiceFactory = container.BeginScope();\n        InjectPrivateFields();\n    }\n\n    private void InjectPrivateFields()\n    {\n        var privateInstanceFields = this.GetType().GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);\n        foreach (var privateInstanceField in privateInstanceFields)\n        {\n            privateInstanceField.SetValue(this, GetInstance(ServiceFactory, privateInstanceField));\n        }\n    }\n\n    internal Scope ServiceFactory { get; }\n\n    public void Dispose() =\u003e ServiceFactory.Dispose();\n\n    public TService GetInstance\u003cTService\u003e(string name = \"\")\n        =\u003e ServiceFactory.GetInstance\u003cTService\u003e(name);\n\n    private object GetInstance(IServiceFactory factory, FieldInfo field)\n        =\u003e ServiceFactory.TryGetInstance(field.FieldType) ?? ServiceFactory.GetInstance(field.FieldType, field.Name);\n\n    internal virtual IServiceContainer CreateContainer() =\u003e new ServiceContainer();\n\n    internal virtual void Configure(IServiceRegistry serviceRegistry) {}\n}\n```\n\n\n\nThis can be use with any test framework as long as it creates a new instance of the test class for each test method and that it calls `Dispose` after the test completes. For `xUnit` this is the default behaviour.\n\nInjecting services now becomes incredible easy. Just declare the service to test as a private field like this.\n\n```c#\npublic class SampleTests : ContainerFixture\n{\n    private ICalculator calculator;\n    \n    [Fact]\n    public void ShouldAddNumbers()\n    {\n        calculator.Add(2,2).ShouldBe(2);\n    }\n}\n```\n\nIf we need to configure the container before executing the test, we can do that by simply overriding the `Configure` method. This could for instance be used to register mock services into the container.\n\n```c#\npublic class SampleTests : ContainerFixture\n{\n    private ICalculator calculator;\n    \n    [Fact]\n    public void ShouldAddNumbers()\n    {\n        calculator.Add(2,2).ShouldBe(2);\n    }\n    \n    internal override Configure(IServiceRegistry serviceRegistry)\n    {\n        // Add registrations related to testing here\n    }\n}\n```\n\n\n\n\n\n\n\n\n\n\n\n\n\n","funding_links":["https://github.com/sponsors/seesharper","https://www.buymeacoffee.com/Y3bqWk1"],"categories":["Frameworks, Libraries and Tools","框架, 库和工具","IOC/DI","Runtime dependency injection","IoC","C# #"],"sub_categories":["IOC","控制反转IOC","GUI - other"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fseesharper%2FLightInject","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fseesharper%2FLightInject","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fseesharper%2FLightInject/lists"}