【问题标题】:How to set CommandTimeout for DbContext?如何为 DbContext 设置 CommandTimeout?
【发布时间】:2012-05-19 22:46:12
【问题描述】:

我正在寻找一种为 DbContext 设置 CommandTimeout 的方法。搜索后,我通过将 DbContext 转换为 ObjectContext 并为 objectContext 的 CommandTimeout 属性设置值找到了方法。

var objectContext = (this.DbContext as IObjectContextAdapter).ObjectContext;

但我必须使用 DbContext。

【问题讨论】:

    标签: c# entity-framework-4 dbcontext command-timeout


    【解决方案1】:

    它将适用于您的方法。

    或者子类化它(来自msdn forum

    public class YourContext : DbContext
    {
      public YourContext()
        : base("YourConnectionString")
      {
        // Get the ObjectContext related to this DbContext
        var objectContext = (this as IObjectContextAdapter).ObjectContext;
    
        // Sets the command timeout for all the commands
        objectContext.CommandTimeout = 120;
      }
    }
    

    【讨论】:

    • 使用as有什么意义?这不是把InvalidCastException 变成NullReferenceException 吗?我认为前者更清晰。
    • 有没有办法从部分类中设置它?我正在使用 EDMX 文件,并且希望在更改模型时避免被覆盖。
    • 查看 Perry Tribolet 的解决方案。简单得多。
    • 如果您像我一样想知道,CommandTimeout 以秒为单位。
    【解决方案2】:
    var ctx = new DbContext();
    ctx.Database.CommandTimeout = 120;
    

    【讨论】:

    • 这是我认为最好的解决方案。使用这种方法可以控制每条命令的超时时间。
    • 我在这个页面上是因为我有生产代码这样做,并且设置被忽略了。
    • 请注意,此值似乎不会传播到通过 Connection.CreateCommand 为同一上下文的 Connection 属性创建的 DbCommand 的 CommandTimeout 属性。您可能需要手动设置。
    • 我没有看到 DbContext.Database 上的 CommandTimeout 属性,使用 EF5 :( 我确实看到了其他属性。虽然我看到了连接超时设置:DbContext.Database.Connection.ConnectionTimeout跨度>
    【解决方案3】:

    这可能会对你有所帮助。

    public class MyContext : DbContext
    {    
        public MyContext () : base(ContextHelper.CreateConnection("my connection string"), true)
        {
            ((IObjectContextAdapter)this).ObjectContext.CommandTimeout = 300;
        }
    }
    

    【讨论】:

      【解决方案4】:

      我发现更改 .tt 文件对我有用,因为我以后不会丢失更改:

      添加这一行:

      ((IObjectContextAdapter)this).ObjectContext.CommandTimeout = 300;
      

      就在 DbContext 创建者之后和 !loader.IsLazy 构造之前:

      <#=Accessibility.ForType(container)#> partial class <#=code.Escape(container)#> : DbContext
      {
          public <#=code.Escape(container)#>()
              : base("name=<#=container.Name#>")
          {
              ((IObjectContextAdapter)this).ObjectContext.CommandTimeout = 300;
      <#
      if (!loader.IsLazyLoadingEnabled(container))
      

      然后它应该出现在您生成的 Context.cs 中:

      public MyEntities()
                  : base("name=MyEntities")
              {
                  ((IObjectContextAdapter)this).ObjectContext.CommandTimeout = 300;
              }
      

      【讨论】:

      • 这是更好的、可扩展的答案。
      【解决方案5】:

      以下是我在使用 EDMX 文件时解决此问题的方法。该解决方案更改了默认的 T4 模板,使生成的类继承自自定义 DbContext 类,该类指定了默认的命令超时,以及用于更改它的属性。

      我正在使用 Visual Studio 2012 和 EF 5.0。您的体验可能与其他版本不同。

      创建自定义 DbContext 类

      public class CustomDbContext : DbContext
      {
          ObjectContext _objectContext;
      
          public CustomDbContext( string nameOrConnectionString )
              : base( nameOrConnectionString )
          {
              var adapter = (( IObjectContextAdapter) this);
      
              _objectContext = adapter.ObjectContext;
      
              if ( _objectContext == null )
              {
                  throw new Exception( "ObjectContext is null." );    
              }
      
              _objectContext.CommandTimeout = Settings.Default.DefaultCommandTimeoutSeconds;
          }
      
          public int? CommandTimeout
          {
              get
              {
                  return _objectContext.CommandTimeout;
              }
              set
              {
                  _objectContext.CommandTimeout = value;
              }
          }
      }
      

      这有一个可选功能:我没有硬编码默认命令超时。相反,我从项目设置中加载它,以便我可以更改配置文件中的值。如何设置和使用项目设置不在此答案的范围内。

      我也没有硬编码连接字符串或连接字符串名称。它已经由生成的上下文类传递给构造函数,因此在这里对其进行硬编码是没有意义的。这不是什么新鲜事。 EDMX 文件已经为您生成了以下构造函数,所以我们只是传递值。

      public MyEntities()
          : base("name=MyEntities")
      {
      }
      

      (这指示 EF 从配置文件中加载名为“MyEntities”的连接字符串。)

      如果ObjectContext 为空,我将抛出自定义异常。我认为它永远不会,但它比获得NullReferenceException 更有意义。

      我将ObjectContext 存储在一个字段中,以便我可以创建一个属性来访问它以覆盖默认值。

      修改实体上下文 T4 模板

      在解决方案资源管理器中,展开 EDMX 文件以查看 T4 模板。他们有一个 .tt 扩展名。

      双击“MyModel.Context.tt”文件将其打开。在第 57 行附近,您应该会看到:

      <#=Accessibility.ForType(container)#> partial class <#=code.Escape(container)#> : DbContext
      

      此模板行生成“MyEntities”类的类定义,该类继承 DbContext。

      改行,让生成的类继承CustomDbContext,改为:

      <#=Accessibility.ForType(container)#> partial class <#=code.Escape(container)#> : CustomDbContext
      

      一旦你保存了这个文件,它就会重新生成这个类。如果没有,您可以右键单击 EDMX 文件并选择“运行自定义工具”。如果您在 EDMX 文件下展开“MyModel.Context.tt”文件,您将看到“MyModel.Context.cs”。这就是生成的文件。打开它,你应该会看到它现在继承了CustomDbContext

      public partial class MyEntities : CustomDbContext
      

      仅此而已。

      问题

      一旦将上下文类从 DbContext 更改为 CustomDbContext,如果您尝试使用“具有读/写操作和视图的控制器,使用 Entity Framework “ 模板。它会说“不支持的上下文类型。”。要解决此问题,请打开生成的“MyModel.Context.cs”类,并将其继承的类型临时更改回DbContext。添加新控制器后,您可以将其改回CustomDbContext

      【讨论】:

        【解决方案6】:

        我喜欢扩展方法:

        public static class DbContextExtensions
        {
           public static void SetCommandTimeout(this ObjectContext dbContext,
               int TimeOut)
           {
               dbContext.CommandTimeout = TimeOut;
           }
        }
        

        然后简单

        ((IObjectContextAdapter)cx).ObjectContext.SetCommandTimeout(300);
        

        【讨论】:

          【解决方案7】:

          如果有帮助,这就是 VB.Net 解决方案:

          Dim objectContext As Objects.ObjectContext = CType(Me,IObjectContextAdapter).ObjectContext
          objectContext.commandTimeout = connectionTimeout
          

          【讨论】:

            【解决方案8】:

            这类似于上面@Glazed 使用的方法,但我的方法也是使用自定义 DbContext 类,但我正在做相反的事情。实际上,我没有修改 T4 模板(.edmx 下的.tt 文件),而是从生成的 MyEntities 类继承,而不是像这样:

            T4 模板生成的 MyEntities 类:

            public partial class MyEntities : DbContext
            {
                public MyEntities()
                    : base("name=MyConnectionStringName")
                {
                }
            ...
            }
            

            然后创建一个新的自定义类作为 MyEntities 的包装器,如下所示:

            public class MyEntitiesContainer : MyEntities
            {
                private static readonly int _DEFAULT_TIMEOUT = 100;
                public MyEntitiesContainer()
                {
                    ((IObjectContextAdapter)this).ObjectContext.CommandTimeout = _DEFAULT_TIMEOUT;
                }
            
                //Use this method to temporarily override the default timeout
                public void SetCommandTimeout(int commandTimeout)
                {
                    ((IObjectContextAdapter)this).ObjectContext.CommandTimeout = commandTimeout;
                }
            
                //Use this method to reset the timeout back to default
                public void ResetCommandTimeout()
                {
                    ((IObjectContextAdapter)this).ObjectContext.CommandTimeout = _COMMAND_TIMEOUT;
                }
            }
            

            在您的代码中,实例化 Container 类,如果您需要为特定命令使用自定义超时,请使用提供的方法手动设置。

            using (var db = new MyEntitiesContainer()) {
                db.SetCommandTimeout(300);
                db.DoSomeLongCommand();
                db.ResetCommandTimeout();
                db.DoShorterCommand1();
                db.DoShorterCommand2();
                ...
            }
            

            这种方法的好处是您还可以为您的 Container 类创建一个接口并使用具有依赖注入的接口实例,然后您可以在单元测试中模拟您的数据库,此外还可以更轻松地控制命令timeout 和对象上下文的其他属性,您可以为其创建方法(例如延迟加载等)。

            【讨论】:

              【解决方案9】:

              我来这里是为了寻找一个为单个命令设置超时而不是这样的全局设置的示例。

              我认为举个例子来说明我是如何做到这一点的,这可能会有所帮助:

              var sqlCmd = new SqlCommand(sql, context.Database.Connection as SqlConnection);
              sqlCmd.Parameters.Add(idParam);
              sqlCmd.CommandTimeout = 90;
              
              if (sqlCmd.Connection.State == System.Data.ConnectionState.Closed)
              {
                  sqlCmd.Connection.Open();
              }
              sqlCmd.ExecuteNonQuery();
              sqlCmd.Connection.Close();
              

              【讨论】:

                【解决方案10】:

                @PerryTribolet 的答案对于 EF6 看起来不错,但对 EF5 确实有效。对于 EF,这是一种方法:创建一个 ObjectContext,在其上设置 CommandTimeout,然后从 ObjectContext 创建一个 DBContext。我将标志设置为将两个对象一起处理。这是 VB.NET 中的一个示例:

                        Dim context As New ObjectContext("name=Our_Entities")
                        Dim dbcontext As New System.Data.Entity.DbContext(context, True)
                
                        With context
                            .CommandTimeout = 300 'DBCommandTimeout
                        End With
                

                当然,您不必使用“With”。

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2011-12-31
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多