Question ASP.NET web API Unity TransientLifetimeManager how to dispose Dbcontext?

raysefo

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

I am using a unit of work, generic repo and Unity as DI. My problem is TransientLifetimeManager can't dispose Dbcontext so I have to do it somehow. Could you please check my code and help me? I couldn't find a way to dispose.

Here is my controller:
C#:
namespace Game.Controllers
{
    [System.Web.Http.RoutePrefix("api/v2/game")]
    public class GameController : ApiController
    {
        private readonly IGameServices _gameServices;
     

        #region Public Constructor

        /// <summary>
        /// Public constructor to initialize game service instance
        /// </summary>
        public GameController(IGameServices gameServices)
        {
            _gameServices = gameServices;
        }

       
        #endregion


        // POST: api/Game
        //[RequireHttps] For Prod Only

        [HttpPost, Route("purchase")]
        public async Task<IHttpActionResult> PurchaseGame(RequestDto game)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }
            //Call Services
            var gameConfirmResponse = await _gameServices.GamePurchase(game);
            switch (gameConfirmResponse.StatusCode)
            {
                case HttpStatusCode.NotFound:
                {
                    return NotFound();
                }
                case HttpStatusCode.InternalServerError:
                {
                    return InternalServerError();
                }
                case HttpStatusCode.OK:
                {
                    if (gameConfirmResponse.Content == null)
                    {
                        return new System.Web.Http.Results.ResponseMessageResult(
                            Request.CreateErrorResponse((HttpStatusCode) 222, new HttpError("No Results Found")));
                    }

                    var responseStream = await gameConfirmResponse.Content.ReadAsStringAsync();

                    var resultResponse = JsonConvert.DeserializeObject<GameConfirmResponse>(responseStream);
                    if (resultResponse.coupons == null)
                    {
                        return new System.Web.Http.Results.ResponseMessageResult(
                            Request.CreateErrorResponse((HttpStatusCode) 222,
                                new HttpError("No Coupons Available for this Game")));
                    }

                    //Transform GameConfirmResponse into DTO for returning result
                    var config = new MapperConfiguration(cfg =>
                    {
                        cfg.CreateMap<GameConfirmResponse, GameConfirmResponseDto>();
                        cfg.CreateMap<Coupon, CouponDto>();
                    });
                    var iMapper = config.CreateMapper();
                    var resultDto = iMapper.Map<GameConfirmResponse, GameConfirmResponseDto>(resultResponse);
                    return Ok(resultDto);
                }
                case HttpStatusCode.Unauthorized:
                {
                    return Unauthorized();
                }
                case HttpStatusCode.RequestTimeout:
                {
                    return InternalServerError();
                }
            }

            return Ok(gameConfirmResponse);
        }

       

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                ???
            }
            base.Dispose(disposing);
        }
    }
}

Here is my Service:
C#:
namespace BusinessService
{
    public class GameServices : IGameServices
    {
        private readonly UnitOfWork _unitOfWork;

        /// <summary>
        /// Public constructor.
        /// </summary>
        public GameServices(UnitOfWork unitOfWork)
        {
            _unitOfWork = unitOfWork;
        }
       
        /// <summary>
        /// Creates a product
        /// </summary>
        /// <param name="requestDto"></param>
        /// <returns></returns>
        public async Task<HttpResponseMessage> GamePurchase(RequestDto requestDto)
        {
            HttpResponseMessage response = null;

            response = await CallGameNew(requestDto) ?? await CallRazerService(requestDto);

            return response;
        }

      

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

            using (var scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
            {
               


                #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")
                {
                    gameRequestDto = iMapper.Map<GameRequest, GameRequestDto>(gameRequest);
                    gameRequestDto.validatedToken = gameResponse.validatedToken;
                    //Create signature
                    var gameConfirmRequest = Utilities.CreateSignature(gameRequestDto, 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(gameConfirmRequest, "purchaseconfirmation");

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

                    //Set service
                    gameConfirmResponse.service = "RAZER";
                    //Add confirm response into database
                    _unitOfWork.GameConfirmResponseRepository.Insert(gameConfirmResponse);
                }

                #endregion

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

            return response;
        }


        }

    }
}

Here is UOW:
C#:
namespace DataModels
{
    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);
        }
    }
}

Here is Unity config:
C#:
namespace Game
{
    /// <summary>
    /// Specifies the Unity configuration for the main container.
    /// </summary>
    public static class UnityConfig
    {
        #region Unity Container
        private static Lazy<IUnityContainer> container =
          new Lazy<IUnityContainer>(() =>
          {
              var container = new UnityContainer();
              RegisterTypes(container);
              return container;
          });

        /// <summary>
        /// Configured Unity Container.
        /// </summary>
        public static IUnityContainer Container => container.Value;
        #endregion

       
        public static void RegisterTypes(IUnityContainer container)
        {
            // Default empty Time manager!
            container.RegisterType<IGameServices, GameServices>().RegisterType<UnitOfWork>(new TransientLifetimeManager());
            container.RegisterType<IUserValidate, UserValidate>(new TransientLifetimeManager());
           
        }
    }
}
 
That's a horrible idea. And bringing multithreading into it is asking for unnecessary complications.

It seems you have a nack for disregarding advice. And what about the issue of two connections as I posted above? You will run into the same problem again.

EF is for people who are lazy and don't want to get down and dirty with linq/Sql. Why not just learn the lingo?

I see that Database.BeginTransaction() is the preferred EF call.
What does it mater, your connection pool is blocking because of the code for your connection, not your transactions. We are going in circles again. You need to alter your code to adapt to using the one connection for all queries.

I am unable to help you further until you make some changes put to you previously, and until you no longer disregard advice.

Edit: typo
 
Last edited:
OK, I will check DB to see if there is more than 1 connection when I run the action method. If there is, I will try to change to code. Thank you.
 
This is more of an opinion piece based on my thoughts, your progress, and my own limited experience with this. You might want to grab a cup of tea first!

In MySQL server, you can check the information_schema for your connection processes, but In SQL server, i think you'll have to do that by command-line. You should check your code, not your database. We know from the last error you got, that your connections are now not closing at some point. And on post 15, I strongly advice against the first link, as I don't see how that would accomplish anything. I have a co-worker who works with EF, not me though, I've never been a fan of it personally, and I wouldn't consider myself an overly confident EF user either, but I know enough to get by. Mostly from watching him, when he is tasked to use it in our company.

Use using blocks and encapsulate a connection and execute your commands inside it. Here is what I am thinking...I am thinking you should create a SQL management class which will handle all your connections for you and bypass EFs auto connection management. This way, you get to explicitly open and close connections where command(s) need executing and you can handle your own connections, and manage their connection states. You can't do that with EF unless you open/close them explicitly. (At-least not to my knowledge.) I have read people have created what I'd call a factory class for SQL management. This takes away from EFs responsibility to manage your connections for you when a command needs to execute, and this then becomes the responsibility of your SQL factory class to handle instead, so if this is something you might consider, make sure its up to par when it comes to reliability and re-usability.

According to Brent Ozar, EF will not interfere with connections which are explicitly opened by the user. Whereas, I believe older versions of EF used to also close them too. What a pain in the ass, but that is no longer an issue with EF6, I don't think. While this is the first time I've actually had sufficient time to go over your first post, and properly identify what was being pointed out. I suggest starting over. I wouldn't rewrite anything, ditch it. I have been pondering your options and while I know ditching EF is probably not favourable to you either. I do think it will ease your pain in the way of troubleshooting, performance, learning and writing more reliable code. I actually found some of your code... well; ucky to follow.

I understand why you are using EF, like most devs, it will, or is meant to ease the burden and quicken up development. Yea you get expeditious development, but you trade off performance in the end, mainly because of how information, and data is parsed in EF, and it is not really something I would trade off personally. (Performance). And since you are stress-testing with excessive numbers (three times above the default), I will assume you expect your application to be a busy one, and if that is the case; are you really best implementing EF at all?

To elaborate on my last point above. What happens if you expect your database to grow in size, or if you need additional fields, columns, tables etc. This will seriously become a huge problem for you later on, (especially if you ever decide to move away from EF to a more manual integration), as the integration with linq will get very complex and this would be something I would avoid. But hay, if you are sure your applications DB is not going to need altering, or need expanding down the line, then you've nothing to worry about on that note.

I think you would get a lot more feedback if you implemented many of the suggestions put to you in your eight or so topics. As Skydiver already said elsewhere, work on getting your application to execute flaw-free before worrying about performance. I am also curious if my above remarks should be considered or do my suggestions take away form the purposes of using Unity/EF if one was to implement their own SQL factory class for managing the connections instead? I have suggested because I have read reports from many users suffering from the same issues.

Something for you to ponder on @raysefo.
 
I have good news for you guys. I made a load test with 1000 users and 10 seconds ramp-up time, there were no errors :) I just removed the transaction scope from the code.
 
Back
Top Bottom