Request nigdy nie wraca, trwa w nieskończoność.

0

Sprawa wygląda tak, że mam kontroler api ,do którego wstrzykuje serwis, do którego wstrzykuje dwa repozytoria, do których wstrzykuje ten sam kontekst entity frameworka.
Serwis, repozytoria i kontekst wstrzykiwany jest w zakresie per request.

Problem jest taki, że jak wykonam metodę API:

Post(IEnumerable<SponsoredDiscountAddOrUpdateViewModel> viewModels)

która

  1. wykonuje metodę AddOrUpdate serwisu, który:
    1. Wykonuje metodę GetAsync repozytorium
    2. Wykonuje Clear na repozytorium
    3. Wykonuje Add na repozytorium
  2. Wykonuje metodę GetAsync na repozytorium - w tym miejscu się zawiesza! bez tej linii leci dalej

Co może być powodem zawieszenia? Nie dostaje, żadnego wyjątku, po prostu request do bazy trwa a nieskończoność i nigdy nie wraca odpowiedź.
Kod:

WebAPI:

[RoutePrefix("api/sponsored")]
    public partial class SponsoredController : BaseApiController
    {
        private readonly ISponsoredService _sponsoredService;

        public SponsoredController(
            ISponsoredService sponsoredService, 
            IBrandService brandService, 
            ICampaignsService discountService)
        {
            _sponsoredService = sponsoredService;
            _discountService = discountService;
            _brandService = brandService;
        }
    }
	public partial class SponsoredController
    {
        private readonly IBrandService _brandService;

        [Route("brands"), HttpGet]
        public async Task<IHttpActionResult> GetBrands()
        {
            try
            {
                var viewModels = await GetBrandViewModels();
                return Ok(viewModels);
            }
            catch (Exception e)
            {
                base.Log(e);
                return InternalServerError();
            }
        }

        [Route("brands"), HttpPost, ValidateModelStateFilter]
        public async Task<IHttpActionResult> Post([FromBody] IEnumerable<SponsoredBrandAddOrUpdateViewModel> viewModels)
        {
            try
            {
                await this._sponsoredService.AddOrUpdate(viewModels.Select(vm => (SponsoredBrand)vm));
                return Created("api/sponsored/brands", GetBrandViewModels());
            }
            catch (Exception e)
            {
                base.Log(e, viewModels);
                return InternalServerError();
            }
        }


        private async Task<List<SponsoredBrandListViewModel>> GetBrandViewModels()
        {
            var dbSponsoredBrands = await this._sponsoredService.GetSponsoredBrandsAsync();
            var viewModels =
                dbSponsoredBrands.Select(sponsoredBrand =>
                {
                    var viewModel = (SponsoredBrandListViewModel)sponsoredBrand;
                    viewModel.Name = (this._brandService.GetBrandByBrandIdAsync(viewModel.BrandId).Result).Entity.Name;
                    return viewModel;
                }).ToList();
            return viewModels;
        }
    }
	
	  public partial class SponsoredController
    {
        private readonly ICampaignsService _discountService;

        [Route("discounts"), HttpGet]
        public async Task<IHttpActionResult> GetDiscounts()
        {
            try
            {
                var viewModels = await GetDiscountsViewModels();
                return Ok(viewModels);
            }
            catch (Exception e)
            {
                base.Log(e);
                return InternalServerError();
            }
        }
        [Route("discounts"), HttpPost, ValidateModelStateFilter]
        public async Task<IHttpActionResult> Post([FromBody] IEnumerable<SponsoredDiscountAddOrUpdateViewModel> viewModels)
        {
            try
            {
                await this._sponsoredService.AddOrUpdate(viewModels.Select(vm => (SponsoredDiscount)vm));
                return Created("api/sponsored/discounts", GetDiscountsViewModels());
            }
            catch (Exception e)
            {
                base.Log(e, viewModels);
                return InternalServerError();
            }
        }

        private async Task<List<SponsoredDiscountListViewModel>> GetDiscountsViewModels()
        {
            var dbSponsoredBrands = await this._sponsoredService.GetSponsoredDiscountsAsync();
            var viewModels =
                dbSponsoredBrands.Select(sponsoredBrand =>
                {
                    var viewModel = (SponsoredDiscountListViewModel)sponsoredBrand;
                    viewModel.Name = (this._discountService.GetCampaignByCampaignIdAsync(viewModel.DiscountId).Result)?.Entity?.Discount?.Name;
                    return viewModel;
                }).ToList();
            return viewModels;
        }
    }
	
Service:

public partial interface ISponsoredService
    {
        Task<IEnumerable<SponsoredDiscount>> GetSponsoredDiscountsAsync();
        Task<IEnumerable<SponsoredDiscount>> AddOrUpdate(IEnumerable<SponsoredDiscount> sponsoredDiscounts);

    }
	public partial interface ISponsoredService
    {
        Task<IEnumerable<SponsoredDiscount>> GetSponsoredDiscountsAsync();
        Task<IEnumerable<SponsoredDiscount>> AddOrUpdate(IEnumerable<SponsoredDiscount> sponsoredDiscounts);
    }
	
	 public partial class SponsoredService : ISponsoredService
    {
        public SponsoredService(
            ISponsoredBrandsRepository sponsoredBrandRepository, 
            ISponsoredDiscountsRepository sponsoredDiscountsRepository, 
            IBrandService brandService, 
            ICampaignsService discountsService)
        {
            _sponsoredBrandRepository = sponsoredBrandRepository;
            _brandService = brandService;
            _discountsService = discountsService;
            _sponsoredDiscountsRepository = sponsoredDiscountsRepository;
        }
    }
	public partial class SponsoredService
    {
        private readonly ISponsoredBrandsRepository _sponsoredBrandRepository;
        private readonly IBrandService _brandService;

        public async Task<IEnumerable<SponsoredBrand>> GetSponsoredBrandsAsync() => await this._sponsoredBrandRepository.GetAsync();


        public async Task<IEnumerable<SponsoredBrand>> AddOrUpdate(IEnumerable<SponsoredBrand> sponsoredBrands)
        {
            // remove
            var dbSponsored = await this.GetSponsoredBrandsAsync();
            foreach (var dbS in dbSponsored)
            {
                if (!sponsoredBrands.Any(s => s.RelatedEntityId == dbS.RelatedEntityId))
                {
                    await this.DeleteSponsoredBrand(dbS.Id);
                }
            }

            // new 
            foreach (var newS in sponsoredBrands)
            {
                var brand = (await this._brandService.GetBrandByBrandIdAsync(newS.RelatedEntityId)).Entity;
                brand.BrandRules = new List<BrandRule>
                {
                    new BrandRule
                    {
                        IsHighlighted = true, Order = newS.Order, ValidTo = newS.To, ValidFrom = newS.From
                    }
                }.ToList();
                await this._brandService.UpdateAsync(brand);
            }

            this._sponsoredBrandRepository.Clear();
            await this._sponsoredBrandRepository.Add(sponsoredBrands);
            return null;
        }

    }
	 public partial class SponsoredService
    {

        private readonly ISponsoredDiscountsRepository _sponsoredDiscountsRepository;
        private readonly ICampaignsService _discountsService;

        public async Task<IEnumerable<SponsoredDiscount>> GetSponsoredDiscountsAsync()
            => await this._sponsoredDiscountsRepository.GetAsync();

        public async Task<IEnumerable<SponsoredDiscount>> AddOrUpdate(IEnumerable<SponsoredDiscount> sponsoredDiscounts)
        {
            // remove
            var dbSponsored = await this.GetSponsoredDiscountsAsync();
            foreach (var dbS in dbSponsored)
                if (!sponsoredDiscounts.Any(s => s.RelatedEntityId == dbS.RelatedEntityId))
                    await this.DeleteSponsoredDiscount(dbS.Id);

            // new 
            foreach (var newS in sponsoredDiscounts)
                if (!await this._discountsService.X(newS.RelatedEntityId, newS))
                    return null;

            this._sponsoredDiscountsRepository.Clear();
            await this._sponsoredDiscountsRepository.Add(sponsoredDiscounts);
            return null;
        }   
    }
	
Repositories:

    public interface ISponsoredRepository<TEntity> : IBaseRepository<TEntity, int>
        where TEntity : Sponsored.Sponsored
    {
        void Clear();
        Task Add(IEnumerable<TEntity> entities);
    }
    public interface ISponsoredBrandsRepository : ISponsoredRepository<SponsoredBrand> { }
    public interface ISponsoredDiscountsRepository : ISponsoredRepository<SponsoredDiscount> { }
	
	
public abstract class SponsoredRepository<TEntity> : ISponsoredRepository<TEntity>
        where TEntity : Domain.Sponsored.Sponsored
    {
        protected readonly Context SponsoredContext;
        protected readonly DbSet<TEntity> DbSet;

        protected SponsoredRepository(Context sponsoredContext)
        {
            SponsoredContext = sponsoredContext;
            DbSet = this.SponsoredContext.Set<TEntity>();
        }

        public virtual async Task<IEnumerable<TEntity>> GetAsync()
        {
            return await this.DbSet.ToListAsync();
        }



        public virtual void Clear()
        {
            this.DbSet.RemoveRange(this.DbSet);
            this.SponsoredContext.SaveChanges();
        }

        public virtual async Task Add(IEnumerable<TEntity> entities)
        {
            this.DbSet.AddRange(entities);
            await this.SponsoredContext.SaveChangesAsync();
        }
    }
	
	  public class SponsoredBrandsRepository : SponsoredRepository<SponsoredBrand>, ISponsoredBrandsRepository
    {
        public SponsoredBrandsRepository(
            Context sponsoredContext) : base(sponsoredContext)
        {
        }
    }
	
	 public class SponsoredDiscountsRepository : SponsoredRepository<SponsoredDiscount>, ISponsoredDiscountsRepository
    {
        public SponsoredDiscountsRepository(
            Context sponsoredContext) : base(sponsoredContext)
        {
        }
    }
	
ContexT:

public class Context
    {
        public virtual DbSet<SponsoredBrand> Brands { get; set; }
        public virtual DbSet<SponsoredDiscount> Discounts { get; set; }


        public Context()
        {
            
        }
      
    }
	
IoC configuration(Ninject):

            kernel.Bind<Context>().ToSelf();
            kernel.Bind<ISponsoredService>().To<SponsoredService>().InRequestScope();
            kernel.Bind<ISponsoredBrandsRepository>().To<SponsoredBrandsRepository>().InRequestScope();
            kernel.Bind<ISponsoredDiscountsRepository>().To<SponsoredDiscountsRepository>().InRequestScope();```csharp

0

Wygląda na jakiś deadlock. Z tego co widzę to kontekst nie jest rejestrowany per request tak jak mówisz.

kernel.Bind<Context>().ToSelf();

0

Z dopiskiem .InRequestScope(), .InSingletonScope() sytuacja jest taka sama.

1

Co zwraca metoda GetCampaignByCampaignIdAsync ? Czemu robisz na tym tasku .Result zamiast zrobić await ? Blokujesz w ten sposób wątek i to może prowadzić do deadlocka.

Ogólnie to trochę smrodu masz w tym kodzie. Metoda nazywa się AddOrUpdate a tak naprawde to czyści ona kolekcje i dodaje elementy od nowa. Sama nazwa to ewidentny gwałt SRP Do tego ma ona zwracać kolekcję (której nigdy nie dostaniesz) bo zwraca zawsze null. Masz coś takiego jak Task.CompletedTask

0

Do tej metody GetCampaignByCampaignAsync nawet nie wchodzi bo zawiesza się na "var dbSponsoredBrands = await this._sponsoredService.GetSponsoredDiscountsAsync();" po wyjściu z "Add" w kontrolerze.

0

Problem polegał na tym, że przy metodzie GetViewModels() nie było await:

  return Created("api/sponsored/discounts", await GetDiscountsViewModels()); // to działa
  return Created("api/sponsored/discounts", GetDiscountsViewModels()); // a to zawisa w ciele metody na pierwszym await 

Czemu?

1 użytkowników online, w tym zalogowanych: 0, gości: 1