The changes to the database were committed successfully, but an error occurred while updating the object contextRSS

raysefo

Well-known member
Joined
Feb 22, 2019
Messages
361
Programming Experience
10+
Hi,

I have a asp.net web API. I am using EF 6 code first approach. I am getting this error only when I load testing my web API with Jmeter. How can I fix this?

C#:
The changes to the database were committed successfully, but an error occurred while updating the object context. The ObjectContext might be in an inconsistent state. Inner exception message: Saving or accepting changes failed because more than one entity of type 'BusinessEntity.GameResponse' have the same primary key value. Ensure that explicitly set primary key values are unique. Ensure that database-generated primary keys are configured correctly in the database and in the Entity Framework model. Use the Entity Designer for Database First/Model First configuration. Use the 'HasDatabaseGeneratedOption" fluent API or 'DatabaseGeneratedAttribute' for Code First configuration

Here is my GameResponse:

C#:
using System;

namespace BusinessEntity
{
    public class GameResponse
    {
        public int Id { get; set; }
        public string referenceId { get; set; }
        public string productCode { get; set; }
        public int quantity { get; set; }
        public string version { get; set; }
        public string signature { get; set; }
        public string ApplicationCode { get; set; }
        public string validatedToken { get; set; }
        public DateTime? responseDateTime { get; set; } = DateTime.Now;
        public string initiationResultCode { get; set; }
        public string companyToken { get; set; }
        public string estimateUnitPrice { get; set; }
        public string currency { get; set; }
    }
}

Here is the method I am load testing:

C#:
private async Task<HttpResponseMessage> CallRazerService(RequestDto requestDto)
        {
            HttpResponseMessage response = null;

            using (var scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
            {
                try
                {
                    //Transform DTO into GameRequest for calling Razer Initiate
                    var config = new MapperConfiguration(cfg =>
                    {
                        cfg.CreateMap<RequestDto, GameRequest>();
                        cfg.CreateMap<GameRequest, GameConfirmRequest>();
                        cfg.CreateMap<GameConfirmResponse, GameConfirmResponseDto>();
                        cfg.CreateMap<Coupon, CouponDto>();
                    });
                    var iMapper = config.CreateMapper();
                    var gameRequest = iMapper.Map<RequestDto, GameRequest>(requestDto);
                    //Unique reference ID
                    gameRequest.referenceId = Guid.NewGuid().ToString();

                    //Create signature
                    gameRequest = Utilities.CreateSignature(gameRequest, RequestType.Initiate);

                    //Add initiation request into database
                    _unitOfWork.GameRepository.Insert(gameRequest);
                    

                    #region Call Razer initiate/confirm

                    //Call Razer for initiation
                    response = await Utilities.CallRazer(gameRequest, "purchaseinitiation");

                    //Read response
                    var htmlResponse = await response.Content.ReadAsStringAsync();
                    var gameResponse = JsonConvert.DeserializeObject<GameResponse>(htmlResponse);

                    //Adding initiation response into database
                    _unitOfWork.GameResponseRepository.Insert(gameResponse);
                    

                    if (gameResponse.initiationResultCode == "00")
                    {
                        gameRequest.validatedToken = gameResponse.validatedToken;
                        //Create signature
                        var gameConfirmRequest = Utilities.CreateSignature(gameRequest, RequestType.Confirm);

                        //Transform DTO into GameRequest for calling Razer Initiate
                        var gameConfirmRequests = iMapper.Map<GameRequest, GameConfirmRequest>(gameConfirmRequest);

                        //Add confirm request into database
                        _unitOfWork.GameConfirmRequestRepository.Insert(gameConfirmRequests);
                        

                        //Call Razer for confirm
                        response = await Utilities.CallRazer(gameRequest, "purchaseconfirmation");

                        //Read response
                        htmlResponse = await response.Content.ReadAsStringAsync();
                        var gameConfirmResponse = JsonConvert.DeserializeObject<GameConfirmResponse>(htmlResponse);

                        //Add confirm response into database
                        _unitOfWork.GameConfirmResponseRepository.Insert(gameConfirmResponse);
                    }
                }
                catch (Exception e)
                {
                    throw e;
                }

                #endregion

                await _unitOfWork.SaveAsync();
                scope.Complete();
            }

            return response;
        }
 
I feel relieved now :) Because there is no other way that I can think of to get the referenceId when I do insert.
C#:
_unitOfWork.GameRepository.Insert(gameRequest);
 
Why would you need to get the ID back? To set up a relationship in another table? If you are using Entity Framework properly, you don't have to manage this type of relationship. As long as one entity has a reference to another entity, EF is supposed to keep the relationships live by managing the IDs for you under the covers.
 
I changed Unity lifetime manager to transient. In JMeter I tried as follows:

  • 10 users 10 seconds: success
  • 50 users 10 seconds: success
  • 100 users 10 seconds: success
  • 150 users 10 seconds: I got 18 {0}Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached.
 
I added TransparentNetworkIPResolution=False into my connection string but still getting the same error. How can I fix it?
 
How many connections do you have simultaneous live when you are stress testing for 150 users? Perhaps the error is telling you exactly what the problem is: all the pooled connections are in use and the max pool size has been reached. Have you tried increasing the pool size?

Personally, I think you are going about your project the wrong way. The correct sequence is make it work. Then make it right. Then lastly make it fast. Based on your other threads, you don't even have it working, much less working right and you are already trying to make it fast.
 
Have you tried increasing the pool size?
I wouldn't really recommend that personally. Haven worked as a server administrator for most of my career, there is really no viable reason to go above 100 in my opinion, even for servers running under heavy load. And if you do, you have created a hog of an application, which clearly need to be rewritten to make better use of reusable queries and code.

I do agree with Skydiver though, get your stuff working first before you start worrying about speed and performance. It doesn't seem like you are quite there yet...
 
How can I detect if I have open connections? Should I use AsNotracking to my queries if I don't insert/update the result?

I am using transaction scope and calling the 3rd party API inside of another class as follows:

C#:
private static readonly HttpClient _httpClient = new HttpClient();
...
var response = await _httpClient.PostAsync("https://teststore.gamesultan.com/" + url, content);
 
Last edited:
If you had already read the documentation on EF, you would not need to ask these questions. I consider these answers you should already know. EF Docs - Overview - EF6

When you execute a linq query, the connection in EF is meant to be managed for you, and thus opens a connection for you. This depends on your code, and if you're not using using blocks (link in my signature), or if you're not disposing of your database objects, then this becomes a problem when you explicitly open a connection manually. This would require you to manually dispose of the connection once your command executes. See this MSDN article which answers your connection management question. - Managing Connections and Transactions On further reading, I found this article which outlines some great irregularities to look for when trying to source already opened connections in your connection pool. - https://www.brentozar.com/archive/2015/07/database-connection-hazards-with-entity-framework/
 
I don't manually open connection. All I do is using unit of work as follows:
C#:
var gameBankProductListResult = await _unitOfWork.GameRepository.GetGameBankProducts(
                    x => x.used == 0, g => new { g.productCode, g.productDescription, g.unitPrice },
                    gcs => new Product
                    {
                        ProductID = gcs.Key.productCode,
                        ProductName = gcs.Key.productDescription,
                        Price = gcs.Key.unitPrice,
                        StockQuantity = gcs.Sum(g => g.quantity),
                        IsStockAvailable = gcs.Sum(g => g.quantity) > 0
                    });

Here is my Unit of Work:
C#:
    public class UnitOfWork : IDisposable
    {
        private readonly GameContext _context;
        private readonly GenericRepository<GameRequest> gameRequestRepository;
      

        public UnitOfWork(GameContext context)
        {
            _context = context;
        }

        public GenericRepository<GameRequest> GameRepository
        {
            get
            {
                return this.gameRequestRepository ?? new GenericRepository<GameRequest>(_context);
            }
        }
      

        public void Save()
        {
            _context.SaveChanges();
        }

        public async Task SaveAsync()
        {
            await _context.SaveChangesAsync();
        }

        private bool disposed = false;

        protected virtual void Dispose(bool disposing)
        {
            if (!this.disposed)
            {
                if (disposing)
                {
                    _context.Dispose();
                }
            }
            this.disposed = true;
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
    }
}
 
It's my understanding that EF will handle your connections for you providing you are not explicitly handling them yourself. So why would you need to check for open connections if you're not opening any explicitly?
 
As I tried to mention, I am getting Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached. when I do a load test with 150 users and 10 sec ramp-up time via Jmeter. I wanna know how to fix this?
 
As I tried alluding to the fact that you are doing something else in your code that you should not be. As I have already pointed out; If you open a connection explicitly, it's required to be closed explicitly. EF doesn't handle your connections if they are opened explicitly. How else do you explain having a full connection pool? The most common cause for your error is due to connection leaks, which is caused by not closing your connections properly and consistently.
 
Back
Top Bottom