Home > Mobile >  how to get data from a one to one and one to many relationship in EF Core 5
how to get data from a one to one and one to many relationship in EF Core 5

Time:11-17

I have this model

ER

In my database when I filter Ofertas by Organismo I get these results

database

Where I see that organismo with id=6 has 2 different Ofertas

When I translate this query to EF in my Web API

 // GET: api/Ofertas/organismo/vasco
[HttpGet("organismo/{organismoId}")]
public IEnumerable<Oferta> GetOfertasByOrganismo(int organismoId)
{
  var result = _context.Ofertas.Include(o => o.Concurso).ThenInclude(c 
  => c.Organismo).Where(o => o.Concurso.Organismo.Id == 
  organismoId).ToList();

  return result;

And I launch this request http://localhost:5000/api/ofertas/organismo/6

I get these results

result
Count = 1
[0]: {Boletus_back_end.Domain.Oferta}

That is, only one Oferta

unaoferta

In my Web API I have my Db Context disconnected

services.AddDbContext<BoletusContext>(opt=>  
opt.UseSqlServer(Configuration.GetConnectionString("BoletusConexion"))
        .EnableSensitiveDataLogging()
        .UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking));

   

Any idea, about how to obtain all the related Ofertas please?

Thanks

CodePudding user response:

You will probably need to post code, but this issue commonly appears when projects disable lazy loading and then try to query against materialized entities where requested related data has not been loaded, or they have loaded that data eagerly and the related entity is optional.

For example, something like this:

var results = context.Ofertas.AsNoTracking().ToList();

var test = results.Where(x => x.Concurso.Organismo.Id == 1).ToList();

The issue here is that we have not fetched the Concursos or Oransimos of the Ofertas in the original query, and we have materialized the results with the first ToList call. The solution is either to ensure we put our Where condition somewhere where it can be integrated into the query, or we ensure we eager load all related data and we also ensure that optional/null references are accounted for:

a) Within the Query:

var results = context.Ofertas.AsNoTracking()
    .Where(x => x.Concurso.Organismo.Id == 1).ToList();

This will pass because our query will look for and return Ofertas that have the desired Organismo ID, even if the relationship in the data is optional. However, it will not eager load the Concurso or Organismo with the Ofertas returned. Those references can still be null.

b) Eager load related data and check for #Null:

var results = context.Ofertas
    .Include(x => x.Concurso)
    .ThenInclude(x => x.Organismo)
    .AsNoTracking()
    .ToList();

var test = results.Where(x => x.Concurso?.Organismo?.Id == 1).ToList();
// or
var test = results.Where(x => x.Concuso != null 
    && x.Concurso.Organismo != null 
    && x.Concurso.Organismo.Id == 1).ToList();

Like the original query this pre-fetches all Ofertas but eager loads all related data. The null checks would only be needed if/when a relationship can be NULL. If the relationships are required and eager loaded then the null checks are not needed, but it requires the data to either be eager loaded, or capable of being lazy loaded.

Where this can get interesting is that EF will populate related entities if/where those entities are already tracked. So for instance if anything had previously loaded one or more of the Concurso's that the first Oferta result references, as well as the Organismo ID #1, then those references would exist in the returned query data In this case I will eager load the Concursos with the Oferta, but pre-fetch just Organismo #1:

var organismo = context.Organismos.Single(x => x.Id == 1); 
// The DbContext is now tracking this entity, we do nothing with this instance.

var results = context.Ofertas.Include(x => x.Concurso).AsNoTracking.ToList();

var test = results[0].Concurso.Organismo.Id == 1; // Passes in our case.

var test2 = results.Where(x => x.Concurso.Organismo.Id == 1).ToList(); // can still fail!

The first test will pass because when we loaded the Ofertas from the DbContext, that context was already tracking Organismo #1 so it pre-populated that even when we don't eager load the organismos. However, unless all references happen to be #1, the rest of the Organismo references will be null and the second test will continue to fail.

CodePudding user response:

Sorry

The problem was the data in Concursos Table the record that related the Ofertra to the Concurso was missing

Now all works fine

test
Count = 2
[0]: {Boletus_back_end.Domain.Oferta}
[1]: {Boletus_back_end.Domain.Oferta}

Thanks

  • Related