Devart ChangeConflictException 但值仍写入数据库
Devart ChangeConflictException but values still written to database
我间歇性 Devart.Data.Linq.ChangeConflictException: Row not found or changed
抬起丑陋的头。搞笑的是,变化还是写入了数据库!
堆栈跟踪显示:
Devart.Data.Linq.ChangeConflictException: Row not found or changed.
at Devart.Data.Linq.Engine.b4.a(IObjectEntry[] A_0, ConflictMode A_1, a A_2)
at Devart.Data.Linq.Engine.b4.a(ConflictMode A_0)
at Devart.Data.Linq.DataContext.SubmitChanges(ConflictMode failureMode)
at Devart.Data.Linq.DataContext.SubmitChanges()
at Billing.Eway.EwayInternal.SuccessCustomerRenewal(String username, Bill bill, EwayTransaction transaction) in 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:line 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()
行,因为该数据库上下文是使用 Devart 的数据库上下文(MySQL 数据库的 Linq 解决方案):另一个上下文使用纯 MS Linq。
不仅将更改写入 MySql DB(内部 using
块),还会写入 MsSql DB(外部 using
块using
块)。但这就是神奇的成功结束的地方。
如果可以的话,我会写一个最小的、完整的和可验证的例子,但奇怪的是我不能。
那么,为什么在 Devart.Data.Linq.ChangeConflictException
之后更改会保存到数据库中?当我之前遇到 System.Data.Linq.ChangeConflictException
更改未保存时。
编辑 1:
我现在还包含了 .PDB 文件并获得了异常确切来源的行号确认。
编辑 2:
我现在明白为什么我无法生成 ChangeConflictException
,这是怎么回事?
这些是 MembershipDate
:_
的属性
[Column(Name = @"Membership_Date", Storage = "_MembershipDate", CanBeNull = false, DbType = "DATETIME NOT NULL", UpdateCheck = UpdateCheck.Never)]
我知道我可以明确地强制我的更改通过以覆盖任何潜在的冲突,但这似乎是不可取的(我不知道我会覆盖什么!)。同样,我可以将提交包裹在 try
块中,然后重试(每次都重新读取)直到成功,但这看起来很笨拙。我应该如何处理这个间歇性问题?
编辑 3:
不是多次调用造成的。此函数由单实例应用程序在一个地方调用。每次 运行 时它都会创建日志条目,并且它们只会被创建一次。从那以后,我将电子邮件调用移到了方法的顶部:电子邮件只发送一次,发生异常,并且数据库更改 仍在进行 .
我相信它与 using
块有关。在通过调试器解决一个不相关的问题时,我进入了 using
块,但在 SubmitChanges()
调用之前停止了执行。并且更改仍然写入数据库。我的理解是 using
块是为了确保资源被清理(连接关闭等),但似乎整个块都在执行。研究的新途径...
但它仍然没有回答 ChangeConflictException
在给定 Devart .
的情况下是如何可能的
编辑 4:
所以我并没有发疯,即使我在 using
块中间结束执行后,数据库更改也确实提交了,但是 .
编辑 5:
根据@Evk 的建议,我包含了一些数据库日志记录(并更新了上面的堆栈跟踪和代码片段)。这个异常的发生率似乎已经下降了,因为它是我实施日志记录后才发生的。以下是更多详细信息:
外部(MS SQL)日志文件:
SELECT TOP (1) [t0].[id], [t0].[Username], [t0].[TokenId], [t0].[PlanId], [t0].[SignupDate], [t0].[NextBillingDate], [t0].[PaymentType], [t0].[RetryCount], [t0].[AccountStatus], [t0].[CancelDate]
FROM [dbo].[EwayCustomer] AS [t0]
WHERE [t0].[Username] = @p0
-- @p0: Input NVarChar (Size = 4000; Prec = 0; Scale = 0) [dyonis]
-- Context: SqlProvider(Sql2008) Model: AttributedMetaModel Build: 4.0.30319.18408a
(只显示更新显示的SELECT调用(.First()
),none)。
内部(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.Email, 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.isEncrypted, t1.PlanId
FROM grasbill.customers t1
WHERE t1.Username = :p0 LIMIT 1
-- p0: Input VarChar (Size = 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: Input DateTime (Size = 0; DbType = DateTime) [8/3/2016 4:42:53 AM]
-- key1: Input Int (Size = 0; DbType = Int32) [7731]
-- Context: Devart.Data.MySql.Linq.Provider.MySqlDataProvider Mapping: AttributeMappingSource Build: 4.4.519.0
(显示 SELECT 和更新调用)
所以日志文件并没有真正提供任何关于正在发生的事情的线索,但是 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 的引用?还是有其他事情发生?
好吧,6 个月后,我终于弄清了这个问题的真相。不确定它是否会对其他人有所帮助,但无论如何我都会详细说明。
这里有 2 个问题,其中 1 个是愚蠢的(通常都是这样),但有一个是我不知道或没有预料到的。
问题 1
即使出现异常也神奇地对数据库进行更改的原因是因为该函数 ApplyBillToCustomerAccount(username, bill, true);
中的 第一行 代码更新了数据库! <脸掌>
问题 2
(Devart) ChangeConflictException
不仅在数据发生变化时抛出,而且 also if you're not making any changes 也抛出。 MS SQL 可以非常精确地存储 DateTime
秒,但是 MySQL(或者我至少是 运行 的那个)只能存储 秒。这就是间歇性出现的地方。如果我的数据库调用足够快,或者刚好接近第二个边界,它们都会同时四舍五入。 Devart 看到没有要写入的更改,并抛出 ChangeConflictException
.
我最近对数据库进行了一些优化,从而提高了响应能力,并且大大增加了此异常的发生率。这就是线索之一。
我还尝试按照链接的 Devart post 中的说明将 Found Rows
参数更改为 true
,但发现它对我的情况没有帮助。或者也许我做错了。不管怎样,现在我已经找到了问题的根源,我可以消除重复的数据库更新。
我间歇性 Devart.Data.Linq.ChangeConflictException: Row not found or changed
抬起丑陋的头。搞笑的是,变化还是写入了数据库!
堆栈跟踪显示:
Devart.Data.Linq.ChangeConflictException: Row not found or changed. at Devart.Data.Linq.Engine.b4.a(IObjectEntry[] A_0, ConflictMode A_1, a A_2) at Devart.Data.Linq.Engine.b4.a(ConflictMode A_0) at Devart.Data.Linq.DataContext.SubmitChanges(ConflictMode failureMode) at Devart.Data.Linq.DataContext.SubmitChanges() at Billing.Eway.EwayInternal.SuccessCustomerRenewal(String username, Bill bill, EwayTransaction transaction) in 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:line 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()
行,因为该数据库上下文是使用 Devart 的数据库上下文(MySQL 数据库的 Linq 解决方案):另一个上下文使用纯 MS Linq。
不仅将更改写入 MySql DB(内部 using
块),还会写入 MsSql DB(外部 using
块using
块)。但这就是神奇的成功结束的地方。
如果可以的话,我会写一个最小的、完整的和可验证的例子,但奇怪的是我不能
那么,为什么在 Devart.Data.Linq.ChangeConflictException
之后更改会保存到数据库中?当我之前遇到 System.Data.Linq.ChangeConflictException
更改未保存时。
编辑 1:
我现在还包含了 .PDB 文件并获得了异常确切来源的行号确认。
编辑 2:
我现在明白为什么我无法生成 ChangeConflictException
,这是怎么回事?
这些是 MembershipDate
:_
[Column(Name = @"Membership_Date", Storage = "_MembershipDate", CanBeNull = false, DbType = "DATETIME NOT NULL", UpdateCheck = UpdateCheck.Never)]
我知道我可以明确地强制我的更改通过以覆盖任何潜在的冲突,但这似乎是不可取的(我不知道我会覆盖什么!)。同样,我可以将提交包裹在 try
块中,然后重试(每次都重新读取)直到成功,但这看起来很笨拙。我应该如何处理这个间歇性问题?
编辑 3:
不是多次调用造成的。此函数由单实例应用程序在一个地方调用。每次 运行 时它都会创建日志条目,并且它们只会被创建一次。从那以后,我将电子邮件调用移到了方法的顶部:电子邮件只发送一次,发生异常,并且数据库更改 仍在进行 .
我相信它与 using
块有关。在通过调试器解决一个不相关的问题时,我进入了 using
块,但在 SubmitChanges()
调用之前停止了执行。并且更改仍然写入数据库。我的理解是 using
块是为了确保资源被清理(连接关闭等),但似乎整个块都在执行。研究的新途径...
但它仍然没有回答 ChangeConflictException
在给定 Devart
编辑 4:
所以我并没有发疯,即使我在 using
块中间结束执行后,数据库更改也确实提交了,但是
编辑 5:
根据@Evk 的建议,我包含了一些数据库日志记录(并更新了上面的堆栈跟踪和代码片段)。这个异常的发生率似乎已经下降了,因为它是我实施日志记录后才发生的。以下是更多详细信息:
外部(MS SQL)日志文件:
SELECT TOP (1) [t0].[id], [t0].[Username], [t0].[TokenId], [t0].[PlanId], [t0].[SignupDate], [t0].[NextBillingDate], [t0].[PaymentType], [t0].[RetryCount], [t0].[AccountStatus], [t0].[CancelDate] FROM [dbo].[EwayCustomer] AS [t0] WHERE [t0].[Username] = @p0 -- @p0: Input NVarChar (Size = 4000; Prec = 0; Scale = 0) [dyonis] -- Context: SqlProvider(Sql2008) Model: AttributedMetaModel Build: 4.0.30319.18408a
(只显示更新显示的SELECT调用(.First()
),none)。
内部(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.Email, 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.isEncrypted, t1.PlanId FROM grasbill.customers t1 WHERE t1.Username = :p0 LIMIT 1 -- p0: Input VarChar (Size = 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: Input DateTime (Size = 0; DbType = DateTime) [8/3/2016 4:42:53 AM] -- key1: Input Int (Size = 0; DbType = Int32) [7731] -- Context: Devart.Data.MySql.Linq.Provider.MySqlDataProvider Mapping: AttributeMappingSource Build: 4.4.519.0
(显示 SELECT 和更新调用)
所以日志文件并没有真正提供任何关于正在发生的事情的线索,但是 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 的引用?还是有其他事情发生?
好吧,6 个月后,我终于弄清了这个问题的真相。不确定它是否会对其他人有所帮助,但无论如何我都会详细说明。
这里有 2 个问题,其中 1 个是愚蠢的(通常都是这样),但有一个是我不知道或没有预料到的。
问题 1
即使出现异常也神奇地对数据库进行更改的原因是因为该函数 ApplyBillToCustomerAccount(username, bill, true);
中的 第一行 代码更新了数据库! <脸掌>
问题 2
(Devart) ChangeConflictException
不仅在数据发生变化时抛出,而且 also if you're not making any changes 也抛出。 MS SQL 可以非常精确地存储 DateTime
秒,但是 MySQL(或者我至少是 运行 的那个)只能存储 秒。这就是间歇性出现的地方。如果我的数据库调用足够快,或者刚好接近第二个边界,它们都会同时四舍五入。 Devart 看到没有要写入的更改,并抛出 ChangeConflictException
.
我最近对数据库进行了一些优化,从而提高了响应能力,并且大大增加了此异常的发生率。这就是线索之一。
我还尝试按照链接的 Devart post 中的说明将 Found Rows
参数更改为 true
,但发现它对我的情况没有帮助。或者也许我做错了。不管怎样,现在我已经找到了问题的根源,我可以消除重复的数据库更新。