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);