registering DbContext with multiple parameters - entity-framework

I'm trying to inject TenantProvider into DbContext
public class AppDbContext : IdentityDbContext<ApplicationUser, ApplicationRole, long>
{
public int? _tenantId;
public ITenantProvider _tenantProvider;
public AppDbContext(
DbContextOptions<AppDbContext> options,
ITenantProvider tenantProvider
)
: base(options)
{
_tenantProvider = tenantProvider;
}
but I don't understand how to register it correctly - if I put the breakpoint in the constructor - tenantProvider is null.
The bit from Startup.cs
services.AddDbContext<AppDbContext>(options => AppDbContextOptionsBuilder.Get());
the next line is required to inject the DbContext into a controller or a service (if I add ServiceLifetime.Scoped as a second parameter to the method above - AddDbContext - the feature doesn't work):
services.AddScoped(p => new AppDbContext(AppDbContextOptionsBuilder.Get(), p.GetService<ITenantProvider>()));
(Entity Framework is a separate project in my solution)
When using .AddScoped method - we can pass TenantProvider into constructor by resolving it using .GetService method.
Does anyone have an idea of how to resolve TenantProvider in .AddDbContext method?
Additional info:
I was trying to replace ITenantProvider in the constructor of DbContext with IHttpContextAccessor - the latter is registered as singleton. But the acessor parameter is still null.
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();

I don’t really understand what your AddScoped call is supposed to do. AddDbContext will already register the database context properly with the service collection. So when you resolve the context through dependency injection, additional dependencies will be automatically resolved.
So it should be enough to do this:
services.AddDbContext<AppDbContext>(options => …);
services.AddSingleton<ITenantProvider, TenantProvider>();
And then, you can depend on your AppDbContext using constructor injection, e.g. in your controllers.
Two notes though:
When configuring options, you should modify the passed options object. So you should not just return AppDbContextOptionsBuilder.Get() but instead use the passed options object and edit that.
You should really think about whether your database context having a dependency on your tenant provider is the right thing to do. As per SRP, your database should only do a single thing and that is provide database access.
Depending on how your tenant provider affects your database access, it might make more sense to move this dependency up one level into some service that uses both the database context and the tenant provider to query data in the right way.

Related

After AddDbContext now I want tu use my context

I decided to use this AddDbContext method to add and setup my context for my Entity Framework Core project.
services.AddDbContext<ExampleContext>(options => options.UseSqlServer(Configuration.GetConnectionString("ExampleConnection")));
// https://stackoverflow.com/a/51970589/196526
I suppose this AddDbContext allow us to add a global context and it is possible to retrieve it later when required in my controller or service class. How can I use it?
Well, dotnet core has got dependency injection inbuilt now. The way you use it in your controllers, service or repository classes is as simple as through a constructor injection.
Example -
public class AccountRepository : IAccountRepository{
private readonly DbContext _exampleContext;
public AccountRepository(ExampleContext context){
_exampleContext = context;
}
}

How do I implement DbContext inheritance for multiple databases in EF7 / .NET Core

I am building web APIs in ASP.NET Core 1.1.
I have a number different databases (for different systems) which have common base schemas for configuration items such as Configuration, Users and groups (about 25 tables in all). I am trying to avoid duplicating the quite extensive EF configuration for the shared part of the model by inheriting from a base class as shown in the diagram.
However, this does not work because of the Entity Framework (EF) requirement to pass DbContextOptions<DerivedRepository> as a parameter to the constructor, where DerivedRepository must match the type of the repository the constructor is called on. The parameter must then be passed down to the base DbContext by calling :base(param).
So when (for example) InvestContext is initialised with DbContextOptions<InvestContext>, it calls base(DbContextOptions<InvestContext>) and EF throws an error because the call to the ConfigurationContext constructor is receiving a parameter of type DbContextOptions<InvestContext> instead of the required type DbContextOptions<ConfigurationContext>. Since the options field on DbContext is defined as
private readonly DbContextOptions _options;
I can't see a way around this.
What is the best way to define the shared model once and use it multiple times? I guess I could create a helper function and call it from every derived context, but it's not nearly as clean or transparent as inheritance.
I would like to bring this post from the OP's GitHub issue to everyone's attention:
I was able to resolve this without a hack by providing a protected constructor that uses DbContextOptions without any type. Making the second constructor protected ensures that it will not get used by DI.
public class MainDbContext : DbContext {
public MainDbContext(DbContextOptions<MainDbContext> options)
: base(options) {
}
protected MainDbContext(DbContextOptions options)
: base(options) {
}
}
public class SubDbContext : MainDbContext {
public SubDbContext (DbContextOptions<SubDbContext> options)
: base(options) {
}
}
OK, I have got this working in a way which still uses the inheritance hierarchy, like this (using InvestContext from above as the example):
As stated, the InvestContext class receives a constructor parameter of type DbContextOptions<InvestContext>, but must pass DbContextOptions<ConfigurationContext> to it's base.
I have written a method which digs the connectionstring out of a DbContextOptions variable, and builds a DbContextOptions instance of the required type. InvestContext uses this method to convert its options parameter to the right type before calling base().
The conversion method looks like this:
protected static DbContextOptions<T> ChangeOptionsType<T>(DbContextOptions options) where T:DbContext
{
var sqlExt = options.Extensions.FirstOrDefault(e => e is SqlServerOptionsExtension);
if (sqlExt == null)
throw (new Exception("Failed to retrieve SQL connection string for base Context"));
return new DbContextOptionsBuilder<T>()
.UseSqlServer(((SqlServerOptionsExtension)sqlExt).ConnectionString)
.Options;
}
and the InvestContext constructor call changes from this:
public InvestContext(DbContextOptions<InvestContext> options):base(options)
to this:
public InvestContext(DbContextOptions<InvestContext> options):base(ChangeOptionsType<ConfigurationContext>(options))
So far both InvestContext and ConfigurationContext work for simple queries, but it seems like a bit of a hack and possibly not something the designers of EF7 had in mind.
I am still concerned that EF is going to get itself in a knot when I try complex queries, updates etc. It appears that this is not a problem, see below)
Edit: I've logged this problem as an issue with the EF7 team here, and a team member has suggested a change to the EF Core core as follows:
"We should update the check to allow TContext to be a type that is derived from the current context type"
This would solve the problem.
After further interaction with that team member (which you can see on the issue) and some digging through the EF Core code, the approach I've outlined above looks safe and the best approach until the suggested change is implemented.
Depending on your requirements you can simply use the non type specific version of DbContextOptions.
Change these:
public ConfigurationContext(DbContextOptions<ConfigurationContext> options):base(options)
public InvestContext(DbContextOptions<InvestContext> options):base(options)
to this:
public ConfigurationContext(DbContextOptions options):base(options)
public InvestContext(DbContextOptions options):base(options)
Then if you create your ConfigurationContext first, the classes that inherit it seem to get the same configuration. It may also depend on the order in which you initialize the different contexts.
Edit:
My working example:
public class QueryContext : DbContext
{
public QueryContext(DbContextOptions options): base(options)
{
}
}
public class CommandContext : QueryContext
{
public CommandContext(DbContextOptions options): base(options)
{
}
}
And in Startup.cs
services.AddDbContext<CommandContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddDbContext<QueryContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
alternatively, in a test class:
var connectionString = "Data Source=MyDatabase;Initial Catalog=MyData;Integrated Security=SSPI;";
var serviceProvider = new ServiceCollection()
.AddDbContext<QueryContext>(options => options.UseSqlServer(connectionString))
.BuildServiceProvider();
_db = serviceProvider.GetService<QueryContext>();

Getting user information in DbContext

I am attempting to use EF migrations to build/seed my database but my DbContext is causing an error when I use the update-database command.
The DbContext I have implemented has two constructors: one accepts no arguments and the other accepts an IUserContext interface. The IUserContext interface returns a username as a string. The IUserContext is used in the SaveChanges() method for setting user-related audit fields (ex. CreatedBy, UpdatedBy) common to all of my entities. Depending on the implementation of the IUserContext interface, the username may be retrieved from a HttpContext (in a mvc app), WindowsIdentity (in a console app), etc.
If a user attempts to call SaveChanges on the DbContext and the IUserContext is not set, it throws an exception. Basically, I do not want changes saved in the DbContext if a username cannot be provided by the IUserContext interface for purposes of recording who is making the modifications. If the DbContext is being used just for querying, using the no-args constructor isn't an issue since IUserContext is only used during SaveChanges.
When I attempt to use the update-database command, the DbMigrationsConfiguration is given an instance of the DbContext instantiated using the no-args constructor. Therefore, it throws an exception when it tries to invoke the DbContext's SaveChanges after the Seed() method.
Question: How do I pass an IUserContext to my DbContext so the update-database command will not fail and, more importantly, be able to set the appropriate entity fields to the name of the user seeding the changes? Is there some form of DI or other customization I can perform in the DbMigrationsConfiguration? Should I simply add an IUserContext accessor method to the DbContext so the IUserContext can be set outside of the constructor?
Maybe I should be asking a larger question: What is the best (most generic?) way of relaying user information to a DbContext for the purposes of recording who is making modifications? I gave some thought about moving this logic to the business layer of my application but it seems so convenient to encapsulate it in the SaveChanges of the DbContext (even more so when change tracking is enabled) .
We have a similar situation where we record modified user in SaveChanges() override. What we ended up doing was creating a BaseWrapper and use IoC to fill it. The second part is creating a MigrationsContextFactory that migrations will use to create the context. This class lives in the same folder as our ApplicationDbContext.
using System.Data.Entity.Infrastructure;
namespace MyApp.Data
{
public class MigrationsContextFactory : IDbContextFactory<ApplicationDbContext>
{
public ApplicationDbContext Create()
{
return new ApplicationDbContext(new HttpContextBaseWrapper());
}
}
}
public class HttpContextBaseWrapper : IHttpContextBaseWrapper
{
public string UserName
{
get
{
if (HttpContext.Current == null || HttpContext.Current.User == null)
return string.Empty;
return HttpContext.Current.User.Identity.Name;
}
}
}
https://marazt.wordpress.com/2015/01/09/entity-framework-dbcontext-idbcontextfactory-and-codefirst-migration-problem/

Workflow: Creating Dependency Chain with Service Locator Pattern

I'm trying to get dependencies set up correctly in my Workflow application. It seems the best way to do this is using the Service Locator pattern that is provided by Workflow's WorkflowExtensions.
My workflow uses two repositories: IAssetRepository and ISenderRepository. Both have implementations using Entity Framework: EFAssetRepository, and EFSenderRepository, but I'd like both to use the same DbContext.
I'm having trouble getting both to use the same DbContext. I'm used to using IoC for dependency injection, so I thought I'd have to inject the DbContext into the EF repositories via their constructor, but this seems like it would be mixing the service locator and IoC pattern, and I couldn't find an easy way to achieve it, so I don't think this is the way forward.
I guess I need to chain the service locator calls? So that the constructor of my EF repositories do something like this:
public class EFAssetRepository
{
private MyEntities entities;
public EFAssetRepository()
{
this.entities = ActivityContext.GetExtension<MyEntities>();
}
}
Obviously the above won't work because the reference to ActivityContext is made up.
How can I achieve some form of dependency chain using the service locator pattern provided for WF?
Thanks,
Nick
EDIT
I've posted a workaround for my issue below, but I'm still not happy with it. I want the code activity to be able to call metadata.Require<>(), because it should be ignorant of how extensions are loaded, it should just expect that they are. As it is, my metadata.Require<> call will stop the workflow because the extension appears to not be loaded.
It seems one way to do this is by implementing IWorkflowInstanceExtension on an extension class, to turn it into a sort of composite extension. Using this method, I can solve my problem thus:
public class UnitOfWorkExtension : IWorkflowInstanceExtension, IUnitOfWork
{
private MyEntities entities = new MyEntities();
IEnumerable<object> IWorkflowInstanceExtension.GetAdditionalExtensions()
{
return new object[] { new EFAssetRepository(this.entities), new EFSenderRepository(this.entities) };
}
void IWorkflowInstanceExtension.SetInstance(WorkflowInstanceProxy instance) { }
public void SaveChanges()
{
this.entities.SaveChanges();
}
}
The biggest downside to doing it this way is that you can't call metadata.RequireExtension<IAssetRepository>() or metadata.RequireExtension<ISenderRepository>() in the CacheMetadata method of a CodeActivity, which is common practice. Instead, you must call metadata.RequireExtension<IUnitOfWork>(), but it is still fine to do context.GetExtension<IAssetRepository>() in the Execute() method of the CodeActivity. I imagine this is because the CacheMetadata method is called before any workflow instances are created, and if no workflow instances are created, the extension factory won't have been called, and therefore the additional extensions won't have been loaded into the WorkflowInstanceExtensionManager, so essentially, it won't know about the additional extensions until a workflow instance is created.

Implementing passive attributes with dependencies that should be resolved by a DI container

I'm trying to implement passive attributes in an ASP.NET Web API. The filter I'm implementing has a dependency on a repository, which itself has a dependency on a custom DbContext.
In the post it says that you can resolve the component with a DI container, but also that the code should be invoked from Application_Start.
I'm not sure how to implement this, while taking advantage of the DI container's lifetime management capabilities (so that a new DbContext will be used per request). Would injecting an abstract factory be a good solution for this? or is there something simpler that I'm missing.
You can resolve this issue by sliding a Decoraptor in between the Filter and the Repository.
Not knowing a lot about your code, you should be able to define a Decoraptorepository using an Abstract Factory:
public class Decoraptorepository : IRepository
{
private readonly IFactory<IRepository> factory;
public Decoraptorepository(IFactory<IRepository> factory)
{
this.factory = factory;
}
// Just guessing IRepository's member(s) here...
public void Save(Foo foo)
{
this.factory.Create().Save(foo);
}
// other members...
}
This enables your Filter to stay a Singleton, while the actual Repository is being created in a Transient manner.
If you need to dispose of objects too, please refer to the follow-up article on how to decommission Transient objects from within a Decoraptor.