C# Linq2Sql更改跟踪不工作
我正在运行下面的代码,根据每天早上发送给我们的银行交易历史文件更新一些记录。这是非常基本的东西,但由于某种原因,当我讲到最后时,C# Linq2Sql更改跟踪不工作,c#,sql-server,linq-to-sql,sqltransaction,C#,Sql Server,Linq To Sql,Sqltransaction,我正在运行下面的代码,根据每天早上发送给我们的银行交易历史文件更新一些记录。这是非常基本的东西,但由于某种原因,当我讲到最后时,dbContext.GetChangeSet()报告所有操作的“0” public void ProcessBatchFile(string fileName) { List<string[]> failed = new List<string[]>(); int recCount = 0; DateTime dtStar
dbContext.GetChangeSet()
报告所有操作的“0”
public void ProcessBatchFile(string fileName)
{
List<string[]> failed = new List<string[]>();
int recCount = 0;
DateTime dtStart = DateTime.Now;
using (ePermitsDataContext dbContext = new ePermitsDataContext())
{
try
{
// A transaction must be begun before any data is read.
dbContext.BeginTransaction();
dbContext.ObjectTrackingEnabled = true;
// Load all the records for this batch file.
var batchRecords = (from b in dbContext.AmegyDailyFiles
where b.FileName == fileName
&& b.BatchProcessed == false
&& (b.FailReason == null || b.FailReason.Trim().Length < 1)
select b);
// Loop through the loaded records
int paymentID;
foreach (var r in batchRecords)
{
paymentID = 0;
try
{
// We have to 'parse' the primary key, since it's stored as a string value with leading zero's.
if (!int.TryParse(r.TransAct.TrimStart('0'), out paymentID))
throw new Exception("TransAct value is not a valid integer: " + r.TransAct);
// Store the parsed, Int32 value in the original record and read the "real" record from the database.
r.OrderPaymentID = paymentID;
var orderPayment = this.GetOrderPayment(dbContext, paymentID);
if (string.IsNullOrWhiteSpace(orderPayment.AuthorizationCode))
// If we haven't processed this payment "Payment Received" do it now.
this.PaymentReceived(orderPayment, r.AuthorizationNumber);
// Update the PaymentTypeDetailID (type of Credit Card--all other types will return NULL).
var paymentTypeDetail = dbContext.PaymentTypes.FirstOrDefault(w => w.PaymentType1 == r.PayType);
orderPayment.PaymentTypeDetailID = (paymentTypeDetail != null ? (int?)paymentTypeDetail.PaymentTypeID : null);
// Match the batch record as processed.
r.BatchProcessed = true;
r.BatchProcessedDateTime = DateTime.Now;
dbContext.SubmitChanges();
}
catch (Exception ex)
{
// If there's a problem, just record the error message and add it to the "failed" list for logging and notification.
if (paymentID > 0)
r.OrderPaymentID = paymentID;
r.BatchProcessed = false;
r.BatchProcessedDateTime = null;
r.FailReason = ex.Message;
failed.Add(new string[] { r.TransAct, ex.Message });
dbContext.SubmitChanges();
}
recCount++;
}
dbContext.CommitTransaction();
}
// Any transaction will already be commited, if the process completed successfully. I just want to make
// absolutely certain that there's no chance of leaving a transaction open.
finally { dbContext.RollbackTransaction(); }
}
TimeSpan procTime = DateTime.Now.Subtract(dtStart);
// Send an email notification that the processor completed.
System.Text.StringBuilder sb = new System.Text.StringBuilder();
sb.AppendFormat("<p>Processed {0} batch records from batch file '{1}'.</p>", recCount, fileName);
if (failed.Count > 0)
{
sb.AppendFormat("<p>The following {0} records failed:</p>", failed.Count);
sb.Append("<ul>");
for (int i = 0; i < failed.Count; i++)
sb.AppendFormat("<li>{0}: {1}</li>", failed[i][0], failed[i][1]);
sb.Append("<ul>");
}
sb.AppendFormat("<p>Time taken: {0}:{1}:{2}.{3}</p>", procTime.Hours, procTime.Minutes, procTime.Seconds, procTime.Milliseconds);
EMailHelper.SendAdminEmailNotification("Batch Processing Complete", sb.ToString(), true);
}
我刚刚发现了问题:我的DBA在为我创建表时没有在表上放置主键,因此LinqToSql没有在实体类中生成任何“PropertyChanged”事件/处理程序,这就是为什么DataContext不知道正在进行更改。显然,如果您的表没有主键,Linq2Sql将不会跟踪对该表的任何更改,这是有意义的,但如果有某种通知,那就更好了。我确信我的DBA没有考虑到这一点,因为这只是一种“跟踪”文本文件中的哪些行项目已被处理,并且与任何其他表没有直接关系的方式。
catch(ObjectDisposedException){}
正在吞噬严重的bug。修复错误。不要隐藏错误消息。此错误处理被微妙地破坏。如果您使一个实体无效,并且它在写入时失败,那么将来的所有写入操作也将失败,因为该实体被卡在上下文中。我建议您放弃那些事务助手方法。通常的使用(var-tran=BeginTran()){tran.Commit();}
就是您所需要的。做同样的事情,但更简单。我正在维护一个100kloc的代码库,它大量使用L2和事务。不过,事务使用的是TransactionScope
,但这应该是类似的。关于隐藏错误,我认为它是允许使用错误API的代码气味。你也可以用这种方式隐藏真正的bug。;运行一个实验以确认可以进行任何更改。添加batchRecords.First().SomeProperty=1234;db.SubmitChanges()代码>。这能通过吗?应该这样。然后可以移动该线并测试不同的位置。这是模糊的,但我们几乎没有选择:)需要调查。我想问题归结为“当我设置实体属性时,更改从未写入”。对的这意味着我们可以忽略发布代码中的几乎所有内容。
/// <summary>
/// Begins a new explicit transaction on this context. This is useful if you need to perform a call to SubmitChanges multiple times due to "circular" foreign key linkage, but still want to maintain an atomic write.
/// </summary>
public void BeginTransaction()
{
if (this.HasOpenTransaction)
return;
if (this.Connection.State != System.Data.ConnectionState.Open)
this.Connection.Open();
System.Data.Common.DbTransaction trans = this.Connection.BeginTransaction();
this.Transaction = trans;
this._openTrans = true;
}
/// <summary>
/// Commits the current transaction (if active) and submits all changes on this context.
/// </summary>
public void CommitTransaction()
{
this.SubmitChanges();
if (this.Transaction != null)
this.Transaction.Commit();
this._openTrans = false;
this.RollbackTransaction(); // Since the transaction has already been committed, this just disposes and decouples the transaction object itself.
}
/// <summary>
/// Disposes and removes an existing transaction on the this context. This is useful if you want to use the context again after an explicit transaction has been used.
/// </summary>
public void RollbackTransaction()
{
// Kill/Rollback the transaction, as necessary.
try
{
if (this.Transaction != null)
{
if (this._openTrans)
this.Transaction.Rollback();
this.Transaction.Dispose();
this.Transaction = null;
}
this._openTrans = false;
}
catch (ObjectDisposedException) { } // If this gets called after the object is disposed, we don't want to let it throw exceptions.
catch { throw; }
}