C# 扩展Linq partial方法时发生SQL Server超时异常
在.NET4.0和LINQtoSQL中,我试图使用分部类从更新方法(现有的DBML方法)中“触发”更改。为简单起见,设想一个包含列Id和列值的表Things auto gen DBML包含一个方法OnValueChanged,我将扩展该方法,作为练习,尝试更改另一行中的一个值:C# 扩展Linq partial方法时发生SQL Server超时异常,c#,.net,linq-to-sql,datacontext,C#,.net,Linq To Sql,Datacontext,在.NET4.0和LINQtoSQL中,我试图使用分部类从更新方法(现有的DBML方法)中“触发”更改。为简单起见,设想一个包含列Id和列值的表Things auto gen DBML包含一个方法OnValueChanged,我将扩展该方法,作为练习,尝试更改另一行中的一个值: public partial class Things { partial void OnValueChanged() { MyAppDataCont
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
}
}
}
公共部分类事物
{
部分无效OnValueChanged()
{
MyAppDataContext dc=新的MyAppDataContext();
var q=从dc.GetTable()中的o开始,其中o.Id==13选择o;
foreach(事物在q中为o)
{
o、 Value=“1”//尝试更改其他行
}
尝试
{
dc.提交更改();
}
捕获(例外)
{
//发生SQL超时
}
}
}
发生SQL超时错误。我怀疑datacontext在当前OnValueChanged()方法处理其datacontext之前尝试提交更改()时会感到困惑,但我不确定
在现有的DBML生成方法中,我通常找不到一个触发DB更新的好模式示例
有没有人能提供一些建议,说明为什么这样做不起作用,以及我如何才能完成一些可行的事情?(我知道我可以在SQL数据库中触发,但我不想采用这种方式。)
谢谢 首先,您根本没有在函数中处理
DataContext
。使用语句将其包装在中
实际的问题来自这样一个事实:您正在通过对检索到的值设置Value
属性来递归调用自己。在点击StackOverflowException
之前,您刚刚进入超时
不清楚你想在这里做什么;如果您试图在此处设置值
属性时与在其他任何地方设置属性时允许不同的行为,那么使用标志就足够简单了。在分部类中,在更新值之前,在foreach
块中的每个项目上声明名为updateingvalue
的internal
实例布尔自动属性,并将其设置为true
,然后在更新值之后将其设置为false
。然后,作为OnValueChanged
中的第一行,检查以确保updateingvalue
为false
像这样:
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();
}
}
}
公共部分类事物
{
内部布尔更新值{get;set;}
部分无效OnValueChanged()
{
if(updatevalue)返回;
使用(MyAppDataContext dc=new MyAppDataContext())
{
var q=从dc.GetTable()中的o开始,其中o.Id==13选择o;
foreach(事物在q中为o)
{
o、 更新值=真;
o、 Value=“1”//尝试更改其他行
o、 updatevalue=false;
}
dc.提交更改();
}
}
}
我怀疑您可能通过更改事物的OnValueChanged事件处理程序中事物的值引入了无限递归
对我来说,解决问题的更简洁的方法不是在DBML文件中生成类,而是在创建的类上使用。通过这样做,您可以在属性/列的设置器中进行“触发器”修改。我也遇到了类似的问题。我不认为这是代码中的错误,我倾向于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();
}
}
}
}
public构造函数(string connString,CogTrkDBLog logWriter0)
{
connectionString=connString;
logWriter=logWriter0;
使用(SqlConnection conn=newsqlconnection(connString))
{
conn.Open();
使用(SqlCommand cmd=newsqlcommand(“SELECT是从sys.databases启用的,其中name='cogtrk',conn))
{
bool r=(bool)cmd.ExecuteScalar();
if(!r)
{
抛出新异常(“is_broker_enabled was false”);
}
}
}
如果(!CanRequestNotifications())
{
抛出新异常(“没有足够的运行权限”);
}
//删除任何现有的依赖关系连接,然后创建一个新的依赖关系连接。
SqlDependency.Stop(connectionString);
SqlDependency.Start(connectionString);
if(连接==null)
{
连接=新的SqlConnection(connectionString);
connection.Open();
}
如果(命令==null)
{
通用域名格式