如何设置Entity Framework Core的迁移超时时间?

51
我正在使用最新版本 (1.0.0) 的 EF Core。我需要在一个相当大的数据库上运行一个迁移。
我运行以下命令:

dotnet ef database update -c ApplicationDbContext

结果是:

超时已过期。操作完成之前超时时间已到或服务器未响应。

在连接字符串中,我明确地设置了超时时间,如下所示:

Connect Timeout=150000

不幸的是,这并没有帮助。我该怎么做?
7个回答

80
您收到的错误信息是命令超时错误,而不是连接超时错误。 更新 如评论中Pace所述,自EF Core 2.0以来,您可以使用IDesignTimeDbContextFactory在设计时更改上下文的行为,例如在迁移时发生的情况。
在项目中创建一个实现IDesignTimeDbContextFactory接口的单独类,并使用DbContextoptionsBuilder配置您想要的行为 - 在本例中,将命令超时值设置为600秒。
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);
        }
    }
}

请确保您现有的DbContext具有一个以DbContextOptions对象作为参数的构造函数:
public AdventureContext(DbContextOptions options) : base(options){}

当工具运行迁移时,首先查找实现了IDesignTimeDbContextFactory接口的类,并且如果找到该类,则使用它来配置上下文。运行时行为不受影响。 原始答案不再适用

在使用EF命令时,没有办法设置上下文的CommandTimeout。但是您可以在构造函数中全局设置它,然后在不需要保留它时将其删除:

public class ApplicationDbContext : DbContext
{
    public ApplicationDbContext()
    {
        Database.SetCommandTimeout(150000);
    }
}

2
请注意,这里设置的超时时间是以秒为单位的,因此超时时间大约为两天。 - BobbyTables
1
@Igor 如果您正在使用EF Core(> 2.0),并且希望超时适用于CLI迁移(例如dotnet ef database update),则可以控制如何在IDesignTimeDbContextFactoryProgram.BuildWebHost中创建上下文对象(请参见此处)。如果您是以编程方式执行此操作,则自己创建上下文对象。只需向上下文添加一个构造函数参数(默认值即可)。 - Pace
4
为了指定超时值,我更喜欢使用TimeSpan,这样意图更加明确:(int)TimeSpan.FromMinutes(20).TotalSeconds - Matt Scully
2
不过注入连接字符串会更好。 - François
1
@François,我通过从appsettings文件中读取连接字符串来改进了Mike Brind的答案。请看下面的回答。 - Patrick Koorevaar
显示剩余7条评论

40
您可以通过在调用迁移方法之前在上下文中设置超时来仅为迁移设置超时时间:

您可以通过在调用“Migrations”方法之前在上下文中设置超时时间来仅为迁移设置超时时间:

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

为 ef .netcore 设置迁移超时


你把这放在哪里? - Saylent

10

您也可以在数据库上下文类的构造函数中完成此操作。

public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
    : base(options)
{
    Database.SetCommandTimeout(150000);
}

4
这个方法是可行的,但它适用于所有事情,而不仅仅是迁移。似乎应该有一种方法只将其应用于迁移。 - Bryant
1
谢谢,这很有帮助。只是您将其设置为属性而不是函数:Database.CommandTimeout = 150000; - Adel Mourad

1
使用 Entity Framework 6(不是 CORE!),我使用 DbMigrationsConfiguration.CommandTimeout 属性为迁移设置了更长的超时时间。
像这样:
在我的 Global.asax.cs 文件中:
protected void Application_Start()
{
    DatabaseMigrationConfig.Register();
    //etc
}

我的DatabaseMigrationConfig类:
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);
        }
    }
}

我的 Migrations.Configuration 类:
using System.Data.Entity.Migrations;

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

参考文献:

迁移:Update-Database 命令中的超时错误 DbMigrationsConfiguration.CommandTimeout 属性

请注意,在迁移过程中我还使用了不同的连接字符串 - 用户权限比网站高,连接超时时间更长。请参阅此问题 - 如何在迁移过程中使用不同的连接字符串(但是相同的数据库)


1
你确定你的答案适用于Entity Framework CORE吗? - Andrzej Gis
我不确定,因为我没有使用它,但我认为如果我已经下载并正在使用它,检查它应该很容易。你是说 DbMigrationsConfiguration.CommandTimeout 已经从 Core 中删除了吗? - Colin
3
EF Core 中已经没有 DbMigrationsConfiguration,该类已被移除。 - Mike Brind
3
回答不错,但请注明它是用于“Entity Framework 6(不是Core)”,因为人们会感到困惑 :) - Dmitry Karpenko
1
我在寻找非CORE EF6的答案,这是我能找到的唯一答案。请确保此答案保持投票,因为虽然它不是针对所问的问题,但非常有帮助。 - rollsch
显示剩余3条评论

1
您可以使用以下命令生成迁移SQL脚本,并直接在SQL服务器上运行它:
dotnet ef migrations script [Baseline migration]

这样做可以避免超时限制的限制。
有关更多信息,请单击此处
要为Entity Framework 6生成此脚本,请使用:
Update-Database -Script -SourceMigration: [Baseline migration]

0

Patrick Koorevaar的回答进行小修改,以考虑分层的appsettings.json文件...

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);
    }
}

0

我通过从appsettings文件中读取连接字符串改进了Mike Brind的答案

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);
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接