Как решить Entity Framework: уже есть открытый DataReader, несколько контекстов - PullRequest
0 голосов
/ 13 февраля 2020

Я строю комплекс. Net Базовое приложение с ядром Entity Framework. У меня есть несколько операций дБ в каждой транзакции. У меня проблема с «DataReader уже открыт, и это вообще не имеет смысла, поскольку я использую несколько контекстов. Вот фиктивные представления соответствующих классов

public class MyDbContext : DbContext
{
    private readonly PjSqlConnectionStringBuilder pjSqlConnectionStringBuilder;

    public MyDbContext(PjSqlConnectionStringBuilder pjSqlConnectionStringBuilder):base()
    {
        this.pjSqlConnectionStringBuilder= pjSqlConnectionStringBuilder;
    }
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseSqlServer(this.pjSqlConnectionStringBuilder.ConnectionString);
    }

    public Transaction FetchByTranId(Decimal TranId)
    {
        string query = "TransactionById @TranId;"; // calling a store procedure
        var pId = new SqlParameter("TranId", TranId);
        return this.Transaction.FromSql(query, pId).First();
    }

    public DbSet<Transaction> Transactions { get; set; }
    public DbSet<Sales> DealNos { get; set; }
    public DbSet<DealAuditTrail> DealAuditTrails { get; set; }
    public DbSet<Deal> Deals { get; set; }
    public DbSet<Audit> Audits { get; set; }
}

Тогда у меня есть два класса, которые используют это оспаривайте следующее:

public class TransactionRepository 
{
    public Decimal dealNo;
    private Decimal transactionId;
    public Decimal TransactionId
    {
        get { return this.transactionId; }
        set
        {
            this.transactionId = value;
            Sales d;
            using (var dbContext = new MyDbContext(new PjSqlConnectionStringBuilder()))
            {
                d = dbContext.DealNos.Where(fd => fd.TransactionId == value).First();
            }
            this.dealNo = d.DealNo;
        }
    }

    public Transaction Fetch()
    {
        Transaction t;
        using (var dbContext = new MyDbContext(new PjSqlConnectionStringBuilder()))
        {
            t = dbContext.FetchByTranId(this.transactionId);
        }
        return t;
    }
}

public class AuditRepository
{

    public Task<int> LogRequest(decimal TransactionId, string json)
    {
        var obj = new Audit(TransactionId, "Request", json);
        return this.logObj(obj);
    }

    public Task<int> LogResponse(decimal TransactionId, string json)
    {
        var obj = new Audit(TransactionId, "Response", json);
        return this.logObj(obj);
    }

    private Task<int> logObj(Audit obj)
    {
        using (var dbContext = new MyDbContext(new PjSqlConnectionStringBuilder()))
        {
            dbContext.Audits.Add(obj);
            return dbContext.SaveChangesAsync();
        }

    }
}

Ниже приведен порядок выполнения, приводящий к ошибке "" Уже существует открытый DataReader, связанный с этой командой, который должен быть закрыт первым. ".

TransactionRepository tr = new TransactionRepository();
tr.TransactionId = 1234;
Transaction  T = tr.Fetch()
.....
.....
AuditRepository ar = new AuditRepository()
var lr1 = ar.LogRequest(tr.TransactionId, T.ToString()) // Exception thrown
....
....

В моем понимании каждый из DbContext является отдельным и не связанным друг с другом. Поэтому я не должен видеть эту ошибку. Любая помощь будет оценена.

1 Ответ

2 голосов
/ 13 февраля 2020

У меня есть догадка, указывающая на это - это может привести к очень странным вещам:

    private Task<int> logObj(Audit obj)
    {
        using (var dbContext = new MyDbContext(new PjSqlConnectionStringBuilder()))
        {
            dbContext.Audits.Add(obj);
            return dbContext.SaveChangesAsync();
        }
    }

В частности, обратите внимание, что вы удаляете контекст, когда операция выполняется. Что вам нужно , так это ожидание ожидающей операции:

    private async Task<int> logObj(Audit obj)
    {
        using (var dbContext = new MyDbContext(new PjSqlConnectionStringBuilder()))
        {
            dbContext.Audits.Add(obj);
            return await dbContext.SaveChangesAsync().ConfigureAwait(false);
        }
    }

Добавление здесь await гарантирует, что мы не будем распоряжаться dbContext до после сохранение фактически сообщило о завершении. ConfigureAwait в основном необязательный; не нужно для того, чтобы этот код вернулся к syn c -контексту, так что он может не беспокоить.

Обратите внимание, что вам не нужно сделать это в LogRequest / LogResponse; они хороши как написано (хотя я, вероятно, добавлю суффикс Async на все 3 метода здесь). Тем не менее, ваш код вызова, вероятно, должен await:

var lr1 = await ar.LogRequest(tr.TransactionId, T.ToString());

, и поскольку мы вернулись на уровень приложений здесь, мы должны позволить syn c -context иметь сказать, что происходит (т.е. не добавляйте ConfigureAwait(false) здесь)

...