Dotnetdi

From bibbleWiki
Jump to navigation Jump to search

Registering a Service

Constructor Injection

With constructor Injection we define the list of required dependencies as parameters of the constructor for the class

class IainsController : Controller
{
    private IIainsWidget _iainsWidget;

    public IainsController(IIainsWidget iainsWidet)
    {
       _iainsWidget = iainsWidget;
    }
}

Action Injection

In some cases the service may only be used in on action so creating an instance for each constructor is not required. We can remove it from being injected on the constructor and change the action to have it instead using the [FromServices] attribute.

public async Task<ActionResult> DoOne([FromServices] IIainsWidget iainsWidget)
{
...
}

Middleware Injection

  • Middleware occurs once for the application
  • Constructor run once for the application
  • Constructor can use singleton but other types should be avoided

Registering a Service

In ASP Core the service must be registered with the IServiceCollection in the startup class configureServices.

public void ConfigureServices(IServiceCollection services)
{
    services.AddTransient<IIainsWidget, IainsWidget>();
}

TryAdd

Adding the same interface twice will fail if you use the TryAdd whereas the without the service is added but not used. You can replace a service using the Replace method and the ServiceDescriptor.

Multiple Registeration for a Service

One use of this, registering a service with x number of implementations might be a set of rules. In the example provided the user created a rule processors.

public void ConfigureServices(IServiceCollection services)
{
    services.Add<ICourtRule, ClubIsOpen>();
    services.Add<ICourtRule, MaxBookingLength>();
    services.Add<ICourtRule, MaxPeakTime>();

    services.Add<ICourtRuleProcessor, CourtRuleProcessor>();
}
...
public class CourtRuleProcessor : ICourtRuleProcessor {

    private readonly IEnumerable<ICourtRule> _rules;

    public CourtRuleProcessor(IEnumerable<ICourtRule> rules) {
        rules = _rules
    }

    public async Task<bool, IEnumerable<string>)> PassesAllRulesAsync(
...
 
}

Factory Example

Some service may require you to have a service already before they can be registered.

services.AddTransient<IMembershipBuilder, MembershipBuilder>();

services.AddScoped<IMembershipAdvert>(serviceProvider => {
   var builder = serviceProvider.GetService(IMembershipBuilder>();
   builder.WithDiscount(10m);
   return builder.Build(); 
}

Composite Services

Perhaps the original service sent only an email. By creating a composite class you can change this behaviour with no change to the controller by create a Notification service which registers two implementations.

public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton<EmailService>();
    services.AddSingleton<SmsService>();

    services.AddSingleton<INotificationService>(serviceProvider => {
        return new CompositeService(
             new INotificationService[] {
                serviceProvider.GetRequiredService<EmailService>,
                serviceProvider.GetRequiredService<SmsService>
            });
        });
}
...
public class CourtRuleProcessor : ICourtRuleProcessor {

    private readonly IEnumerable<ICourtRule> _rules;

    public CourtRuleProcessor(IEnumerable<ICourtRule> rules) {
        rules = _rules
    }

    public async Task<bool, IEnumerable<string>)> PassesAllRulesAsync(
...
 
}

Microsoft Dependency Injection Container

Introduction

When building a site try and build a dependency graph which will help determine what services you will need
Di graph.png

Configuration

An options class:

  • Must be non-abstract with a public parameterless constructor.
  • All public read-write properties of the type are bound.
  • Fields are not bound. In the preceding code, Position is not bound. The Position property is used so the string "Position" doesn't need to be hard coded in the app when binding the class to a configuration provider.

Example

public class PositionOptions
{
    public const string Position = "Position";

    public string Title { get; set; }
    public string Name { get; set; }
}

Now this can be passed using Dependency Injection using the Startup

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<PositionOptions>(Configuration.GetSection(
                                        PositionOptions.Position));
}

Add in the Controller

public void HomeController : Controller
{
    private PostionalOptions _positionalOptions;

    public HomeController(IOptions<PositionOptions> options)
    {
        _positionalOptions = options;
    } 
}

Service Lifetimes

These can be

  • Transient, create each time they are requested
  • Singleton once
  • Scoped on demand (each request)