Skip to content
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

Unexpected behaviour of AsNoTracking() #19276

Closed
VitaliiIsaenko opened this issue Dec 11, 2019 · 11 comments
Closed

Unexpected behaviour of AsNoTracking() #19276

VitaliiIsaenko opened this issue Dec 11, 2019 · 11 comments
Labels
closed-no-further-action The issue is closed and no further action is planned. customer-reported

Comments

@VitaliiIsaenko
Copy link

VitaliiIsaenko commented Dec 11, 2019

AsNoTracking method does not allow to include data for all elements in a collection. Reference properties are filled only for first matching by id objects. Full info can be found here, in my SO question

Steps to reproduce

There is a sample project branch states-countries-relation

Further technical details

EF Core version: 2.2
Database provider: Pomelo.EntityFrameworkCore.MySql
Target framework: .NET Core 2.2
Operating system: macOS Mojave
IDE: Rider 2019.2

@ajcvickers
Copy link
Contributor

@VitaliiIsaenko I suspect the model configuration doesn't match what you have in the database. This is the model EF is building:

Model: 
  EntityType: AddressDto
    Properties: 
      Id (int) Required PK AfterSave:Throw ValueGenerated.OnAdd 0 0 0 -1 0
      CountryId (int) Required 1 1 -1 -1 -1
    Navigations: 
      CountryLocale (<CountryLocale>k__BackingField, List<CountryLocaleDto>) Collection ToDependent CountryLocaleDto 0 -1 1 -1 -1
    Keys: 
      Id PK
  EntityType: CountryLocaleDto
    Properties: 
      Id (int) Required PK AfterSave:Throw 0 0 0 -1 -1
      Locale (string) Required PK AfterSave:Throw 1 1 1 -1 -1
      AddressDtoId (no field, Nullable<int>) Shadow FK Index 2 2 2 0 0
    Keys: 
      Id, Locale PK
    Foreign keys: 
      CountryLocaleDto {'AddressDtoId'} -> AddressDto {'Id'} ToDependent: CountryLocale

I suspect that maybe you want to use address.countryId as a foreign key, but that will only work if the address is on the dependent end of the relationship, which is in conflict with using a collection navigation property here.

You might want to take a look at the documentation on modeling relationships: https://docs.microsoft.com/en-us/ef/core/modeling/relationships?tabs=fluent-api

@VitaliiIsaenko
Copy link
Author

@ajcvickers thank you for helping out! Unfortunately, you had a look at the master branch that I didn't want to modify due to another question on the topic on SO. Branch states-countries-relation has an example of the described case. However, it is possible, that the same appears in the address-country relation as well. I understand that the relation setup might be wrong, however, it was the only way to achieve what I need and it works (thanks for help for my SO question). This relation is indeed non-traditional (due to locales support) but not that difficult to understand.
However, the question I'm posting here is relevant to another SO question I posted and has sample code on states-countries relation branch of the same repository. It also requires a similar setup in Context but I could not find any other option. I would be thankful for helping me with that as well but usually, it ends up referencing me to documentation without understanding this specific case's requirements. So, since it is working, I'm fine with this option that at least works. The only problem is - EF Core fills my reference collection for every object without AsNoTracking() and fills only the first element among ones with the same ids with AsNoTracking(). Following the documentation, this option should influence only the modification of data and should not influence its fetching. Therefore I opened this issue. I would be happy to either know the explanation of why it happens with further documentation adjustments or adding the bug into the queue if it is a bug.
Thank you again, hope now it is clear!

@ajcvickers
Copy link
Contributor

@VitaliiIsaenko This line:

.HasPrincipalKey(x => x.CountryId);

sets up an alternate key using CountryId. However, the code then inserts duplicate values with the same key value. This is not valid; each primary or alternate key much uniquely identify each entity.

@VitaliiIsaenko
Copy link
Author

Interesting, thank you! However, it works somehow.. maybe you have a better idea for such relation then?
But still, the question about AsNoTracking() is open and it indeed influences how reference fields are filled with .Include(). Isn't it a separate problem?

@ajcvickers
Copy link
Contributor

@VitaliiIsaenko The reason you are only seeing inconsistent results is that there are multiple entities with the same key. One of the entities is being discarded because it has the same key as an entity that was already retrieved, indicating that these are duplicates.

@VitaliiIsaenko
Copy link
Author

So does it mean that AsNoTracking() method influences data retrieval in this case? It's not documented anywhere, it is meant to affect data modification due to documentation. That's why I call it unexpected behaviour (maybe undocumented fits better).

@smitpatel
Copy link
Contributor

@ajcvickers
Copy link
Contributor

@VitaliiIsaenko Note that all EF behavior is undefined when the data does not comply with the constraints inherent in the configured model, which is the case here.

@VitaliiIsaenko
Copy link
Author

Thank you, I understand. Is there any example of such a relation? I haven't found it anywhere and on SO people suggested only this option. Basically, it's country having the same id for several rows due to locales referencing states having the same id for several rows for the same reason. The state has one country logically but many with the same id because of locales and vice versa. I would appreciate an example or maybe an it is not possible response :) Could not get this relation from documentation only.
But anyway, thank you for help!

@ajcvickers
Copy link
Contributor

@VitaliiIsaenko Consider this data from your post:

Example data would be:
address (id, countryId)
(1, 1)
(2, 1)
address_country (id, locale, name)
(1, 'en', 'Germany')
(1, 'de', 'Deutschland')

This means that address.countryId matches two rows in the address_country table. This could be modeled as so (using both navigation properties for clarity):

public class AddressDto
{
    public int Id { get; set; }

    public int CountryId { get; set; }
    public CountryLocaleDto CountryLocale { get; set; }
}

public class CountryLocaleDto
{
    public int Id { get; set; }
    public string Locale { get; set; }

    public List<AddressDto> Address { get; set; }
}
modelBuilder.Entity<AddressDto>()
    .HasOne(e => e.CountryLocale)
    .WithMany(e => e.Address)
    .HasForeignKey(e => e.CountryId);

But this limits each Address to a single CountryLocale. If you also want each address to be associated with many CountryLocale's, then this is a many-to-many relationship that requires a join table.

@VitaliiIsaenko
Copy link
Author

@ajcvickers, thank you a lot for your help! Also, thank you, @smitpatel, the link is very useful and explains much.
I understood that there is no way to configure the relationship I explained in EF Core. Just so it stays here for anyone else interested.

@ajcvickers ajcvickers added closed-no-further-action The issue is closed and no further action is planned. and removed type-bug labels Dec 18, 2019
@ajcvickers ajcvickers reopened this Oct 16, 2022
@ajcvickers ajcvickers closed this as not planned Won't fix, can't repro, duplicate, stale Oct 16, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
closed-no-further-action The issue is closed and no further action is planned. customer-reported
Projects
None yet
Development

No branches or pull requests

3 participants