Search code examples
.net-coreasp.net-core-mvcentity-framework-coreasp.net-core-webapicode-first

Related Data Automatically Loaded in .net core 2.2


I'm using .net core 2.2 with Entity Framework Code-First, and I create some models with relationships, and all relations working well unless relations with lookup model which are automatically load the related data of the model and the inverse property too

Here is the code:

Company.cs

public class Company
{

    public Company()
    {
        Lookups = new HashSet<Lookup>();
    }
    
    public Guid Id { get; set; }
    
    // Relationships

    // Address
    [ForeignKey("AddressForeignKey")]
    public Address Address { get; set; }

    // User
    [InverseProperty("Company")]
    public List<User> UsersInCompany { get; set; }

    // Lookup as a country
    public Guid? CountryId { get; set; }
    public Lookup Country { get; set; }

    // Lookup as a bank
    public Guid? BankId { get; set; }
    public Lookup DefaultBank { get; set; }

    // Lookup as a socialInsuranceOffice
    public Guid? SocialInsuranceOfficeId { get; set; }
    public Lookup DefaultSocialInsuranceOffice { get; set; }

    // Lookup as a currency
    //[ForeignKey("DefaultCurrencyForeignKey")]
    public Guid? CurrencyId { get; set; }
    public Lookup Currency { get; set; }
    
    // Lookup for all lookups types
    public ICollection<Lookup> Lookups { get; set; }
    
}

Address.Cs

public class Address
{
    public Guid Id { get; set; }
    
    // Relationships

    // Company
    [InverseProperty("Address")]
    public List<Company> Companies { get; set; }

    // Lookup as a governorate
    [ForeignKey("LookupForeignKey")]
    public Lookup GovernorateLookup { get; set; }
}

DataContext.Cs

   // Company and Address
        modelBuilder.Entity<Company>()
            .HasOne(u => u.Address)
            .WithMany(r => r.Companies)
            .OnDelete(DeleteBehavior.SetNull);

   // Address and Lookup
        modelBuilder.Entity<Address>()
            .HasOne(a => a.GovernorateLookup)
            .WithMany(l => l.GovernoratesUserAsAddress)
            .OnDelete(DeleteBehavior.SetNull);

  modelBuilder.Entity<Company>(com =>
        {
            // Company and Lookup for defaultCountry
            com.HasOne(c => c.Country)
            .WithMany(d => d.CompanyCountries)
            .HasForeignKey(f => f.CountryId)
            .HasConstraintName("COM_COUNTRY_FK")
            .OnDelete(DeleteBehavior.Restrict);

            // Company and Lookup for defaultBank
            com.HasOne(c => c.DefaultBank)
            .WithMany(d => d.CompanyBanks)
            .HasForeignKey(f => f.BankId)
            .HasConstraintName("COM_BANK_FK")
            .OnDelete(DeleteBehavior.Restrict);

            // Company and Lookup for defaultSocialInsuranceOffice
            com.HasOne(c => c.DefaultSocialInsuranceOffice)
            .WithMany(d => d.CompanySocialInsuranceOffices)
            .HasForeignKey(f => f.SocialInsuranceOfficeId)
            .HasConstraintName("COM_SOCIALINSURANCEOFFICE_FK")
            .OnDelete(DeleteBehavior.Restrict);

            // Company and Lookup for currency
            com.HasOne(c => c.Currency)
            .WithMany(d => d.CompanyCurrencies)
            .HasForeignKey(f => f.CurrencyId)
            .HasConstraintName("COM_CURRENCY_FK")
            .OnDelete(DeleteBehavior.Restrict);


            // Company and Lookup for all Lookups types
            com.HasMany(c => c.Lookups)
           .WithOne(d => d.Company)
           .HasForeignKey(f => f.CompanyId)
           .HasConstraintName("COM_ALL_LOOKUPS_FK")
           .OnDelete(DeleteBehavior.Cascade);

        });

Please note that all relations are working well but only the relation with lookup "I have 5 relation with the same table lookup" load all data and inverse automatically, and I need to know why this is happening and how can I stop it.

Thanks in advance.


Solution

  • You're question is difficult to understand, but I believe the "issue" here is EF's object fixup. I quote "issue", because this is actually a feature.

    EF has an object cache. Whenever you query anything, the instances created are stored in that object cache, allowing future queries for the same instances to be pulled from that cache, rather than having to roundtrip to the database again.

    When it comes to relationships, if EF has the related entities already in its object cache, it performs a "fixup", where it adds the related instances automatically to the navigation property. Again, this is an optimization feature, as now you do not need to issue another query to retrieve those.

    The only way around this is to use AsNoTracking when you query. For example:

    var companies = await _context.Companies.AsNoTracking().ToListAsync();
    

    With AsNoTracking, EF will not maintain the object cache and it will also not perform change tracking on those entities. That last part is important, because you then need to be very careful if you attempt to modify these entities in any way, as EF will not be aware of them until you explicitly attach them. However, if you're just performing reads, using AsNoTracking is actually more performant anyways.