2
\$\begingroup\$

I have an asp.net web API and I am using EF6 to connect to a third party rest API which sells online game codes. We made a deal with the company so clients have to call my rest API in order to get the game codes. Clients can list the available games via product method. Initiation is the first step of buying process. Initiation method sends JWT token in order to let client use it and finish purchase of the game code in the confirmation method. And there are reconciliation methods, recon initiation is like the initiation method just for sending back a JWT. Client uses this JWT and call recon confirmation to update a specific purchase like sold, not sold.

Is my usage correct? Would you please guide me? Dbcontext is disposed when the request completed right? And if I pass context to a method inside of another class (Utilities) to make some DB process, after completion of the process does ninject dispose the context?

Here is my sample Dbcontext:

using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Web;

namespace EPINMiddleWareAPI.Models
{
    public class EPINMiddleWareAPIContext : DbContext
    {


        public EPINMiddleWareAPIContext() : base("name=EPINMiddleWareAPIContext")
        {
        }

        public DbSet<InitiateRequest> InitiateRequests { get; set; }
        public DbSet<InitiateResponse> InitiateResponses { get; set; }
        public DbSet<Company> Companies { get; set; }
        public DbSet<ConfirmRequest> ConfirmRequests { get; set; }
        public DbSet<ConfirmResponse> ConfirmResponses { get; set; }
        public DbSet<GameBank> GameBanks { get; set; }
        public DbSet<GameCouponBank> GameCouponBanks { get; set; }

    }

}

Here is my ninject:

using EPINMiddleWareAPI.Controllers;

[assembly: WebActivatorEx.PreApplicationStartMethod(typeof(EPINMiddleWareAPI.App_Start.NinjectWebCommon), "Start")]
[assembly: WebActivatorEx.ApplicationShutdownMethodAttribute(typeof(EPINMiddleWareAPI.App_Start.NinjectWebCommon), "Stop")]

namespace EPINMiddleWareAPI.App_Start
{
    using System;
    using System.Web;

    using Microsoft.Web.Infrastructure.DynamicModuleHelper;

    using Ninject;
    using Ninject.Web.Common;
    using Models;
    using Ninject.Web.Common.WebHost;

    public static class NinjectWebCommon 
    {
        private static readonly Bootstrapper bootstrapper = new Bootstrapper();

        public static void Start() 
        {
            DynamicModuleUtility.RegisterModule(typeof(OnePerRequestHttpModule));
            DynamicModuleUtility.RegisterModule(typeof(NinjectHttpModule));
            bootstrapper.Initialize(CreateKernel);
        }


        public static void Stop()
        {
            bootstrapper.ShutDown();
        }


        private static IKernel CreateKernel()
        {
            var kernel = new StandardKernel();
            try
            {
                kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
                kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();
                RegisterServices(kernel);
                return kernel;
            }
            catch
            {
                kernel.Dispose();
                throw;
            }
        }


        private static void RegisterServices(IKernel kernel)
        {
            kernel.Bind<EPINMiddleWareAPIContext>().ToSelf().InRequestScope();

        }        
    }
}

And my controller:

using System.Net.Http;
using System.Threading.Tasks;
using System.Web.Http;
using EPINMiddleWareAPI.Models;
using Newtonsoft.Json;
using System.Net;
using EPINMiddleWareAPI.Utility;
using System.Linq;
using System.Data.Entity;
using System;
using System.Collections.Generic;
using Newtonsoft.Json.Linq;
using System.Collections;
using System.Linq;

namespace EPINMiddleWareAPI.Controllers
{
    [System.Web.Http.RoutePrefix("api/v2/pin")]
    public class InitiatesController : ApiController
    {
        private readonly EPINMiddleWareAPIContext context;

        public InitiatesController(EPINMiddleWareAPIContext context)
        {
            this.context = context;
        }



        // POST: api/Game
        //[RequireHttps] For Prod Only
        [HttpPost, Route("initiation")]
        public async Task<IHttpActionResult> PostInitiate(InitiateRequest initiate)
        {
            #region Is referenceId Unique?

            if (await Utilities.IsUnique(context, initiate) > 0)
            {
                ModelState.AddModelError("referenceId", "This referenceId has used!");
            }

            #endregion
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }


            #region Validate Company
            var responseMsg = new HttpResponseMessage();
            var isUsernamePasswordValid = false;

            //Find Company in our database
            var companyCheck =
                await (context.Companies.Where(p => p.customerID == initiate.customerID)).SingleOrDefaultAsync();

            //Check company password in our database
            if (companyCheck != null)
            {
                isUsernamePasswordValid = Utilities.VerifyPassword(initiate.password, companyCheck.hash, companyCheck.salt);

            }

            //Send UnAuthorized if we don't know the company
            if (!isUsernamePasswordValid)
            {
                // if credentials are not valid send unauthorized status code in response
                responseMsg.StatusCode = HttpStatusCode.Unauthorized;
                IHttpActionResult rMessage = ResponseMessage(responseMsg);
                return rMessage;
            }

            #endregion

            #region Check Code Bank database
            //Check Games in our database - all or nothing
            var gameBankResult = await (context.GameBanks.Where(g => g.productCode == initiate.productCode)
                .Where(l => l.referenceId == null)
                ).ToListAsync();
            //If we have exact number of games in our database, mark them!
            if (gameBankResult.Count() != 0 && gameBankResult.Count() >= initiate.quantity)
            {
                for (var index = 0; index < initiate.quantity; index++)
                {
                    var item = gameBankResult[index];
                    item.referenceId = initiate.referenceId;
                    context.Entry(item).State = System.Data.Entity.EntityState.Modified;
                }


                //Update marked games
                await context.SaveChangesAsync();
                //Create token
                var token = Utilities.createToken(companyCheck.userName);
                //Return marked games in our database
                var gameBankResultVM = await (context.GameBanks.Where(g => g.productCode == initiate.productCode)
                    .Where(l => l.referenceId == initiate.referenceId)
                    .Take(initiate.quantity)
                    .Select(g => new GameBankInitiationResponseVM()
                    {
                        referenceId = g.referenceId,
                        productCode = g.productCode,
                        quantity = initiate.quantity,
                        initiationResultCode = g.initiationResultCode,
                        validatedToken = g.validatedToken,
                        currency = g.currency,
                        estimateUnitPrice = g.estimateUnitPrice,
                        version = g.version,
                        signature = g.signature,
                        ApplicationCode = g.ApplicationCode,
                        companyToken = token,
                    })).ToListAsync();


                var resultObj = gameBankResultVM[0];

                var initiateResponse = JsonConvert.SerializeObject(resultObj);
                var responseDB = JsonConvert.DeserializeObject<InitiateResponse>(initiateResponse);
                //Adding Response into database
                context.InitiateResponses.Add(responseDB);
                await context.SaveChangesAsync();
                return Ok(resultObj);

            }


            #endregion

            // Add Signature
            initiate = Utilities.CreateSignature(initiate);


            #region Insert Request into DB
            //Adding Request into database
            context.InitiateRequests.Add(initiate);
            await context.SaveChangesAsync();
            #endregion


            #region Call Game
            var httpClient = new HttpClient();
            //Convert request
            var keyValues = initiate.ToKeyValue();
            var content = new FormUrlEncodedContent(keyValues);
            //Call Game Sultan
            var response =
                 await httpClient.PostAsync("https://test.com/purchaseinitiation",content);

            #endregion
            //Read response
            var htmlResponse = await response.Content.ReadAsStringAsync();
            switch (response.StatusCode)
            {
                case HttpStatusCode.NotFound:
                {
                    return NotFound();
                }

                case HttpStatusCode.InternalServerError:
                {
                    return InternalServerError();
                }

                case HttpStatusCode.OK:
                {

                    var initiateResponse = JsonConvert.DeserializeObject<InitiateResponse>(htmlResponse);
                    //Create Token if the result is SUCCESS
                    if (initiateResponse.initiationResultCode == "00")
                    {
                        //Create TOKEN and set
                        initiateResponse.companyToken = Utilities.createToken(companyCheck.userName); 
                    }

                    //Adding Response into database
                    context.InitiateResponses.Add(initiateResponse);
                    await context.SaveChangesAsync();
                    return Ok(initiateResponse);
                }

                case HttpStatusCode.BadRequest:
                {
                    return BadRequest(htmlResponse);
                }

                case HttpStatusCode.Unauthorized:
                {
                    return Unauthorized();
                }

                case HttpStatusCode.RequestTimeout:
                {
                    return InternalServerError();
                }

                default:
                {
                    htmlResponse = await response.Content.ReadAsStringAsync();
                    break;
                }
            }

            return Ok(htmlResponse);

        }


        // POST: api/Game
        //[RequireHttps] For Prod Only
        [Authorize]
        [HttpPost, Route("confirmation")]
        public async Task<IHttpActionResult> PostConfirmation(ConfirmRequest confirm)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }

            #region Validate Company
            var responseMsg = new HttpResponseMessage();
            var isUsernamePasswordValid = false;

            //Find Company in our database
            var companyCheck =
                await (context.Companies.Where(p => p.customerID == confirm.customerID)).SingleOrDefaultAsync();
            //Check company password in our database
            if (companyCheck != null)
                isUsernamePasswordValid = Utilities.VerifyPassword(confirm.password, companyCheck.hash, companyCheck.salt);
            //Send UnAuthorized if we don't know the company
            if (!isUsernamePasswordValid)
            {
                // if credentials are not valid send unauthorized status code in response
                responseMsg.StatusCode = HttpStatusCode.Unauthorized;
                IHttpActionResult rMessage = ResponseMessage(responseMsg);
                return rMessage;
            }

            #endregion

            #region Check Code Bank database
            //Return marked games in our database
            var gameBankResultVM = await (context.GameBanks
                .Where(l => l.referenceId == confirm.referenceId)
                .Select(g => new GameBankConfirmResponseVM()
                {
                    referenceId = g.referenceId,
                    paymentId = null,
                    productCode = g.productCode,
                    quantity = g.quantity,
                    deliveredQuantity = g.quantity,
                    currency = g.currency,
                    version = g.version,
                    signature = g.signature,
                    ApplicationCode = g.ApplicationCode,
                    productDescription = g.productDescription,
                    unitPrice = g.unitPrice,
                    totalPrice = g.totalPrice,
                    totalPayablePrice = g.totalPrice,
                    coupons = g.coupons.Select(c => new GameCouponBankVM()
                    {
                        Pin = c.Pin,
                        Serial = c.Serial,
                        expiryDate = c.expiryDate
                    }).ToList()
                })).ToListAsync();

            //If we have games in our database, select them!
            if (gameBankResultVM.Count() != 0)
            {
                if (gameBankResultVM.Count <= 1)
                {
                    //Adding Response into database
                    var responseNew = JsonConvert.SerializeObject(gameBankResultVM[0]);
                    var confirmResponse = JsonConvert.DeserializeObject<ConfirmResponse>(responseNew);
                    context.ConfirmResponses.Add(confirmResponse);
                    await context.SaveChangesAsync();

                    return Ok(gameBankResultVM[0]);
                }
                else if(gameBankResultVM.Count > 1)
                {
                    var gameResult = new GameBankConfirmResponseVM();
                    var price = 0.0;
                    var quantity = 0;

                    foreach (var item in gameBankResultVM)
                    {
                        price = price + item.unitPrice;
                        quantity = quantity + 1;
                        foreach (var coupons in item.coupons)
                        {
                            var gameCouponResult = new GameCouponBankVM
                            {
                                expiryDate = coupons.expiryDate,
                                Pin = coupons.Pin,
                                Serial = coupons.Serial
                            };
                            //Add coupon values
                            gameResult.coupons.Add(gameCouponResult);
                        }

                    }
                    //Set summed/counted values
                    gameResult.referenceId = gameBankResultVM[0].referenceId;
                    gameResult.paymentId = gameBankResultVM[0].paymentId;
                    gameResult.productCode = gameBankResultVM[0].productCode;
                    gameResult.quantity = quantity;
                    gameResult.deliveredQuantity = quantity;
                    gameResult.currency = gameBankResultVM[0].currency;
                    gameResult.unitPrice = gameBankResultVM[0].unitPrice;
                    gameResult.totalPrice = price;
                    gameResult.purchaseStatusDate = gameBankResultVM[0].purchaseStatusDate;
                    gameResult.productDescription = gameBankResultVM[0].productDescription;
                    gameResult.totalPayablePrice = price;
                    gameResult.version = gameBankResultVM[0].version;
                    gameResult.signature = null;
                    gameResult.ApplicationCode = null;



                    //Adding Response into database
                    var responseNew = JsonConvert.SerializeObject(gameResult);
                    var confirmResponse = JsonConvert.DeserializeObject<ConfirmResponse>(responseNew);
                    context.ConfirmResponses.Add(confirmResponse);
                    await context.SaveChangesAsync();

                    return Ok(gameResult);
                }
            }

            #endregion

            // Add Signature
            confirm = Utilities.CreateSignature(confirm);
            // Save Request into Table
            context.ConfirmRequests.Add(confirm);
            await context.SaveChangesAsync();

            var httpClient = new HttpClient();

            var keyValues = confirm.ToKeyValue();
            var content = new FormUrlEncodedContent(keyValues);

           var response =
                await httpClient.PostAsync("https://test.com/purchaseconfirmation", content);


           var htmlResponse = await response.Content.ReadAsStringAsync();
            switch (response.StatusCode)
            {
                case HttpStatusCode.NotFound:
                    return NotFound();

                case HttpStatusCode.InternalServerError:
                    return InternalServerError();

                case HttpStatusCode.OK:

                    //Adding Response into database
                    var newResult = JObject.Parse(htmlResponse);
                    var confirmResponse = JsonConvert.DeserializeObject<ConfirmResponse>(newResult.ToString());
                    context.ConfirmResponses.Add(confirmResponse);
                    await context.SaveChangesAsync();
                    return Ok(newResult);

                case HttpStatusCode.BadRequest:
                    return BadRequest();

                case HttpStatusCode.Unauthorized:
                    return Unauthorized();

                case HttpStatusCode.RequestTimeout:
                    return InternalServerError();

                default:
                    htmlResponse = await response.Content.ReadAsStringAsync();
                    break;
            }

            return Ok(htmlResponse);
        }


        [HttpPatch, Route("reconInitiation")]
        public async Task<IHttpActionResult> PatchReconciliationInitiation(Recon recon)
        {

            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }

            #region Validate Company
            var responseMsg = new HttpResponseMessage();
            var isUsernamePasswordValid = false;

            //Company password check
            var companyCheck =
                await (context.Companies.Where(p => p.customerID == recon.customerID)).SingleOrDefaultAsync();
            if (companyCheck != null)
                isUsernamePasswordValid = Utilities.VerifyPassword(recon.password, companyCheck.hash, companyCheck.salt);
            if (!isUsernamePasswordValid)
            {
                // if credentials are not valid send unauthorized status code in response
                responseMsg.StatusCode = HttpStatusCode.Unauthorized;
                IHttpActionResult rMessage = ResponseMessage(responseMsg);
                return rMessage;
            }

            #endregion
            //Projection
            var reconInitiateVm = new ReconInitiationVM();
            //Create TOKEN and set
            reconInitiateVm.companyToken = Utilities.createToken(companyCheck.userName);

            return Ok(reconInitiateVm);
        }

        [Authorize]
        [HttpPatch, Route("reconConfirmation")]
        public async Task<IHttpActionResult> PatchReconciliationConfirmation(Recon recon)
        {

            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }

            #region Validate Company
            var responseMsg = new HttpResponseMessage();
            var isUsernamePasswordValid = false;

            //Company password check
            var companyCheck =
                await (context.Companies.Where(p => p.customerID == recon.customerID)).SingleOrDefaultAsync();
            if (companyCheck != null)
                isUsernamePasswordValid = Utilities.VerifyPassword(recon.password, companyCheck.hash, companyCheck.salt);
            if (!isUsernamePasswordValid)
            {
                // if credentials are not valid send unauthorized status code in response
                responseMsg.StatusCode = HttpStatusCode.Unauthorized;
                IHttpActionResult rMessage = ResponseMessage(responseMsg);
                return rMessage;
            }

            #endregion

            #region Check Our Database
            DateTime? today = DateTime.Today;
            var reconResult = await (context.GameBanks.Where(g => g.used == 0)
                    .Where(l => l.referenceId != null)
                    .Where(t => System.Data.Entity.DbFunctions.TruncateTime(t.responseDateTime) == today)
                ).ToListAsync();

            if (reconResult.Count() != 0)
            {

                foreach (var item in reconResult)
                {
                    foreach (var reference in recon.referenceId)
                    {

                        if (item.referenceId == reference)
                        {
                                item.used = 1;
                                context.Entry(item).State = System.Data.Entity.EntityState.Modified;
                        }

                    }
                }


                await context.SaveChangesAsync();

            }

            #endregion

            #region Check ConfirmResponse - Razer

            var reconResultRazer = await (context.ConfirmResponses.Where(g => g.used == 0)
                    .Where(l => l.referenceId != null)
                    .Where(t => System.Data.Entity.DbFunctions.TruncateTime(t.purchaseStatusDate) == today)
                ).ToListAsync();

            if (reconResultRazer.Count() != 0)
            {

                foreach (var item in reconResultRazer)
                {
                    foreach (var reference in recon.referenceId)
                    {

                        if (item.referenceId == reference)
                        {
                            item.used = 1;
                            context.Entry(item).State = System.Data.Entity.EntityState.Modified;
                        }

                    }
                }


                await context.SaveChangesAsync();

            }
            #endregion


            return StatusCode(HttpStatusCode.NoContent);


        }


        // POST: api/Game
        //[RequireHttps] For Prod Only
        [HttpPost, Route("products")]
        public async Task<IHttpActionResult> PostProducts(ProductRequest products)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }


            #region Validate Company

            var responseMsg = new HttpResponseMessage();
            var isUsernamePasswordValid = false;

            //Find Company in our database
            var companyCheck =
                await (context.Companies.Where(p => p.customerID == products.customerID)).SingleOrDefaultAsync();

            //Check company password in our database
            if (companyCheck != null)
            {
                isUsernamePasswordValid =
                    Utilities.VerifyPassword(products.password, companyCheck.hash, companyCheck.salt);
            }

            //Send UnAuthorized if we don't know the company
            if (!isUsernamePasswordValid)
            {
                // if credentials are not valid send unauthorized status code in response
                responseMsg.StatusCode = HttpStatusCode.Unauthorized;
                IHttpActionResult rMessage = ResponseMessage(responseMsg);
                return rMessage;
            }

            #endregion

            #region Check Code Bank database

            //Return marked games in our database
            var productsList = (context.GameBanks.AsEnumerable()
                .Where(x => x.used == 0)
                .GroupBy(g => new
                {
                    g.productCode,
                    g.productDescription,
                    g.unitPrice
                    //g.used,
                })
                .Select(gcs => new
                    {
                        ProductID = Utilities.ConvertToLong(gcs.Key.productCode),
                        ProductName = gcs.Key.productDescription,
                        Price = gcs.Key.unitPrice,
                        //gcs.Key.used,
                        StockQuantity = gcs.Sum(g => g.quantity),
                        IsStockAvailable = Utilities.ConvertToBoolean(gcs.Sum(g => g.quantity) > 0 ? "True" : "False"),
                    }
                )).ToList();

            var list1 = productsList;


            //var productResult = new ProductsResponse();

            //foreach (var item in productsList)
            //{
            //    var productDetailsResult = new Product()
            //    {
            //        ProductID = Convert.ToInt64(item),
            //        ProductName = item.ProductName,
            //        GameName = item.ProductName,
            //        Price = item.Price,
            //        StockQuantity = item.StockQuantity,
            //        IsStockAvailable = Convert.ToBoolean(item.IsStockAvailable),
            //        SmallProductImage = "No Image",
            //        SupplierName = "No Supplier",
            //        SupplierID = 1,
            //        GameID = 1
            //    };

            //    //Add coupon values
            //    productResult.Products.Add(productDetailsResult);
            //}

            // Add Signature
            products = Utilities.CreateSignature(products);

            var httpClient = new HttpClient();
            var keyValues = products.ToKeyValue();
            var content = new FormUrlEncodedContent(keyValues);

            var response =
                await httpClient.PostAsync("https://test.com/Product/", content);


            var htmlResponse = await response.Content.ReadAsStringAsync();
            var model = JsonConvert.DeserializeObject<ProductsResponse>(htmlResponse);

            var productsListRazer = model.Products
                .Select(gcs => new
                    {
                        gcs.ProductID,
                        gcs.ProductName,
                        gcs.Price,
                        gcs.StockQuantity,
                        gcs.IsStockAvailable
                    }
                ).ToList();

            var list2 = productsListRazer;

            var merged = list1.Concat(list2)
                .GroupBy(g => new
                {
                    g.ProductID,
                    g.ProductName,
                    g.Price,
                    //g.StockQuantity,
                    g.IsStockAvailable
                })
                .Select(gcs => new
                {
                    gcs.First().ProductID,
                    gcs.Key.ProductName,
                    gcs.Key.Price,
                    StockQuantity = gcs.Sum(g => g.StockQuantity),
                    gcs.Key.IsStockAvailable
                }).ToList();


            return Ok(merged);

            #endregion
        }

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


    }
}
\$\endgroup\$
  • \$\begingroup\$ I have one last question, I think... is there any particular reason you're not using asp.net-core? Or can't I just recognize it correctly? mhmmm It's such a mess with that asp frameworks ;-] I'm asking because there are several things that can be optimized in a much nicer way if done with asp.net-core... not sure whether I should suggest them. Could be in vain if it's not an option. \$\endgroup\$ – t3chb0t May 28 at 17:10
  • \$\begingroup\$ @t3chb0t I am glad you asked this question :) Well I never implemented rest API before. 5 months ago when all this stuff started, this API requirement was urgent so I started with Python Flask but it is not suitable for enterprise I think. So I started reading articles and I am familiar with C# asp.net. I found a couple of step by step articles and now I am here :) At least code is working but I would like to make it better in terms of solid principles and OOP. My next move is to use core but after making this mess better. Are you with me? :) \$\endgroup\$ – Cenk May 28 at 18:26
  • \$\begingroup\$ @Cenk you don't need override the Dispose method with the purpose to dispose the db context. In general if an object does not create a disposable object, then he also doesn't have to dispose it. In this case Ninject is the one that creates the dbcontext and it also disposes it. Also since you pass around the dbcontext object to the Utilities class (that's a form of a dependency injection too), then you should be fine. Also the Utilities class is not part of your Ninject configuration, so there should be no interaction between them, outside your code. \$\endgroup\$ – Adrian Iftode May 28 at 21:26
  • \$\begingroup\$ is there any good news for me :) @t3chb0t \$\endgroup\$ – Cenk May 30 at 5:16
  • \$\begingroup\$ is there any progress @t3chb0t ? \$\endgroup\$ – Cenk Jun 1 at 18:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.