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

Using GetQueryAsync produces more complex SQL query than regular OData query #122

Closed
hmz777 opened this issue Feb 12, 2022 · 2 comments
Closed

Comments

@hmz777
Copy link

hmz777 commented Feb 12, 2022

This is my first time using AutoMapper with OData, I have a test EFCore API enabled with all the odata features with the following URL:
https://localhost:<port>/odata/patients

Patients:

public class PatientDTO : DTOBase
{
    public string? FirstName { get; set; }
    public string? MiddleName { get; set; }
    public string? LastName { get; set; }
    public int Age { get; set; }
    public virtual Gender Gender { get; set; }
    public string? PhoneNumber { get; set; }
    public ExtraDataDTO? ExtraData { get; set; }
    public virtual List<AppointmentDTO> Appointments { get; set; }
    public virtual List<TreatmentDTO> Treatments { get; set; }
    public virtual List<NoteDTO> Notes { get; set; }
}

(The DTO in this situation has the exact same structure as actual DB Entity)

but using GetQueryAsync like this:

// GET: api/Patients
[HttpGet]
public async Task<IActionResult> GetPatients(ODataQueryOptions<PatientDTO> options)
{
    return Ok(await _context.Patients.GetQueryAsync<PatientDTO, Patient>(mapper, options));
}

produces this SQL:

SELECT p."Id",
       p."FirstName",
       p."MiddleName",
       p."LastName",
       p."Age",
       p."Gender",
       p."PhoneNumber", (e."Id" IS NULL), e."Id",
                                          e."Data",
                                          a."AppointmentType",
                                          a."Date",
                                          a."Id",
                                          t0."Id",
                                          t0."TreatmentType",
                                          t0."TotalCost",
                                          t0."PaymentType",
                                          t0."PaymentStatus",
                                          t0."Id0",
                                          t0."Value",
                                          n."Id",
                                          n."Value"
FROM "Patients" AS p
LEFT JOIN "ExtraDatas" AS e ON p."ExtraDataId" = e."Id"
LEFT JOIN "Appointments" AS a ON p."Id" = a."PatientId"
LEFT JOIN
  (SELECT t."Id",
          t."TreatmentType",
          t."TotalCost",
          t."PaymentType",
          t."PaymentStatus",
          p0."Id" AS "Id0",
          p0."Value",
          t."PatientId"
   FROM "Treatments" AS t
   LEFT JOIN "Payments" AS p0 ON t."Id" = p0."TreatmentId") AS t0 ON p."Id" = t0."PatientId"
LEFT JOIN "Notes" AS n ON p."Id" = n."PatientId"
ORDER BY p."Id",
         e."Id",
         a."Id",
         t0."Id",
         t0."Id0"

while a regular odata action with EnableQuery with the same URL:

 // GET: api/Patients
[HttpGet]
[EnableQuery]
public async Task<IActionResult> GetPatients()
{
   return Ok(_context.Patients);
}
SELECT p."Id",
       p."Age",
       p."CreationDate",
       p."ExtraDataId",
       p."FirstName",
       p."Gender",
       p."LastName",
       p."MiddleName",
       p."PhoneNumber",
       p."UpdateDate"
FROM "Patients" AS p                                                                                        

Am I missing something here?

@wbuck
Copy link
Contributor

wbuck commented Feb 12, 2022

Can you show your AutoMapper profile for the PatientDTO?
I believe what you need to do is call ExplicitExpansion in your Patient profile.

CreateMap<Patient, PatientDTO>( )
    .ForAllMembers( opts => opts.ExplicitExpansion( ) );

The default behavior will expand all of your navigation properties. Setting ExplicitExpansion will of course mean that a consumer of the API will now have to explicitly expand the navigation properties in order to include those entities in the result set.

Also, as a side note (unrelated to this library) you're currently executing your query synchronously.
Take a look at this issue. In order to get around that you can explicitly call ToListAsync on the IQueryable.

[HttpGet]
public async Task<IActionResult> GetPatients(ODataQueryOptions<PatientDTO> options)
{
    var queryable = await _context.Patients.GetQueryAsync( mapper, options )
    return Ok( await queryable.ToListAsync( ) );
}

@hmz777
Copy link
Author

hmz777 commented Feb 13, 2022

@wbuck Thank you! This works as expected. And I appreciate the issue link, I didn't know about this.

@hmz777 hmz777 closed this as completed Feb 13, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants