Help with Sqlquery in context Entity Framework

Asked

Viewed 680 times

3

I need to make a return from my database in a specific way, but I have a problem, the situation is as follows:

I have this query already tested directly on SQL Server, and is working perfectly:

SELECT
    COUNT (ate.id) AS data,
    pes.tp_raca AS categories,
    pes.genero AS name
FROM
    Atendimento ate
INNER JOIN Pessoa pes ON pes.id = ate.pessoa_id
WHERE
    ate.dt_atendimento >= '2018-08-20'
AND ate.dt_atendimento <= '2018-11-10'
GROUP BY
    pes.tp_raca,
    pes.genero

And returns the following result:

data categories  name
 1        5      Feminino
 1        2      Masculino
 2        5      Masculino

In the code C#, query looked like this:

using (var context = new SiviasContext())
{
   DateTime inicio = new DateTime(2018,08,20);
   DateTime fim = new DateTime(2018,11,10);
   var result = context.Database.SqlQuery<List<Tuple<int, string, string>>>("SELECT COUNT (ate.id) AS data, pes.tp_raca AS categories, " +
    "pes.genero AS name FROM Atendimento ate INNER JOIN Pessoa pes ON pes.id = ate.pessoa_id " +
    "WHERE ate.dt_atendimento >= "+inicio.ToString("yyyy-MM-dd")+" AND ate.dt_atendimento <= "+ fim.ToString("yyyy-MM-dd") +
    " GROUP BY pes.tp_raca, pes.genero").ToList();
   ViewBag.result = JsonConvert.SerializeObject(result);
}

The way it is, it does not present an error, but it returns an empty list. Note that direct execution on SQL I use the data as a string, but if I use it as a string in the code C#, it presents an error saying that it is not possible to use a string in a Datetime type. That is, it recognizes that the date attribute in the database is of the type Datetime.

I’ve tried using the date without passing the .Tostring("yyyy-MM-dd") to format, but it also presents an error complaining of 00 (zeroes) of the team contained in the date.

So I wish you could help me out on this, how do I fix?

==============================================================

The resolution was given following the idea of @Pedro Paulo, thus:

List<RetornoConsulta> result = context.Atendimentos.Where(x => x.dt_atendimento >= dt_inicio)
                            .Where(x => x.dt_atendimento <= dt_fim)
                            .Select(g => new {
                                categories = g.Pessoa.tp_raca.ToString(),
                                name = g.Pessoa.genero
                            })
                            .GroupBy(g => new {
                                categories = g.categories,
                                name = g.name
                            })
                            .Select(g => new RetornoConsulta
                             {
                                 data = g.Count(),
                                 categories = g.Key.categories.ToString(),
                                 name = g.Key.name
                             }).ToList();
  • Why doesn’t Voce use Linq? Your models aren’t mapped? With Linq the search is simpler and easier to use

  • I would like but could not get by the following: Here I am presenting an example of query already ready. Because I need to mount a query that receives filters as optional parameters and also cross-reference two information using the group by, but I couldn’t use it in Iqueryable. It would work like this, the user chooses the two fields to be crossed. In the return I check which is the first field to be crossed and do the group by on top of him, then the second to be crossed and do another group by of it on top of the previous one, but I had difficulty in it, so I chose to do it by typing the query.

3 answers

4


First create a class that represents the return of your query data:

public class RetornoConsulta
{
    public int Data { get; set; }
    public string Categories { get; set; }
    public string Name { get; set; }
}

In your query we will pass the start and end dates as parameters for the query with the class SQLParameter of the Entity Framework, and we will inform you that the return will be a list of objects in our class RetornoConsulta:

List<SqlParameter> parametros = new List<SqlParameter> 
{
    new SqlParameter("@dataInicio", inicio),
    new SqlParameter("@dataFim", fim)
};

var result = context.Database.SqlQuery<RetornoConsulta>(
                 @"SELECT COUNT (ate.id) AS Data, 
                     pes.tp_raca AS Categories, 
                     pes.genero AS Name
                   FROM Atendimento ate 
                   INNER JOIN Pessoa pes 
                    ON pes.id = ate.pessoa_id
                   WHERE ate.dt_atendimento BETWEEN @dataInicio AND @dataFim
                   GROUP BY pes.tp_raca, pes.genero", 
                   parametros.ToArray()
                ).ToList();
  • I managed to run, it did not return error, and returned a list in the amount I expected, but they came empty: result: [[],[],[]]

  • How do I make him return the result already recognizing the class?

  • Try to change the way it is now by removing the List<RetornoConsulta> of SqlQuery and adding the ToList<RetornoConsulta>().

  • It doesn’t work, Visual Studio itself complains: Unable to convert from 'string' to 'System.Type'

  • Try again in edited form.

  • Returns: There is no mapping of the object type System.Collections.Generic.List`1[[System.Data.Sqlclient.Sqlparameter, System.Data, Version=4.0.0.0, Culture=neutral, Publickeytoken=b77a5c561934e089]] to a native type managed provider.

  • Try to pass the parameters SqlParameter individually, instead of a list, see here, or use parametros.ToArray() instead of the list, as suggested here. Or do a mixed solution, write the query as I suggested, no parameters, but use the class RetornoConsulta Peter Paul suggested in place of Tuple<int, string, string>.

  • I followed your idea and managed to solve what I needed, but using Linq. I will edit my question to demonstrate how the solution was

  • @Pedropaulo, then it would be interesting to edit your answer by modifying the list passage parametros as argument from the method SqlQuery(), because it gives error. You would have to pass each argument individually or turn the list into array, as I suggested above.

  • Done, thank you!

Show 5 more comments

1

The SQL Server query works because there you are delimiting the values of the date field between simple quotes ('):

WHERE
    ate.dt_atendimento >= '2018-08-20'
AND ate.dt_atendimento <= '2018-11-10'

But in C# it’s not.

Do so:

var dtIni = inicio.ToString("yyyy-MM-dd");
var dtFim = fim.ToString("yyyy-MM-dd");
var result = context.Database.SqlQuery<List<Tuple<int, string, string>>>(
      "SELECT COUNT(ate.id) AS data, pes.tp_raca AS categories, pes.genero AS name" +
      " FROM Atendimento ate INNER JOIN Pessoa pes ON pes.id = ate.pessoa_id" +
     $" WHERE ate.dt_atendimento BETWEEN '{dtIni}' AND '{dtFim}'" +
      " GROUP BY pes.tp_raca, pes.genero"
   ).ToList();

I used string interpolation (example: $"'{var}'", available from C# 6.0, Visual Studio 2015) to make code readable:

     $" WHERE ate.dt_atendimento BETWEEN '{dtIni}' AND '{dtFim}'" +

If you are using an earlier version of C#/Visual Studio, you can change this filter line to use String.Format():

      String.Format(" WHERE ate.dt_atendimento BETWEEN '{0}' AND '{1}'", dtIni, dtFim) +

Or use the concatenation of strings simply, as I was doing before:

      " WHERE ate.dt_atendimento BETWEEN '" + dtIni + "' AND '" + dtFim + "'" +

EDITION

Still the questioner reported the error:

Conversion of an nvarchar data type into a datetime data type resulted in a value outside the range.
(The Conversion of a varchar data type to a datetime data type resulted in an out-of-range value.)

In this case the date format configured for the base in SQL Server may be different from ymd. To check which format is configured for the database, run the query:

DBCC USEROPTIONS

And check the log whose column Set Option be it dateformat.

To force a format in this query, individually, use the command SET DATEFORMAT ymd (docmentation and reference):

var result = context.Database.SqlQuery<List<Tuple<int, string, string>>>(
      "SET DATEFORMAT ymd; " +
      "SELECT COUNT(ate.id) AS data, pes.tp_raca AS categories, pes.genero AS name" +
      " FROM Atendimento ate INNER JOIN Pessoa pes ON pes.id = ate.pessoa_id" +
     $" WHERE ate.dt_atendimento BETWEEN '{dtIni}' AND '{dtFim}'" +
      " GROUP BY pes.tp_raca, pes.genero"
   ).ToList();
  • Hello Peter, in case I did the test here, but he gave anyway, he returned the error Conversion of a data type varchar into a data type datetime resulted in a value outside the range.

  • Strange, it doesn’t make much sense. Do again as Marcelo Macedo had suggested, put a breakpoint ai on that line where you assemble the query, select all the query text and check what was the text it generated, then take that same text and paste a query into SQL Server Management Studio and run, to see if it gives the same error.

  • Very strange indeed. I ran on SQL Server, it returned Conversion of a data type varchar into a data type datetime resulted in a value outside the range. If I take out the quotes, it does not give error but also does not return result

  • Single quotes (') or double quotes (")? But you hadn’t said that question query, with simple quotes (') was working on SQL Server? The normal way to represent dates on SQL Server is by using simple quotes, see here.

  • When you do a query in SQL Server Management Studio, in what format does it display dates? See the command SET DATEFORMAT and that answer here.

  • Got it, so I checked here, I was using the Navicat Premium to execute the querys, was running on it, but when I ran on SQL Server presented the date conversion error

  • And how dates are displayed when you view the records in SQL Server Management?

  • I returned a select * from and shows so: 2018-11-08 10:36:27.710

  • Try to make the appointment like this: SET DATEFORMAT ymd; SELECT ... to see if it works.

  • It worked in SQL Server, and as you would in the system then?

  • I managed to run by putting the SET DATEFORMAT ymd; before the SELECT, it did not return error, and returned a list in the amount I expected, but came empty: result: [[],[],[]]

  • @Taciiobrito, instead of doing context.Database.SqlQuery<List<Tuple<int, string, string>>>("SELECT ...").ToList();, tries to do so: context.Database.SqlQuery("SELECT ...").ToList<Tuple<int, string, string>>();.

  • @Taciiobrito, I was doing some research and it seems that the context.Database.SqlQuery() cannot be used even in "anonymous" types, such as Tuple. You may have to make a solution like the one Pedro Paulo suggested, creating a specific class with the fields that will be returned in the query.

  • I get it, thank you

Show 9 more comments

0

Try to use between. Get like this:

var result = context.Database.SqlQuery<List<Tuple<int, string, string>>>("SELECT COUNT (ate.id) AS data, pes.tp_raca AS categories, " +
    "pes.genero AS name FROM Atendimento ate INNER JOIN Pessoa pes ON pes.id = ate.pessoa_id " +
    "WHERE ate.dt_atendimento between "+inicio.ToString("yyyy-MM-dd")+" AND "+ fim.ToString("yyyy-MM-dd") +
    " GROUP BY pes.tp_raca, pes.genero").ToList();
   ViewBag.result = JsonConvert.SerializeObject(result);
  • The Between gave anyway, it’s the same result

  • Have you tried "dd/MM/yyyy" ?

  • Also not without the WHERE condition of the date it returns, but with it not working

  • Guy puts a breakpoint in the result variable and passes the mouse to see what’s going to it string. You may have to put two double quotes before and two after the internal strings, so: ""yyyy-MM-dd"" .

  • Another thing I saw here @Táciobrito is that date and name are reserved SQL words, try other words instead.

  • The query returns as follows: SELECT COUNT (ate.id) AS data, pes.tp_raca AS Categories, pes.genero AS name FROM Contact ate INNER JOIN Pessoa pes ON pes.id = ate.pessoa_id WHERE ate.dt_contact BETWEEN 2018-08-20 AND 2018-11-10 GROUP BY pes.tp_raca, pes.genero the date comes without quotation mark, but if I quote it complains that it is sttring, there is incompatibility with the type Datetime. But in direct SQL works

  • Try that other date formatting. "dd/MM/yyyy". And test that double quote thing too.

  • I have tested all these suggestions you gave me, is issued Exception in both

  • Even the question of the reserved words I spoke to you above?

  • Another thing I saw here @Taciiobrito is that date and name are reserved SQL words, try other words instead. - Marcelo Macedo 13 mins ago

  • Yes I put others: COUNT (ate.id) AS Qtd, pes.tp_raca AS categories, "pes.genero AS NAME , you don’t understand. Thus, I could use another way for this query, for example Codefirst, but I need to do group by fields dynamically, as the user selects, to cross data, but I could not return by Iqueryable

  • Dude, I got you wrong. Your query has two columns and you’re passing it to a list. Wouldn’t List support 1 column? Take a column to test.

  • Returned me an empty array array, like this: [[]]

Show 8 more comments

Browser other questions tagged

You are not signed in. Login or sign up in order to post.