49

I'm using the latest (1.0.0) version of EF Core. I have a migration to run on a quite big database.

I run:

dotnet ef database update -c ApplicationDbContext

And get:

Timeout expired. The timeout period elapsed prior to completion of the operation or the server is not responding.

In the connection string I explicitly set the timeout like so:

Connect Timeout=150000

Unfortunately, it didn't help. How should I do this?

Artiom
  • 7,694
  • 3
  • 38
  • 45
Andrzej Gis
  • 13,706
  • 14
  • 86
  • 130

7 Answers7

79

The error message you are getting is for a Command timeout, not a connection timeout.

UPDATE

As mentioned by Pace in comments, since EF Core 2.0 you are able to use IDesignTimeDbContextFactory to change the behaviour of your context when it is being created by tooling at design time such as happens with Migrations.

Create a separate class in your project that implements the IDesignTimeDbContextFactory interface and use the DbContextoptionsBuilder to configure the behaviour you want - in this case, setting the command timeout value to 600 seconds:

using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;

namespace EFCoreSample.Model
{
    public class SampleContextFactory : IDesignTimeDbContextFactory<SampleContext>
    {
        public SampleContext CreateDbContext(string[] args)
        {
            var optionsBuilder = new DbContextOptionsBuilder<SampleContext>();
            optionsBuilder.UseSqlServer(@"Server=.\;Database=db;Trusted_Connection=True;",
                opts => opts.CommandTimeout((int)TimeSpan.FromMinutes(10).TotalSeconds));

            return new SampleContext(optionsBuilder.Options);
        }
    }
}

Make sure that your existing DbContext has a constructor that takes a DbContextOptions object as a parameter:

public AdventureContext(DbContextOptions options) : base(options){}

When the tooling runs the migration, it looks first for a class that implements IDesignTimeDbContextFactory and if found, will use that for configuring the context. Runtime behaviour is not affected.

Original Answer No Longer Applies

There is no way to set the CommandTimeout on a context when using EF commands. But you can set it globally in the constructor, and then remove it later if you don't need to keep it:

public class ApplicationDbContext : DbContext
{
    public ApplicationDbContext()
    {
        Database.SetCommandTimeout(150000);
    }
}
Mike Brind
  • 28,238
  • 6
  • 56
  • 88
  • Created issue asking about allowing for timeouts specific to migrations: https://github.com/aspnet/EntityFramework/issues/6613 – Micah Zoltu Sep 27 '16 at 04:26
  • 2
    note that these are seconds, so the timeout set here is about two days. – BobbyTables Aug 18 '17 at 11:38
  • @BobbyTables Yes - the actual number comes from the question, but your point is a valid one. – Mike Brind Aug 18 '17 at 12:02
  • 1
    As a workaround, is there any condition I can use to increase the timeout only for migrations? Like `if (IsMigrations()) { Database.SetCommandTimeout(1500) }` – Igor Oct 02 '17 at 07:35
  • @Igor Not that I know of but I haven't been keeping up with EF Core recently. – Mike Brind Oct 03 '17 at 14:11
  • 1
    @Igor If you are using EF Core (>2.0) and you want the timeout to apply for CLI migration (e.g. `dotnet ef database update`) then you can control how the context object is created in `IDesignTimeDbContextFactory` or `Program.BuildWebHost` (see more [here](https://learn.microsoft.com/en-us/ef/core/miscellaneous/1x-2x-upgrade)). If you are doing it programmatically then you are creating a context object yourself. Just add a constructor argument (with a default) to your context. – Pace Oct 10 '17 at 19:31
  • 4
    For assigning timeout values, I prefer to use `TimeSpan` so the intent is clear: `(int)TimeSpan.FromMinutes(20).TotalSeconds` – Matt Scully Mar 08 '18 at 21:04
  • Is this still the case that it is global? I often want a long timeout for maintenance type jobs but not for normal CRUD stuff. This is quite bizarre. This DI stuff can really bite you sometimes. – Simon_Weaver Mar 22 '18 at 22:13
  • 1
    @Simon_Weaver As Pace mentioned in his comment, there is a solution now. I've updated the answer to provide an example. – Mike Brind Mar 23 '18 at 13:53
  • 2
    it'd be great to inject the connection string though. – François Jul 26 '18 at 12:19
  • "$ dotnet ef" will use your host builder to configure your context, if you have one. – Jeremy Lakeman Jun 24 '21 at 02:31
  • 1
    @François, I improved Mike Brind's answer by reading the connection string from the appsettings file. See my answer below. – Patrick Koorevaar Feb 04 '22 at 14:26
38

You can set the timeout for migration only by setting the timeout on the context before calling the Migrations method:

using (var context = new DispatchingDbContext(_configuration))
{
    context.Database.SetCommandTimeout(300);
    await context.Database.MigrateAsync().ConfigureAwait(false);
}

Set timeout for migrations ef .netcore

Peter
  • 27,590
  • 8
  • 64
  • 84
10

You can do it also in the constructor of your database context class.

public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
    : base(options)
{
    Database.SetCommandTimeout(150000);
}
Nishan
  • 3,644
  • 1
  • 32
  • 41
  • 4
    This works but it applies to everything, not just the migration. Seems like there should be a way to apply it to only the migration. – Bryant Jan 17 '19 at 22:55
  • 1
    thanks, this helped, it is just you set it as a property not a function: Database.CommandTimeout = 150000; – Adel Mourad Jul 25 '20 at 00:07
1

Using Entity Framework 6 (NOT CORE!), I set a longer timeout for migrations using the DbMigrationsConfiguration.CommandTimeout property.

Like this:

In my Global.asax.cs:

protected void Application_Start()
{
    DatabaseMigrationConfig.Register();
    //etc
}

My DatabaseMigrationConfig Class:

public class DatabaseMigrationConfig
{
    internal static void Register()
    {
        using (var context = new MyContext(Config.ConnectionStringMigrations))
        {
            Database.SetInitializer(new MigrateDatabaseToLatestVersion<MyContext,
                                        Migrations.Configuration>());
            context.Database.Initialize(false);
        }
    }
}

My Migrations.Configuration class:

using System.Data.Entity.Migrations;

internal sealed class Configuration : DbMigrationsConfiguration<MyContext>
{
    public Configuration()
    {
        AutomaticMigrationsEnabled = false;
        AutomaticMigrationDataLossAllowed = false;
        CommandTimeout = 360;// <----- 6 minute timeout!
    }
}

References:

Migrations: timeout error in Update-Database commands DbMigrationsConfiguration.CommandTimeout Property

Note that I also use a different connection string during migrations - the user has higher permissions than the website and the connection timeout is longer. See this question - How to use a different connection string (but same database) for migrations

Colin
  • 22,328
  • 17
  • 103
  • 197
  • 1
    Are you sure, your answer is valid for Entity Framework **CORE**? – Andrzej Gis Aug 18 '16 at 19:52
  • I am not sure, because I'm not using it, but I would have thought that it would be easy to check if I had downloaded it and was using it already. Are you saying that the `DbMigrationsConfiguration.CommandTimeout` has been removed from Core? – Colin Aug 24 '16 at 16:56
  • 3
    There is no DbMigrationsConfiguration in EF Core. It has been removed. – Mike Brind Aug 26 '16 at 19:12
  • 3
    The answer is good, but please specify that it's for "Entity Framework 6 **(not Core)**" as people get confused :) – Dmitry Karpenko Jan 15 '21 at 10:38
  • 1
    I was looking for an answer for non CORE EF6 and this was the only answer I could find. Please ensure this answer stays upvoted as whilst it isn't for the question asked it was very helpful. – rollsch Aug 14 '21 at 01:15
  • This answer should be somewhere else. This is confusing. Please remove it and place it on the corresponding question – Julian Aug 19 '22 at 06:00
  • @Julian very happy to. Can you provide a link to the corresponding question? – Colin Aug 19 '22 at 16:10
  • @Colin I guess this one. But haven't checked if your answer is a duplicate. https://stackoverflow.com/questions/24051417/code-first-custom-sql-migration-timeout-exception – Julian Aug 19 '22 at 17:44
1

You can generate the migration SQL script and run it on your own directly on the SQL server using this command:

dotnet ef migrations script [Baseline migration]

This way you won't be limited to timeout limitations.

More info can be found here.

To generate this script for Entity Framework 6, use:

Update-Database -Script -SourceMigration: [Baseline migration]
Nir
  • 1,836
  • 23
  • 26
0

I improved Mike Brind's answer by reading the connection string from the appsettings file:

using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;

namespace EFCoreSample.Model
{
    public class SampleContextFactory : IDesignTimeDbContextFactory<SampleContext>
    {
        public SampleContext CreateDbContext(string[] args)
        {
            var env = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT");
            var settingsFileName = $"appsettings.{env}.json";
            
            if (!File.Exists(settingsFileName))
            {
                settingsFileName = "appsettings.json";
            }
            
            var connectionString = new ConfigurationBuilder().AddJsonFile(settingsFileName).Build().GetConnectionString("GlobalConnection");

            var optionsBuilder = new DbContextOptionsBuilder<SampleContext>();
            optionsBuilder.UseSqlServer(connectionString,
                opts => opts.CommandTimeout((int)TimeSpan.FromMinutes(10).TotalSeconds));

            return new SampleContext(optionsBuilder.Options);
        }
    }
}
Patrick Koorevaar
  • 1,219
  • 15
  • 24
0

Small modification to Patrick Koorevaar's answer to account for layered appsettings.json files...

public class DesignTimeDbContextFactory : IDesignTimeDbContextFactory<MyDbContext>
{
    public MyDbContext CreateDbContext(string[] args)
    {
        var builder = new ConfigurationBuilder();
        
        var settingsFileName = "appsettings.json";
        if (File.Exists(settingsFileName))
        {
            builder.AddJsonFile(settingsFileName);
        }
        
        var env = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT");
        var envSettingsFileName = $"appsettings.{env}.json";
        if (File.Exists(envSettingsFileName))
        {
            builder.AddJsonFile(envSettingsFileName);
        }
        
        var connectionString = builder.Build().GetConnectionString("MyConnString");

        var optionsBuilder = new DbContextOptionsBuilder<MyDbContext>();
        optionsBuilder.UseSqlServer(connectionString,
            opts => opts.CommandTimeout((int)TimeSpan.FromMinutes(10).TotalSeconds));

        return new MyDbContext(optionsBuilder.Options);
    }
}