What is "DbContextOptions`1"? - asp.net-core

I have Web API in ASP .NET Core. When I add a db context in Startup.ConfigureServices:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<FixturesContext>(
options => options.UseSqlServer(Configuration.GetConnectionString("FixturesDatabase")));
services.AddControllers();
}
I see the number of services in the "services" container raises by three, I think those are:
FixturesContext
DbContextOptions
DbContextOptions`1
I am curious what is "DbContextOptions1"? Does anyone know? I have tried googling it but not satysfying result. My goal is to replace original context with in-memory (to run integration tests without original database), so I'm deleting db context and its options and adding in-memory context instead of them.

The third service you are getting is a generic version of the DbContextOptions. When calling .ToString() on a generic type it often looks like this.
The reason why there are three instances is that EF adds a general DbContextOptions object and a more specific one for your defined context.
If you inspect the calls of the third service you should find the type of your DbContext as a generic parameter.

DbContextOptions'1 would be the generic DbContextOptions<FixturesContext> registered to be injected into the context when being initialized.
Reference Configuring DbContextOptions
public class FixturesContext : DbContext
{
public FixturesContext(DbContextOptions<FixturesContext> options)
: base(options)
{ }
//...
}

Related

Working with multiple DbContext instances causes wrong values to be loaded from database Entity Framework Core

I'm writing an ASP.NET Core API in which the dependency injection container provides me with instances of my DbContext class. I set the service lifetime of the DbContext to transient (which means that the container will create a new instance for every class and every request).
builder.Services.AddDbContext<ComparisonInfoContext>(dbContextOptions =>
dbContextOptions.UseSqlServer("Connection string"), ServiceLifetime.Transient, ServiceLifetime.Transient);
Here is my DbContext class:
public class ComparisonInfoContext : DbContext
{
public DbSet<ComparisonInfo> Comparisons { get; set; } = null!;
public ComparisonInfoContext(DbContextOptions<ComparisonInfoContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<ComparisonInfo>().OwnsMany(c => c.DiscrepancyInfos);
}
}
I also have a BackgroundService class called JobChecker which I added by calling:
builder.Services.AddHostedService<JobChecker>();
In JobChecker, I have a function that runs every minute and the first thing that function does is call context.Comparisons.ToListAsync(). The first time the function runs everything works fine, but the second time, this call returns outdated values for some reason (The values it returns used to be in the database but were changed prior to this call being made).
What I think is happening is the following:
JobChecker (the BackgroundService class) receives it's instance of DbContext at the very start of the program.
The function inside JobChecker calls context.Comparisons.ToListAsync() and gets the correct values from the database (no problems here).
Some controller somewhere changes the values in the database using it's own instance of the DbContext.
The function inside JobChecker runs and calls context.Comparisons.ToListAsync(). It sees that it is already tracking instances with the same id which came from the previous call (step 2). These instances were not effected by the changes made by the controller in step 3. It does not materialize the values it got from the database, instead it returns the instances that already exist. This is explained more here: The life of a query
Does anyone know whether this is in fact what is happening? Is it even correct to set the service lifetime of the DbContext class to transient?
Your DB Context might be transient, but the BackgroundService is not. In effect you only have a single instance of the context in the BackgroundService.
I would look at injecting in an DB Context Factory:
services.AddDbContextFactory<ApplicationDbContext>(...)
Then inject one into your background service:
public class MyBackgroundService(IDbContextFactory<ApplicationDbContext> contextFactory)
Then every iteration of your loop in the background service (it sounds like it is running on a timer), you can use this to instantiate a new context.
using (var context = _contextFactory.CreateDbContext())
{
// ...
}
Docs:
https://learn.microsoft.com/en-us/ef/core/dbcontext-configuration/#using-a-dbcontext-factory-eg-for-blazor
I once faced the same problem with you ,and finally fixed it follow the document:
public SomeBackgroundService( IServiceProvider services)
{
......
}
using (var scope = Services.CreateScope())
{
var context = scope.ServiceProvider.GetService<BackgroundServiceProjContext>();
var personlist1 = context?.Person.ToList();
}
I modified the name and the context injected directly into backgroundservice still read the former value:

Adding DbContext for list of context types at runtime

To implement a plug-in system in a AspNet Core Mvc app, I would like a non-generic method to add a data context from a list of assemblies loaded dynamically at runtime, taking a Type parameter like this:
foreach(Type tp in pluginContexts)
{
services.AddDbContext(tp, options => ...);
}
instead of the usual
services.AddDbContext<PluginDataContext>(options => ...);
That's because for dynamically loaded assemblies, I can not provide the TContext type parameter to the AddDbContextPool method, since that's statically compiled and not available at compile time.
Background
This is for a larger Asp.Net Core MVC app. The plugins must be able to both access the main database of the overall app and a separate database of their own.
Plugin assemblies, containing domain code and their private database context are to be dropped in a specified directory.
The main app loads the plugin assembly dynamically upon startup.
The way I am solving this now is to have each controller get the IConfiguration instance injected, obtain the appropriate connection string from the config, and the database context is instantiated in the controller. Not so nice but does work.
One can easily inject a general class into the Services collection with AddScoped<>, and then use it as a sort of ServiceLocator - however, that is considered an antipattern.
I looked into the source code for AddDbContext but honestly I am lost.
Is there any simple way to achieve this?
Solved it by creating an extensibility point in the plugin assembly.
Define an interface in the main app, which all plugins must implement.
public interface IPluginContextRegistration
{
void RegisterContext(ref IServiceCollection services, Action<DbContextOptionsBuilder> optionsAction);
String GetDatabaseName();
}
Create a class implementing this interface (in the plugin). It has access to the type of its private database context, thus can use the generic AddDbContext method:
public class DatabaseRegistration : IPluginContextRegistration
{
public void RegisterContext(ref IServiceCollection services, Action<DbContextOptionsBuilder> optionsAction)
{
services.AddDbContext<Test1DbContext>(optionsAction);
}
public String GetDatabaseName()
{
return "test-plugin-db";
}
}
Then in the main app ASP.Net Startup.cs file, add following code, which calls the RegisterContext() method for each plugin. For example, if you want to use Sql Server:
void RegisterPluginDbContexts(ref IServiceCollection services, List<Assembly> assemblyList)
{
IEnumerable<IPluginContextRegistration> registrars = new List<IPluginContextRegistration>();
foreach (Assembly assembly in assemblyList)
{
registrars = registrars.Concat(GetClassInstances<IPluginContextRegistration>(assembly));
}
foreach (var reg in registrars)
{
String name = reg.GetDatabaseName();
String connStr = Configuration.GetConnectionString(name);
reg.RegisterContext(ref services, options => options.UseSqlServer(connStr));
}
}
For completeness - the method "GetClassInstances" is just a helper method using Reflection to obtain an instance of classes implementing the specified interface.
So it's simple after all - no need for re-writing framework code .

Translate property name in error messages with FluentValidation

I use FluentValidation in my project in order to validate almost every requests coming into my WebApi.
It works fine, but I've been asked to translate property names in the error messages. My projet must handle at least french and english, so for example, what I want to achieve is :
'First Name' is required (english case)
'Prénom' est requis (french case)
I already have a IPropertyLabelService for other purposes, that is injected in the Startup.cs, that I want to use. It finds translations of property names in a .json, which already works fine.
My problem is that I don't know how to use it globally. I know that FluentValidation's doc says to set the ValidatorOptions.DisplayNameResolver in the Startup file, like this :
FluentValidation.ValidatorOptions.DisplayNameResolver = (type, memberInfo, expression) => {
// Do something
};
I don't know how I can use my IPropertyLabelService inside this, as the Startup.ConfigureServices method is not over yet, so I can't resolve my service...
Any other solution to achieve this behaviour is also more than welcome. I considered using .WithMessage() or .WithName() but I have a really big amount of validators, that would be really long to add this to all individually.
I answered this over on the FluentValidation issue tracker, but for completeness will include the answer here too:
Ssetting FluentValidation.ValidatorOptions.Global.DisplayNameResolver is the correct way to handle this globally (or you can use WithName at the individual rule level).
You need to ensure that this is set once, globally. If you need the service provider to have been initialized first, then make sure you call it at a point after the service provider has been configured (but ensure you still only set it once).
The "options" configuration mechanism in .NET Core allows you to defer configuration until after the point services have been constructed, so you can create a class that implements IConfigureOptions, which will be instantiated and executed during the configuration phase for a particular options type. FluentValidation doesn't provide any options configuration itself, so you can just hook into one of the built-in options classes (ASP.NET's MvcOptions is probably the simplest, but you can also use a different one if you're not using mvc).
For example, you could do something like this inside your ConfigureServices method:
public void ConfigureServices(IServiceCollection services) {
// ... your normal configuration ...
services.AddMvc().AddFluentValidation();
// Afterwards define some deferred configuration:
services.AddSingleton<IConfigureOptions<MvcOptions>, DeferredConfiguration>();
}
// And here's the configuration class. You can inject any services you need in its constructor as with any other DI-enabled service. Make sure your IPropertyLabelService is registered as a singleton.
public class DeferredConfiguration : IConfigureOptions<MvcOptions> {
private IPropertyLabelService _labelService;
public DeferredConfiguration(IPropertyLabelService labelService) {
_labelService = labelService;
}
public void Configure(MvcOptions options) {
FluentValidation.ValidatorOptions.Global.DisplayNameResolver = (type, memberInfo, expression) => {
return _labelService.GetPropertyOrWhatever(memberInfo.Name);
};
}
}

Azure Function Uses Wrong DbContext Constructor

I have an existing EF Core 2.2 DbContext that works fine in an ASPNET Core application as well as LinqPad. Now I am trying to add it to an Azure function. In both ASPNET and the Azure function I am using dependency injection.
The DbContext class has three constructors - an empty one, one that takes a connection string and another that takes a DbOptionsBuilder instance. The ASPNET Core app seems to invoke the one that takes the DbOptionsBuilder instance while LinqPad uses the one that takes the connection string. As I said, both of these work fine.
The Azure function app tries to use the one that takes a string, but it passes null instead of a value. This causes an error later saying that a provider hasn't been configured.
I can force the function app to use the DbOptionsBuilder constructor by removing the one that takes a string. When I do this the function app works fine. However, I can no longer use the context in LinqPad if I do.
My question is, first, how can I make the Azure function call the appropriate constructor without removing the others? Second, and less importantly, why the different behavior between the ASPNET runtime and the Azure function runtime?
EDIT
I am only running the AZ function locally at this point so it is reading the connection string from 'local.settings.json' file. This part is working.
Here is the Startup.Configure method of the function project.
public class Startup : FunctionsStartup
{
/// <summary>
/// This method gets called by the runtime. Use this method to add services to the DI container.
/// </summary>
/// <param name="builder">The function host builder</param>
public override void Configure(IFunctionsHostBuilder builder)
{
// Add database context
string env = Environment.GetEnvironmentVariable("AZURE_FUNCTIONS_ENVIRONMENT");
string connectionString = Environment.GetEnvironmentVariable($"ConnectionStrings:{env}");
builder.Services.AddDbContext<FullContext>(x => x.UseSqlServer(connectionString), ServiceLifetime.Transient);
}
}
As I said, it is reading the connection string and appears to pass it to the AddDbContext method. But something is going wrong somewhere.
EDIT 2
Here are the three constructors from my DbContext subclass. Nothing special. Also including the OnConfiguring method.
public FullContext() { }
public FullContext(string connectionString)
{
ConnectionString = connectionString;
}
public FullContext(DbContextOptions<FullContext> options) : base(options) { }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (ConnectionString != null)
optionsBuilder.UseSqlServer(ConnectionString);
base.OnConfiguring(optionsBuilder);
}
EDIT 3
After reviewing the link #Jack Jia suggested I tried the following.
First, I create my own instance of the DbContextOptionsBuilder and specify the provider and connection string.
var options = new DbContextOptionsBuilder<FullContext>();
options.UseSqlServer(connectionString);
I then try to force the DI service to use these options. However, this fails when using the AddDbContext method - it still tries to call the wrong constructor using a null string as the parameter.
In other words, this fails:
builder.Services.AddDbContext<FullContext>(x => new FullContext(options.Options), ServiceLifetime.Transient);
but this seems to work:
builder.Services.AddTransient<FullContext>(x => new FullContext(options.Options));
Assuming I am understanding the docs correctly both calls should be forcing the DI service to use the constructor taking an DbContextOptions parameter. But this doesn't seem to be the case.
You may refer to: Service registration methods
If there are multiple constructors, you can specify one as following:
Add{LIFETIME}<{SERVICE}>(sp => new {IMPLEMENTATION})
For example:
// Constructor1
builder.Services.AddScoped<IMyDep>(sp => new MyDep());
// Constructor2
builder.Services.AddScoped<IMyDep>(sp => new MyDep("A string!"));
// Constructor3
builder.Services.AddScoped<IClass1, Class1>();
builder.Services.AddScoped<IMyDep>(sp =>
{
IClass1 class1 = sp.GetRequiredService<IClass1>();
//class1.doSomething(...);
return new MyDep(class1);
});
So, you do not need to change the DbContext class, just specifically use different constructors in different apps.
Where are storing the connections string value?
I would check the source. Out of the box asp.net core has the a application.settings.json file configured for injection. AZ Function does not do this.
If you are using an application.settings.json then you have to configure it to load settings from that file.
Here a sample how to load a config file in DI that allows you to have similar access to the content as in asp.net core:
var config = new ConfigurationBuilder().SetBasePath(Environment.CurrentDirectory)
.AddJsonFile("application.settings.json", optional: false, reloadOnChange: true)
.AddEnvironmentVariables()
.Build();
builder.Services.AddSingleton<IConfiguration>(config);
And getting a value in the Configure method:
string SqlConnectionString = config.GetConnectionString("SqlConnectionString");
This is done in the public override void Configure(IFunctionsHostBuilder builder).
Here is how to use DI in Azure Functions.
The other possibility I can think of is Azure Key Vault or environment variables.

Container.GetInstance(Type) when using WcfOperationLifestyle throws ActivationException

I have a WebAPI service using SimpleInjector. I have this set up using AsyncScopedLifestyle for my scoped dependencies, and one of these dependencies is my Entity Framework DataContext. Many things in my service depend on the DataContext, and it is generally injected in to my MediatR handlers using constructor injection - this works well. Separately I have a few areas where I need to create an instance of an object given its type (as a string), so I have created a custom activator class (ResolvingActivator) that is configured with a reference to Container.GetInstance(Type):
In my container bootstrap code:
ResolvingActivator.Configure(container.GetInstance);
I can then create objects by using methods such as:
ResolvingActivator.CreateInstance<T>(typeName)
When I'm using WebAPI, the above is working perfectly.
A further part of the project is a legacy API that uses WCF. I have implemented this as a translation layer, where I translate old message formats to new message formats and then dispatch the messages to the Mediator; I then translate the responses (in new format) back to old format and return those to the caller. Because I need access to the Mediator in my WCF services, I'm injecting this in their constructors, and using the SimpleInjector.Integration.Wcf package to let SimpleInjector's supplied SimpleInjectorServiceHostFactory build instances of the services. I've also created a hybrid lifestyle, so I can use the same container for my both my WebAPI and WCF services:
container.Options.DefaultScopedLifestyle = Lifestyle.CreateHybrid(
new AsyncScopedLifestyle(),
new WcfOperationLifestyle());
This works well for some calls, but when a call ultimately calls my ResolvingActivator class, I get an ActivationException thrown, with the following message:
The DataContext is registered as 'Hybrid Async Scoped / WCF Operation' lifestyle, but the instance is requested outside the context of an active (Hybrid Async Scoped / WCF Operation) scope.
As I only receive this error when making WCF calls, I'm wondering if I have something wrong in my configuration. In a nutshell, this will work:
public class SomeClass
{
private readonly DataContext db;
public SomeClass(DataContext db)
{
this.db = db;
}
public bool SomeMethod() => this.db.Table.Any();
}
But this will not:
public class SomeClass
{
public bool SomeMethod()
{
// Code behind is calling container.GetInstance(typeof(DataContext))
var db = ResolvingActivator.CreateInstance<DataContext>();
return db.Table.Any();
}
}
Any ideas where I'm going wrong?
Edit: here is the stack trace from the ActivationException:
at SimpleInjector.Scope.GetScopelessInstance[TImplementation](ScopedRegistration`1 registration)
at SimpleInjector.Scope.GetInstance[TImplementation](ScopedRegistration`1 registration, Scope scope)
at SimpleInjector.Advanced.Internal.LazyScopedRegistration`1.GetInstance(Scope scope)
at lambda_method(Closure )
at SimpleInjector.InstanceProducer.GetInstance()
at SimpleInjector.Container.GetInstance(Type serviceType)
at Service.Core.ResolvingActivator.CreateInstance(Type type) in Service.Core\ResolvingActivator.cs:line 43
at Service.Core.ResolvingActivator.CreateInstance(String typeName) in Service.Core\ResolvingActivator.cs:line 35
at Service.Core.ResolvingActivator.CreateInstance[TService](String typeName) in Service.Core\ResolvingActivator.cs:line 69
With a full stack trace here: https://pastebin.com/0WkyHGKv
After close inspection of the stack trace, I can conclude what's going on: async.
The WcfOperationLifestyle under the covers depends on WCF's OperationContext.Current property, but this property has a thread-affinity and doesn't flow with async operations. This is something that has to be fixed in the integration library for Simple Injector; it simply doesn't support async at the moment.
Instead, wrap a decorator around your handlers that start and end a new async scope. This prevents you from having to use the WcfOperationLifestyle all together. Take a look at the ThreadScopedCommandHandlerProxy<T> implementation here to get an idea how to do this (but use AsyncScopedLifestyle instead).