我有一个间歇性的Devart.Data.Linq.ChangeConflictException: Row not found or changed
提升它丑陋的头脑。有趣的是,更改仍然写入数据库!
堆栈跟踪说:
Devart.Data.Linq.ChangeConflictException:未找到或更改行。 在Devart.Data.Linq.Engine.b4.a(IObjectEntry [] A_0,ConflictMode A_1,A_2) 在Devart.Data.Linq.Engine.b4.a(ConflictMode A_0) 在Devart.Data.Linq.DataContext.SubmitChanges(ConflictMode failureMode) 在Devart.Data.Linq.DataContext.SubmitChanges() at Billing.Eway.EwayInternal.SuccessCustomerRenewal(String username,Bill bill,EwayTransaction transaction)c:\ Users \ Ian \ Source \ Repos \ billing-class-library \ Billing \ Billing \ Eway \ EwayInternal.cs:line 552 at Billing.Eway.Eway.BillAllUsers()in c:\ Users \ Ian \ Source \ Repos \ billing-class-library \ Billing \ Billing \ Eway \ Eway.cs:138行
我的Billing.Eway.EwayInternal.SuccessCustomerRenewal
代码:
internal static void SuccessCustomerRenewal(string username, Bill bill, EwayTransaction transaction)
{
// Give them their points!
ApplyBillToCustomerAccount(username, bill, true);
BillingEmail.SendRenewalSuccessEmail(username, bill, transaction);
using (MsSqlDataClassesDataContext msSqlDb = new MsSqlDataClassesDataContext())
{
// TODO: Remove this logging
msSqlDb.Log = new StreamWriter(@"logs\db\" + Common.GetCurrentTimeStamp() + "-MsSQL.txt", true) { AutoFlush = true };
EwayCustomer ewayCustomer = msSqlDb.EwayCustomers.First(c => c.Username == username);
ewayCustomer.NextBillingDate = Common.GetPlanExpiry(bill.BillPlan);
using (MySqlDataContext mySqlDb = new MySqlDataContext())
{
// TODO: Remove this logging
mySqlDb.Log = new StreamWriter(@"logs\db\" + Common.GetCurrentTimeStamp() + "-MySQL.txt", true) { AutoFlush = true };
BillingMySqlContext.Customer grasCustomer = mySqlDb.Customers.First(c => c.Username == username);
// Extend their membership date out so that the plan doesn't expire because of a failed credit card charge.
grasCustomer.MembershipDate =
ewayCustomer.NextBillingDate.AddDays(1);
mySqlDb.SubmitChanges(); // <-- This is line 552
}
msSqlDb.SubmitChanges();
}
}
我知道问题出现在mySqlDb.SubmitChanges()
行,因为该DB上下文是使用Devart(用于MySQL数据库的Linq解决方案)的上下文:另一个上下文使用纯MS Linq。
不仅将更改写入MySql DB(内部using
块),而且还将其写入 MsSql DB(外部using
块)。但那是神奇的成功结束的地方。
如果可以,我会写一个最小的,完整的和可验证的例子,但奇怪的是我无法generate a Devart ChangeConflictException。
那么,为什么在Devart.Data.Linq.ChangeConflictException
之后将更改保存到数据库?当我之前遇到System.Data.Linq.ChangeConflictException
时,未保存更改。
我现在还包含了.PDB文件,并获得了异常确切来源的行号确认。
我现在明白为什么我无法生成ChangeConflictException
,所以这是怎么回事?
这些是MembershipDate
:_
[Column(Name = @"Membership_Date", Storage = "_MembershipDate", CanBeNull = false, DbType = "DATETIME NOT NULL", UpdateCheck = UpdateCheck.Never)]
我知道我可以明确强制我的改变来覆盖任何潜在的冲突,但这似乎是不可取的(我不知道我会覆盖什么!)。类似地,我可以将提交包装在try
块中,然后重试(每次重读)直到成功,但这看起来很笨拙。我应该如何处理这个间歇性问题?
它不是由多个电话引起的。通过单实例应用程序在一个地方调用此函数。它每次运行时都会创建日志条目,并且只会创建一次。我已经将电子邮件调用移到方法的顶部:电子邮件只发送一次,发生异常,数据库更改仍然是。
我认为它与using
块有关。在针对不相关的问题逐步调试调试器时,我进入using
块,但在SubmitChanges()
调用之前停止执行。并且更改仍写入数据库。我的理解是using
块是为了确保清理资源(连接关闭等),但似乎整个块正在执行。一种新的研究途径......
但鉴于Devart explicitly ignores them,它仍然无法解答ChangeConflictException
的可能性。
所以我没有发疯,即使在using
块中间执行结束后,数据库更改仍然提交,但是it only works for websites。
根据@Evk的建议,我已经包含了一些数据库日志记录(并更新了上面的堆栈跟踪和代码片段)。这个异常的发生率似乎已经下降,因为它刚刚实施了日志记录。以下是其他详细信息:
外(MS SQL)日志文件:
SELECT TOP(1)[t0]。[id],[t0]。[用户名],[t0]。[TokenId],[t0]。[PlanId],[t0]。[SignupDate],[t0 ]。[NextBillingDate],[t0]。[PaymentType],[t0]。[RetryCount],[t0]。[AccountStatus],[t0]。[CancelDate] 来自[dbo]。[EwayCustomer] AS [t0] WHERE [t0]。[用户名] = @ p0 - @ p0:输入NVarChar(大小= 4000; Prec = 0; Scale = 0)[dyonis] - 上下文:SqlProvider(Sql2008)型号:AttributedMetaModel Build:4.0.30319.18408a
(它只显示SELECT调用(.First()
),没有更新显示)。
内部(MySQL)日志文件:
SELECT t1.Customer_ID,t1.Username,t1.Account_Group,t1.Account_Password,t1.First_Name,t1.Last_Name,t1.Account_Type,t1.Points,t1.PromoPoints,t1.Phone,t1.Cell,t1。电子邮件,t1.Address1,t1.Address2,t1.City,t1.State,t1.Country,t1.Postcode,t1.Membership_Group,t1.Suspend_On_Zero_Points,t1.Yahoo_ID,t1.MSN_ID,t1.Skype_ID,t1.Repurchase_Thresh, t1.Active,t1.Delete_Account,t1.Last_Activity,t1.Membership_Expires_After_x_Days,t1.Membership_Date,t1.auth_name,t1.created_by,t1.created_on,t1.AccountGroup_Points_Used,t1.AccountGroup_Points_Threashold,t1.LegacyPoints,t1.Can_Make_Reservation,t1。 Gallery_Access,t1.Blog_Access,t1.Private_FTP,t1.Photometrica,t1.Promo_Code,t1.Promo_Expire_DTime,t1.Gift_FirstName,t1.Gift_LastName,t1.Gift_Email,t1.Gift_Phone,t1.Gift_Active,t1.NoMarketingEmail,t1.Can_Schedule, t1.Refered_By,t1.Q1_Hear_About_Us,t1.Q2_Exp_Level,t1.Q3_Intrests,t1.GIS_DTime_UTC,t1.Membership_Expire_Notice_Sent,t1.Promo_Expire_Notice_Sent,t1.isEncrypt ed,t1.PlanId 来自grasbill.customers t1 在哪里t1.Username =:p0 LIMIT 1 - p0:输入VarChar(大小= 6; DbType = AnsiString)[dyonis] - Context:Devart.Data.MySql.Linq.Provider.MySqlDataProvider Mapping:AttributeMappingSource Build:4.4.519.0
UPDATE grasbill.customers SET Membership_Date =:p1 WHERE Customer_ID =:key1 - p1:输入DateTime(大小= 0; DbType = DateTime)[8/3/2016 4:42:53 AM] - key1:输入Int(大小= 0; DbType = Int32)[7731] - Context:Devart.Data.MySql.Linq.Provider.MySqlDataProvider Mapping:AttributeMappingSource Build:4.4.519.0
(显示SELECT和UPDATE调用)
因此,日志文件并没有真正提供任何关于发生了什么的线索,但是再次 MS SQL数据库已经更新了! NextBillingDate
字段已经已正确设置,按此行:
ewayCustomer.NextBillingDate = Common.GetPlanExpiry(bill.BillPlan);
如果它没有更新,用户将在下一个计时器滴答(5分钟后)再次收费,我可以从记录中看到没有发生。
另一个值得注意的有趣事情是日志文件时间戳。从上面的代码中可以看出,我获取了日志文件名的当前(UTC)时间。以下是Windows文件资源管理器显示的信息:
MS SQL日志文件创建于04:42(UTC),最后一次修改时间为14:42(UTC + 10,Windows本地时间),但MySQL日志文件最后修改时间为15:23(UTC + 10) ,创建后 41分钟。现在我假设日志文件StreamWriter在离开作用域后立即关闭。这种延迟是否是例外的预期副作用?垃圾收集器需要41分钟才能意识到我不再需要对StreamWriter的引用吗?或者还有其他事情发生了吗?
答案 0 :(得分:0)
6个月后,我终于找到了问题的根源。不确定它是否能帮助其他任何人,但无论如何我都会详细说明。
这里有两个问题,其中一个是愚蠢的(通常是这样),但其中一个是合法的,我不知道或期待的。
即使存在异常,神奇地对数据库进行了更改的原因是因为该函数ApplyBillToCustomerAccount(username, bill, true);
中的第一个代码行更新了数据库! &LT;捂脸&GT;
如果数据已更改,则只会抛出(Devart)ChangeConflictException
,但 also if you're not making any changes 。 MS SQL以极高的精度存储DateTime
,但MySQL(或者我至少运行的那个)只能存储到秒。在这里,间歇性进入。如果我的数据库调用足够快,或者只是在第二个边界附近,它们都会同时进行。 Devart没有看到任何变化,并投了ChangeConflictException
。
我最近对数据库进行了一些优化,这导致了更高的响应速度,并且大量增加了此异常的发生率。这是线索之一。
此外,我尝试按照链接的Devart帖子中的说明将Found Rows
参数更改为true
,但发现它对我的情况没有帮助。或许我做错了。无论哪种方式,我发现了问题的根源,我可以消除重复的数据库更新。