Last active
July 31, 2021 17:30
-
-
Save bbrt3/543551738be183764345db1e706e0dcb to your computer and use it in GitHub Desktop.
Dependency Injection
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public interface IClass | |
{ | |
void TestMethod(); | |
} | |
public class Class : IClass | |
{ | |
void TestMethod() | |
{ | |
Console.WriteLine("LOL"); | |
} | |
} | |
public class Startup | |
{ | |
public void ConfigureService(IServiceCollection services) | |
{ | |
... | |
// still need to map it [not sure] | |
services.TryAddTransient<IClass, Class>(); | |
... | |
} | |
} | |
public class ExampleController | |
{ | |
public class ExampleController() | |
{ | |
// we dont use controller dependency injection here!! | |
} | |
// what we do instead is inject it inside some method parameter | |
// using [FromService] decorator | |
public Method([FromService] IClass Class) | |
{ | |
IClass _class = Class; | |
_class.TestMethod(); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Captive dependencies are those in which service uses another service, | |
but the service that is being used is shorter lived than the one | |
that is using it, which may cause problems. | |
That's why selecting correct scopes is important. | |
Safe dependencies: | |
TRANSIENT SCOPED SINGLETON | |
TRANSIENT YES YES YES | |
SCOPED NO YES YES | |
SINGLETON NO NO YES | |
Scope Validation is automoatic check if there are no captive dependencies. | |
It is enabled by default in development environment and does validation at startup. | |
*/ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
POCO (Plan Old CLR Object) is a class that only contains basic properties. | |
Configuration files usually are POCO classes. | |
*/ | |
public class ExampleConfig | |
{ | |
// POCO | |
public bool EnableX {get; set;} | |
public bool EnableY {get; set;} | |
} | |
appsettings.json | |
{ | |
"Example":{ | |
"EnableX" : true, | |
"EnableY" : false | |
} | |
} | |
public class Startup | |
{ | |
public void ConfigureServices(IServiceCollection services) | |
{ | |
... | |
// reading our configuration section from appsettings.json | |
// and mapping it to ExampleConfig class object | |
services.Configure<ExampleConfig>(Configuration.GetSection("Example")); | |
... | |
} | |
} | |
// accessing configuration from controller | |
public class ExampleController | |
{ | |
// property to store our configuration | |
private readonly ExampleConfig _config; | |
// getting config data with constructor dependency injection | |
public class ExampleController(IOptions<ExampleConfig> config) | |
{ | |
// assigning injected configuration to our config property | |
_config = config; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Details should depend upon abstraction, not the other way around. | |
That's why, when we notice that we use new keyword to create object instance, | |
we should consider dependency injection instead. | |
Benefits: | |
- loose coupling of components | |
- logical abstraction | |
- supports unit testing (easier mock-up creation) | |
- cleaner code | |
*/ | |
// interface of which implementation | |
public interface IClass | |
{ | |
void Test(); | |
} | |
// implementation of interface | |
public class Class : IClass | |
{ | |
void Test() | |
{ | |
Console.WriteLine("LOL"); | |
} | |
} | |
public ClassController : Controller | |
{ | |
// proparty for holding our implementation object | |
private readonly IClass _class; | |
// constructor dependency injection | |
public ClassController(IClass Class) | |
{ | |
// assigning injected dependency into our property | |
_class = Class; | |
} | |
public IActionResult Index() | |
{ | |
... | |
var viewmodel = new ClassViewModel(); | |
// var currentClass = new Class(); BREAKING DIP RULE!!! | |
// using our injected dependency instead | |
// loose coupling refactoring | |
var currentClass = _class.Test(); | |
... | |
} | |
} | |
public class Startup | |
{ | |
ConfigureServices(IServiceCollection services) | |
{ | |
... | |
// <Interface, Implementation> | |
// Registering service with Dependency Injection Controller | |
services.AddTransient(<IClass, Class>)(); | |
... | |
} | |
} | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Most popualar alternatives are: | |
a) Scrutor | |
- assembly scanning | |
- decorator pattern | |
b) Autofac | |
- somehow better for more specialized use | |
*/ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace Microsoft.Extensions.DependencyInjection | |
{ | |
public static class ConfigurationServiceCollectionExtensions | |
{ | |
public static IServiceCollection AddAppConfiguration(this IServiceCollection services, IConfiguration configuration) | |
{ | |
services.AddSingleton<IClass, ClassA>(); | |
services.AddSingleton<IClass, ClassB>(); | |
services.AddTransient<IClass, ClassC>(); | |
services.AddScoped<IClass, ClassD>(); | |
return services; | |
} | |
} | |
} | |
public class Startup | |
{ | |
public void ConfigureServices(IServiceCollection services, IConfiguration configuration) | |
{ | |
services.AddAppConfiguration(configuration); | |
... | |
... | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class IConfig | |
{ | |
public bool SettingA {get; set;} | |
public bool SettingB {get; set;} | |
} | |
public class ConfigA : IConfig | |
{ | |
public bool SettingA {get; set;} | |
public bool SettingB {get; set;} | |
} | |
public interface IClass | |
{ | |
void TestMethod(); | |
} | |
public class ClassA : IClass | |
{ | |
private readonly IConfig _config; | |
public ClassA(IConfig config) | |
{ | |
_config = config; | |
} | |
void TestMethod() | |
{ | |
Console.WriteLine("LOL"); | |
} | |
} | |
public class Startup | |
{ | |
public void ConfigureServices(IServiceCollection services) | |
{ | |
... | |
// injecting correct configuration for given service | |
services.TryAddSingleton<IConfig>(sp => sp.GetRequiredService<IOptions<ConfigA>>().Value); | |
... | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
There are three lifecycle options: | |
a) TRANSIENT | |
Objects are created each time they are requested. | |
Class1 Class2 | |
------------------------------- | |
Object1 Object2 | |
------------------------REQUEST | |
Object3 Object4 | |
------------------------REQUEST | |
MUST NOT BE THREAD-SAFE | |
b) SINGLETON | |
Objects are created once for the lifetime of the application. | |
Class1 Class2 | |
------------------------------- | |
Object1 Object1 | |
------------------------REQUEST | |
Object1 Object1 | |
------------------------REQUEST | |
MUST BE THREAD-SAFE | |
IS THREAD-SAFE | |
c) SCOPED | |
Objects are created once per request. | |
Class1 Class2 | |
------------------------------- | |
Object1 Object1 | |
------------------------REQUEST | |
Object2 Object2 | |
------------------------REQUEST | |
MUST NOT BE THREAD-SAFE | |
*/ | |
public class Startup | |
{ | |
public void ConfigureServices(IServiceCollection services) | |
{ | |
// assigning different scopes to different services | |
// should depend on our needs | |
// service that depends on other service shouldn't have shorter lifetime | |
// than the one that it depends on | |
services.AddTransient<IServiceA, ServiceA>(); | |
services.AddSingleton<IServiceB, ServiceB>(); | |
services.AddScoped<IServiceC, ServiceC>(); | |
} | |
} | |
public class SomeController() | |
{ | |
// properties for storing our services implementations | |
private readonly IServiceA _serviceA; | |
private readonly IServiceB _serviceB; | |
private readonly IServiceC _serviceC; | |
// controlle dependency injection | |
public SomeController(IServiceA serviceA, IServiceB serviceB, IServiceC serviceC) | |
{ | |
_serviceA = ServiceA; | |
_serviceB = ServiceB; | |
_serviceC = ServiceC; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// some poco interface | |
public interface IClass | |
{ | |
public bool IsImportant {get; set;} | |
} | |
public class Startup | |
{ | |
public void ConfigureService(IServiceCollection services) | |
{ | |
... | |
// TryAddEnumerable method check if there is already | |
// no other collection like the one we try to add | |
// if not it makes it available for injection | |
services.TryAddEnumerable(new[] | |
{ | |
ServiceDescriptor.Singleton<IClass, Class>(), | |
ServiceDescriptor.Singleton<IClass, ClassA>(), | |
ServiceDescriptor.Singleton<IClass, ClassB>(), | |
ServiceDescriptor.Singleton<IClass, ClassC>() | |
} | |
); | |
... | |
} | |
} | |
public class SomeController | |
{ | |
// property for storing collection of injected dependencies | |
private readonly IEnumerable<IClass> _classes; | |
// controller dependency collection injection | |
public class SomeController(IEnumerable<IClass> classes) | |
{ | |
_classes = classes; | |
} | |
public void SomeMethod() | |
{ | |
// iterating over stored injected dependencies | |
foreach (var Class in _classes) | |
{ | |
// checking if some condition is correct | |
if (Class.IsImportant == true) | |
{ | |
Console.WriteLine("YES"); | |
} | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Registering service multiple times using AddLifetime | |
will create new entry inside IServiceCollection. | |
With multiple entires for the same interface, | |
only the last provided implementation will be used. | |
Unless we use TryAddLifetime, then the first one | |
will be used and no other ones will be added. | |
*/ | |
public class Startup | |
{ | |
public void ConfigureServices(IServiceCollection services) | |
{ | |
... | |
// In situations of multiple service regirestrations, | |
// last registered implementation will be used. | |
// First one is inside services array but it won't be used! | |
services.AddSingleton<IClass, Class>(); | |
services.AddSingleton<IClass, AnotherClass>(); | |
// If we want to avoid situations like this we should do | |
// the same project like that: | |
services.AddSingleton<IClass, Class>(); | |
// When using TryLifetime, method will check if implementation is already provided | |
// If it is then it will not add another one. | |
// If there is no entry, then new one will be added and used. | |
services.TryAddSingleton<IClass, AnotherClass>(); | |
... | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class Weather | |
{ | |
public string Summary {get; set;} | |
} | |
public class Person | |
{ | |
public string Name {get; set;} | |
} | |
public interface IClass<T> | |
{ | |
public void TestMethod(); | |
} | |
public class ClassA<Weather> | |
{ | |
public void TestMethod() | |
{ | |
var weather = new Weather(); | |
Console.WriteLine(Weather.Summary); | |
} | |
} | |
public class ClassA<Person> | |
{ | |
public void TestMethod() | |
{ | |
var person = new Person(); | |
Console.WriteLine(Person.Name); | |
} | |
} | |
public class Startup | |
{ | |
public void ConfigureServices(IServiceCollection services) | |
{ | |
... | |
// includes classimplementations for all types that we declare | |
services.TryAddSingleton(typeof(IClass<>), typeof(ClassA<>)); | |
... | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
It is possible to replace provided implementation inside IServiceCollection. | |
We can also delete all provided implementations for selected interface. | |
*/ | |
public class Startup | |
{ | |
public void ConfigureServices(IServiceCollection services) | |
{ | |
... | |
// Replace example | |
services.AddSingleton<IClass, Class>(); | |
services.Replace(ServiceDescriptor.Singleton<IClass, AnotherClass>(); | |
// RemoveAll example | |
services.AddSingleton<IClass, Class>(); | |
services.AddSingleton<IClass, AnotherClass>(); | |
services.RemoveAll<IClass>(); | |
... | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class Startup | |
{ | |
public void ConfigureServices(IServiceCollection services) | |
{ | |
... | |
// creating service instance | |
services.TryAddSingleton<GreetingService>(); | |
// using created instance inside first interface | |
services.TryAddSingleton<IHomePageGreetingService>(sp => | |
sp.GetRequiredService<GreetingService>()); | |
// using created instance inside second interface | |
services.TryAddSingleton<IGreetingService>(sp => | |
sp.GetRequiredService<GreetingService>()); | |
... | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment