efcore icon indicating copy to clipboard operation
efcore copied to clipboard

Navigation properties not loading properly

Open apra-spa opened this issue 1 year ago • 3 comments

I'm having issues retrieving navigation properties after an update. I am using UseLazyLoadingProxies in context configuration.

I have to store a not tracked entity that has the foreign keys setted but the navigation properties are null, for example:

{
                FirstId = 1, 
                FirstObject = null
                SecondId = 1, 
                SecondObject = null
                //...data to update
              }

I'm using this code to store the entity.

context.Update(entity);
context.SaveChanges();

Then if I try to access the navigation property it will be null (even if the main object is tracked after the savechanges); But if I query the dbset with a separate query for the required id (even if I don't assign the value directly to the navigation properties) I get the correct value:

var test = context.Set<FirstObject>().Where(e=> e.Id == entity.FirstId ).First();
entity.FirstObject ; //in this case I have the correct value
entity.SecondObject; //SecondObject is null

If I skip the first row of the previous snippet FirstObject will be null too.

Is there a way to make entity framework load the navigation property automatically (without querying the related dbset)?

EF Core version: 7 Database provider: Microsoft.EntityFrameworkCore.SqlServer

apra-spa avatar Feb 01 '24 17:02 apra-spa

This issue is lacking enough information for us to be able to fully understand what is happening. Please attach a small, runnable project or post a small, runnable code listing that reproduces what you are seeing so that we can investigate.

ajcvickers avatar Feb 05 '24 10:02 ajcvickers

I made this simple example. Example.zip

In this project there is a Controller with 2 methods: The method "Update" and the method "UpdateWithWorkingLazyLoading"; Both these methods take as input an object, save the object and return the saved object.

I used this json as request body:

{
  "id": 1,
  "name": "Test",
  "unitOfMeasureId": 1,
  "unitOfMeasure": null,
  "supplierId": 1,
  "supplier": null
}

If I call the method Update using the previous json the result is:

{
  "id": 1,
  "name": "Test",
  "unitOfMeasureId": 1,
  "unitOfMeasure": null,
  "supplierId": 1,
  "supplier": null
}

As you can see the navigation properties are not loaded (are equal to null).

If I call the method UpdateWithWorkingLazyLoading the result is:

{
  "id": 1,
  "name": "Test",
  "unitOfMeasureId": 1,
  "unitOfMeasure": {
    "id": 1,
    "name": "Liters"
  },
  "supplierId": 1,
  "supplier": {
    "id": 1,
    "name": "Test inc"
  }
}

In this case the properties are loaded. To make it work I have to load the navigation properties with separated queries before (or after) call the Update and SaveChanges methods:

  //these two rows allow the context to correctly lazy load the navigation properties
  var supplier = _context.Set<Supplier>().Where(e => e.Id == item.SupplierId).Single();
  var unitOfMeasure = _context.Set<UnitOfMeasure>().Where(e => e.Id == item.UnitOfMeasureId).Single();
  _context.Update(item);
  _context.SaveChanges();
  return item;

I am expecting that the navigations properties are loaded automatically, whitout having to query their DbSet separately.

apra-spa avatar Feb 16 '24 15:02 apra-spa

@apra-spa In the case where the navigations are null, this is because the entity instance is not a proxy instance, and hence lazy-loading is not available. You could create the instance using context.CreateProxy<T>(), but I don't think this can be done with the default ASP.NET Core model binding.

In the case where the navigations are not null, there still isn't any lazy-loading available, but the code explicitly loads and tracks the two related entities which are then referenced from the new entity when it is tracked.

These kinds of issues are one of the reasons many people consider lazy-loading to be an anti-pattern.

ajcvickers avatar Feb 19 '24 13:02 ajcvickers