Home > database >  How to use LINQ group by in ASP.NET MVC?
How to use LINQ group by in ASP.NET MVC?

Time:12-16

I have a database:

enter image description here

  • My query SQL I need to show in my web:
   strDateStart = Format(Me.ctlNgayBD.Value, "yyyy/MM/dd")
   strDateEnd = Format(Me.ctlNgayKT.Value, "yyyy/MM/dd")
  Select Loaive, LoaiXe,
      Count(*) As Tongxe,
      Sum(Phi) As Tongphi,
      phi
  From View_XeQuaTramreport As a  
  Where catruc = " & catruc & " And Convert(varchar(10),NgayCa,111) = '" & strDate & "' 
  Group by Loaive,Loaixe,phi 
  Order by Loaive,Loaixe,phi
   Select Loaive, LoaiXe,
       Count(*) As Tongxe,
       Sum(Phi) As Tongphi,
       phi
   From View_XeQuaTramreport As a 
   Where Convert(varchar(10),NgayCa,111) >= '" & strDateStart & "'
   and Convert(varchar(10),NgayCa,111) <= '" & strDateEnd & "'
   Group by Loaive,Loaixe,phi
   Order by Loaive,Loaixe,phi"
  • Image example SQL select:

    enter image description here

    enter image description here

  • My controller: Is there a way to use LINQ a instead of string SQL query in controller?

  public DongphuocDbContext db = new DongphuocDbContext();
  
  // GET: Report
  public ActionResult Index(DateTime? start, DateTime? end )
  {
      var _context = new DongphuocDbContext();
      string mainconn = ConfigurationManager.ConnectionStrings["DongphuocDbContext"].ConnectionString;
      SqlConnection sqlconn = new SqlConnection(mainconn);
      string sqlquery = "select Loaive,LoaiXe,Count(*) As Tongxe , Sum(Phi) As Tongphi ,Phi"   " from [dbo].[View_XeQuaTramReport] As a  "  "where NgayCa between '"   start   "' and '"   end   "'"   " Group by Loaive,Loaixe,phi"   " Order by Loaive,Loaixe,phi";
  
      SqlCommand sqlcomm = new SqlCommand(sqlquery, sqlconn);
      sqlconn.Open();
      SqlDataAdapter sda = new SqlDataAdapter(sqlcomm);
      DataSet ds = new DataSet();
      sda.Fill(ds);
  
      List<View_XeQuaTramReport> lc = new List<View_XeQuaTramReport>();
  
      foreach (DataRow dr in ds.Tables[0].Rows)
      {
          lc.Add(new View_XeQuaTramReport
          {  
              LoaiVe = Convert.ToByte(dr["LoaiVe"]),
              LoaiXe = Convert.ToByte(dr["LoaiXe"]),
              Phi = Convert.ToDecimal(dr["Phi"])
          });
      }
      sqlconn.Close();
      ModelState.Clear();
      return View(lc);  
  }

My view: I want to show 2 tables in roll one view. And add Count"Tongxe" & Sum"TongPhi" but I can't add.

<center>
    <p>
        @using (Html.BeginForm("Index", "Report", FormMethod.Get))
        {
            <input type="date" name="start" />
            <input type="date" name="end" />
            <input type="submit" name="submit" value="Search" />
        }
    </p>
    <table >
        <tr>
            <th> @Html.DisplayName("LoaiVe") </th>
            <th> @Html.DisplayName("LoaiXe") </th>
            <th> @Html.DisplayName("Phi") </th>
        </tr>

        @foreach (var item in Model)
        {
    <tr>
        <td> @Html.DisplayFor(modelItem => item.LoaiVe)</td>
        <td> @Html.DisplayFor(modelItem => item.LoaiXe)</td>
        <td> @Html.DisplayFor(modelItem => item.Phi)</td>

    </tr>
        }
    </table>
</center> 

This is the model configuration of DongphuocDbContext:

modelBuilder.Entity<View_XeQuaTramReport>()
            .Property(e => e.Phi)
            .HasPrecision(19, 4); 
modelBuilder.Entity<View_XeQuaTramReport>()
            .Property(e => e.MSNV)
            .IsUnicode(false); 
modelBuilder.Entity<View_XeQuaTramReport>()
            .Property(e => e.maNhanVien)
            .IsUnicode(false); 
modelBuilder.Entity<View_XeQuaTramReport>()
            .Property(e => e.ID)
            .IsUnicode(false); 
  1. I need to help change string SQL query -> LINQ

  2. I need to add Count"Tongxe" and sum"TongPhi"

CodePudding user response:

Look in your DongphuocDbContext to see if a View_XeQuaTramReport collection is already defined there. If so, the following may do what you need:

DateTime start = ...
DateTime end = ...

var Result = _context.View_XeQuaTramReport
    .Where(item => item.NgayCa >= start && item.NgayCa <= end) // Date arithmetic, not text
    .GroupBy(item => new {item.Loaive, item.Loaixe, item.Phi}) // Multi-value key
    .Select(grp => new {
        Loaive = grp.Key.Loaive,
        Loaixe = grp.Key.Loaixe,
        Tongxe = grp.Count(),
        Tongphi = grp.Sum(item => item.Phi),
        Phi = grp.Key.Phi
    })
    .ToList();

A side note regarding your original query: You should never do date comparisons by converting the dates to text (as in Convert(varchar(10),NgayCa,111)). Instead, learn how to parameterize your queries, passing start and end in as proper DATE/DATETIME/DATETIME2 types and perform direct date-to-date comparisons. This is critical for database efficiency, because if you had an index on NgayCa (you likely should), this index would become useless if you do string comparisons instead of date comparisons.

CodePudding user response:

This looks like SQL Server, and the date values in the images look like datetime values. There's no reason to convert dates to strings, even from the date picker controls. In fact, the current SQL query runs far slower than it should and may even return the wrong results due to the string conversions.

The SQL query should look like this, using the date-typed parameters @from and @to

 Select Loaive, LoaiXe,
       Count(*) As Tongxe,
       Sum(Phi) As Tongphi,
       phi
   From View_XeQuaTramreport As a 
   Where NgayCa between @from and @to
   Group by Loaive,Loaixe,phi
   Order by Loaive,Loaixe,phi

EF Core allows creating an equivalent query with LINQ :

public ActionResult Index(DateTime start, DateTime end )
{
  using (var context = new DongphuocDbContext())
  {
      var results=context.View_XeQuaTramReport
                         .AsNoTracking()
                         .Where(r=>r.NgayCa >= start.Date && 
                                   r.NgayCa < end.Date.AddDays(1))
                         .GroupBy(r=>new{r.Loaive,r.Loaixe,r.phi})
                         .Select(g=>new View_XeQuaTramReport
                          {  
                              LoaiVe = g.Key.LoaiVe,
                              LoaiXe = g.Key.LoaiXe,
                              Phi = g.Key.Phi,
                              Tongxe = g.Key.Count(),
                              Tongphi = g.Sum(r=>r.Phi)
                          })
                          .OrderBy(r=>new {r.Loaive,r.Loaixe,r.Phi})
                          .ToList();
      return View(results);  
  }
}

LINQ has no Between clause. Date comparisons are tricky too, if the database field contains a time component. The Where clause in this query will return all rows with a date value greater or equal than the start date (start.Date) and strictly less than the next day after end (end.Date.AddDays(1)). This way, there's no reason to trim r.NgayCa from its date component, if it has one :

.Where(r=>r.NgayCa >= start.Date && r.NgayCa < end.Date.AddDays(1))

AsNoTracking() is used to tell EF to not track the entities it loads. Normally EF will track all the entities it loads so it can detect any changes and generate modification SQL statements when SaveChanges is called. This takes more memory and slows down the query.

Finally, the DbContext is defined in a using block to ensure it's disposed when it's no longer needed. Otherwise the DbContext and the entities it tracks will remain in memory until the garbage collector runs.

Conditional queries

The question's action uses nullable dates. That's only meaningful if we want the same action to search before, after or between dates. In that case both the SQL and LINQ queries would have to be rewritten. None of these would work with a NULL date: Convert(varchar(10),NgayCa,111) >=NULL, Ngayca>= NULL or Ngayca between NULL and ....

In LINQ, the query can be build dynamically :

public ActionResult Index(DateTime? start, DateTime end )
{

    var query=context.View_XeQuaTramReport
                     .AsNoTracking()
                     .AsQueryable();
if(startDate.HasValue)
{
    query=query.Where(r=>r.NgayCa >= start.Value.Date);
}
if(endDate.HasValue)
{
    query=query.Where(r=>r.NgayCa < end.Value.Date.AddDays(1));
}
var results=query.GroupBy(r=>new{r.Loaive,r.Loaixe,r.phi})
                 .Select(g=>new View_XeQuaTramReport {  
                         LoaiVe = g.Key.LoaiVe,
                         LoaiXe = g.Key.LoaiXe,
                         Phi = g.Key.Phi,
                         Tongxe = g.Key.Count(),
                         Tongphi = g.Sum(r=>r.Phi)
                  })
                  .OrderBy(r=>new {r.Loaive,r.Loaixe,r.Phi})
                  .ToList();

CodePudding user response:

@Panagiotis Kanavos Something error here

enter image description here

enter image description here

  • Related