Quantcast
Channel: Customized Commerce 13 and earlier versions
Viewing all articles
Browse latest Browse all 9642

BusinessManager.Load throws NullReferenceException when called within IBackgroundContext

$
0
0

Hi,

As in title - the BusinessManager.Load throws NullReferenceException when called within IBackgroundContext.
The BusinessManager.Load method is called by IPromotionEngineExtensions.GetDiscountPrices within a service, that we'd like to parallelize using IBackgroundContext and Task.WhenAll.
On the other hand, if we're not using IBackgroundContext, we're getting errors mentioned in:
https://world.optimizely.com/blogs/Johan-Bjornfot/Dates1/2023/8/parallel-tasks-and-backgroundcontext/

Is the there a way to utilize the GetDiscountPrices (and by extent the BusinessManager.Load) within IBackgroundContext?

Code to reproduce:

var range = Enumerable.Range(0, 10).Select(_ => "4263b71a-69a4-43ff-b9a1-59a1d0cfbf9a"); // valid Customer Contact id
var customerContactTasks = range.Select(reference => Task.Run(() =>
{
    using var backgroundContext = _backgroundContextFactory.Create();
    // example to reproduce the problem, here is requested service which relies on BusinessManager.Load
    // using var service = backgroundContext.Services.GetRequiredService<IProblematicService>();
    // and then the service is called, internally using BusinessManager.Load via IPromotionEngine / IPromotionEngineExtensions
    var contactId = new PrimaryKeyId(new Guid(reference));
    var customerContact = BusinessManager.Load("Contact", contactId) as CustomerContact;
    return customerContact;
}));
var customerContacts = await Task.WhenAll(customerContactTasks);

The stack trace of above example:

   System.NullReferenceException: Object reference not set to an instance of an object.
   at Mediachase.BusinessFoundation.Data.Business.BusinessManager.Execute(Request request)
   at Mediachase.BusinessFoundation.Data.Business.BusinessManager.Load(String metaClassName, PrimaryKeyId primaryKeyId)
   at Project.Commerce.Features.Start.Builders.SomeViewModelBuilder.<>c__DisplayClass4_0.<Build>b__4() in C:\repo\src\Project.Commerce\Features\Start\Builders\SomeViewModelBuilder.cs:line 42
   at System.Threading.Tasks.Task`1.InnerInvoke()
   at System.Threading.Tasks.Task.<>c.<.cctor>b__272_0(Object obj)
   at System.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(Thread threadPoolThread, ExecutionContext executionContext, ContextCallback callback, Object state)

The stack trace of real-world example (anonimized):

System.NullReferenceException: Object reference not set to an instance of an object.
   at Mediachase.BusinessFoundation.Data.Business.BusinessManager.Load(String metaClassName, PrimaryKeyId primaryKeyId)
   at Mediachase.Commerce.Customers.CustomerContext.InnerGetContactById(Guid contactId)
   at Mediachase.Commerce.Customers.CustomerContext.<>c__DisplayClass32_0.<GetContactById>b__0()
   at Mediachase.Commerce.Customers.CustomersCache.<>c__DisplayClass6_0`1.<ReadThrough>g__WrapperAction|0()
   at EPiServer.Framework.Cache.ObjectInstanceCacheExtensions.ReadThrough[T](IObjectInstanceCache cache, String key, Func`1 readValue, Func`2 evictionPolicy)
   at EPiServer.Framework.Cache.ObjectInstanceCacheExtensions.ReadThrough[T](IObjectInstanceCache cache, String key, Func`1 readValue, Func`1 evictionPolicy)
   at Mediachase.Commerce.Extensions.ObjectInstanceCacheExtensions.ReadThrough[T](IObjectInstanceCache cache, Boolean useCache, String cacheKey, IEnumerable`1 masterKeys, TimeSpan duration, Func`1 load)
   at Mediachase.Commerce.Extensions.ObjectInstanceCacheExtensions.ReadThrough[T](IObjectInstanceCache cache, String cacheKey, IEnumerable`1 masterKeys, TimeSpan duration, Func`1 load)
   at Mediachase.Commerce.Customers.CustomersCache.ReadThrough[T](String key, IEnumerable`1 masterKeys, TimeSpan timeout, Func`1 load)
   at Mediachase.Commerce.Customers.CustomerContext.ExecuteThroughCache[T](String key, TimeSpan timeout, Func`1 load)
   at Mediachase.Commerce.Customers.CustomerContext.GetContactById(Guid contactId)
   at EPiServer.Commerce.Marketing.RedemptionLimitService.GetContactById(Guid customerId)
   at EPiServer.Commerce.Marketing.RedemptionLimitService.GetPerPromotionAndCustomerLimit(IEnumerable`1 promotions, Guid customerId, Int32 orderFormId)
   at EPiServer.Commerce.Marketing.RedemptionLimitService.GetRemainingRedemptions(IEnumerable`1 promotions, Guid customerId, Int32 orderFormId)
   at EPiServer.Commerce.Marketing.RedemptionLimits.Load(IEnumerable`1 promotions, Guid customerId, Int32 orderformId)
   at EPiServer.Commerce.Marketing.PromotionEngine.Run(IOrderGroup orderGroup, PromotionEngineSettings settings)
   at EPiServer.Commerce.Marketing.PromotionEngine.Evaluate(IEnumerable`1 entryLinks, IMarket market, Currency currency, RequestFulfillmentStatus requestFulfillmentStatus)
   at EPiServer.Commerce.Marketing.IPromotionEngineExtensions.Evaluate(IPromotionEngine promotionEngine, IEnumerable`1 entryLinks, IMarket market, Currency currency, RequestFulfillmentStatus requestFulfillmentStatus)
   at EPiServer.Commerce.Marketing.IPromotionEngineExtensions.GetDiscountedEntries(IPromotionEngine promotionEngine, ContentReference entryLink, IMarket market, Currency currency, ReferenceConverter referenceConverter, ILineItemCalculator lineItemCalculator)
   at EPiServer.Commerce.Marketing.IPromotionEngineExtensions.<>c__DisplayClass27_0.<ReadThroughCache>b__0()
   at EPiServer.Framework.Cache.ObjectInstanceCacheExtensions.ReadThrough[T](IObjectInstanceCache cache, String key, Func`1 readValue, Func`2 evictionPolicy)
   at EPiServer.Framework.Cache.ObjectInstanceCacheExtensions.ReadThrough[T](IObjectInstanceCache cache, String key, Func`1 readValue, Func`1 evictionPolicy)
   at EPiServer.Commerce.Marketing.IPromotionEngineExtensions.ReadThroughCache(IPromotionEngine promotionEngine, ContentReference contentLink, IMarket market, Currency currency, Guid contextId, ReferenceConverter referenceConverter, ILineItemCalculator lineItemCalculator, Func`5 getCacheKey, Func`7 readValues)
   at EPiServer.Commerce.Marketing.IPromotionEngineExtensions.GetDiscountPrices(IPromotionEngine promotionEngine, IEnumerable`1 entryLinks, IMarket market, Currency marketCurrency, ReferenceConverter referenceConverter, ILineItemCalculator lineItemCalculator)
   at EPiServer.Commerce.Marketing.IPromotionEngineExtensions.GetDiscountPrices(IPromotionEngine promotionEngine, IEnumerable`1 entryLinks, IMarket market, Currency marketCurrency)
   at Project.Commerce.Core.Prices.Services.PricingService.GetDiscountedPrice(String code) in C:\repo\src\Project.Commerce.Core\Prices\Services\PricingService.cs:line 54
   at Project.Commerce.Features.Product.Builders.SomeVariationViewModelBuilder.BuildDiscountedPrice(SomeVariationViewModel viewModel, SomeVariation variation, UserInfo user) in C:\repo\src\Project.Commerce\Features\Product\Builders\SomeVariationViewModelBuilder.cs:line 128
   at Project.Commerce.Features.Product.Builders.SomeVariationViewModelBuilder.Build(SomeVariationViewModel viewModel, SomeVariation variation, IIdentity identity) in C:\repo\src\Project.Commerce\Features\Product\Builders\SomeVariationViewModelBuilder.cs:line 82




Viewing all articles
Browse latest Browse all 9642

Trending Articles