【问题标题】:SQL Server timeout exception when extending Linq partial methods扩展 Linq 部分方法时 SQL Server 超时异常
【发布时间】:2010-06-25 17:46:35
【问题描述】:

在 .NET 4.0 和 Linq to SQL 中,我尝试使用部分类从更新方法(现有 DBML 方法)中“触发”更改。为简单起见,想象一个带有列 Id 和 Value 的表格 Things

自动生成 DBML 包含一个方法 OnValueChanged,我将对其进行扩展,并作为练习尝试更改另一行中的一个值:

   public partial class Things
    {
        partial void OnValueChanged()
        {
            MyAppDataContext dc = new MyAppDataContext();
            var q = from o in dc.GetTable<Things>() where o.Id == 13 select o;
            foreach (Things o in q)
            {
                o.Value = "1";  // try to change some other row
            }
            try
            {
                dc.SubmitChanges();
            }
            catch (Exception)
            {
                // SQL timeout occurs 
            }
        }
    }

发生 SQL 超时错误。我怀疑数据上下文在当前 OnValueChanged() 方法处理它的数据上下文之前尝试 SubmitChanges() 会感到困惑,但我不确定。

在现有的 DBML 生成方法中,我通常找不到触发数据库更新的良好模式示例。

任何人都可以提供任何关于为什么这不起作用以及我如何完成可以正常工作的事情的指示吗? (我意识到我可以在 SQL 数据库中触发,但不想走那条路。)

谢谢!

【问题讨论】:

    标签: c# .net linq-to-sql datacontext


    【解决方案1】:

    首先,您根本没有在函数中处理 DataContext。将其包装在 using 语句中。

    实际问题来自于您通过在检索到的值上设置 Value 属性来递归调用自己。在您点击StackOverflowException 之前,您只是遇到了超时。

    目前还不清楚您要在这里做什么;如果您尝试在此处设置 Value 属性与在其他任何地方设置不同的行为,那么使用标志就足够简单了。在您的部分类中,声明一个名为 UpdatingValueinternal 实例布尔自动属性,并在更新值之前将其设置为 true 在您的 foreach 块内的每个项目上,然后将其设置为 false更新值。然后,作为OnValueChanged 中的第一行,检查以确保UpdatingValuefalse

    像这样:

    public partial class Things
    {
         internal bool UpdatingValue { get; set; }
    
         partial void OnValueChanged()
         {
             if (UpdatingValue) return;
    
             using(MyAppDataContext dc = new MyAppDataContext())
             {
                 var q = from o in dc.GetTable<Things>() where o.Id == 13 select o;
                 foreach (Things o in q)
                 {
                     o.UpdatingValue = true;
                     o.Value = "1";  // try to change some other row
                     o.UpdatingValue = false;
                 }
    
                 dc.SubmitChanges();
             }
         }
     }
    

    【讨论】:

      【解决方案2】:

      我怀疑您可能通过在 Things 的 OnValueChanged 事件处理程序中更改 Things 的值来引入无限递归。

      对我来说,一个更简洁的解决方案不是在 DBML 文件中生成您的类,而是在您创建的类上使用 LinqToSql attributes。通过这样做,您可以在属性/列的设置器中进行“触发”修改。

      【讨论】:

        【解决方案3】:

        我遇到了类似的问题。我不认为这是您代码中的错误,我倾向于 SqlDependency 工作方式的错误。我和你做了同样的事情,但我逐渐测试了它。如果 select 语句返回 1-100 行,那么它工作正常。如果 select 语句返回 1000 行,那么我会得到 SqlException(超时)。

        这不是堆栈溢出问题(至少在此客户端代码中没有)。在 OnValueChanged 事件处理程序处设置一个断点表明它不会在 SubmitChanges 调用挂起时再次被调用。

        可能要求 OnValueChanged 调用必须返回才能调用 SubmitChanges。也许在不同的线程上调用 SubmitChanges 可能会有所帮助。

        我的解决方案是将代码包装在一个大的 try/catch 块中以捕获 SqlException。如果发生这种情况,那么我执行相同的查询,但我不使用 SqlDependency 并且不将其附加到命令。这不再挂起 SubmitChanges 调用。然后在此之后,我重新创建 SqlDependency,然后再次进行查询,以重新注册依赖项。

        这并不理想,但至少它最终会处理所有行。只有在有很多行要选择的情况下才会出现该问题,并且如果程序运行顺利,则不应发生这种情况,因为它会不断追赶。

            public Constructor(string connString, CogTrkDBLog logWriter0)
            {
                connectionString = connString;
                logWriter = logWriter0;
        
                using (SqlConnection conn = new SqlConnection(connString))
                {
                    conn.Open();
                    using (SqlCommand cmd = new SqlCommand("SELECT is_broker_enabled FROM sys.databases WHERE name = 'cogtrk'", conn))
                    {
                        bool r = (bool) cmd.ExecuteScalar();
                        if (!r)
                        {
                            throw new Exception("is_broker_enabled was false");
                        }
                    }
                }
                if (!CanRequestNotifications())
                {
                    throw new Exception("Not enough permission to run");
                }
        
        
                // Remove any existing dependency connection, then create a new one.
                SqlDependency.Stop(connectionString);
                SqlDependency.Start(connectionString);
        
                if (connection == null)
                {
                    connection = new SqlConnection(connectionString);
                    connection.Open();
                }
        
                if (command == null)
                {
                    command = new SqlCommand(GetSQL(), connection);
                }
        
                GetData(false);
                GetData(true);
            }
        
        
            private string GetSQL()
            {
                return "SELECT id, command, state, value " +
                " FROM dbo.commandqueue WHERE state = 0 ORDER BY id";
        
            }
            void dependency_OnChange(object sender, SqlNotificationEventArgs e)
            {
                // Remove the handler, since it is only good
                // for a single notification.
                SqlDependency dependency = (SqlDependency)sender;
                dependency.OnChange -= dependency_OnChange;
        
                GetData(true);
            }
        
            void GetData(bool withDependency)
            {
                lock (this)
                {
                    bool repeat = false;
                    do {
                        repeat = false;
                        try
                        {
                            GetDataRetry(withDependency);
                        }
                        catch (SqlException)
                        {
                            if (withDependency) {
                                GetDataRetry(false);
                                repeat = true;
                            }
                        }
                    } while (repeat);
                }
            }
        
            private void GetDataRetry(bool withDependency)
            {
                // Make sure the command object does not already have
                // a notification object associated with it.
                command.Notification = null;
        
                // Create and bind the SqlDependency object
                // to the command object.
        
                if (withDependency)
                {
                    SqlDependency dependency = new SqlDependency(command);
                    dependency.OnChange += dependency_OnChange;
                }
        
        
                Console.WriteLine("Getting a batch of commands");
                // Execute the command.
                using (SqlDataReader reader = command.ExecuteReader())
                {
                    using (CommandQueueDb db = new CommandQueueDb(connectionString))
                    {
                        foreach (CommandEntry c in db.Translate<CommandEntry>(reader))
                        {
                            Console.WriteLine("id:" + c.id);
                            c.state = 1;
                            db.SubmitChanges();
                        }
                    }
                }
            }
        

        【讨论】:

          猜你喜欢
          • 2017-07-02
          • 2014-01-11
          • 1970-01-01
          • 2023-03-15
          • 2016-05-03
          • 1970-01-01
          • 1970-01-01
          • 2011-04-27
          • 1970-01-01
          相关资源
          最近更新 更多