Azure Function Uses Wrong DbContext Constructor - asp.net-core

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.

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 .

What is "DbContextOptions`1"?

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)
{ }
//...
}

Property Injection in Microsoft.Extensions.DependencyInjection

I am relatively new to working with DI containers and have hit a bit of a roadblock.
SimpleInjector has a method with the following signature:
Container.RegisterInitializer<TService>(Action<TService>)
In our code base we do use it like this:
// this is a property injection of the abstract file system
container.RegisterInitializer<IFileSystemInjection>(
fs => fs.FileSystem = new FileSystem());
I am wondering how I would achieve the same using the IServiceCollection parameter in the ConfigureServices method in the Startup.cs class. So far I have been able to register all my types using the services.AddTransient(); but I am not sure how what the equivalent simpleinjector.RegisterInitializer is within the IServiceCollection.
You'd use the factory overload(s) of AddSingleton, AddScoped, and AddTransient. I'm not sure what scope IFileSystemInjection should be in, but it sounds like something that could be a singleton. If not, change the method you call appropriately:
service.AddSingleton<IFileSystemInjection>(p =>
{
var fs = new FileSystemInjection();
fs.FileSystem = new FileSystem();
});
In short, if you provide a factory, then you're responsible for the entire object initialization, hence the new FileSystemInjection(), which I'm subbing as the actual implementation of IFileSystemInjection your using.
If that implementation has dependencies that need to be injected in order to create it, you can pull those from p, which is an instance of IServiceProvider:
var myDep = p.GetRequiredService<MyDep>();
var fs = new FileSystemImplementation(myDep);
You can use this nuget package, that extends standard Microsoft Dependency Injection and adds property injection:
https://www.nuget.org/packages/DJMJ.Extensions.DependencyInjection.Property/1.1.0
Mark property for injection
using Microsoft.Extensions.DependencyInjection;
public class FooService
{
[Inject]
public IBooService BooService { get; set; }
public void Foo()
{
// just start using injected property
BooService...
}
}
Add services scan method in ConfigureServices
using Microsoft.Extensions.DependencyInjection;
...
host.ConfigureServices((services)=>
{
services.AddTransient<IBooService, BooService>();
services.AddTransient<IFooService, FooService>();
// scan method
services.AddPropertyInjectedServices();
});
If you using this extension in asp net and want add property injection support in controllers too, you should add in ConfigureServices this statement:
services.AddControllers().AddControllersAsServices()

Autofac.Multitenant in an aspnet core application does not seem to resolve tenant scoped dependencies correctly

I'm in the process of upgrading a Multitenant dotnet core solution which utilises the Autofac.Multitenant framework. I'm not having a lot of luck getting tenancy resolution working correctly. I've created a simple demonstration of the problem here: https://github.com/SaltyDH/AutofacMultitenancy1
This repo demonstrates registering a InstancePerTenant scoped dependency TestMultitenancyContext which is resolved in the Home Controller. Due to issues with using IHttpContextAccessor, I'm using a custom RequestMiddleware class to capture the current HttpContext object so that I can perform logic on the current HttpContext request object in the MultitenantIdentificationStrategy.
Finally, TestFixture provides a simple xUnit test which, at least on my machine returns "tenant1" for both tenants.
Is there something I've missed here or is this just not currently working?
UPDATE 10/6/2017: We released Autofac.AspNetCore.Multitenant to wrap up the solution to this in a more easy to consume package. I'll leave the original answer/explanation here for posterity, but if you're hitting this you can go grab that package and move on.
I think you're running into a timing issue.
If you pop open the debugger on the HttpContext in the middleware you can see that there's a RequestServicesFeature object on a property called ServiceProvidersFeature. That's what's responsible for creating the per-request scope. The scope gets created the first time it's accessed.
It appears that the order goes roughly like this:
The WebHostBuilder adds a startup filter to enable request services to be added to the pipeline.
The startup filter, AutoRequestServicesStartupFilter, adds middleware to the very beginning of the pipeline to trigger the creation of request services.
The middleware that gets added, RequestServicesContainerMiddleware, basically just invokes the RequestServices property from the ServiceProvidersFeature to trigger creation of the per-request lifetime scope. However, in its constructor is where it gets the IServiceScopeFactory that it uses to create the request scope, which isn't so great because it'll be created from the root container before a tenant can be established.
All that yields a situation where the per-request scope has already been determined to be for the default tenant and you can't really change it.
To work around this, you need to set up request services yourself such that they account for multitenancy.
It sounds worse than it is.
First, we need a reference to the application container. We need the ability to resolve something from application-level services rather than request services. I did that by adding a static property to your Startup class and keeping the container there.
public static IContainer ApplicationContainer { get; private set; }
Next, we're going to change your middleware to look more like the RequestServicesContainerMiddleware. You need to set the HttpContext first so your tenant ID strategy works. After that, you can get an IServiceScopeFactory and follow the same pattern they do in RequestServicesContainerMiddleware.
public class RequestMiddleware
{
private static readonly AsyncLocal<HttpContext> _context = new AsyncLocal<HttpContext>();
private readonly RequestDelegate _next;
public RequestMiddleware(RequestDelegate next)
{
this._next = next;
}
public static HttpContext Context => _context.Value;
public async Task Invoke(HttpContext context)
{
_context.Value = context;
var existingFeature = context.Features.Get<IServiceProvidersFeature>();
using (var feature = new RequestServicesFeature(Startup.ApplicationContainer.Resolve<IServiceScopeFactory>()))
{
try
{
context.Features.Set<IServiceProvidersFeature>(feature);
await this._next.Invoke(context);
}
finally
{
context.Features.Set(existingFeature);
_context.Value = null;
}
}
}
}
Now you need a startup filter to get your middleware in there. You need a startup filter because otherwise the RequestServicesContainerMiddleware will run too early in the pipeline and things will already start resolving from the wrong tenant scope.
public class RequestStartupFilter : IStartupFilter
{
public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next)
{
return builder =>
{
builder.UseMiddleware<RequestMiddleware>();
next(builder);
};
}
}
Add the startup filter to the very start of the services collection. You need your startup filter to run before AutoRequestServicesStartupFilter.
The ConfigureServices ends up looking like this:
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.Insert(0, new ServiceDescriptor(typeof(IStartupFilter), typeof(RequestStartupFilter), ServiceLifetime.Transient));
services.AddMvc();
var builder = new ContainerBuilder();
builder.RegisterType<TestMultitenancyContext>().InstancePerTenant();
builder.Populate(services);
var container = new MultitenantContainer(new MultitenantIdentificationStrategy(), builder.Build());
ApplicationContainer = container;
return new AutofacServiceProvider(container);
}
Note the Insert call in there to jam your service registration at the top, before their startup filter.
The new order of operations will be:
At app startup...
Your startup filter will add your custom request services middleware to the pipeline.
The AutoRequestServicesStartupFilter will add the RequestServicesContainerMiddleware to the pipeline.
During a request...
Your custom request middleware will set up request services based on the inbound request information.
The RequestServicesContainerMiddleware will see that request services are already set up and will do nothing.
When services are resolved, the request service scope will already be the tenant scope as set up by your custom request middleware and the correct thing will show up.
I tested this locally by switching the tenant ID to come from querystring rather than host name (so I didn't have to set up hosts file entries and all that jazz) and I was able to switch tenant by switching querystring parameters.
Now, you may be able to simplify this a bit. For example, you may be able to get away without a startup filter by doing something directly to the web host builder in the Program class. You may be able to register your startup filter right with the ContainerBuilder before calling builder.Populate and skip that Insert call. You may be able to store the IServiceProvider in the Startup class property if you don't like having Autofac spread through the system. You may be able to get away without a static container property if you create the middleware instance and pass the container in as a constructor parameter yourself. Unfortunately, I already spent a loooot of time trying to figure out the workaround so I'm going to have to leave "optimize it" as an exercise for the reader.
Again, sorry this wasn't clear. I've filed an issue on your behalf to get the docs updated and maybe figure out a better way to do this that's a little more straightforward.
I have an alternate solution, related to work I've done on a pending PR on the Autofac DI extension. The solution there can't be used exactly, because it depends on classes that are (rightly) internal. It can be adapted by providing shims that reproduce the functionality in those classes. Since they are compact, this doesn't require the addition of a lot of code. Until the functionality is fixed, this is the solution I'm using.
The other aspect of the solution is to eschew the custom middleware and instead make the ITenantIdentificationStrategy a service that can take any dependency required to do what it needs to.
Fixing the DI
The "DI" side of the problem is that the Autofac DI extension uses resolution to supply IServiceProvider and IServiceScopeFactory implementations. This is possible, because under the hood these are IComponentContext and ILifetimeScope (which are themselves different interfaces for the same thing). In most cases this works fine, but ASP.NET Core proceeds by resolving a singleton IServiceScopeFactory very early in the application cycle. In a multi-tenant scenario this resolution will return the ILifetimeScope for either the first tenant requested, or for the "default" tenant, and that will be the root scope (as far as MS DI is concerned) for the application lifetime. (See the PR for further discussion.)
The classes below implement an alternate behavior: instead of resolving the DI interfaces, it builds (news-up) the initially-requested ones from the IContainer directly. With the initial IServiceScopeFactory based directly on IContainer, further scope requests will resolve correctly.
public class ContainerServiceProvider : IServiceProvider, ISupportRequiredService
{
private readonly IContainer container;
public ContainerServiceProvider(IContainer container)
{
this.container = container;
}
public object GetRequiredService(Type serviceType)
{
if (TryGetContainer(serviceType, out object containerSvc)) return containerSvc;
else return container.Resolve(serviceType);
}
public object GetService(Type serviceType)
{
if (TryGetContainer(serviceType, out object containerSvc)) return containerSvc;
else return container.ResolveOptional(serviceType);
}
bool TryGetContainer(Type serviceType, out object containerSvc)
{
if (serviceType == typeof(IServiceProvider)) { containerSvc = this; return true; }
if (serviceType == typeof(IServiceScopeFactory)) { containerSvc = new ContainerServiceScopeFactory(container); return true; }
else { containerSvc = null; return false; }
}
}
// uses IContainer, but could use copy of AutofacServiceScopeFactory
internal class ContainerServiceScopeFactory : IServiceScopeFactory
{
private IContainer container;
public ContainerServiceScopeFactory(IContainer container)
{
this.container = container;
}
public IServiceScope CreateScope()
{
return new BecauseAutofacsIsInternalServiceScope(container.BeginLifetimeScope());
}
}
// direct copy of AutofacServiceScope
internal class BecauseAutofacsIsInternalServiceScope : IServiceScope
{
private readonly ILifetimeScope _lifetimeScope;
/// <summary>
/// Initializes a new instance of the <see cref="AutofacServiceScope"/> class.
/// </summary>
/// <param name="lifetimeScope">
/// The lifetime scope from which services should be resolved for this service scope.
/// </param>
public BecauseAutofacsIsInternalServiceScope(ILifetimeScope lifetimeScope)
{
this._lifetimeScope = lifetimeScope;
this.ServiceProvider = this._lifetimeScope.Resolve<IServiceProvider>();
}
/// <summary>
/// Gets an <see cref="IServiceProvider" /> corresponding to this service scope.
/// </summary>
/// <value>
/// An <see cref="IServiceProvider" /> that can be used to resolve dependencies from the scope.
/// </value>
public IServiceProvider ServiceProvider { get; }
/// <summary>
/// Disposes of the lifetime scope and resolved disposable services.
/// </summary>
public void Dispose()
{
this._lifetimeScope.Dispose();
}
}
Fixing Identification Strategy
As for making the identification-strategy a service, I would rework your implementation like so:
public class MultitenantIdentificationStrategy : ITenantIdentificationStrategy
{
public const string DefaultTenantId = null;
private readonly IHttpContextAccessor contextaccessor;
public MultitenantTenantIdentificationStrategy(IHttpContextAccessor contextaccessor)
{
this.contextaccessor = contextaccessor;
}
public bool TryIdentifyTenant(out object tenantId)
{
var context = contextaccessor.HttpContext;
// after this is unchanged
.
.
}
.
.
}
Use in Startup.ConfigureServices
This shows the fragment of how these last few pieces are registered and fed to MS DI for ASP.NET.
. . .
builder.RegisterType<MultitenantIdentificationStrategy>().AsImplementedInterfaces(); // tenant identification
// register do Autofac DI integration
builder.Populate(services);
var underlyingcontainer = builder.Build();
ApplicationContainer = new MultitenantContainer(underlyingcontainer.Resolve<ITenantIdentificationStrategy>(), underlyingContainer);
return new ContainerServiceProvider(ApplicationContainer);
If you find this solution workable, please give a thumbs up to DI PR 10--or PR 11, if after reviewing you think that is the better/more elegant solution. Either will save having to add the "shim" code above.