Registering multiple DbContext Instances on startup for use in Generic Repository

.net-core entity-framework-core


I'm trying to create a Generic repository which accepts 2 generic types e.g.

public class EfRepository<T, TS> : IAsyncRepository<T,TS> where T : BaseEntity
                                                           where TS : DbContext

and in my startup.cs I have the usual mapping:

services.AddScoped<DbContext, ConfigDbContext>();

How can I now add another mapping to DbContext? I have tried adding another mapping between DbContext and another Context i created but It only ever uses the first mapping.

I have multiple databases I need to consume and would ideally like to have a DbContext for each one but i can't see a way of having multiple DI mappings.

In my EfRepository class the following code exceptions when I add an extra DbContext to my code and use it:

protected readonly DbContext _dbContext;
        public EfRepository(DbContext dbContext)
            this._dbContext = (TS)dbContext;

The exception is Unable to Convert from Type1 to Type2 and I know this is because the DbContext is bound to Type1 in my startup.cs.

How can I (if possible) use multiple DbContext's in a generic fashion?

7/8/2019 2:39:07 PM

Popular Answer

That's not how you register DbContext, which is the source of your problem. The correct method is:

services.AddDbContext<ConfigDbContext>(o =>

Done correctly, adding another is exactly the same:

services.AddDbContext<SomeOtherContext>(o =>

Then, which one gets pulled depends on which one you inject, which yes, does mean that you need to specify the actual type you want to inject, not DbContext generically. However, that can be done only one the derived class. In other words, you can keep the code you have (though you should not cast the context) and simply do:

public class FooRepository : EFRepository<Foo, ConfigDbContext>
    public FooRepository(ConfigDbContext context)
        : base(context) {}

You can leave it upcast to DbContext, as you don't need the actual type to do EF things. To get at the DbSets, you can use the generic Set<T>:

var foos = _dbContext.Set<Foo>();

And now, with all that said, throw it all out. It's completely unacceptable to use the repository pattern with an ORM like EF. EF already implements the repository and unit of work patterns. The DbContext is your unit of work, and each DbSet is a repository. Adding an extra layer on top of that does nothing but add maintenance concerns and extra entropy to your code, and frankly, creating a repository/unit of work that plays nice with EF, is so trying as to be impossible, so more often than not, you're just going to hamstring EF, making it less efficient and harder to use.

Using an ORM like EF is choosing to use a third-party DAL. That is all. There's no need to create your own DAL at that point, because you've outsourced it. I'm not sure why so many people get hung up on this. When was the last time you created your own routing framework or your own templated view preprocessor. Never. You just third party libraries for that (the framework), so why is it a problem to use a third party library for your DAL as well?

Then, you make ask well what about abstracting the EF dependency. Well, first, if you're thinking you might switch ORMs some day down the road, you won't. It just never happens. You'll sooner rewrite the whole app from the ground up. Second, the repository pattern doesn't even achieve this. You still have an EF dependency that bubbles all the way up to the front-line app. There's no way around that.

For true abstraction, you can use something like microservices architecture. Other than that, just embrace the dependency or don't use it at all, and really create your own DAL.

7/8/2019 3:05:50 PM

Related Questions


Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow