Entity Framework 超时问题

391

我在使用实体框架(EF)时,当使用一个需要超过30秒才能完成的函数导入时,会出现超时问题。我尝试了以下方法,但无法解决这个问题:

我按照这里建议,在包含EDMX文件的项目的App.Config文件中的连接字符串中添加了Default Command Timeout=300000

这是我的连接字符串:

<add 
    name="MyEntityConnectionString" 
    connectionString="metadata=res://*/MyEntities.csdl|res://*/MyEntities.ssdl|
       res://*/MyEntities.msl;
       provider=System.Data.SqlClient;provider connection string=&quot;
       Data Source=trekdevbox;Initial Catalog=StarTrekDatabase;
       Persist Security Info=True;User ID=JamesTKirk;Password=IsFriendsWithSpock;
       MultipleActiveResultSets=True;Default Command Timeout=300000;&quot;"
    providerName="System.Data.EntityClient" />

我尝试直接在我的存储库中设置CommandTimeout,如下所示:

private TrekEntities context = new TrekEntities();

public IEnumerable<TrekMatches> GetKirksFriends()
{
    this.context.CommandTimeout = 180;
    return this.context.GetKirksFriends();
}

我还能做什么来避免EF超时?这只会发生在非常大的数据集上。小数据集一切正常。

这是我得到的其中一个错误:

System.Data.EntityCommandExecutionException:执行命令定义时发生错误。有关详细信息,请参见内部异常。 ---> System.Data.SqlClient.SqlException:超时已过期。操作未完成或服务器未响应。


好的 - 我已经解决了这个问题,发生了一些愚蠢的事情。我既在连接字符串中使用了 Default Command Timeout=300000,又将 CommandTimeout 设置为 180。当我从连接字符串中删除了 Default Command Timeout 后,它就起作用了。因此,答案是在您的上下文对象上手动设置存储库中的 CommandTimeout,如下所示:

this.context.CommandTimeout = 180;

显然,在连接字符串中设置超时设置对其没有影响。


从连接字符串中删除“”。 - Brian Webster
请参考以下内容,关于.NET 4 EF中的SQL异常问题,请点击此链接:https://dev59.com/9VLTa4cB1Zd3GeqPbIaN - Saif Khan
5
在 EF 连接字符串中,需要明确定义哪部分是连接字符串,哪部分是 EF 元数据。请在字符串中保留 &quot; - Chev
2
我的建议是,在增加超时时间之前,先调查一下为什么 EF 会超时。在我们的情况下,我们意识到需要向一些表添加“NONCLUSTERED”索引,这解决了我们的超时问题。 - zulucoda
我正在与微软支持团队一起解决SQL超时问题 - 当数据库托管在SQL Azure中时会出现此问题。我被告知,在所有Azure PaaS服务(PaaS网站和SQL Azure等)中,存在一个普遍的230秒超时时间限制,即使您手动设置超时时间,该限制也始终具有优先权。这是为了保护多租户PaaS基础架构的资源。 - Ian Robertson
11个回答

654

在EF连接字符串中指定默认命令超时存在已知的bug。

http://bugs.mysql.com/bug.php?id=56806

从连接字符串中删除该值,并将其设置在数据上下文对象本身上。如果从连接字符串中删除了冲突的值,这将起作用。

Entity Framework Core 1.0:

this.context.Database.SetCommandTimeout(180);

Entity Framework 6:

this.context.Database.CommandTimeout = 180;

Entity Framework 5:

((IObjectContextAdapter)this.context).ObjectContext.CommandTimeout = 180;

Entity Framework 4及以下版本:

this.context.CommandTimeout = 180;

5
我可以使用edmx实现这个吗? - iroel
7
我认为这不是一个错误,而是根据设计所做的决定,请参见此处的“备注”部分(链接)。 - Mick P
5
因为某些设置单位是毫秒,而某些设置单位是秒,所以我在这里查找了一下(https://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlcommand.commandtimeout(v=vs.110).aspx),CommandTimeout的单位是秒。 - JabberwockyDecompiler
6
在 Entity Framework 7 中,您可以在 DbContext / IdentityDbContext 的构造函数中设置此项: this.Database.SetCommandTimeout(180); - Thomas Hagström
5
哇... EF有5个版本,有5种设置CommandTimeout的方法。都没有在ConnectionString中解决吗? - Himalaya Garg
显示剩余11条评论

105

如果你正在使用DbContext,请使用以下构造函数来设置命令超时时间:

public class MyContext : DbContext
{
    public MyContext ()
    {
        var adapter = (IObjectContextAdapter)this;
        var objectContext = adapter.ObjectContext;
        objectContext.CommandTimeout = 1 * 60; // value in seconds
    }
}

3
@ErickPetru,这样你就可以轻松地将它更改为不同的分钟数:),另外,如果编译器将该乘法优化掉,我也不会感到太惊讶! - Joel Verhagen
2
@JoelVerhagen,不要惊讶。这里有一个很好的解释,说明何时会发生自动优化:https://dev59.com/EUXRa4cB1Zd3GeqPrGLO。在这种情况下,我认为甚至会发生(因为它们是两个文字值),但老实说,我认为这种写法有点奇怪。 - Erick Petrucelli
47
嗯...孩子们正在挨饿...谁会关心1*60? - Timmerz
14
@ErikPetru,这实际上是一个非常常见的做法,并且使代码更易读。 - Calvin
@matt-burland,请为您生成的DbContext创建另一个部分类,并在其构造函数中实现此内容。 - avenmore
显示剩余6条评论

54

如果您正在使用 DbContext 和 EF v6+,您可以选择使用以下方法:

this.context.Database.CommandTimeout = 180;

20
如果你像我一样使用Entity Framework,你应该在Startup类中定义超时时间,如下所示:
 services.AddDbContext<ApplicationDbContext>(
   options => options.UseSqlServer(
     Configuration.GetConnectionString("DefaultConnection"), 
     a => a.CommandTimeout(180)));

15

通常我在一个事务内处理操作。就我所知,仅设置上下文命令超时时间是不够的,而且事务需要一个带有超时参数的构造函数。我必须同时设置两个超时值才能使其正常工作。

int? prevto = uow.Context.Database.CommandTimeout;
uow.Context.Database.CommandTimeout = 900;
using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required, TimeSpan.FromSeconds(900))) {
...
}
在函数末尾,我将命令超时设置回 prevto 中的先前值。
使用 EF6。

这绝不是一个好的方法。我曾经在一个项目中添加了很多事务范围,结果变成了一场噩梦。最终用 EF 6+ 中的单个 SAVEChanges() 替换了所有的事务范围。请查看 https://coderwall.com/p/jnniww/why-you-shouldn-t-use-entity-framework-with-transactions。 - Moons
这个回答应该有更高的投票。我尝试了各种不同的方法来增加超时时间,但只有当我设置了上下文命令超时和事务范围时才有效。 - Gang

10
在.NET Core中,使用以下语法将超时时间从默认的30秒更改为90秒:
public class DataContext : DbContext
{
    public DataContext(DbContextOptions<DataContext> options) : base(options)
    {
        this.Database.SetCommandTimeout(90); // <-- 90 seconds
    }
}

9

我知道这是一个非常古老的帖子,但 EF 仍未解决此问题。对于使用自动生成的 DbContext 的人可以使用以下代码手动设置超时时间。

public partial class SampleContext : DbContext
{
    public SampleContext()
        : base("name=SampleContext")
    {
        this.SetCommandTimeOut(180);
    }

    public void SetCommandTimeOut(int Timeout)
    {
        var objectContext = (this as IObjectContextAdapter).ObjectContext;
        objectContext.CommandTimeout = Timeout;
    }
}

1
在部分代码结尾处添加缺失的 }。 - Marc Roussel
谢谢!这在我的 EF 4.4.0 项目中起作用。是的,虽然旧,但有效! - Dave Stuart
这篇帖子似乎有很长的寿命。我之前也实现了类似的反射,但现在我正试图说服自己为什么创建一个单独的函数(你的"SetCommandTimeOut")是个好主意。你能帮我吗? - undefined

2
这是我发现的内容。也许对某些人有帮助:
以下是具体步骤:
如果您使用LINQ和EF在列表中查找某些确切元素,如下所示:
await context.MyObject1.Include("MyObject2").Where(t => IdList.Contains(t.MyObjectId)).ToListAsync();

只要IdList中包含一个以上的Id,一切都很正常。

如果列表只包含一个Id,则会出现“超时”问题。为了解决此问题,请使用if条件检查IdList中的Id数量。

例如:

if (IdList.Count == 1)
{
    result = await entities. MyObject1.Include("MyObject2").Where(t => IdList.FirstOrDefault()==t. MyObjectId).ToListAsync();
}
else
{
    result = await entities. MyObject1.Include("MyObject2").Where(t => IdList.Contains(t. MyObjectId)).ToListAsync();
}

解释:

尝试使用 SQL Profiler 并检查 Entity Framework 生成的 Select 语句。


2

对于Entity Framework 6,我使用这个注释,它可以正常工作。

  public partial class MyDbContext : DbContext
  {
      private const int TimeoutDuration = 300;

      public MyDbContext ()
          : base("name=Model1")
      {
          this.Database.CommandTimeout = TimeoutDuration;
      }
       // Some other codes
    }

CommandTimeout参数是一个可空整数,以秒为单位设置超时值。如果您设置为null或不设置,则将使用您使用的提供程序的默认值。


0
对于Postgres SQL用户,根据此处的文档 - https://www.npgsql.org/doc/connection-string-parameters.html 连接字符串中有两个超时参数可以设置

Timeout=300;CommandTimeout=300;

Host=localhost;Port=5432;database=mydatabase;username=postgres;password=postgres;Timeout=300;CommandTimeout=300;

一个用于命令,一个用于连接。


在EF连接字符串中使用CommandTimeout会导致System.ArgumentException错误,错误信息为Keyword not supported: 'commandtimeout'。使用Timeout不会崩溃,但似乎完全没有影响命令超时时间。 - ADyson
我使用这个针对Postgres数据库,并且它总是有效的。你使用哪个数据库?文档在这里 - https://www.npgsql.org/doc/connection-string-parameters.html - Santosh Karanam
这个问题是关于SQL Server连接而不是postgreSQL的。因此,它作为答案的相关性很少,但如果您确实想包含它,您需要编辑并明确说明这仅适用于与问题中不同的数据库。否则,更多人可能会浪费时间在上面。 - ADyson
是的,我同意,我已经编辑了答案。 - Santosh Karanam

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