Stephen York
Stephen York

Reputation: 1429

Autofac Multiple Regsistrations to Single service. Simple Injector -> Autofac translation

I've developed a CQRS style database access framework based on Tripod and other inspirations but targeting .NET Standard and simplifying for easier use. I want to split the IoC into separate integration packages so consumers can get the type registration I'm currently doing internally easily without being locked into a specific IoC container. My issue is I've only really worked closely with SimpleInjector so not familiar with other systems and their nuances around how they handle specific scenarios. I have an iminent need to support Autofac so thought I'd try here to see if anyone can translate.

I have the following Simple Injector CompositionRoot static class:

public static void RegisterDatabase(this Container container, DbContextOptions<EntityDbContext> dbContextOptions, params Assembly[] assemblies)
{
    var scopedLifeStyle = container.Options.DefaultScopedLifestyle;

    //container.Register<ICreateDbModel, DefaultDbModelCreator>(scopedLifeStyle); // lifestyle c
    container.RegisterInitializer<EntityDbContext>( //(container.InjectProperties);
        handlerToInitialise => handlerToInitialise.ModelCreator = new DefaultDbModelCreator()
    );

    // Setup DbContext
    var ctxReg = scopedLifeStyle.CreateRegistration(
        () => new EntityDbContext(dbContextOptions),
        container);

    container.AddRegistration<IUnitOfWork>(ctxReg);
    container.AddRegistration<IReadEntities>(ctxReg);
    container.AddRegistration<IWriteEntities>(ctxReg);
}

In ASP.NET Core solutions I invoke the above from Startup.Configure(...) with:

var optionsBuilder = new DbContextOptionsBuilder<EntityDbContext>()
    //.UseInMemoryDatabase("Snoogans");
    .UseSqlServer(_config.GetConnectionString("DefaultConnection"));
container.RegisterDatabase(optionsBuilder.Options);

which allows me to switch out to an in memory database for unit testing if needed. EntityDbContext contains all my unit of work methods for calling onto the context without having to specify explicit DbSet for each table. The IUnitOfWork, IReadEntities and IWriteEntities interfaces all define methods on the EntityDbContext.

So I'm not sure how I'd go about making an Autofac module that allows scoped registration of the dbcontext with passed in DbContextOptions followed by multiple registrations of interfaces to this registration.

Does anyone know how this can be achieved?

Upvotes: 0

Views: 155

Answers (1)

Stephen York
Stephen York

Reputation: 1429

I worked out the process and now have an AutoFac module. I was able to registermodule by instance of the class and also pass in the options when I instantiate. Because EntityDbContext implements the three interfaces I was registering separately in the Simple Injector scenario, AutoFac has the convenience of being able to just infer them and register with AsImplementedInterfaces()

public class EntityFrameworkModule : Module
    {
        private readonly DbContextOptions<EntityDbContext> _dbContextOptions;

        public EntityFrameworkModule(DbContextOptions<EntityDbContext> dbContextOptions)
        {
            _dbContextOptions = dbContextOptions;

        }

        protected override void Load(ContainerBuilder builder)
        {
            // If the calling code hasn't already registered a custom 
            // ICreateDbModel then register the internal DefaultDbModelCreator
            builder.RegisterType<DefaultDbModelCreator>()
                .IfNotRegistered(typeof(ICreateDbModel))
                .As<ICreateDbModel>();

            // Expecting IUnitOfWork, IReadEntities and IWriteEntities to be registered with this call
            builder.Register(c => new EntityDbContext(_dbContextOptions)
                {
                    ModelCreator = c.Resolve<ICreateDbModel>()
                })
                .AsImplementedInterfaces()
                .InstancePerLifetimeScope();

        }
    }

Upvotes: 0

Related Questions