-
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
Trying to understand why setting a entity to detached can alter the loaded classes #19926
Comments
PS. I tried this on EF Core 2.1 and everything worked, i.e. it passed all the asserts. I did have to add a |
This is not something that I would recommend, although I have come to accept that people are going to do it. :-) In my opinion its better to have tests that do setup in one context instance and then test in another context instance. This matches the one context per unit-of-work guideline much more closely that attempting to reset state. This is what I always show when posting examples. But, as I said, people do what to do this. Issue #15577 would make this easier and safer. We are also looking into ways to make it easier to get a short-lived context instance from D.I. (#18575) but that's probably not relevant to your tests. As well as being less clean than creating a new context instance, detaching also has some other issues:
All this being said, I think you are running into bugs here. In particular, take a look at #19911 which is a PR I intend to merge today (or at the weekend, based on how today is going!) and will hopefully make it into the March patch. The issues linked from this PR seem to cover cases like yours. If you could try your code with the daily build of 5.0 and verify that it no longer fails, then that would be great! You may also want to look into changing the default cascade and delete orphans timing as a workaround, as described in those issues. Hope this helps! :-) |
Thanks @ajcvickers for taking the time to explain that. To elaborate why I was looking for a simpler way to separate the data setup to the actual test is because using two DbContexts often (always?) requires passing a key(s) in the outer level for the second part to use. For instance: [Fact]
public void TestLinkEntitiesOk()
{
//SETUP
int pk;
var options = //... setup database options. in-memory
using (var context = new MyContext(options))
{
context.Database.EnsureCreated();
pk = ...setup data and return its primary key; //note: could be a few of these
}
using (var context = new MyContext(options))
{
//ATTEMPT
//... run test that uses pk to pick up setup data
//VERIFY
//etc....
}
} This takes more time so I (and other) only do it when we think its needed (I am a working contractor and being fast is quite important to my client). Recently I missed some bugs because I didn't use a two-context approach, which was bad. What I would looking for is a method which I would at ALL my unit tests involving the database. Then I would a) never miss a missing .Include etc. and b) its really quick/easy to add. For that reason I think issue #15577 could be a good fit. Also, as you said, there is clearly a bug there. I have subscribed to #19911 and will track that and test. |
@JonPSmith We talked about this, but we really don't see how creating two context instances is significantly more work when writing tests than using a single context instance. With copy/paste and refactoring tools it should literally take only a few seconds more. Also, when I'm writing tests I tend to avoid using the PK value unless I need to for the specific test I am writing. Often the data has some other unique value (e.g. username) that is not dynamically generated and therefore can be hard-coded into the test. Alternatively, when using the in-memory database, sometimes the key value can be explicitly set for the test even if it is generated when the app is running normally. |
@ajcvickers . OK, you and I have different approaches, but that's fine. I will, of course, look at #15577 when it comes out to see if its useful. BTW. I haven't tried my code, which is one case of #19911, which against an nightly build, but I can at the weekend. How do I get the EF Core nightly build? |
@JonPSmith Ensure that the 3.1 SDK is installed then configure a NuGet feed: https://github.com/dotnet/aspnetcore/blob/master/docs/DailyBuilds.md |
OK, I tried that and all of my tests passed, apart from one that had another issue. I think this says that your update fixes my problem. I don't think you are interested in the exception I had, but here it is. It applies to the unit test called 1. simplified unit test in my original comment.
The nightly build was 5.0.0-preview.2.20119.2 |
@JonPSmith You're still referencing the 3.0 package for SQL Server: |
Yep. That was it. I hadn't added Microsoft.EntityFrameworkCore.SqlServer so it was using the Microsoft.EntityFrameworkCore.SqlServer, Version=3.0.0.0 from my EfCore.TestSupport library. Loading the nightly version of SQL Server fixed that. I consider this issue closed and have set that. I hope that matches your view too. |
@JonPSmith Thanks for testing and thanks for the feedback as always. :-) |
Context on why I am asking this questions
When writing unit tests that use EF Core there is the problem that the setup of the database can hide problems in the EF Core code you are testing, especially missing
.Include
s. One way is to have one DbContext instance to set up the database and another DbContext instance to run the test. This works, but you have to have other variables to hold the key(s) of the setup data you want to use in the actual test.I thought one way to use one DbContext in a unit test would be to clear the tracking snapshots by detaching all the tracked entities. i.e.
I used this with when working on a client's system and I hit a problem. The rest of this issue describes the problem I encountered.
Problem
If a non-required relationship is created and written to the database and I set the
State
of the relationship toDetached
, then the foreign key is set to null.NOTE: I my client code I have also seen the navigational set to null, but I couldn't reproduce that in a simple example.
Steps to reproduce
1. simplified unit test
This unit test fails on the last line.
2. Unit test checking each part
Here is a more detailed unit test with the the lines that fail marked with
//NEXT LINE FAILS
As you can see from this some pretty odd things going on in that code! Different data returned depending on the DbContext.
Note: I tried it with SQL Server and it failed too.
Entity classes and the DbContext
Further technical details
EF Core version: 3.1.1
Database provider: (e.g. Microsoft.EntityFrameworkCore.SqlServer and Sqlite 3.1.1)
Target framework: .NET Core 3.1
Operating system: Windows
IDE: (e.g. Visual Studio 2019 16.4.2)
The text was updated successfully, but these errors were encountered: