已成功提交对数据库的更改,但是在更新对象上下文时发生了错误

Raysefo

知名会员
已加入
2019年2月22日
留言内容
192
编程经验
10+
你好

我有一个asp.net Web API。我正在使用EF 6代码优先方法。仅当我使用Jmeter对Web API进行负载测试时,才出现此错误。我怎样才能解决这个问题?

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 明确地 set primary key values are unique. Ensure that database-generated primary keys are configured correctly in the database and in the 实体框架 model. Use the Entity Designer for 数据库  第一 /Model  第一  configuration. Use the 'HasDatabaseGeneratedOption" fluent API or 'DatabaseGeneratedAttribute' for Code  第一  configuration

这是我的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; }
    }
}

这是我正在测试负载的方法:

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

跳伞

工作人员
已加入
2019年4月6日
留言内容
2,500
地点
弗吉尼亚州切萨皮克
编程经验
10+
你有多少个联系 同时直播 什么时候对150位用户进行压力测试?也许错误是在告诉您问题到底出在哪里:所有池连接都在使用中,并且已达到最大池大小。您是否尝试增加泳池大小?

就个人而言,我认为您的项目方向错误。正确的顺序是使其起作用。然后将其正确。然后最后使其快速。根据您的其他线程,您甚至无法正常运行,更不用说正常工作了,并且您已经在尝试使其更快。
 

羊皮

退休程序员
工作人员
已加入
2018年9月5日
留言内容
1,926
地点
英国
编程经验
10+
您是否尝试增加泳池大小?
我个人不建议这样做。在我职业生涯的大部分时间里,Haven都不曾担任服务器管理员,我认为,甚至对于负载很重的服务器,确实没有超过300的可行理由。并且,如果您这样做了,就已经创建了一个应用程序,显然需要对其进行重写以更好地利用可重用的查询和代码。

我确实同意跳伞运动员的观点,在开始担心速度和性能之前,请先让您的工作开始。看来您还不在那儿...
 

Raysefo

知名会员
已加入
2019年2月22日
留言内容
192
编程经验
10+
如何检测我的连接是否打开?如果不插入/更新结果,是否应该对查询使用AsNotracking?

我正在使用事务作用域,并在另一个类中调用第三方API,如下所示:

C#:
private static readonly HttpClient _httpClient = new HttpClient();
...
var response = await _httpClient.PostAsync("//teststore.gamesultan.com/" + url, content);
 
Last edited:

羊皮

退休程序员
工作人员
已加入
2018年9月5日
留言内容
1,926
地点
英国
编程经验
10+
如果您已经阅读了有关EF的文档,则无需问这些问题。我认为您应该已经知道这些答案。 EF文件- 概述-EF6

当您执行linq查询时,EF中的连接是为您管理的,因此可以为您打开连接。这取决于您的代码,并且如果您不使用块(我的签名中的链接),或者您不处置数据库对象,那么当您 明确地 手动打开连接。这将要求您在命令执行后手动处置连接。请参阅此MSDN文章,它回答了您的连接管理问题。 -- 管理联系和交易 在进一步阅读时,我发现本文概述了在尝试在连接池中寻找已经打开的连接时要寻找的一些不规则之处。 -- //www.brentozar.com/archive/2015/07/database-connection-hazards-with-entity-framework/
 

Raysefo

知名会员
已加入
2019年2月22日
留言内容
192
编程经验
10+
我没有手动打开连接。我所做的只是使用工作单元,如下所示:
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
                    });

这是我的工作单位:
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);
        }
    }
}
 

羊皮

退休程序员
工作人员
已加入
2018年9月5日
留言内容
1,926
地点
英国
编程经验
10+
我的理解是,EF会为您处理连接,前提是您自己没有明确处理它们。那么,如果您未明确打开任何连接,为什么还要检查打开的连接?
 

Raysefo

知名会员
已加入
2019年2月22日
留言内容
192
编程经验
10+
正如我试图提到的那样,我越来越 超时时间已到。从池中获取连接之前已经过超时时间。这可能是因为所有池化连接都在使用中,并且达到了最大池大小。当我通过Jmeter对150个用户和10秒的加速时间进行负载测试时。我想知道如何解决这个问题?
 

羊皮

退休程序员
工作人员
已加入
2018年9月5日
留言内容
1,926
地点
英国
编程经验
10+
当我尝试暗示您在代码中正在做其他事情时,您不应该这样做。正如我已经指出的;如果显式打开连接,则需要显式关闭。如果显式打开了连接,EF将不处理它们。您还如何解释拥有完整的连接池?导致错误的最常见原因是由于连接泄漏,这是由于未正确且持续地关闭连接引起的。
 

羊皮

退休程序员
工作人员
已加入
2018年9月5日
留言内容
1,926
地点
英国
编程经验
10+
我帮了你 @raysefo,在第25页上,阅读我给您的链接,尤其是"管理联系和交易".
 

Raysefo

知名会员
已加入
2019年2月22日
留言内容
192
编程经验
10+
我对你们有个好消息。我对1000个用户和10秒的启动时间进行了负载测试,没有错误  :) 我只是从代码中删除了交易范围。
 
最佳 底部