0

When I do a POST to a controller, I intermittently get threading errors - though a lot of the time everything will work perfectly. These are the most common two:

New transaction is not allowed because there are other threads running in the session.

The transaction operation cannot be performed because there are pending requests working on this transaction.

I have read multiple replies on why this happens. Such as this one and this one, but I can't say I understand. I know just posting code is horrible practice on SO, and I generally do my best to avoid it, but I really don't know what to ask past "Can you please take a look and let me know if you see any obvious problems."

// POST: api/CheckOuts
[AccessType(AccessType.Create)]
[HttpPost, Route("")]
[ResponseType(typeof(CheckOut))]
public async Task<IHttpActionResult> PostCheckOut(TransactionViewModel<CheckOut> tx)
{
    //check if tx is a valid CheckOut Model Object
    if (!ModelState.IsValid)
    {
        return BadRequest(ModelState);
    }

    tx.Transaction.OrganizationId = SelectedOrganizationID;
    tx.Transaction.OfficeId = SelectedOfficeID;
    tx.Transaction.CheckedOutById = UserId;
    tx.Transaction.Date = DateTime.UtcNow;

    var items = tx.Transaction.Items.ToList();
    long checkedOutStatus =  db.Statuses.First( a => a.Name == "Checked Out" ).Id;
    long checkedInStatus =  db.Statuses.First( a => a.Name == "Checked In" ).Id;  

    //check item status
    if( !items.TrueForAll( i=> i.StatusId == checkedInStatus ) )
    {
        return BadRequest("Not all items are in 'Checked In' state.");
    }

    var mongoItems = GetMongoCollection<BsonDocument>(MongoConstants.ItemsCollection);
    items.ForEach(async item =>
    {
        var builder = Builders<BsonDocument>.Update;
        var update = builder.Set("StatusId", checkedOutStatus)
            .Set("CustodianId", tx.Transaction.TakenById)
            .Set<BsonDocument, int?>("LocationId", null)
            .Set<BsonDocument, string>("Location", null);

        await mongoItems.FindOneAndUpdateAsync(
        Builders<BsonDocument>.Filter.Eq("_id", item.Id),
                update);
        await AddItemHistory(item, UserId);
        var primaryKeyOfTheItemId = db.ItemIds.Single(i => i.Item_Id == item.Id);
        tx.Transaction.ItemIds.Add(primaryKeyOfTheItemId);
    });

        //add item
    db.CheckOuts.Add(tx.Transaction);
       
    await db.SaveChangesAsync(UserId);

    return CreatedAtRoute("DefaultApi", new {controller = "checkouts", id = tx.Transaction.Id }, tx.Transaction);
}

Note: I am 100% sure this has to do with async and is not MongoDb specific. The code below is equivalent of saving something to SQL with EF. I am sure the other Mongo bits are completely irrelevant to the question.

await mongoItems.FindOneAndUpdateAsync(
    Builders<BsonDocument>.Filter.Eq("_id", item.Id),
    update);

db is our Entity Framework dbContext (just normal dbContext used with SQL, we have two databases - Mongo and SQL).

Community
  • 1
  • 1
VSO
  • 11,546
  • 25
  • 99
  • 187

1 Answers1

2

I don't see you creating your context in your method body. Entity framework contexts are not thread safe.

Jeff Dunlop
  • 893
  • 1
  • 7
  • 20
  • :All our controllers inherit from a custom controller class (inheriting from API controller). This custom class defines 'db', which is our context (and inherits from dbContext). Should I instantiate another instance of dbContext in my method? Thank you for the reply btw. – VSO Aug 03 '15 at 22:43
  • It's generally a poor practice to instantiate a context in a controller base precisely for the problem you've run into. In general you want the shortest lifetime possible on your contexts. – Jeff Dunlop Aug 04 '15 at 10:09
  • Looks like this is the best answer I am going to get. Ty again - really glad at least someone replied. A lot of the time these vague questions get completely passed by. – VSO Aug 04 '15 at 17:44