Home > Software design >  SQL-Query returns 'sql-select' string instead of record-set
SQL-Query returns 'sql-select' string instead of record-set

Time:03-29

I'm querying a SQL table via an API and I expect the API to return a <IQueryable> collection of 'Outfit' objects however, the response object is actually just a string containing the sql-select statement.

Questions:

  1. Why is the response object returning the SQL-select statement as a string?
  2. Should the IQueryable<> contain a collection of 'Outfit' objecy-types? And should the object-type 'Outfit' be a C# class OR can I use a generic type like 'object' to hold each 'outfit' in the collection?
//Endpoint setup within the APIcontroller.cs file
[Route("api/getSummerOutfits")]
[HttpGet]
[Authorize]
 public string getSummerOutfits()
{
    IQueryable<Outfit> outfits = _dbContext.Outfits.Where(outfit => outfit.Type == 'Summer');
    
    return outfits;
}
//Setup for the service within the api.service.ts file
getSummerOutfits(): Observable<Object> 
{
    return this.httpClient.get('/api/getSummerOutfits').pipe();
}
//A snippet of the response-string when the API is called within Postman
    "SELECT \r\n  ....... WHERE Outfit.Type = 'Summer'"

I have tried setting the IQueryable<> to contain objects of-type 'outfit' however the response continues to be a string containing the sql-select statement.

CodePudding user response:

The query is declared but never executed.

IQueryable<T> (Remarks section)

Enumeration causes the expression tree associated with an IQueryable object to be executed.

Queries that do not return enumerable results are executed when the Execute method is called.

You have to materialize the query with .ToList() or .AsEnumerable().

public List<Outfit> getSummerOutfits()
{
    List<Outfit> outfits = _dbContext.Outfits
        .Where(outfit => outfit.Type == 'Summer')
        .ToList();

    return outfits;
}

While I suggest removing.pipe() as you didn't perform any operation in the response. And return the value of Observable<any[]> or Observable<Outfit[]> if you have write Outfit class/interface.

getSummerOutfits(): Observable<any[]> 
{
    return this.httpClient.get<any[]>('/api/getSummerOutfits');
}

CodePudding user response:

I'm surprised that even worked. Essentially it passed back an IQueryable<Outfit>.ToString() result.

To return a collection of Outfits Yong Shun's answer covers that using a ToList() and having the return type being an IEnumerable<Outfit>/ ICollection<Outfit>.

As a general rule though I don't recommend passing entities back to views or API, especially for asynchronous AJAX calls as this will typically send far more information than the consumer needs, and potentially opens you up to serialization "traps" with lazy loading.

Instead, define a view model or a DTO, which is a serializable POCO C# object containing just the fields your consumer needs. Then your method becomes:

public IEnumerable<OutfitDto> getSummerOutfits()
{
    var outfits = _dbContext.Outfits
        .Where(outfit => outfit.Type == 'Summer')
        .Select(outfit => new OutfitDto
        {
            // copy values across here.
        }).ToList();

    return outfits;
}

This avoids the possibility that a serializer attempts to lazy load navigation properties in Outfit. It reduces the size of the payload by just including the fields that you need, and removes any need to eager-load entire related entities if there are details you want from those. (just reference the related entity properties, EF will build the suitable query) It also avoids confusion especially if you go to pass an outfit DTO back to the server rather than attempting to send what you think is an Entity which is actually nothing more than a serialized JSON object or set of parameters type-cast into an Entity class.

  • Related