Devart ChangeConflictException但值仍写入数据库

时间:2016-04-13 01:08:39

标签: c# linq concurrency devart

我有一个间歇性的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时,未保存更改。

编辑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块是为了确保清理资源(连接关闭等),但似乎整个块正在执行。一种新的研究途径......
但鉴于Devart explicitly ignores them,它仍然无法解答ChangeConflictException的可能性。

编辑4:

所以我没有发疯,即使在using块中间执行结束后,数据库更改仍然提交,但是it only works for websites

编辑5:

根据@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文件资源管理器显示的信息:

Log file timestamp details

MS SQL日志文件创建于04:42(UTC),最后一次修改时间为14:42(UTC + 10,Windows本地时间),但MySQL日志文件最后修改时间为15:23(UTC + 10) ,创建后 41分钟。现在我假设日志文件StreamWriter在离开作用域后立即关闭。这种延迟是否是例外的预期副作用?垃圾收集器需要41分钟才能意识到我不再需要对StreamWriter的引用吗?或者还有其他事情发生了吗?

1 个答案:

答案 0 :(得分:0)

6个月后,我终于找到了问题的根源。不确定它是否能帮助其他任何人,但无论如何我都会详细说明。

这里有两个问题,其中一个是愚蠢的(通常是这样),但其中一个是合法的,我不知道或期待的。

问题1

即使存在异常,神奇地对数据库进行了更改的原因是因为该函数ApplyBillToCustomerAccount(username, bill, true);中的第一个代码行更新了数据库! &LT;捂脸&GT;

问题2

如果数据已更改,则只会抛出(Devart)ChangeConflictException,但 also if you're not making any changes 。 MS SQL以极高的精度存储DateTime,但MySQL(或者我至少运行的那个)只能存储到。在这里,间歇性进入。如果我的数据库调用足够快,或者只是在第二个边界附近,它们都会同时进行。 Devart没有看到任何变化,并投了ChangeConflictException

我最近对数据库进行了一些优化,这导致了更高的响应速度,并且大量增加了此异常的发生率。这是线索之一。

此外,我尝试按照链接的Devart帖子中的说明将Found Rows参数更改为true,但发现它对我的情况没有帮助。或许我做错了。无论哪种方式,我发现了问题的根源,我可以消除重复的数据库更新。