Thursday, April 27, 2017

Creating an IOC Container for ASP.NET Core

I recently added support for ASP.NET Core to my Tact.NET IOC Container, and I thought that I would share some of the interesting requirements I discovered while doing so. I had originally expected the default ASP.NET Core container would follow the same rules as the official Microsoft Unity container, but that turned out not to be the case!

1) Register is first in win.

The Unity container was last in win, and it would completely disregard the original registration. With ASP.NET Core you need to preserve the original registration and then treat all subsequent registrations as addition registrations that will only be resolved when ResolveAll is invoked, similar to keyed registrations in Unity. Which brings us to our next difference...

public class RegisterIsFirstInWinTests
{
    [Fact]
    public void Unity()
    {
        var log = new EmptyLog();
        using (var container = new TactContainer(log))
        {
            container.RegisterSingleton<IExample, ExampleA>();
            container.RegisterSingleton<IExample, ExampleB>();
 
            var example = container.Resolve<IExample>();
            Assert.IsType<ExampleB>(example);
        }
    }
 
    [Fact]
    public void AspNetCore()
    {
        var log = new EmptyLog();
        using (var container = new AspNetCoreContainer(log))
        {
            container.RegisterSingleton<IExample, ExampleA>();
            container.RegisterSingleton<IExample, ExampleB>();
 
            var example = container.Resolve<IExample>();
            Assert.IsType<ExampleA>(example);
        }
    }
 
    public interface IExample
    {
        string Name { get; }
    }
 
    public class ExampleA : IExample
    {
        public string Name => nameof(ExampleA);
    }
 
    public class ExampleB : IExample
    {
        public string Name => nameof(ExampleB);
    }
}

2) ResolveAll includes single registrations.

Unity has the concept of keyed registrations, where you can register the same type multiple times with distinct keys (strings) and then resolve them individually by key or as an IEnumerable with ResolveAll. In ASP.NET Core there is no concept of keyed registration, and ResolveAll will return all registrations made for a given type. This means that registering a single type will cause ResolveAll to return an IEnumerable with one value.

public class ResolveAllIncludesSingleRegistrations
{
    [Fact]
    public void Unity()
    {
        var log = new EmptyLog();
        using (var container = new TactContainer(log))
        {
            container.RegisterSingleton<IExample, ExampleA>();
            container.RegisterSingleton<IExample, ExampleB>();
 
            var examples = container.ResolveAll<IExample>();
            Assert.Equal(0, examples.Count());
        }
    }
 
    [Fact]
    public void AspNetCore()
    {
        var log = new EmptyLog();
        using (var container = new AspNetCoreContainer(log))
        {
            container.RegisterSingleton<IExample, ExampleA>();
            container.RegisterSingleton<IExample, ExampleB>();
 
            var examples = container.ResolveAll<IExample>();
            Assert.Equal(2, examples.Count());
            }
    }
 
    public interface IExample
    {
        string Name { get; }
    }
 
    public class ExampleA : IExample
    {
        public string Name => nameof(ExampleA);
    }
 
    public class ExampleB : IExample
    {
        public string Name => nameof(ExampleB);
    }
}

3) Generic resolution is required.

This was a neat feature that I had not used before! You can register generic types without any generic parameters, and then the container will fill in the generic parameters based on the resolve request. So, for example, I could register IRepository<> to type DataRepository<>, and then when I call container.Resolve<IRepository<User>>, the IOC container will automatically try to construct a DataRepository<User>

public class TactContainerTests
{
    [Fact]
    public void GenericResolution()
    {
        var log = new EmptyLog();
        using (var container = new TactContainer(log))
        {
            container.RegisterSingleton(typeof(IRepository<>), typeof(Repository<>));
 
            var userRepository = container.Resolve<IRepository<User>>();
            Assert.IsType<Repository<User>>(userRepository);
        }
    }
 
    public interface IRepository<T>
    {
        string ClassName { get; }
        string GenericName { get; }
    }
 
    public class Repository<T> : IRepository<T>
    {
        public string ClassName => nameof(Repository<T>);
        public string GenericName => typeof(T).Name;
    }
 
    public class User
    {
    }
}

4) You have to convert IServiceCollection to your container.

I like how the new .NET separates their service registration from the container itself. In your ASP.NET Core wire up you will work with an IServiceCollection, which will then be converted into a container after you are done filling in your registrations. This means that if you want to create your own container, you will need to map the service collection into your container.

Enjoy,
Tom

10 comments:

  1. You’ve written a really great article here. Your writing style makes this material easy to understand.. I agree with some of the many points you have made. Thank you for this is real thought-provoking content.
    MSBI Online Training
    MSBI Online Certification
    Learn MSBI Course

    ReplyDelete
  2. It's really nice and meanful. it's really cool blog. Linking is very useful thing. You have really helped lots of people who visit blog and provide them useful information.
    한국야동

    ReplyDelete
  3. This post is disseminating valuable information to people who are most concerned of the following issues being targeted by this site. Many certainly will keep coming back to check out updated posts.
    안전놀이터 모음

    ReplyDelete
  4. Nice Information I read this hope you share another ane thanks.here, is Nora our organization are providing security software and build your website and promote your brand you can go and read this.
    crypto wallet|
    exodus wallet|
    metamask wallet|
    trezor wallet|
    phantom wallet|

    ReplyDelete
  5. This comment has been removed by the author.

    ReplyDelete
  6. Well-written information. Very impressive and to the point
    Have a look at my new blog Windows 11 Activator Crack 2023

    ReplyDelete
  7. This comment has been removed by the author.

    ReplyDelete

Real Time Web Analytics