55

As the title suggest i'm having a problem with the first query against a SQL Server database using the Entity Framework. I have tried looking for an answer but no one seems to actually have a solution to this.

The tests was done in Visual Studio 2012 using Entity Framework 6, I also used the T4 views template to pre-compile the views. The database was on a SQL Server 2008. We have about 400 POCOs (400 mapping files), only have 100 rows data in database table.

Following capture is my test code and result.

static void Main(string[] args){
    Stopwatch st=new Stopwatch();
    st.Start();
    new TestDbContext().Set<Table1>.FirstOrDefault();
    st.stop();
    Console.WriteLine("First Time "+st.ElapsedMilliseconds+ " milliseconds");

    st.Reset();
    st.Start();
    new TestDbContext().Set<Table1>.FirstOrDefault();
    st.stop();
    Console.WriteLine("Second Time "+st.ElapsedMilliseconds+ " milliseconds");
}

Test results

First Time 15480 milliseconds
Second Time 10 milliseconds
Daniel
  • 9,491
  • 12
  • 50
  • 66
LeoLi
  • 553
  • 1
  • 5
  • 6

7 Answers7

47

On the first query EF compiles the model. This can take some serious time for a model this large.

Here are 3 suggestions: http://www.fusonic.net/en/blog/2014/07/09/three-steps-for-fast-entityframework-6.1-first-query-performance/

A summary:

  1. Using a cached db model store
  2. Generate pre-compiled views
  3. Generate pre-compiled version of entityframework using n-gen to avoid jitting

I would also make sure that I compile the application in release mode when doing the benchmarks.

Another solution is to look at splitting the DBContext. 400 entities is a lot and it should be nicer to work with smaller chunks. I haven't tried it but I assume it would be possible to build the models one by one meaning no single load takes 15s. See this post by Julie Lerman https://msdn.microsoft.com/en-us/magazine/jj883952.aspx

InteXX
  • 6,135
  • 6
  • 43
  • 80
Mikael Eliasson
  • 5,157
  • 23
  • 27
  • Must i use Davidroth' EF.dll and EntityFramework.SqlServer.dll ? I download the source code and add reference to my solution, but at run time ,it throws a exception. Exception information: Can not load file or Assembly "Entityframework, version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" .... – LeoLi May 25 '15 at 08:28
  • Glad to help. How much faster did it become? – Mikael Eliasson May 25 '15 at 12:02
  • 1
    From 15 seconds to 5 seconds, it makes me painful long time , – LeoLi May 25 '15 at 12:09
  • 4
    @MikaelEliasson The tools introduced in the article ("_nuget package: Interactive Pregenerated Views for Entity Framework 6._" and "_DbModelStore branch on github_") is **not updated after 2014**. What alternative tools or methods we can use now? – Iman Mahmoudinasab Aug 29 '15 at 07:09
  • Another suggestion is call model while application loading in first time – Moayad Myro Oct 21 '16 at 00:11
  • @MikaelEliasson Can you clarify `On the first query EF compiles the model` ? Is this the first query in the application's lifecycle? i.e. the compiling should only occur after a recycle/etc or app crash, not any other point during the lifecycle of the app. – ferr May 30 '17 at 19:04
  • The fusonic link is dead – Immortal Blue Jul 03 '23 at 11:06
12

With EF Core, you can cheat and load the model early after you call services.AddDbContext (you can probably do something similar with EF6 too, but I haven't tested it).

services.AddDbContext<MyDbContext>(options => ...);
var options = services.BuildServiceProvider()
                      .GetRequiredService<DbContextOptions<MyDbContext>>();
Task.Run(() =>
{
    using(var dbContext = new MyDbContext(options))
    {
        var model = dbContext.Model; //force the model creation
    }
});

This will create the model of the dbcontext in another thread while the rest of the initialization of the application is done (and maybe other warmups) and the beginning of a request. This way, it will be ready sooner. When you need it, EFCore will wait for the Model to be created if it hasn't finished already. The Model is shared across all DbContext instances so it is ok to fire and forget this dummy dbcontext.

Yepeekai
  • 2,545
  • 29
  • 22
  • 2
    `services.BuildServiceProvider()` shouldn't be called at all [Dependency injection in ASP.NET Core](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-3.1#recommendations). You should only call `services.AddDbContext(options => ...);` from within ConfigureServices and inject it in Configure `public void Configure(..., MyDbContext dbContext)` and then only `Task.Run(() => { _ = dbContext.Model; });` – Alef Duarte Apr 01 '21 at 14:31
  • 2
    I tried but this doesn't work in EF Core, still the first query is slow. – Abdurrahman I. May 31 '22 at 14:27
5

You can try something like this: (it worked for me)

protected void Application_Start()
{

    Start(() =>
    {
        using (EF.DMEntities context = new EF.DMEntities())
        {
            context.DMUsers.FirstOrDefault();
        }
    });
}
private void Start(Action a)
{
    a.BeginInvoke(null, null);
} 

Entity Framework - First query slow

AllmanTool
  • 1,384
  • 1
  • 16
  • 26
  • 1
    I find if use another thread to init EF, and Use EF in Main Thread after just short time, it ma invoke `Multi Thread access EF Exception, not thread safe` Exception, the mean of ex msg is mean about this, so I think if use `another thread init` and `really use EF in main thread` two action is close, I may want to not use another thread to init, to avoid the excpetion. – yu yang Jian Nov 06 '19 at 04:45
2

this work for me:

using (MyEntities db = new MyEntities())                
{
   db.Configuration.AutoDetectChangesEnabled = false; // <----- trick
   db.Configuration.LazyLoadingEnabled = false; // <----- trick

   DateTime Created = DateTime.Now;

   var obj = from tbl in db.MyTable
      where DateTime.Compare(tbl.Created, Created) == 0
      select tbl;

   dataGrid1.ItemsSource = obj.ToList();
   dataGrid.Items.Refresh();
}
0

If you have many tables that are not being used on c#, exclude them.

Add a partial class, add the following code and reference this function on OnModelCreating

void ExcludedTables(DbModelBuilder modelBuilder)
{
    modelBuilder.Ignore<Table1>();
    modelBuilder.Ignore<Table>();
   // And so on
}
roncansan
  • 2,310
  • 6
  • 27
  • 34
0

For me, just using AsParallel() in the first query solved the problem. This runs the query on multiple processor cores (apparently). All my subsequent queries are unchanged, it is only the first one which was causing the delay.

I also tried pre-generated mapping views https://learn.microsoft.com/en-us/ef/ef6/fundamentals/performance/pre-generated-views but this did not improve startup time by much.

ardmark
  • 81
  • 1
  • 9
-6

I think that is not a very good solution. Ado.net looks like a lot more performance. However, this is my opinion.

Alternatively look at them.

https://msdn.microsoft.com/tr-tr/data/dn582034

https://msdn.microsoft.com/en-us/library/cc853327(v=vs.100).aspx

Caner
  • 813
  • 1
  • 12
  • 26