ASP.NET Core : Why do we need IDesignTimeDbContextFactory? - c#

I have an ASP.NET Core application and all I have is the DataContext, I don't have IDesignTimeDbContextFactory implemented.
public class DataContext : DbContext, IUnitOfWork
{...}
With that I can do Add-Migration, Update-Database & Script-Migration.
However, I came across an another project where they have implemented IDesignTimeDbContextFactory, mentioned that this is to generate migration classes.
public class DesignTimeDbContextFactory : IDesignTimeDbContextFactory<CodingBlastDbContext>
{
public CodingBlastDbContext CreateDbContext(string[] args)
{
IConfigurationRoot configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.Build();
var builder = new DbContextOptionsBuilder<CodingBlastDbContext>();
var connectionString = configuration.GetConnectionString("DefaultConnection");
builder.UseSqlServer(connectionString);
return new CodingBlastDbContext(builder.Options);
}
}
I wonder why this is needed? especially the first project works without implementing the IDesignTimeDbContextFactory..

Docs have some explanation on when you can leverage the design-time factory:
A design-time factory can be especially useful if you need to configure the DbContext differently for design time than at run time, if the DbContext constructor takes additional parameters are not registered in DI, if you are not using DI at all, or if for some reason you prefer not to have a CreateHostBuilder method in your ASP.NET Core application's Main class.
The only use case I personally encountered was when DbContext was moved into a separate library and we did not want to run CreateHostBuilder for context designing purposes (startup involved some relatively heavy stuff and we didn't want to invoke that). Like for example here.

Related

Is it possible to pass the ServiceProvider into a constructor parameter?

Problem:
We have a .NET 5 WPF application that has an EntityFramework Core entities class file DbEntities, which implements the DbContext. We use constructor injection when instantiating it. One of the options that we use is AddInterceptors in order to append an Access Token to the SqlConnection. The interceptor is called AzureAuthenticationInterceptor. When registering the service, we would like to pass in the ServiceProvider so that it is available in the interceptors constructor, which can be used to get a service that implements Access Token in-memory caching.
The reason for it is that we have a project with 50+ classes that all use the same DbEntities file, which takes 0 arguments in the constructor. This was upgraded to .NET 5 where Dependency Injection was avoided due to the work it would take to apply it to all of the forms. So, the DbEntities is instantiated in the forms with new DbEntities();.
But, in this case, we are implementing an access token cache, which needs to be registered as a service. Otherwise, if we just instantiate the cache every time we create a new DbContext, then the cache will be wiped out.
The access token in-memory cache is implemented using this method https://mderriey.com/2020/09/12/resolve-ef-core-interceptors-with-dependency-injection/
We only want to use dependency injection for the in-memory token cache. The only way we think of as a shortcut is to pass the ServiceProvider in the interceptor's constructor, but it does not appear available in the ConfigureServices method.
Question:
Is it possible to pass in the ServiceProvider? If not, is there any other way we can implement dependency injection on the interceptor without having to change 50 class files?
Program.cs
Public static void Main()
{
...
Host = Microsoft.Extensions.Hosting.Host.CreateDefaultBuilder()
.ConfigureAppConfiguration((context, builder) =>
{
builder.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
})
.ConfigureServices((context, services) =>
{
Configuration = context.Configuration;
ConfigureServices(Configuration, services);
})
.Build();
...
}
private static void ConfigureServices(IConfiguration objConfiguration, IServiceCollection objServices)
{
objServices.AddMemoryCache()
.AddSingleton<IAzureSqlTokenProvider, AzureIdentityAzureSqlTokenProvider>()
.Decorate<IAzureSqlTokenProvider, CacheAzureSqlTokenProvider>()
.AddSingleton(new AzureAuthenticationInterceptor(IServiceProvider_NeededHere))
;
}
DbEntities.cs
public DbEntities() :
base(new DbContextOptionsBuilder<DbEntities>()
.UseSqlServer(ConfigurationManager.ConnectionStrings["DbEntities"].ConnectionString)
.AddInterceptors(new AzureAuthenticationInterceptor())
.Options)
{ }
AzureAuthenticationInterceptor.cs
public AzureAuthenticationInterceptor(IServiceProvider objServiceProvider)
{
this.IAzureSqlTokenProvider = (IAzureSqlTokenProvider)objServiceProvider.GetService(typeof(IAzureSqlTokenProvider));
}
First, avoid injecting IServiceProvider, it is a code smell and leads to poor design.
Refactor AzureAuthenticationInterceptor.cs
public AzureAuthenticationInterceptor(IAzureSqlTokenProvider tokenProvider) {
this.IAzureSqlTokenProvider = tokenProvider;
}
So that way explicit dependencies can be injected as needed
//...
.AddSingleton<AzureAuthenticationInterceptor>()
//...
When resolving the interceptor while configuring the DbEntities
//...
services.AddDbContext<DbEntities>((provider, options) => {
options.UseSqlServer(Configuration.GetConnectionString("<connection-string-name>"));
options.AddInterceptors(provider.GetRequiredService<AzureAuthenticationInterceptor>());
});
//...
Note that if you are manually initializing the context using the default constructor, ie:new DbEntities(); Then this bypasses the opportunity to apply dependency injection via constructor injection.

Dependency Injection in .NET Core Main() Not Working

I have a library with a class that I instantiate in the Main() method of my .NET Core Web API (netcoreapp2.2) for retrieving the Web API's configuration:
public static void Main(string[] args)
{
var configBuilder = new ConfigBuilder("configuration-v1.json").Build();
WebHost.CreateDefaultBuilder(args)
.UseConfiguration(configuration)
.UseStartup<Startup>()
.Build()
.Run();
}
The constructor ConfigBuilder is:
public AwsConfigurationBuilder(string configKey, IAmazonS3ClientAdapter s3Client)
My ConfigureServices method in Startup.cs is:
public void ConfigureServices(IServiceCollection services)
{
services
.AddScoped<IAmazonS3ClientAdapter, AmazonS3ClientAdapter>()
.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}
And I am getting the following error concerning ConfigBuilder instantiation:
There is no argument given that corresponds to the required formal parameter 's3Client' of 'ConfigBuilder.ConfigBuilder(string, IAmazonS3ClientAdapter)' [Foo.BarService]csharp(CS7036)
Is it just not possible to do dependency injection in the service's Main method?
You can do this, assuming you injected your service properly, and not sure if this is a good idea or not but...
public static void Main(string[] args)
{
var builder = CreateWebHostBuilder(args);
var host = builder.Build();
using (var scope = host.Services.CreateScope())
{
var services = scope.ServiceProvider;
var aws = services.GetService<AwsConfigurationBuilder>();
}
The starting point of any Dependency Injection application is called the Composition Root. The composition root is where we define all the mappings of the application. Loosely put mappings between abstraction and concretion.
Once this mapping is defined the object creation is handed over to the DI framework. DI framework then traverses through these mappings and creates objects for us.
A very good analogy that #Mark Seemann gave in his seminal book on DI is that imagine your code as a little kid. That kid is not allowed to open the fridge. Now if the kid wants to eat something he asks his mom about it. DI framework is the mom who knows what's there in the fridge and how to serve it to the kid.
No, you cannot apply it within the Main method as that is the entrypoint to your application (and its a static method, not a constructor).
You are also instantiating the class by creating a new object (new ConfigBuilder), that's not how you are supposed to use dependency injection.
You are using ASP .NET core. The wiring for your dependencies is done within ConfigureServices(IServiceCollection services) after which you can use the dependencies. For example, by specifying the needed dependencies (interfaces and/or classes) in your Controller constructor.
You can find a more in-depth explanation here: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection.

.Net Core 2.1 Web and Console DbContexts

Im not known for my clarity when asking questions, so forgive me, also i have no formal training for any of this but im stumped.
I am mid upgrade from .Net 4.7.1 to .Net Core 2.1, my solution consists of 2 parts, an IIS Web Application for MVC, and a Console Application, the IIS App displays data, and the console application does all the actual processing.
Before i started this port for my console app when i needed stuff from the database i would simply
using (var db = new ApplicationDbContext())
{
SomethingModel model = db.X.First(x => x.Whatever == whatever);
}
And just like that i have the data i want from the database, butttt do you think i can do that with Core 2.1 can i hell.
I got all the code ported all the refrences resolved and so far as i can tell its ready to run. Except i cant call data from the database and im stumped, google just shows code first and ef stuff, or i dont know what im really asking.
So if anyone can help its much appreciated
-- Update 1---
The error is An Object refrence is required for the non-static field, metho or property Program._db
The DbModel is defined in Data/ApplicationDbContext.cs for the IIS App and is as follows
public ApplicationDbContext(DbContextOptions<ApplicationDbContext>
options)
: base(options)
{
}
-- Program.cs for Console App
class Program
{
private ApplicationDbContext _db { get; }
public Program(ApplicationDbContext context)
{
_db = context;
}
static void Main(string[] args)
{
new ExecutionEngine(_db).Run();
}
}
The previous way you wrote the code (using) was never a good idea. Your context should be request-scoped; using using can lead to all sorts of issues with entity tracking and totally destroys all the helpful caching EF does. The best method for getting a context instance was always dependency injection via a DI container.
ASP.NET Core uses dependency injection for everything, and because of this EF Core's DbContext is designed to be dependency injected. In this regard, it no longer uses a default constructor out of the box, which is why your old code is failing (it depends on there being a default constructor).
Long and short, do things right and inject your context. It looks like you're attempting to do this based on your update. However, you cannot inject into something like Program. This is the entry point for your application, which means literally nothing exists yet. If you take a look at your web app, you'll notice that Program there sets up the web host builder (using Startup) and then builds and runs it. Behind the scenes this is doing a bunch of stuff, including setting up the service collection. This is what you need to do in your console app (set up the service collection). That's relatively straight forward:
class Program
{
static void Main(string[] args)
{
var serviceProvider = new ServiceCollection()
.AddDbContext<ApplicationDbContext>(o =>
o.UseSqlServer("connection string"))
.BuildServiceProvider();
var context = serviceProvider.GetRequiredService<ApplicationDbContext>();
new ExecutionEngine(context).Run();
}
}
Now, this is a bit of overkill just based on the code you have here. You can simply new up an instance of your context via DbContextOptionsBuilder:
var options = new DbContextOptionsBuilder<ApplicationDbContext>()
.UseSqlServer("connection string")
.Options;
var context = new ApplicationDbContext(options);
However, using the service collection allows you to handle more advanced scenarios and better reuse your instances of things like your context across your codebase. Also it's worth mentioning that you should probably consider integrating configuration providers as well, so you don't need to hardcode your connection string. That's also relatively straight-forward:
var config = new ConfigurationBuilder()
.SetBasePath(Path.Combine(AppContext.BaseDirectory))
.AddJsonFile("appsettings.json", optional: true)
.Build();
You might also want to add environment-specific configuration:
var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
Then:
.AddJsonFile($"appsettings.{environment}.json", optional: true);
This is just the same as doing all this in a web app, so you can add whatever type of configuration you like.

Why is an ASP.NET-Core app 'Configuration/AppSettings' POCO passed around as IOptions<T> instead of just T? [duplicate]

It seems to me that it's a bad idea to have a domain service require an instance of IOptions<T> to pass it configuration. Now I've got to pull additional (unnecessary?) dependencies into the library. I've seen lots of examples of injecting IOptions all over the web, but I fail to see the added benefit of it.
Why not just inject that actual POCO into the service?
services.AddTransient<IConnectionResolver>(x =>
{
var appSettings = x.GetService<IOptions<AppSettings>>();
return new ConnectionResolver(appSettings.Value);
});
Or even use this mechanism:
AppSettings appSettings = new AppSettings();
Configuration.GetSection("AppSettings").Bind(appSettings);
services.AddTransient<IConnectionResolver>(x =>
{
return new ConnectionResolver(appSettings.SomeValue);
});
Usage of the settings:
public class MyConnectionResolver
{
// Why this?
public MyConnectionResolver(IOptions<AppSettings> appSettings)
{
...
}
// Why not this?
public MyConnectionResolver(AppSettings appSettings)
{
...
}
// Or this
public MyConnectionResolver(IAppSettings appSettings)
{
...
}
}
Why the additional dependencies? What does IOptions buy me instead of the old school way of injecting stuff?
Technically nothing prevents you from registering your POCO classes with ASP.NET Core's Dependency Injection or create a wrapper class and return the IOption<T>.Value from it.
But you will lose the advanced features of the Options package, namely to get them updated automatically when the source changes as you can see in the source here.
As you can see in that code example, if you register your options via services.Configure<AppSettings>(Configuration.GetSection("AppSettings")); it will read and bind the settings from appsettings.json into the model and additionally track it for changes. When appsettings.json is edited, and will rebind the model with the new values as seen here.
Of course you need to decide for yourself, if you want to leak a bit of infrastructure into your domain or pass on the extra features offered by the Microsoft.Extensions.Options package. It's a pretty small package which is not tied to ASP.NET Core, so it can be used independent of it.
The Microsoft.Extensions.Options package is small enough that it only contains abstractions and the concrete services.Configure overload which for IConfiguration (which is closer tied to how the configuration is obtained, command line, json, environment, azure key vault, etc.) is a separate package.
So all in all, its dependencies on "infrastructure" is pretty limited.
In order to avoid constructors pollution of IOptions<>:
With this two simple lines in startup.cs inside ConfigureServices you can inject the IOptions value like:
public void ConfigureServices(IServiceCollection services)
{
//...
services.Configure<AppSettings>(Configuration.GetSection("AppSettings"));
services.AddScoped(cfg => cfg.GetService<IOptions<AppSettings>>().Value);
}
And then use with:
public MyService(AppSettings appSettings)
{
...
}
credit
While using IOption is the official way of doing things, I just can't seem to move past the fact that our external libraries shouldn't need to know anything about the DI container or the way it is implemented. IOption seems to violate this concept since we are now telling our class library something about the way the DI container will be injecting settings - we should just be injecting a POCO or interface defined by that class.
This annoyed me badly enough that I've written a utility to inject a POCO into my class library populated with values from an appSettings.json section. Add the following class to your application project:
public static class ConfigurationHelper
{
public static T GetObjectFromConfigSection<T>(
this IConfigurationRoot configurationRoot,
string configSection) where T : new()
{
var result = new T();
foreach (var propInfo in typeof(T).GetProperties())
{
var propertyType = propInfo.PropertyType;
if (propInfo?.CanWrite ?? false)
{
var value = Convert.ChangeType(configurationRoot.GetValue<string>($"{configSection}:{propInfo.Name}"), propInfo.PropertyType);
propInfo.SetValue(result, value, null);
}
}
return result;
}
}
There's probably some enhancements that could be made, but it worked well when I tested it with simple string and integer values. Here's an example of where I used this in the application project's Startup.cs -> ConfigureServices method for a settings class named DataStoreConfiguration and an appSettings.json section by the same name:
services.AddSingleton<DataStoreConfiguration>((_) =>
Configuration.GetObjectFromConfigSection<DataStoreConfiguration>("DataStoreConfiguration"));
The appSettings.json config looked something like the following:
{
"DataStoreConfiguration": {
"ConnectionString": "Server=Server-goes-here;Database=My-database-name;Trusted_Connection=True;MultipleActiveResultSets=true",
"MeaningOfLifeInt" : "42"
},
"AnotherSection" : {
"Prop1" : "etc."
}
}
The DataStoreConfiguration class was defined in my library project and looked like the following:
namespace MyLibrary.DataAccessors
{
public class DataStoreConfiguration
{
public string ConnectionString { get; set; }
public int MeaningOfLifeInt { get; set; }
}
}
With this application and libraries configuration, I was able to inject a concrete instance of DataStoreConfiguration directly into my library using constructor injection without the IOption wrapper:
using System.Data.SqlClient;
namespace MyLibrary.DataAccessors
{
public class DatabaseConnectionFactory : IDatabaseConnectionFactory
{
private readonly DataStoreConfiguration dataStoreConfiguration;
public DatabaseConnectionFactory(
DataStoreConfiguration dataStoreConfiguration)
{
// Here we inject a concrete instance of DataStoreConfiguration
// without the `IOption` wrapper.
this.dataStoreConfiguration = dataStoreConfiguration;
}
public SqlConnection NewConnection()
{
return new SqlConnection(dataStoreConfiguration.ConnectionString);
}
}
}
Decoupling is an important consideration for DI, so I'm not sure why Microsoft have funnelled users into coupling their class libraries to an external dependency like IOptions, no matter how trivial it seems or what benefits it supposedly provides. I would also suggest that some of the benefits of IOptions seem like over-engineering. For example, it allows me to dynamically change configuration and have the changes tracked - I've used three other DI containers which included this feature and I've never used it once... Meanwhile, I can virtually guarantee you that teams will want to inject POCO classes or interfaces into libraries for their settings to replace ConfigurationManager, and seasoned developers will not be happy about an extraneous wrapper interface. I hope a utility similar to what I have described here is included in future versions of ASP.NET Core OR that someone provides me with a convincing argument for why I'm wrong.
I can't stand the IOptions recommendation either. It's a crappy design to force this on developers. IOptions should be clearly documented as optional, oh the irony.
This is what I do for my configuraition values
var mySettings = new MySettings();
Configuration.GetSection("Key").Bind(mySettings);
services.AddTransient(p => new MyService(mySettings));
You retain strong typing and don't need need to use IOptions in your services/libraries.
You can do something like this:
services.AddTransient(
o => ConfigurationBinder.Get<AppSettings>(Configuration.GetSection("AppSettings")
);
Using Net.Core v.2.2, it's worked for me.
Or then, use IOption<T>.Value
It would look something like this
services.Configure<AppSettings>(Configuration.GetSection("AppSettings"));
I would recommend avoiding it wherever possible. I used to really like IOptions back when I was working primarily with core but as soon as you're in a hybrid framework scenario it's enough to drive you spare.
I found a similar issue with ILogger - Code that should work across frameworks won't because I just can't get it to bind properly as the code is too dependent on the DI framework.

Entity Framework Core 1.0 Connection Strings

We are working on a vary large ASP.NET Core MVC 1.0 application. We have 4-tiers to each of our applications as follows:
DTO
Repository (Entity Framework - Code First)
Service (Business Logic)
MVC (UI-MVC)
Currently, in our repositories, which handle all database operations we have hard coded the database connection strings in the DbContext as follows:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) {
optionsBuilder.UseSqlServer("Data Source=somedatabase.database.windows.net;Initial Catalog=database;Integrated Security=False;User ID=username;Password=password;Connect Timeout=60;Encrypt=True;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False;MultipleActiveResultSets=true");
}
This project is outside the MVC project as a standalone ASP.NET Core 1.0 project. It also has a empty Program.cs file in it which seems to be required to execute the code-to-database command lines (dotnet ef migrations add and dotnet ef database update).
The reason we have a hard coded connection string in the DbConext is because when we use the following code, we get an object reference not set to an instance to an object exception, when executing the dotnet ef commands.
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) {
optionsBuilder.UseSqlServer(ConfigurationManager.ConnectionStrings["StandardDatabase"].ConnectionString);
}
However, since we have a Program.cs, if we add a Debug.WriteLine for the connection string and run the project, it does return the correct connections string and if we set the connection string in the appsettings.json file in the UI, the UI will successfully connect as well.
THE ISSUE:
The above mentioned stack is what we use for several "Micro Apps", which means we have several projects that connect to several databases. We also want to take advantage of Development, Staging and Production connection strings.
If we use Configuration Manager Connection String, everything is good for daily operations; however, when ever we want to utilize Entity Frameworks code to database command lines, we need to go in to each repository we want to update and change the DbContext to a hard coded connection string, execute the commands, then change them back to when done, which becomes quite troublesome.
THE QUESTION:
Are we just doing this wrong, is there a preferred practice for setting up an Entity Framework Core 1.0 stack which allows us not to manually have to change the DbContext but take advantage of configuration files across the board?
Any direction would be appreciated!
EF Core is intended to be configured via dependency injection. Dependency injection keeps your DbContext clean, and independent of implementation details of the environment.
Your initial solution of hard-coding connection strings tightly coupled the DbContext to the knowledge of where the database is located. That's obviously a problem. But your proposed solution tightly couples the DbContext to the knowledge of a particular configuration file. That, too, is a problem.
To keep the DbContext independent of environmental details, create a constructor that takes a DbContextOptions parameter and calls the base class constructor.
public class MyContext : DbContext
{
public MyContext(DbContextOptions options) :
base(options)
{
}
}
Do this instead of overriding OnConfiguring. Then initialize it in the Startup.cs of your host application. That's where the knowledge of the configuration file belongs.
public class Startup
{
private IConfigurationRoot _configuration;
public Startup(IHostingEnvironment env)
{
_configuration = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json")
.Build();
}
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IConfigurationRoot>(_configuration);
services.AddDbContext<MyContext>(options => options
.UseSqlServer(_configuration.GetConnectionString("MyContext")));
}
}
Now you can use your DbContext from anywhere.
ANSWER: I was making this much more difficult then it actually was. I followed Juunas' advise and added in the following code in my Repository DbContext Class:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
// get the configuration from the app settings
var config = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.Build();
// define the database to use
optionsBuilder.UseSqlServer(config.GetConnectionString("StandardDatabase"));
}
Which works perfect with the dotnet ef command line tools and far as the multiple environment setup goes with my MVC UI sticking with the following default code in my startup.cs works great as well.
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddJsonFile("project.json", optional: true, reloadOnChange: true);
IDbContextFactory might also help. EF Command Line Tools and DI can use this factory to create instances of your DBContext. Design Time services (e.g. Migrations) will discover implementations of this interface that are in the same assembly as the derived context.
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
namespace MyProject
{
public class BloggingContextFactory : IDbContextFactory<BloggingContext>
{
public BloggingContext Create()
{
var optionsBuilder = new DbContextOptionsBuilder<BloggingContext>();
optionsBuilder.UseSqlite("Filename=./blog.db");
return new BloggingContext(optionsBuilder.Options);
}
}
}

Categories

Resources