-
Notifications
You must be signed in to change notification settings - Fork 3.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Make IDbContext #16470
Comments
@eltiare Shipping an interface for |
I might be completely missing the idea here, please bear with me:
Can you expand on that? Isn't the interface SUPPOSED to be the API contract? You're not locking anything into the framework, it's enabling the application of dependency inversion as well as interface segregation. The lack of an EF is already a repository pattern, it seems counter-intuitive to force users to implement their own abstraction on top of that if it isn't necessary? Sure, some will want to, but in other cases it's not a necessity. It feels like selecting implementation of principles that Asp.Net Core and other .Net Core projects has been putting to great use. Example: Say I have three projects. Following the Clean Architecture, but implementing DDD (Domain Driven Design):
The reference structure goes The necessity to have business logic within domain entities (specifically the aggregate root), puts those into the My Domain Services expect an interface that's equatable to The problem now is The jist of this is the lack of an abstract API for Hopefully you can see how the exclusion of DI-friendliness can quickly cascade into a gigantic host of other problems. |
Everything is evolving isn't it? This should not be a reason to not use Interfaces. I agree with @eltiare that IDbContext would be helpful. Even if the API is evolving, simply update the interface if necessary to allow for any evolutions. It will only affect those that are referencing the interface, and they can adjust the code appropriately. An example could be:
|
The issue is that adding a new method to an interface breaks any existing implementations of that interface. However, now with interface default methods we may be able to mitigate this in ways that were not possible in the past. Re-opening to consider. |
We discussed this in triage and it isn't something we are going to implement, mainly because attempting to create different implementations of the |
Revisiting this as this has been a pain for several projects when using DDD now.... @ajcvickers Aren't you and your team completely sidestepping the point of an interface in this context? Asp.net core clearly shows how effectively dependency inversion and inversion of control works. EF Core on the other hand seems to pretend it doesn't exist, and in the process creates a lot of unnecessary headaches. An This also ties into the lack of interfaces for This breaks IoC, and forces us to adopt awkward, best-practice violating, inconsistent, architectures because EF Core is the odd one out. Unless of course I copy/paste the method & property signatures from |
To understand the request better, are you asking for an interface in order to mock the DbContext for testing purposes? Or is your aim to use IDbContext in your actual product code? If it's the latter, can you provide more details on why you think an interface is needed for that, rather than subclassing DbContext? I'm looking to understand exactly what's blocking you because of the lack of an interface. An interface usually makes more sense where it's reasonable for the implementation to be swapped out for another, which really isn't the case for DbContext. |
@roji I think @douglasg14b clearly states why he wants the interface version - so we can effectively utilize DI, which means ease of testing. Yes an interface makes sense when you need implementations to be swapped out, but not usually. In fact most of the time interfaces are used to make way for testing, why do you think we make interfaces for services every time we use it in a controller or another service, do you think because we plan to swap out the implementation? Of course not, we do it for testing. Dependency injection is a pattern which is clearly promoted by the .Net community and in the case of DbContext there's a clear need for it, yet you seem to be under the impression that @douglasg14b is introducing a new concept. Also subclassing the DbContext does not solve the issue which is again ease of testing. Yes we can introduce a repository pattern, but there's lot of reasons we don't want to do that - firstly its another abstraction over abstraction and secondly you lose a lot of power and flexibility that your ORM provides. |
@alcjamgit @douglasg14b we generally don't believe that mocking the DbContext for testing is a successful testing pattern - mainly because it implies mocking LINQ as well (in order to do any operation on that context). We generally recommend either:
|
This isn't necessarily for JUST testing, don't get hungup on individual items. We shouldn't have to enumerate the numerous ways IoC and Dependency Inversion are used in OOP languages. There is literature on those topics spanning back decades, and more recently plenty of guidelines and documentation on how they are effectively implemented and made first-class citizens in .Net Core libraries like Asp.Net Core. The point is that EF Core prevents effective application of core tenants of SOLID, and it hurts the architecture of many projects, and is in direct conflict with application design guidelines and other libraries. Emphasis above. Here is an overly simplified example: Say your project follows the clean architecture and Domain Driven Design. You have an You need But wait. EF Core provides no This gets much more complicated when applying DDD and you start considering the lack interface support for DbSet and it's entities, application architecture layers start to get intermingled all because one library (EF Core) is being an outlier as far as DI and IoC goes. However, that DbSet issue is for a different topic, but the point still stands. See where this is going? |
@douglasg14b FWIW my response was about testing since this is what @alcjamgit wrote about above. @douglasg14b I'm not aware of a principle in DI, SOLID or IoC that says that everything must have an interface. Regardless, EF Core is successfully and widely-used in many DI scenarios (e.g. virtually every ASP.NET+EF application). You are typically expected to have your AppDbContext in a relatively low data layer (I guess that would correspond to your Core layer), and reference it from above layers, wherever data operations need to happen. |
@roji, you're typically expected to have AppDbContext in a low data layer, because that's the only option (yes with InMemory Database you can directly use DbContext), but that doesn't mean you can't make it better, does it? What's your measure of success wide adoption? Yes, wide adoption EF Core has that, that doesn't mean developer experience is great when it comes to DI. |
Just in case any newbies stumble across this thread: Do NOT test against your actual production database. Make a copy if you want real life data and test against that. |
@eltiare thanks, that was some poor wording on my part 😄 Corrected that comment... |
If you end up testing against your actual production database, keep a backup job handy. |
+1 for adding an IDbContext @roji - Say for example we have an app with more than one than one type of concrete DbContext and we want to use DI and/or create methods that operate upon an interface. This may be e.g. to avoid circular references in actual production code (in our case, from using a fork of the DbContextScope library) or to allow for mocking. How can we do that currently? There's no IDbContext that the interfaces can inherit from, so we have to create our own which, as mentioned above, imposes an additional maintenance burden. |
I don't think @roji cares :/ they seemed unwilling to even give the principles that .NET uses as foundations a second thought when it comes to this issue... Which is pretty disappointing coming from a .NET Platform org member. This issue would be open otherwise, as it's a very valid problem caused by an implementation ignorant of these principles. Obviously logic and reason won't work here, hopefully more individuals who are adopting these patterns & principles start showing up to complain, maybe that will prompt... something? |
@zejji and @douglasg14b, I think it's really worth distinguishing between the question of a DbContext interface for testing/mocking purposes, and the question of that interface for actual application use. For testing (see docs), we do recommend testing against your actual database whenever possible. But when that's not desirable, the recommend way to mock is to use the InMemory database. That functionally gives you a DbContext working against memory data structure, which you can assert against etc. An actual mock of a DbContext for this purpose is a problematic proposal because mocking LINQ can be very difficult, depending on exactly what you're doing. However, having written all that, there's nothing preventing mocking DbContext via subclassing, rather than via an interface, if that's really desired. Now, regarding a DbContext interface for non-testing purposes...
I'm not sure I'm following your scenario. If you're proposing to inject actual DbContexts in a non-testing scenario, how do you propose to run queries against those contexts, given that LINQ queries are strongly-typed etc.? What would the shape of IDbContext look like in that scenario? @douglasg14b I promise you that I do care - but I'm still trying to understand precisely what you find insufficient. Looking at the clean architecture which you linked to above, the "ASP.NET Core Architecture" diagram represents the recommended architecture for an ASP.NET DI application. The EF DbContext is in the Infrastructure layer, the entity POCOs are in Core (which Infrastructure depends on), etc. How is the current EF design not respecting that document? And as above, what exact API shape would IDbContext have, and how would you use it in a productive way? A code sample of what you're looking for could help clear up the confusion here. |
@roji I have run into a similar issue, and maybe I can help break it down a little better. When working with the Clean Architecture (I have used this template in the past). You are essentially splitting the Product into consumable loosely coupled projects as follows. In order to avoid a circular dependencies in the csproj configuration:
This setup creates an awkward workaround with an interface, because the application layer cannot include the infrastructure layer(this would create a circular dependency) an IApplicationDbContext interface is created to bridge the gap. The interfaces helps infer the available EF service injected from the infrastructure layer. The Application layer cannot include the infrastructure layer in order to extend the context, resulting in the need to hardcode out all of the available context members. While this is doable, the ability to extend out an IDbContext would significantly improve the ability and accuracy of representing EF as a services when direct access to the DbContext is not avalible. Feel free to reference this repo that implements this template. Files important to this issue:
Also if you would like this issue to be specific to testing, would you like another issue to be created for application use? I think most people are bringing this up here because of the issue title not because of eltiare's original issue (though I think it may come back to the same solution). |
@Rob-Page thanks for trying to provide more details. I've re-read all the documents and sample, and I'm still struggling to understand the issue: As I wrote above, the ASP.NET Core Infrastructure part in the clean architecture document places the DbContext in Infrastructure project, and not in the Application project. Is there any reason why EF Core should be present in your Application project, even in the form of an interface? As far as I can tell from the document, the recommendation is to have your entities there, but definitely not your context. More importantly, seeing your IApplicationDbContext.cs, is there anything stopping you from implementing that in your concrete DbContext implementation? In other words, why is an IDbContext interface needed (which is what this issue is about), and what would its shape even be? |
@roji thank you for your response, upon further investigation and thinking through your comments, I believe that you are correct that this is not really an issue that EF Core can solve. I had knee jerk reaction to extend a an interface in order to easily access all of the methods and properties so that things, "work right." This is what erroneously drove my desire for a DbContext Interface. In this scenario the complex architectural setup has costs. Your last suggestion of implementing a custom IApplicationDbContext in the DbContext helped flip my brain around enough to see that this is an item that the developer should be maintaining as it is very specific to the application setup and implementation of EF Core. I am going to test out that and a few more ideas and when I have a more concrete solution for my application I will post an update here so that people that run into this in the future will have a little direction. Thanks again for the suggestions and direction here, and to the whole team that works on EF Core, it is an amazing product! |
@Rob-Page Thanks for the additional validation on this concept/design. It's really nice to see some external validation on this thread. :-) Also, thanks for the kind words. |
@Rob-Page did't you found some clean solution for this? I am refactoring my project to use the same clean architecture as you mentioned and I am having the same problem 🤕 |
It's an interesting issue. Of course, we can create our own interfaces and implementations, maybe generic ones, but isn't this a violation of the DRY and KISS rules? Because in that we would have two repository patterns, two UoW patterns. We can think about our UoW/Repositories as adapters to EF core ones, or bridges. It would be great to see an example of how to properly abstract from EF Core(for example, during the implementation of the DDD architecture) and not to reinvent the whole framework. |
why its closed if it's very important? rn i have problem with multiple repositories. i cant do multiple inheritance from classes, also dont want to create interface for DbContext that will have all methods that DbContext has |
@ParadiseFallen please read the discussion above, specifically about mocking vs. production uses; if you have more context or something new to add to the above, please post that and we can discuss. |
interface IDbContext
{
void SaveChanges();
//.... etc change tracker and other staff
}
interface IIdentityDbContext<...> : IDbContext
{
//.... identity staff
}
// comes from package CANT CHANGE!
interface IFileStorageDbContext : IDbContest
{
DbSet<Files> Files {get;set;}
}
// this one in project
class ApplicationDbContext : IdentityDbContext<...>, IIdentityDbContext<...>,IFileStorageDbContext
{
// cuz interface said that you need to impliment it!
DbSet<Files> Files {get;set;}
// just a identity db context
}
// comes from package CANT CHANGE!
class FileStorageService
{
private IFileStorageDbContext Context {get;}
FileStorageService(IFileStorageDbContext context)=>Context = context;
public void Foo()
{
Context.Files.Add(...);
Context.SaveChanges();
}
} in that sample you has an repository for files and actuall |
if c# has multiple inheritance it will be easy to do just //from nuget package
class FilesDbContext : DbContext
{
DbSet<Files> Files {get;set;}
}
// from package
class FileService
{
[Inject]
prop FilesDbContext Context;
}
// in project
class ApplicationDbContext: IdentityDbContext<...>, FilesDbContext
{
} |
@ParadiseFallen Honestly I've given up on this item, EF Core seems staunchly opposed to following patterns the wider .Net ecosystem have been increasingly adopting since .Net Core 1 (And even long before that). Especially since it's essentially a sibling project to Asp.Net Core which has laid down great guidelines & patterns for better architecture code bases (eg. native dependency inversion encouragement). EF Core fits into these patterns like a square peg into a round hole. I've come to terms with accepting that EF Core will push against certain patterns, seemingly without logical reasoning, and will just be that sore-thumb sticking out in DDD-like codebases, with parts of the codebase breaking from the expectations solely for EF core usage. :/ Maybe one day I'll make am more detailed example project highlighting the problem, but that's a lot of effort to be dismissed through a lack of understanding again. Edit: Also multi-inheritance is something that will probably never happen in C#, that's a mess. Also your use case is kind of outside of the purpose of this discussion, though having a sane interface does open up how we can use EF Core, and lets it slot in much easier depending on the architecture. |
@douglasg14b multiple inheritance is bad. i just said that that can be solution in c++. also i send example only with interfaces tho its show actuall problem |
it will be enough to create an interface for everything to work. we just can't implement it in ef core. this should be done by the efcore guys |
@ajcvickers @roji @douglasg14b https://github.com/ParadiseFallen/efcore-IDbContext-Issue I hope you will understand my bad english. I will be glad to answer your questions |
@ParadiseFallen The only thing required to make the code in your project work is to define your own public interface IDBContext
{
Task<int> SaveChangesAsync(CancellationToken cancellationToken = default);
} Add any other methods from DbContext that you need in the abstraction. |
@ajcvickers then it will be stupid copy / paste. It will be nice to have IDbContext in EFCore by default. Why you dont want to made it? cuz it seems that even i can write that kinda interfaces. I just need that I can write that interfaces and made pull request for them if you dont want to write it. |
@ParadiseFallen There is a lot of discussion above covering the reasons why it won't be added to EF Core itself. I don't intend to re-engage on that discussion. |
I ran into some similar issue which is why I end up here. Following Clean Architecture, I have these layers
I have no problem using DbContext as is in my Core layer for various reasons. I treat DbContext as my Repository implementation and thus Core contains DbContext and any database (provider) related things, I move them into Infrastructure layer. But I am stuck on a little problem. I have to register
|
@ajcvickers |
Are there any other libraries that allow us to do Dependency Invertion? |
I could not find anything specific to this particular problem. Apologies if this is a dup. I feel an interface for DbContext would be helpful for inheriting context interfaces. Something like this:
I'm thinking it might make testing easier because you could drop in anything that conforms to the interface in testing / different environments.
EF Core version: 2.2.4
The text was updated successfully, but these errors were encountered: