I am working on a store that sells shoes. Shoes come in standard sizes but in order to avoid the monotonous entry of the same shoe sizes for EVERY product I want to setup default options when a new product is created. For some reason this does not appear to be working:
No they don't appear after rebuilding the Examine product index. I also checked the [merchProductOption] table and the product option is not in there so there is possibly something not firing in that chain events.
Copy option is handy to know - thanks. I might be able to find some time tonight to step through the source to see if I can spot why the save event is not completing as expected.
In case its of relevance the site is using v1.13.3.
I upgraded last night to v1.14.0 before attempting to do anything further but the problem still persists.
I also took a look at the CopyProduct task chain and from what I can see its not really much different in terms of the core steps but I updated my event handler a little to more closely match, still no luck.
Stepping through the process I see that after uow.Commit(); is called the production option appears in the [merchProductOption] table. When we get into the EnsureVariants method and product.GetPossibleProductAttributeCombinations() is called there are no lists returned however the Save event is executed twice and in the next pass we get back the 13 expected combinations but the product no longer has any ProductOption in the collection.
The call stack shows the second execution of the save event coming from the ProductApiController. Inside the AddProduct method is a call to the extension method ToProduct(this ProductDisplay productDisplay, IProduct destination) and this it seems is where we lose the product options I think.
I will try and see if I can get any further with this today.
Customer just contacted me to advise they can't actually copy a product either due to an exception:
Received an error from the server
Failed to delete detached content
Object reference not set to an instance of an object.
EXCEPTION DETAILS:
System.NullReferenceException: Object reference not set to an instance of an object.
STACKTRACE:
at Merchello.Web.WebApi.JsonCamelCaseFormatter.OnActionExecuted(HttpActionExecutedContext httpActionExecutedContext)
at System.Web.Http.Filters.ActionFilterAttribute.OnActionExecutedAsync(HttpActionExecutedContext actionExecutedContext, CancellationToken cancellationToken)
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Web.Http.Filters.ActionFilterAttribute.<CallOnActionExecutedAsync>d__5.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Web.Http.Filters.ActionFilterAttribute.<ExecuteActionFilterAsyncCore>d__0.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
at System.Web.Http.Controllers.ActionFilterResult.<ExecuteAsync>d__2.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Web.Http.Filters.AuthorizationFilterAttribute.<ExecuteAuthorizationFilterAsyncCore>d__2.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
at System.Web.Http.Dispatcher.HttpControllerDispatcher.<SendAsync>d__1.MoveNext()
I don't think this is related but posting just in case.
Yes, I just discovered that and disabled them. The products he created prior to this have errors so will need to be recreated, I think that is a result of now having 2 product options with the same name. If I save the products now a null reference exception is thrown even after deleting the duplicated product option.
Thought I'd add my NZD0.02 in here as I struggled with the same thing under Merchello 2.1.0.
Ultimately, any ProductOptions which I attempted to add in the Creating or Created event handlers for the Merchello.Core.Services.ProductService were not persisted to the database on save, even with an explicit call to sender.Save() such as you were doing above.
However, they can be persisted from the Saving event handler off the same service.
For your example above, try:
public class UmbracoEventHandler : ApplicationEventHandler {
private static UmbracoApplicationBase _umbracoApplication;
private static ApplicationContext _applicationContext;
private static object _startLock = new object();
private static bool _started = false;
public UmbracoEventHandler() {
}
protected override void ApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext) {
if (!_started) {
lock (_startLock) {
if (!_started) {
ProductService.Saving += ProductService_Saving;
_started = true;
}
}
}
}
private void ProductService_Saving(IProductService sender, Umbraco.Core.Events.SaveEventArgs<IProduct> e) {
foreach (var entity in e.SavedEntities) {
if (entity.CreateDate == entity.UpdateDate) { //new product
entity.ProductOptions = new ProductOptionCollection() {
new ProductOption("Size") {
Choices = {
new ProductAttribute("UK2", "UK2"),
new ProductAttribute("UK2.5", "UK25")
},
Required = true
}
};
};
}
}
Note that in absence of an "IsNew" or "IsInitialSave" field, I'm using entity.CreateDate == entity.UpdateDate to check whether the Product is a new product. You might consider a different approach here depending on your situation, and I can't guarantee that mine doesn't break under certain circumstances.
You also don't need to explicitly call sender.Save() - in fact, doing so will kick you back into the event handler again.
Hope that helps you or someone else in the future with this same issue. Let me know if you have any difficulties.
Adding Default Product Options/values
I am working on a store that sells shoes. Shoes come in standard sizes but in order to avoid the monotonous entry of the same shoe sizes for EVERY product I want to setup default options when a new product is created. For some reason this does not appear to be working:
The UI loads and the
This variant has options (like size or color)
option remains unchecked and no product options are visible.Is there another step required?
Hmm - your code looks fine. Maybe it's the order the events are firing.
Can you add a product and then rebuild the product Examine index and see if they appear? I'd have to think about that one if it is the case.
You can also create a template product and "Copy" it which copies all the options and extended content.
Copy is available through the back office under the "Save" button.
Hi Rusty,
No they don't appear after rebuilding the Examine product index. I also checked the
[merchProductOption]
table and the product option is not in there so there is possibly something not firing in that chain events.Copy option is handy to know - thanks. I might be able to find some time tonight to step through the source to see if I can spot why the save event is not completing as expected.
In case its of relevance the site is using v1.13.3.
Cheers, Simon
Great Simon!
The copy product is done through a chain of tasks. You can find the actual types in the merchello.config if you want to see how that is done.
I upgraded last night to v1.14.0 before attempting to do anything further but the problem still persists.
I also took a look at the
CopyProduct
task chain and from what I can see its not really much different in terms of the core steps but I updated my event handler a little to more closely match, still no luck.Stepping through the process I see that after
uow.Commit();
is called the production option appears in the[merchProductOption]
table. When we get into theEnsureVariants
method andproduct.GetPossibleProductAttributeCombinations()
is called there are no lists returned however the Save event is executed twice and in the next pass we get back the 13 expected combinations but the product no longer has anyProductOption
in the collection.The call stack shows the second execution of the save event coming from the
ProductApiController
. Inside theAddProduct
method is a call to the extension methodToProduct(this ProductDisplay productDisplay, IProduct destination)
and this it seems is where we lose the product options I think.I will try and see if I can get any further with this today.
Customer just contacted me to advise they can't actually copy a product either due to an exception:
I don't think this is related but posting just in case.
Are your event handlers in kicking in when he copies?
Yes, I just discovered that and disabled them. The products he created prior to this have errors so will need to be recreated, I think that is a result of now having 2 product options with the same name. If I save the products now a null reference exception is thrown even after deleting the duplicated product option.
Try rebuilding the ProductIndex?
Unfortunately not. It will be quicker for me to recreate them at the moment in order to keep them happy.
Hey Simon,
Thought I'd add my NZD0.02 in here as I struggled with the same thing under Merchello 2.1.0.
Ultimately, any
ProductOptions
which I attempted to add in theCreating
orCreated
event handlers for theMerchello.Core.Services.ProductService
were not persisted to the database on save, even with an explicit call tosender.Save()
such as you were doing above.However, they can be persisted from the
Saving
event handler off the same service.For your example above, try:
Note that in absence of an "IsNew" or "IsInitialSave" field, I'm using
entity.CreateDate == entity.UpdateDate
to check whether theProduct
is a new product. You might consider a different approach here depending on your situation, and I can't guarantee that mine doesn't break under certain circumstances.You also don't need to explicitly call
sender.Save()
- in fact, doing so will kick you back into the event handler again.Hope that helps you or someone else in the future with this same issue. Let me know if you have any difficulties.
Cheers and good luck!
-Geoff R G Williams for Primal Blaze.
is working on a reply...
This forum is in read-only mode while we transition to the new forum.
You can continue this topic on the new forum by tapping the "Continue discussion" link below.