DbContext 实例被同时使用两次
DbContext instance is used twice at the same time
我在 Internet 上看到了类似的问题,但无法将其中任何一个真正适合我的代码。
我正在 运行 执行两个 hangfire 作业,有时它们必须同时 运行。
RecurringJob.AddOrUpdate(()=>abill.CheckUserPayment(),Cron.Minutely);
RecurringJob.AddOrUpdate("CalculateUserCharge",()=>abill.CalculateUserCharge(DateTime.Today.AddDays(-1)),Cron.Daily(12,37),TimeZoneInfo.Utc);
RecurringJobs 是从启动时的 Configure() 方法启动的,我将 AccountBilling abill 作为参数传递。
AccountBilling.cs(不存在完整代码)
public readonly EntityContext context;
private TBCPaymentOptions _tbcPaymentOptions = null;
public AccountBilling (EntityContext _context) {
context = _context;
}
public AccountBilling (EntityContext _context, IOptions<TBCPaymentOptions> tbcPaymentOptions) {
context = _context;
this._tbcPaymentOptions = tbcPaymentOptions.Value;
}
public void Save () {
try {
context.SaveChanges ();
} catch (Exception e) {
Console.WriteLine (e);
throw;
}
}
public void CalculateUserCharge (DateTime date) {
var latestJob = context.JobLogs.Include (c => c.JobStatus).OrderByDescending (c => c.StartDate).Where (c => c.JobId == (int) JobEnum.CloseDay).FirstOrDefault ();
var jobLog = new JobLog ();
jobLog.JobId = (int) JobEnum.CloseDay;
jobLog.JobStatusID = (int) JobStatusEnum.Active;
jobLog.StartDate = DateTime.Now;
context.Add (jobLog);
this.Save ();
Console.WriteLine ("Starting...");
if (latestJob != null && latestJob.JobStatusID == (int) JobStatusEnum.Active) {
jobLog.JobStatusID = (int) JobStatusEnum.Canceled;
jobLog.EndDate = DateTime.Now;
context.Update (jobLog);
context.SaveChanges ();
} else {
try {
var result = new List<GetActiveUserPackagesForOpenBillingPeriodResult> ();
using (var conn = new NpgsqlConnection (context.ConnectionString)) {
conn.Open ();
using (var cmd = new NpgsqlCommand ("\"GetActiveUserPackagesForOpenBillingPeriod\"", conn)) {
Console.WriteLine ("გავუშვი command");
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue ("somedate", DateTime.Today);
var reader = cmd.ExecuteReader ();
string x = DBNull.Value.Equals (reader) ? " " : reader.ToString ();
if (x != null) {
while (reader.Read ()) {
result.Add (
new GetActiveUserPackagesForOpenBillingPeriodResult {
Amount = (decimal) reader["Amount"],
PackageID = (int) reader["PackageID"],
UserID = (int) reader["UserID"],
AccountID = (int) reader["AccountID"],
UserPackageStartDate = (DateTime) reader["UserPackageStartDate"],
}
);
}
}
conn.Close ();
}
var groupByResults = result.GroupBy (c => c.AccountID).Select (a => new {
accountId = a.Key,
lines = a.ToList ()
});
foreach (var group in groupByResults) {
var transactionHeader = new TransactionHeader () {
TransactionHeaderTypeID = (int) TransactionHeaderTypeEnum.Charge,
Date = date,
CorrectionDescription = null,
AccountID = group.accountId
};
foreach (var lineItem in group.lines) {
transactionHeader.TransactionLines.Add (new TransactionLine () {
UserID = lineItem.UserID,
PackageID = lineItem.PackageID,
Amount = this.CalculateUserChargeMethod (date, lineItem.Amount, lineItem.UserPackageStartDate)
});
}
transactionHeader.TotalAmount = transactionHeader.TransactionLines.Sum (c => c.Amount);
this.context.TransactionHeaders.Add (transactionHeader);
context.Add (transactionHeader);
this.Save ();
}
jobLog.EndDate = DateTime.Now;
jobLog.JobStatusID = (int) JobStatusEnum.Inactive;
context.Update (jobLog);
this.Save ();
ClosePeriodOnEndOfMonth (date, conn);
}
} catch (Exception ex) {
jobLog.EndDate = DateTime.Now;
jobLog.JobStatusID = (int) JobStatusEnum.Canceled;
jobLog.Comment = ex.ToString ();
context.Update (jobLog);
this.Save ();
//throw ex;
}
}
........
public void CheckUserPayment () {
var Cert = new X509Certificate2 ("cert.p12", _tbcPaymentOptions.TBCPayCertificatePassword, X509KeyStorageFlags.MachineKeySet);
var time = DateTime.Now.AddMinutes (-5);
var latestJob = context.JobLogs.Include (c => c.JobStatus).OrderByDescending (c => c.StartDate).Where (c => c.JobId == (int) JobEnum.CheckPayment).FirstOrDefault ();
var jobLog = new JobLog ();
jobLog.JobId = (int) JobEnum.CheckPayment;
jobLog.JobStatusID = (int) JobStatusEnum.Active;
jobLog.StartDate = DateTime.Now;
context.Add (jobLog);
this.Save ();
if (latestJob != null && latestJob.JobStatusID == (int) JobStatusEnum.Active) {
jobLog.JobStatusID = (int) JobStatusEnum.Canceled;
jobLog.EndDate = DateTime.Now;
context.Update (jobLog);
this.Save ();
} else {
try {
var processingPayments = context.Payments.Where (c => c.AcceptanceAct.AcceptanceActStatusID == (int) AcceptanceActStatusEnum.Processing && c.CreatedDate < time).ToList ();
System.Console.WriteLine ("IN METHOD");
foreach (var item in processingPayments) {
System.Console.WriteLine ("Job Started");
this.CheckUserPaymentMethod (item.BankPaymentCode, item, _tbcPaymentOptions.AppPlatformIP, _tbcPaymentOptions.MerchantURL, Cert);
}
Console.WriteLine ("Job Done");
jobLog.JobStatusID = (int) JobStatusEnum.Inactive;
jobLog.EndDate = DateTime.Now;
context.Update (jobLog);
this.Save ();
//this.Save ();
} catch (Exception ex) {
jobLog.JobStatusID = (int) JobStatusEnum.Canceled;
jobLog.EndDate = DateTime.Now;
jobLog.Comment = ex.ToString ();
context.Update (jobLog);
this.Save ();
}
}
}
Startup.cs(完整代码不存在):
services.AddEntityFrameworkNpgsql ()
.AddDbContext<EntityContext> (
options => options.UseNpgsql (connectionString)
);
问题是 abill
被有效地用作单例对象。这会给您带来麻烦,尤其是 DbContext
操作。
您需要更改它并允许 Hangfire 正确利用 DI。首先给你的 AccountBilling
class 一个用于注入的接口:
public class AccountBilling : IAccountBilling
{
// snip
}
现在将其添加到您的容器中:
services.AddScoped<IAccountBilling, AccountBilling>();
现在在您的启动代码中,不要使用 abill
对象,而是正确地注入它:
RecurringJob.AddOrUpdate<IAccountBilling>(a => a.CheckUserPayment(), Cron.Minutely);
我在 Internet 上看到了类似的问题,但无法将其中任何一个真正适合我的代码。
我正在 运行 执行两个 hangfire 作业,有时它们必须同时 运行。
RecurringJob.AddOrUpdate(()=>abill.CheckUserPayment(),Cron.Minutely);
RecurringJob.AddOrUpdate("CalculateUserCharge",()=>abill.CalculateUserCharge(DateTime.Today.AddDays(-1)),Cron.Daily(12,37),TimeZoneInfo.Utc);
RecurringJobs 是从启动时的 Configure() 方法启动的,我将 AccountBilling abill 作为参数传递。
AccountBilling.cs(不存在完整代码)
public readonly EntityContext context;
private TBCPaymentOptions _tbcPaymentOptions = null;
public AccountBilling (EntityContext _context) {
context = _context;
}
public AccountBilling (EntityContext _context, IOptions<TBCPaymentOptions> tbcPaymentOptions) {
context = _context;
this._tbcPaymentOptions = tbcPaymentOptions.Value;
}
public void Save () {
try {
context.SaveChanges ();
} catch (Exception e) {
Console.WriteLine (e);
throw;
}
}
public void CalculateUserCharge (DateTime date) {
var latestJob = context.JobLogs.Include (c => c.JobStatus).OrderByDescending (c => c.StartDate).Where (c => c.JobId == (int) JobEnum.CloseDay).FirstOrDefault ();
var jobLog = new JobLog ();
jobLog.JobId = (int) JobEnum.CloseDay;
jobLog.JobStatusID = (int) JobStatusEnum.Active;
jobLog.StartDate = DateTime.Now;
context.Add (jobLog);
this.Save ();
Console.WriteLine ("Starting...");
if (latestJob != null && latestJob.JobStatusID == (int) JobStatusEnum.Active) {
jobLog.JobStatusID = (int) JobStatusEnum.Canceled;
jobLog.EndDate = DateTime.Now;
context.Update (jobLog);
context.SaveChanges ();
} else {
try {
var result = new List<GetActiveUserPackagesForOpenBillingPeriodResult> ();
using (var conn = new NpgsqlConnection (context.ConnectionString)) {
conn.Open ();
using (var cmd = new NpgsqlCommand ("\"GetActiveUserPackagesForOpenBillingPeriod\"", conn)) {
Console.WriteLine ("გავუშვი command");
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue ("somedate", DateTime.Today);
var reader = cmd.ExecuteReader ();
string x = DBNull.Value.Equals (reader) ? " " : reader.ToString ();
if (x != null) {
while (reader.Read ()) {
result.Add (
new GetActiveUserPackagesForOpenBillingPeriodResult {
Amount = (decimal) reader["Amount"],
PackageID = (int) reader["PackageID"],
UserID = (int) reader["UserID"],
AccountID = (int) reader["AccountID"],
UserPackageStartDate = (DateTime) reader["UserPackageStartDate"],
}
);
}
}
conn.Close ();
}
var groupByResults = result.GroupBy (c => c.AccountID).Select (a => new {
accountId = a.Key,
lines = a.ToList ()
});
foreach (var group in groupByResults) {
var transactionHeader = new TransactionHeader () {
TransactionHeaderTypeID = (int) TransactionHeaderTypeEnum.Charge,
Date = date,
CorrectionDescription = null,
AccountID = group.accountId
};
foreach (var lineItem in group.lines) {
transactionHeader.TransactionLines.Add (new TransactionLine () {
UserID = lineItem.UserID,
PackageID = lineItem.PackageID,
Amount = this.CalculateUserChargeMethod (date, lineItem.Amount, lineItem.UserPackageStartDate)
});
}
transactionHeader.TotalAmount = transactionHeader.TransactionLines.Sum (c => c.Amount);
this.context.TransactionHeaders.Add (transactionHeader);
context.Add (transactionHeader);
this.Save ();
}
jobLog.EndDate = DateTime.Now;
jobLog.JobStatusID = (int) JobStatusEnum.Inactive;
context.Update (jobLog);
this.Save ();
ClosePeriodOnEndOfMonth (date, conn);
}
} catch (Exception ex) {
jobLog.EndDate = DateTime.Now;
jobLog.JobStatusID = (int) JobStatusEnum.Canceled;
jobLog.Comment = ex.ToString ();
context.Update (jobLog);
this.Save ();
//throw ex;
}
}
........
public void CheckUserPayment () {
var Cert = new X509Certificate2 ("cert.p12", _tbcPaymentOptions.TBCPayCertificatePassword, X509KeyStorageFlags.MachineKeySet);
var time = DateTime.Now.AddMinutes (-5);
var latestJob = context.JobLogs.Include (c => c.JobStatus).OrderByDescending (c => c.StartDate).Where (c => c.JobId == (int) JobEnum.CheckPayment).FirstOrDefault ();
var jobLog = new JobLog ();
jobLog.JobId = (int) JobEnum.CheckPayment;
jobLog.JobStatusID = (int) JobStatusEnum.Active;
jobLog.StartDate = DateTime.Now;
context.Add (jobLog);
this.Save ();
if (latestJob != null && latestJob.JobStatusID == (int) JobStatusEnum.Active) {
jobLog.JobStatusID = (int) JobStatusEnum.Canceled;
jobLog.EndDate = DateTime.Now;
context.Update (jobLog);
this.Save ();
} else {
try {
var processingPayments = context.Payments.Where (c => c.AcceptanceAct.AcceptanceActStatusID == (int) AcceptanceActStatusEnum.Processing && c.CreatedDate < time).ToList ();
System.Console.WriteLine ("IN METHOD");
foreach (var item in processingPayments) {
System.Console.WriteLine ("Job Started");
this.CheckUserPaymentMethod (item.BankPaymentCode, item, _tbcPaymentOptions.AppPlatformIP, _tbcPaymentOptions.MerchantURL, Cert);
}
Console.WriteLine ("Job Done");
jobLog.JobStatusID = (int) JobStatusEnum.Inactive;
jobLog.EndDate = DateTime.Now;
context.Update (jobLog);
this.Save ();
//this.Save ();
} catch (Exception ex) {
jobLog.JobStatusID = (int) JobStatusEnum.Canceled;
jobLog.EndDate = DateTime.Now;
jobLog.Comment = ex.ToString ();
context.Update (jobLog);
this.Save ();
}
}
}
Startup.cs(完整代码不存在):
services.AddEntityFrameworkNpgsql ()
.AddDbContext<EntityContext> (
options => options.UseNpgsql (connectionString)
);
问题是 abill
被有效地用作单例对象。这会给您带来麻烦,尤其是 DbContext
操作。
您需要更改它并允许 Hangfire 正确利用 DI。首先给你的 AccountBilling
class 一个用于注入的接口:
public class AccountBilling : IAccountBilling
{
// snip
}
现在将其添加到您的容器中:
services.AddScoped<IAccountBilling, AccountBilling>();
现在在您的启动代码中,不要使用 abill
对象,而是正确地注入它:
RecurringJob.AddOrUpdate<IAccountBilling>(a => a.CheckUserPayment(), Cron.Minutely);