Monday 1 November 2021

DbContext Creation

There are several ways to create a DBContext in (modern) .Net and they are explained here. There 2 of them that I normally use. This one, when I'm not using any IoC container:


var connectionstring = "Data Source=blogging.db";

var optionsBuilder = new DbContextOptionsBuilder<BloggingContext>();
optionsBuilder.UseSqlite(connectionstring);
optionsBuilder.UseLazyLoadingProxies();

BloggingContext dbContext = new BloggingContext(optionsBuilder.Options);

The above is pretty clear. We set several options through a builder object, build the options object and pass it over to the DBContext constructor, so that we get a DBContext fully set with all its options. This is much better than if we wereconstructing the object and then setting options on it, that would allow having a partially initialized (and useless) object.

This other mechanism is used when using the built-in IoC container provided by the .Net Base Class Library:


var connectionstring = "Data Source=blogging.db";
ServiceCollection services = new ServiceCollection();
services.AddDbContextFactory<BloggingContext>( optionsBuilder => {
	optionsBuilder.UseSqlite(connectionstring);
	optionsBuilder.UseLazyLoadingProxies();
});

var provider = services.BuildServiceProvider();

BloggingContext dbContext = provider.GetService<BloggingContext>();


Usually I just copy/paste it from some article or from a previous project and don't pay it much more attention, but it has always seemed a bit odd to me, so I've been reflecting a bit on it. We register in the IoC container our DbContext, providing a callback function that configures the DbContextOptionsBuilder and will be invoked when the Container instanciates the DbContext. This callback is not a factory, as it does not create the DbContext, it helps to configure it. This technique is possible becaue the built-in container has "extra knowledge" about DbContexts. I could think of 2 different ways to create the DbContext through the container that look a bit more natural to me.

1. Almost any IoC container allows registering parameters to pass to a constructor. For example in Autofac we can register constant values or register a factory function that is invoked by the container to create the non constant parameter each time it needs to provide the service. We would need that for our case with the DbContextOptions. I think the built-in .Net container does not support this.

2. Registering a Factory that creates a new DbContext each time the IoC container needs one. Indeed we can do that with the built-in container:


            var connectionstring = "Data Source=blogging.db";
            ServiceCollection services = new ServiceCollection();
            
            services.AddTransient<BloggingContext>(s => {
                var optionsBuilder = new DbContextOptionsBuilder<BloggingContext>();
                optionsBuilder.UseSqlite(connectionstring);
                optionsBuilder.UseLazyLoadingProxies();
                return new BloggingContext(optionsBuilder.Options);
            });
            var provider = services.BuildServiceProvider();
            return provider.GetService<BloggingContext>();

I think this seems more natural to me as it's a generic technique that I'm more accustomed to use with other containers and for different object types, but it's slightly more verbose than the specific technique featured by the built-in container.

No comments:

Post a Comment