I have a custom product adapter that checks the product reference and returns the correct snapshot object. This all seems to be working correctly until one of the snapshots is requested from a callback from the payment provider.
The error I receive is:
Unable to cast object of type 'HOI.Core.Adapters.HOIContentAccessSnapshop' to type 'Vendr.Web.Adapters.UmbracoProductSnapshot'.", "ExceptionType" => "System.InvalidCastException", "Message" => "An error has occurred.", "StackTrace" => " at Vendr.Web.Services.UmbracoStockService.GetStockSource(String productReference, String productVariantReference) in D:\a\1\s\src\Vendr.Web\Services\UmbracoStockService.cs:line 147\r\n at Vendr.Web.Services.UmbracoStockService.ReduceStock(String productReference, String productVariantReference, Decimal reduceBy) in D:\a\1\s\src\Vendr.Web\Services\UmbracoStockService.cs:line 84\r\n at Vendr.Core.Services.ProductService.ReduceProductStock(String productReference, String productVariantReference, Decimal reduceBy) in D:\a\1\s\src\Vendr.Core\Services\ProductService.cs:line 44\r\n at Vendr.Core.Events.Domain.Handlers.Order.ReduceOrderStockLevels.ReduceOrderLineStockLevelsRecursive(OrderLineReadOnly orderLine, Decimal parentQuantity) in D:\a\1\s\src\Vendr.Core\Events\Domain\Handlers\Order\ReduceOrderStockLevels.cs:line 27\r\n at Vendr.Core.Events.Domain.Handlers.Order.ReduceOrderStockLevels.Handle(OrderFinalized evt) in D:\a\1\s\src\Vendr.Core\Events\Domain\Handlers\Order\ReduceOrderStockLevels.cs:line 19\r\n at Vendr.Core.Events.Domain.DomainEventHandlerBase1.Vendr.Core.Events.IEventHandler.Handle(IEvent evt) in D:\\a\\1\\s\\src\\Vendr.Core\\Events\\Domain\\DomainEventHandlerBase.cs:line 9\r\n at Vendr.Core.Events.InProcEventDispatcher.Dispatch[T](IEnumerable1 handlers, T evt) in D:\a\1\s\src\Vendr.Core\Events\InProcEventDispatcher.cs:line 11\r\n at Vendr.Core.Events.EventBus.Dispatch[T](T evt) in D:\a\1\s\src\Vendr.Core\Events\EventBus.cs:line 41\r\n at Vendr.Core.Models.AggregateBase3.RaiseDomainEvents() in D:\\a\\1\\s\\src\\Vendr.Core\\Models\\AggregateBase.cs:line 98\r\n at Vendr.Core.Services.OrderService.SaveOrder(Order entity) in D:\\a\\1\\s\\src\\Vendr.Core\\Services\\OrderService.cs:line 213\r\n at Vendr.Core.Web.Controllers.VendrPaymentController.Callback(IPaymentProvider paymentProvider, OrderReference orderReference) in D:\\a\\1\\s\\src\\Vendr.Core.Web\\Controllers\\VendrPaymentController.cs:line 231\r\n at Vendr.Core.Web.Controllers.VendrPaymentController.HandleRequestWithOrder(String requestType, String paymentProviderAlias, Guid orderId, String orderNumber, String hash, Func3 handler) in D:\a\1\s\src\Vendr.Core.Web\Controllers\VendrPaymentController.cs:line 270\r\n at Vendr.Core.Web.Controllers.VendrPaymentController.Callback(String vendrPaymentProviderAlias, Guid vendrOrderId, String vendrOrderNumber, String vendrHash) in D:\a\1\s\src\Vendr.Core.Web\Controllers\VendrPaymentController.cs:line 144\r\n at lambdamethod(Closure , Object , Object[] )\r\n at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.<>cDisplayClass62.b2(Object instance, Object[] methodParameters)\r\n at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ExecuteAsync(HttpControllerContext controllerContext, IDictionary`2 arguments, CancellationToken cancellationToken)\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Web.Http.Controllers.ApiControllerActionInvoker.d1.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Web.Http.Controllers.ActionFilterResult.d5.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Web.Http.Filters.AuthorizationFilterAttribute.d__3.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebugger
I'm not using the stock functionality on any of the products at the moment, and I can't see to find out what is causing this issue on only one of the of the snapshots.
Can you explain a little bit about how the caching now works since the upgrade from 1.6 -> 1.7 please?
Since the upgrade I'm finding that my cart is not displaying any updates that are made to it, unless I force a recompile, which leads me to think that I am not forcing an update on the cache when I am making changes to the order.
It's a pretty fundamental change TBH. We swapped out the whole caching mechanism for a custom one that allows sliding expiration. Before we were just caching into Umbraco's own cache, but we needed more control over the cache so had to implement our own (though it's based technically on how the Umbraco isolated caches work).
Hmm, if you are needing to restart the app pool to see the changes then it would suggest the changes are being made but the cache isn't updating for some reason.
I've just tested on my local build and the orders seem to be updating OK in the checkout pages. I can increase/decrease quantities and remove items from the cart and all seems to update as expected.
Because I am handling all cart updates in ajax I can see that it is passing updated order data back to the client (and then updating the totals etc as I would expect).
If I then refresh the page it reverts back to the previous values, as though it has not saved the order. Restarting the app pool and then refreshing after this does display the updated order data.
I have a couple of extension methods that I am using to add a product:
public static BasketData AddItem(this OrderReadOnly orderRO, string productReference, decimal quantity)
{
using (var uow = VendrApi.Instance.Uow.Create())
{
var ret = orderRO.AsWritable(uow)
.AddProduct(productReference.GetProductSnapshot(), quantity, null, "", "", null)
.CompleteBasketAction(uow, BasketData.BasketAction.AddItem);
ret.SetProduct(productReference);
return ret;
}
}
public static BasketData CompleteBasketAction(this Order order, IUnitOfWork uow, BasketData.BasketAction action)
{
//To do - Run basket checks...
VendrApi.Instance.SaveOrder(order);
uow.Complete();
return new BasketData(action, order);
}
Is there anything that jumps out as to why this wouldn't work?
I have also tries putting the uow.Complete() within the using statement rather than trying to pass the UOW as a parameter and the result is the same.
Do you have any front end caching in play at all? And what is your code for retrieving the order initially on page load?
The AddProduct call seems kind of odd having to pass the snapshot and the null params. I think if you add a using Vendr.Core; statement at the top you'll get access to some more useful AddProduct extension methods where you won't have to supply those. Not sure this is the root of your issue though.
Without debugging into what is actually happening and when things are actually changing I'm not sure what to suggest.
You could try accessing the ICacheAccessor in Vendr and look in the EntityCaches for the order cache and check to see if it's getting added into the cache once the UoW transaction is complete.
Effectively how our caching works within transactions is we have a global cache, then in a transaction, any changes to the cache are made to a temporary cache, then when the transaction completes, all the changed items in the temporary cache are copied over to the global cache. So if you can follow that flow you can check it's actually doing what it's supposed to do.
Sure, add ICacheAccessor as a parameter on your controllers constructor, then store it in a private readonly var. Then check it's EntityCaches property for the order cache and see if your updated order is in there and up to date.
If I do a recompile, with a qty of 1 product in the order, then increase the quantity in the basket by one each time, my debug logging of the OrderState values from before calling "SetQuantity", before calling "uow.Complete" and after calling "uow.Complete" returns:
BEFORE CHANGE QTY
CART: HOICART-0475-0835-97XK
LineId: 0aafaa23-1968-42ed-8a18-0b499db9701a
QTY: 1.0000
BEFORE UOW.COMPLETE
CART: HOICART-0475-0835-97XK
LineId: 0aafaa23-1968-42ed-8a18-0b499db9701a
QTY: 1.0000
AFTER UOW.COMPLETE
CART: HOICART-0475-0835-97XK
LineId: 0aafaa23-1968-42ed-8a18-0b499db9701a
QTY: 1.0000
BEFORE CHANGE QTY
CART: HOICART-0475-0835-97XK
LineId: 0aafaa23-1968-42ed-8a18-0b499db9701a
QTY: 2
BEFORE UOW.COMPLETE
CART: HOICART-0475-0835-97XK
LineId: 0aafaa23-1968-42ed-8a18-0b499db9701a
QTY: 2
AFTER UOW.COMPLETE
CART: HOICART-0475-0835-97XK
LineId: 0aafaa23-1968-42ed-8a18-0b499db9701a
QTY: 2
BEFORE CHANGE QTY
CART: HOICART-0475-0835-97XK
LineId: 0aafaa23-1968-42ed-8a18-0b499db9701a
QTY: 2
BEFORE UOW.COMPLETE
CART: HOICART-0475-0835-97XK
LineId: 0aafaa23-1968-42ed-8a18-0b499db9701a
QTY: 2
AFTER UOW.COMPLETE
CART: HOICART-0475-0835-97XK
LineId: 0aafaa23-1968-42ed-8a18-0b499db9701a
QTY: 2
BEFORE CHANGE QTY
CART: HOICART-0475-0835-97XK
LineId: 0aafaa23-1968-42ed-8a18-0b499db9701a
QTY: 2
BEFORE UOW.COMPLETE
CART: HOICART-0475-0835-97XK
LineId: 0aafaa23-1968-42ed-8a18-0b499db9701a
QTY: 2
AFTER UOW.COMPLETE
CART: HOICART-0475-0835-97XK
LineId: 0aafaa23-1968-42ed-8a18-0b499db9701a
QTY: 2
I'm kinda back to where I was earlier now though and really I think the only way I can get to the issue is if I can debug the project for myself and see what the problem is.
Does the project contain any sensitive information? Would it be zippable so I can test it? You can send a link to [email protected].
Custom Product Adapter/Product Snapshot
Hi Matt,
I have a custom product adapter that checks the product reference and returns the correct snapshot object. This all seems to be working correctly until one of the snapshots is requested from a callback from the payment provider.
The error I receive is:
Unable to cast object of type 'HOI.Core.Adapters.HOIContentAccessSnapshop' to type 'Vendr.Web.Adapters.UmbracoProductSnapshot'.", "ExceptionType" => "System.InvalidCastException", "Message" => "An error has occurred.", "StackTrace" => " at Vendr.Web.Services.UmbracoStockService.GetStockSource(String productReference, String productVariantReference) in D:\a\1\s\src\Vendr.Web\Services\UmbracoStockService.cs:line 147\r\n at Vendr.Web.Services.UmbracoStockService.ReduceStock(String productReference, String productVariantReference, Decimal reduceBy) in D:\a\1\s\src\Vendr.Web\Services\UmbracoStockService.cs:line 84\r\n at Vendr.Core.Services.ProductService.ReduceProductStock(String productReference, String productVariantReference, Decimal reduceBy) in D:\a\1\s\src\Vendr.Core\Services\ProductService.cs:line 44\r\n at Vendr.Core.Events.Domain.Handlers.Order.ReduceOrderStockLevels.ReduceOrderLineStockLevelsRecursive(OrderLineReadOnly orderLine, Decimal parentQuantity) in D:\a\1\s\src\Vendr.Core\Events\Domain\Handlers\Order\ReduceOrderStockLevels.cs:line 27\r\n at Vendr.Core.Events.Domain.Handlers.Order.ReduceOrderStockLevels.Handle(OrderFinalized evt) in D:\a\1\s\src\Vendr.Core\Events\Domain\Handlers\Order\ReduceOrderStockLevels.cs:line 19\r\n at Vendr.Core.Events.Domain.DomainEventHandlerBase
1.Vendr.Core.Events.IEventHandler.Handle(IEvent evt) in D:\\a\\1\\s\\src\\Vendr.Core\\Events\\Domain\\DomainEventHandlerBase.cs:line 9\r\n at Vendr.Core.Events.InProcEventDispatcher.Dispatch[T](IEnumerable
1 handlers, T evt) in D:\a\1\s\src\Vendr.Core\Events\InProcEventDispatcher.cs:line 11\r\n at Vendr.Core.Events.EventBus.Dispatch[T](T evt) in D:\a\1\s\src\Vendr.Core\Events\EventBus.cs:line 41\r\n at Vendr.Core.Models.AggregateBase3.RaiseDomainEvents() in D:\\a\\1\\s\\src\\Vendr.Core\\Models\\AggregateBase.cs:line 98\r\n at Vendr.Core.Services.OrderService.SaveOrder(Order entity) in D:\\a\\1\\s\\src\\Vendr.Core\\Services\\OrderService.cs:line 213\r\n at Vendr.Core.Web.Controllers.VendrPaymentController.Callback(IPaymentProvider paymentProvider, OrderReference orderReference) in D:\\a\\1\\s\\src\\Vendr.Core.Web\\Controllers\\VendrPaymentController.cs:line 231\r\n at Vendr.Core.Web.Controllers.VendrPaymentController.HandleRequestWithOrder(String requestType, String paymentProviderAlias, Guid orderId, String orderNumber, String hash, Func
3 handler) in D:\a\1\s\src\Vendr.Core.Web\Controllers\VendrPaymentController.cs:line 270\r\n at Vendr.Core.Web.Controllers.VendrPaymentController.Callback(String vendrPaymentProviderAlias, Guid vendrOrderId, String vendrOrderNumber, String vendrHash) in D:\a\1\s\src\Vendr.Core.Web\Controllers\VendrPaymentController.cs:line 144\r\n at lambdamethod(Closure , Object , Object[] )\r\n at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.<>cDisplayClass62.b2(Object instance, Object[] methodParameters)\r\n at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ExecuteAsync(HttpControllerContext controllerContext, IDictionary`2 arguments, CancellationToken cancellationToken)\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Web.Http.Controllers.ApiControllerActionInvoker.d1.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Web.Http.Controllers.ActionFilterResult.d5.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Web.Http.Filters.AuthorizationFilterAttribute.d__3.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerI'm not using the stock functionality on any of the products at the moment, and I can't see to find out what is causing this issue on only one of the of the snapshots.
Any ideas?
Thanks,
Paul.
Hi Paul,
I believe I fixed this in 1.7.0 which was released last week so upgrading should resolve this.
Be sure to check the changelog for details on anything else that might have changed https://vendr.net/docs/core/changelog/
Matt
Ah that would explain it - Thanks as always!
Hi Matt,
Can you explain a little bit about how the caching now works since the upgrade from 1.6 -> 1.7 please?
Since the upgrade I'm finding that my cart is not displaying any updates that are made to it, unless I force a recompile, which leads me to think that I am not forcing an update on the cache when I am making changes to the order.
Thanks,
Paul.
Hey Paul,
It's a pretty fundamental change TBH. We swapped out the whole caching mechanism for a custom one that allows sliding expiration. Before we were just caching into Umbraco's own cache, but we needed more control over the cache so had to implement our own (though it's based technically on how the Umbraco isolated caches work).
Hmm, if you are needing to restart the app pool to see the changes then it would suggest the changes are being made but the cache isn't updating for some reason.
I'll have to give this a test.
Matt
I've just tested on my local build and the orders seem to be updating OK in the checkout pages. I can increase/decrease quantities and remove items from the cart and all seems to update as expected.
Are there any errors in your trace log?
Matt
Matt,
Nothing in the trace log.
Because I am handling all cart updates in ajax I can see that it is passing updated order data back to the client (and then updating the totals etc as I would expect).
If I then refresh the page it reverts back to the previous values, as though it has not saved the order. Restarting the app pool and then refreshing after this does display the updated order data.
I have a couple of extension methods that I am using to add a product:
Is there anything that jumps out as to why this wouldn't work?
I have also tries putting the uow.Complete() within the using statement rather than trying to pass the UOW as a parameter and the result is the same.
Do you have any front end caching in play at all? And what is your code for retrieving the order initially on page load?
The
AddProduct
call seems kind of odd having to pass the snapshot and the null params. I think if you add ausing Vendr.Core;
statement at the top you'll get access to some more usefulAddProduct
extension methods where you won't have to supply those. Not sure this is the root of your issue though.Matt
I think those null parameters and empty strings are just a throwback to something I was doing previously, I have removed those now, with no effect.
The only caching will be the default out of the box settings, as I have not changed anything.
I'm using the following to get the order on page load...
Hmm, that seems OK to me 🤔
I'm not sure why it's working for me then and not you. Short of getting you to send me your project I'm not sure how else to debug this 🤔
Matt
Hmmm. It's odd as it was working before I upgraded to 1.7.
My surface controller looks like this:
--
I assume that looks ok too?
Hey Paul,
Yea, seems ok to me.
Without debugging into what is actually happening and when things are actually changing I'm not sure what to suggest.
You could try accessing the
ICacheAccessor
in Vendr and look in theEntityCaches
for the order cache and check to see if it's getting added into the cache once the UoW transaction is complete.Effectively how our caching works within transactions is we have a global cache, then in a transaction, any changes to the cache are made to a temporary cache, then when the transaction completes, all the changed items in the temporary cache are copied over to the global cache. So if you can follow that flow you can check it's actually doing what it's supposed to do.
Matt
Sorry Matt, Can you just give a really quick starting point as to how I could access the ICacheAccessor please?
Ok I'll have to have a hunt.
I can see that when I update a qty on the cart the value in the DB is updating, so it appears that it's writing the data correctly....
Hi Paul,
Sure, add
ICacheAccessor
as a parameter on your controllers constructor, then store it in a private readonly var. Then check it'sEntityCaches
property for the order cache and see if your updated order is in there and up to date.Matt
So would I get the Order cache by
?
Not quite. All entities have an internal object that holds their state so for orders that
OrderState
Matt
So,
If I do a recompile, with a qty of 1 product in the order, then increase the quantity in the basket by one each time, my debug logging of the OrderState values from before calling "SetQuantity", before calling "uow.Complete" and after calling "uow.Complete" returns:
Not sure if this tells me anything at all!
Well, it's telling you that the cache isn't getting updated so something is stopping it
I'm kinda back to where I was earlier now though and really I think the only way I can get to the issue is if I can debug the project for myself and see what the problem is.
Does the project contain any sensitive information? Would it be zippable so I can test it? You can send a link to [email protected].
Thanks Matt - I’ll package it up and send a link over first thing tomorrow am.
is working on a reply...