1

I've built a simple Asp.Net Core MVC app for use at work displaying data from a view in our MSSQL database. When querying this view from SSMS, execution time is ~100ms on average. When the same query is executed within my app, execution time is anywhere from ~800ms to ~1.5s.

Here is the LINQ from the controller:

public IActionResult Index()
{
    var query =
    from p in _context.vWebQuery
    where p.Almachine == "600L"
    orderby p.Aldatsta
    select p;
    return View(query);
}

Here is the Entity Class:

namespace BetaKestrel2.Models
{
    public class vWebQuery
    {
        public double Wruntim { get; set; }
        public short Wper { get; set; }
        public double Quantity { get; set; }
        [Column("Total Op TIme")]
        public double? TotalTime { get; set; }
        public string Alwon { get; set; }
        public short Alopnum { get; set; }
        public string Almachine { get; set; }
        public double Alpersta { get; set; }
        [DisplayFormat(DataFormatString = "{0:F2}")]
        public double Allen { get; set; }
        public short Alprevop { get; set; }
        [DisplayFormat(DataFormatString = "{0:d}")]     
        public DateTime Aldatsta { get; set; }
        public string Altimsta { get; set; }
        public string Alstatus { get; set; }
        public short Alperno { get; set; }
        public string Macid { get; set; }
        public string Macdesc { get; set; }
        public string Partid { get; set; }
        public string Partrevisionid { get; set; }
        public string Routingmethod { get; set; }
        public double Wqleft { get; set; }
        public string Wstate { get; set; }
        public string Wdesc { get; set; }
        public string Partdesc { get; set; }
        public string Toolid { get; set; }
        public string Childpartid { get; set; }
        public string msection { get; set; }

    }
}

And the DbContext:(using .Net Core 3.0 for the .HasNoKey())

public partial class EfacDBContext : DbContext
{
    public EfacDBContext()
    {
    }

    public EfacDBContext(DbContextOptions<EfacDBContext> options)
        : base(options)
    {
    }

    public DbSet<vWebQuery>         vWebQuery { get; set; }
    public DbSet<vGRN>              vGRN      { get; set; }
    public DbSet<vQuotationTracker> vQuotationTracker { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {

        modelBuilder.Entity<vWebQuery>(entity =>
        {
            entity.HasNoKey();
            entity.ToTable("vwebquery");
        });

And an example .cshtml View:

@model IEnumerable<BetaKestrel2.Models.vWebQuery>

@{
    ViewData["Title"] = "600L";
    string highlight = "";
}


<h1>Work Centre Plan - @ViewData["Title"]</h1>

<table class="table table-sm table-bordered">
    <thead>
        <tr>
            <th>Works Order</th>
            <th>Part Number</th>
            <th>Description</th>
            <th>Op Number</th>
            <th>Quantity</th>
            <th>Latest Start Date</th>
            <th>Previous Op</th>
            <th>Total Op Time (mins)</th>
            <th>Status</th>
            <th>Qty Left</th>
        </tr>
    </thead>
    <tbody>

    @foreach (var item in Model)
    {

        @if (item.Wstate == "COMP")
        {
            highlight = "background-color: green;";
        }
        else if (item.Alprevop == 0)
        {
            highlight = "background-color: yellow;";
        }
        else
        {
            highlight = "";
        }
        <tr style="@highlight">
            <td>@Html.DisplayFor(modelItem => item.Alwon)</td>
            <td>@Html.DisplayFor(modelItem => item.Partid)</td>
            <td>@Html.DisplayFor(modelItem => item.Partdesc)</td>
            <td>@Html.DisplayFor(modelItem => item.Alopnum)</td>
            <td>@Html.DisplayFor(modelItem => item.Quantity)</td>
            <td>@Html.DisplayFor(modelItem => item.Aldatsta)</td>
            <td>@Html.DisplayFor(modelItem => item.Alprevop)</td>
            <td>@Html.DisplayFor(modelItem => item.TotalTime)</td>
            <td>@Html.DisplayFor(modelItem => item.Wstate)</td>
            <td>@Html.DisplayFor(modelItem => item.Wqleft)</td>
        </tr>
    }
    </tbody>
</table>

EF Core converts to the following SQL

SELECT 
    [v].[Aldatsta]
    ,[v].[Allen]
    ,[v].[Almachine]
    ,[v].[Alopnum]
    ,[v].[Alperno]
    ,[v].[Alpersta]
    ,[v].[Alprevop]
    ,[v].[Alstatus]
    ,[v].[Altimsta]
    ,[v].[Alwon]
    ,[v].[Childpartid]
    ,[v].[Macdesc]
    ,[v].[Macid]
    ,[v].[Partdesc]
    ,[v].[Partid]
    ,[v].[Partrevisionid]
    ,[v].[Quantity]
    ,[v].[Routingmethod]
    ,[v].[Toolid]
    ,[v].[Total Op TIme]
    ,[v].[Wdesc]
    ,[v].[Wper]
    ,[v].[Wqleft]
    ,[v].[Wruntim]
    ,[v].[Wstate]
    ,[v].[msection]
FROM [vwebquery] AS [v]
WHERE 
    [v].[Almachine] = N'BENDD'
AND [v].[Almachine] IS NOT NULL
ORDER BY 
    [v].[Aldatsta]

Results in:

info: Microsoft.AspNetCore.Mvc.ViewFeatures.ViewResultExecutor[4] Executed ViewResult - view Index executed in 1540.6805000000002ms.

info: Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker[2] Executed action BetaKestrel2.Controllers.BenddController.Index (BetaKestrel2) in 1541.4348ms

info: Microsoft.AspNetCore.Routing.EndpointMiddleware[1] Executed endpoint 'BetaKestrel2.Controllers.BenddController.Index (BetaKestrel2)'

info: Microsoft.AspNetCore.Hosting.Diagnostics[2] Request finished in 1541.8467ms 200 text/html; charset=utf-8

Whereas, in SSMS

Total execution time 124ms

I tried the AsNoTracking() and that didn't make a difference. Your last comment got me curious. 291 rows are returned from the query so I tried .Take(5) and execution time went down to 24ms. Could it literally just be the iteration of my foreach loop in the view that is taking up all the time?

henry-js
  • 69
  • 1
  • 6
  • Well, first, that number is your *total* execution time to return the response, not just the time it takes to run the query. There's any number of other things that could be slowing the response down, but we don't have any visibility into any of the rest of your code. – Chris Pratt Sep 04 '19 at 12:27
  • Apologies, I'm fairly new to OOP, and still getting my head around it all. I've edited my question for more clarity. – henry-js Sep 04 '19 at 14:26
  • see my answer, you are measuring things which are not the same, just measure the execution of the query not additional OOP and rendering pipelines. yes will be slower 1v1 but not as much as the usefulness of what EF is giving you. you taking about probably less than 3ms (probabaly why less depending) – Seabizkit Sep 04 '19 at 14:38
  • just seen your update. 291 rows with that many fields (20) should be way way fast like 24ms for all 291, so i'm guessing it may have something to do with DisplayFormat and Column, does one of the columns contain lots of text... like 500 plus chars.... check the sizes of the data in the fields.. – Seabizkit Sep 05 '19 at 12:30
  • wdesc is nvarchar(255), but the highest length in that column is 41 chars – henry-js Sep 05 '19 at 13:52

1 Answers1

0

When querying this view from SSMS, execution time is ~100ms on average. When the same query is executed within my app, execution time is anywhere from ~800ms to ~1.5s.

SQL Server Management Studio (SSMS)

  • RAW SQL NO OVER HEAD

EF Entityfamework

put this

var query = 
from p in _context.vWebQuery
where p.Almachine == "BENDD"
orderby p.Aldatsta
select p;

in a block which times it, loop it 3 times and take the last time.

--create dbContext here. (_context)

--start loop (run 3 times)

    --start timer

var query = ( 
            from p in _context.vWebQuery
           where p.Almachine == "BENDD"
          orderby p.Aldatsta
          select p
        ).Tolist();

    --end timer -this is what you want to compare after the 3 run.
               - yes it will be slower but you could make as non tracking 
               - should be a must fairer comparison.

-- end loop

tip - put this in debug, put break-point on index... change your query to ToList(), so that it executes the query at that point and not wen its in the view section.

Code from test: Modified

        for (int i = 0; i <= 3; i++)
        {
            Stopwatch stopwatch = new Stopwatch();
            stopwatch.Start();

            var query = (from p in _context.vWebQuery
                         where p.Almachine == "600L"
                         orderby p.Aldatsta
                         select p
            ).ToList();

            stopwatch.Stop();
            Console.WriteLine(stopwatch.ElapsedMilliseconds);
        }
     .....
Seabizkit
  • 2,417
  • 2
  • 15
  • 32
  • Ok so I did what you suggested, and the System.Diagnostics.Stopwatch time is ~1200ms average – henry-js Sep 04 '19 at 15:10
  • in future do not modify someone elses answer rather include it in your own Q or A, anyway I modified what was there... as it was not considering the timing correctly – Seabizkit Sep 05 '19 at 11:24
  • also consider the following https://stackoverflow.com/questions/18925111/turn-off-ef-change-tracking-for-any-instance-of-the-context – Seabizkit Sep 05 '19 at 11:26
  • find it interesting that ef is adding `AND [v].[Almachine] IS NOT NULL` from my understand it should not – Seabizkit Sep 05 '19 at 11:50
  • Also consider removing DisplayFormat and Column, also why does the table not have any PK, and are there any index's on the table. our of interest how many rows are we dealing with. – Seabizkit Sep 05 '19 at 11:52
  • Sorry for the mod. I'm using .NET Core 3.0 which allows me to use entity types that have no key, which then maps to my database View instead of a Table [EF Core Docs](https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-3.0/breaking-changes#qt). I've no idea why the 'IS NOT NULL' is added. – henry-js Sep 05 '19 at 12:22
  • Include the life scope of _context as i want to see its short lifed per request or UOW – Seabizkit Sep 05 '19 at 12:25
  • I don't know what that means – henry-js Sep 05 '19 at 12:30
  • show code of where _context gets its instance created. – Seabizkit Sep 05 '19 at 12:30