扩展Linq部分方法时SQL Server超时异常

时间:2010-06-25 17:46:36

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

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

的表格

auto gen 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()方法处理了它的datacontext之前,datacontext在尝试SubmitChanges()时感到困惑,但我不确定。

大多数情况下,我找不到一个好的模式示例,用于在现有DBML生成的方法中触发对DB的更新。

任何人都可以提供任何指示,说明为什么这不起作用以及我如何能够完成一些有效的工作? (我意识到我可以在SQL数据库中触发,但不想采用那条路线。)

谢谢!

3 个答案:

答案 0 :(得分:3)

首先,您的功能中根本没有处理DataContext。用using语句包装它。

实际问题来自于您通过在检索到的值上设置Value属性来递归调用自己的事实。您只是在遇到StackOverflowException之前就已经进入超时状态。

目前还不清楚你在这里要做什么;如果你试图在这里设置Value属性与其他任何地方之间允许不同的行为,那么使用标志就足够了。在您的部分类中,声明一个名为internal的{​​{1}}实例布尔自动属性,并在更新值之前将UpdatingValue块中的每个项目设置为true,然后更新值后将其设置为foreach。然后,作为false中的第一行,检查以确保OnValueChangedUpdatingValue

像这样:

false

答案 1 :(得分:0)

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

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

答案 2 :(得分:0)

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

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

可能需要在调用SubmitChanges之前必须返回OnValueChanged调用。也许在另一个线程上调用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();
                }
            }
        }
    }