Don't miss out Virtual Happy Hour this Friday (April 26).

Try our conversational search powered by Generative AI!

Updating a property of product programmatically in the catalog

Vote:
 

Hi Guys,

i have a EntryUpdating Event listener.  Basically given a boolean property (ShowCommercialName) on the TileVariant model in my catalog it should update the RouteSegment (for the url) of the entry.

But using the code below when i turn on the repo.Save method it just fails. 

I'm newbie in Commerce, so i'm getting confused with the DTO method. I'm using Commerce 10.7.3.

How i trigger the save for a product in the catalog?

 [ServiceConfiguration(typeof(CatalogEventListenerBase))]
    public class CustomCatalogEventListenerBase : CatalogEventListenerBase
    {
        public override void EntryUpdating(object source, EntryEventArgs args)
        {
            base.EntryUpdating(source, args);
            var entry = source as CatalogEntryDto;
            var catalogEntryRow = entry.CatalogEntry.FirstOrDefault();
            
            if (catalogEntryRow == null)
                return;

            if (catalogEntryRow.RowState == System.Data.DataRowState.Modified)
            {
                var workId = 0;

                var referenceConverter = ServiceLocator.Current.GetInstance();
                var variantLink = referenceConverter.GetContentLink(catalogEntryRow.CatalogEntryId, workId);
                //var variantLink2 = new ContentReference(catalogEntryRow.CatalogEntryId, workId, "CatalogContent" );

                var repo = ServiceLocator.Current.GetInstance();
                var variant = repo.Get(variantLink);
                                

                if(variant is TileVariant)
                {
                    var tileVariant = variant as TileVariant;
                    if (!tileVariant.ShowCommercialName && !tileVariant.RouteSegment.Equals(tileVariant.ShowroomCode.Slugify(), StringComparison.InvariantCultureIgnoreCase))
                    {
                        var writableTileVariant = tileVariant.CreateWritableClone();
                        writableTileVariant.RouteSegment = tileVariant.ShowroomCode.Slugify();
                        //repo.Save(writableTileVariant, SaveAction.Publish, AccessLevel.NoAccess);
                    }
                }             
               
            }

            
        }
    }
#187264
Edited, Jan 17, 2018 10:01
Vote:
 

You said Save method fails, what is the error?

#187268
Jan 17, 2018 11:29
Vote:
 

oww yes sorry...  So no error, but i keep getting looped into this EntryUpdating event, which continues and then just breaks the application due to too many loops. 

#187275
Jan 17, 2018 14:52
Vote:
 

Reading your code more carefully I think I understand why. EntryUpdating works in a lower level (i.e. when a CatalogEntryDto is saved), and if you call Save, it will internally save the DTO, which again triggers EntryUpdating, resulting in an endless loop.

Instead of CatalogEventListenerBase, you can try using IContentEvents - if you only updates entries via content APIs (for example, by using Catalog UI), and try to use PublishedContent instead of PublisingContent event. The *ed one happens after the data is committed, while *ing happens before. I did not test that so you might want to try it first. 

#187280
Jan 17, 2018 16:50
Vote:
 

If I don't take totaly wrong, then something like this should work. This is not tested code and can't guarantee that it works :)

[InitializableModule]
    [ModuleDependency(typeof(EPiServer.Web.InitializationModule))]
    public class ContentEventHandlersInitialization : IInitializableModule
    {
        public void Initialize(InitializationEngine context)
        {
            var events = ServiceLocator.Current.GetInstance<IContentEvents>();
            events.PublishingContent += PublishingContent;
        }

        private void PublishingContent(object sender, ContentEventArgs e)
        {
            TileVariant tileVariant = e.Content as TileVariant;
            if (tileVariant != null)
            {
                if (!tileVariant.ShowCommercialName && !tileVariant.RouteSegment.Equals(tileVariant.ShowroomCode.Slugify(), StringComparison.InvariantCultureIgnoreCase))
                {
                    // we don't need to create a writeableclone as this is the object getting saved 
                    tileVariant.RouteSegment = tileVariant.ShowroomCode.Slugify();
                    // for the same reason, we don't need to call the save method on ContentRepository
                }
            }
        }

        public void Uninitialize(InitializationEngine context)
        {
            var events = ServiceLocator.Current.GetInstance<IContentEvents>();
            events.PublishingContent -= PublishingContent;
        }

        public void Preload(string[] parameters)
        {
        }
    }
#187287
Jan 17, 2018 21:58
Vote:
 

Hi Sebastian,

i didnt know that these events were also working for commerce section of episerver. I thought only for CMS. 

So the following problem. WHen i add this initialization module. And i try to publish a normal contentpage i never get into this module. I set my break point in the Initialize method, but never comes there.. 

What am i missing?

I also tried to add 

[ModuleDependency(typeof(InitializableModule))]

instead of your

[ModuleDependency(typeof(EPiServer.Web.InitializationModule))]

I saw someone else use that. I dont know what the difference is but i saw someone else use that, but it doesnt hit my breakpoint either.

#187464
Edited, Jan 23, 2018 15:44
Vote:
 

If you are using content APIs to update your catalog items, or if you are using the Catalog UI to update them manually, then yes, you can listen to certain events in IContentEvents. If you are using CatalogEntryDto/ICatalogSystem for example, then those are in lower level and will not fire events in IContentEvents.

In Sebastian's code he added a check for TileVariant, make sure your break point is place before that. Also:

  • It's recommended to have ModuleDependency on EPiServer.Commerce.Initialization.InitializationModule
  • Inside Initializate, use context.Locate.Advanced.GetInstance<T> instead
#187465
Edited, Jan 23, 2018 15:49
Vote:
 

thanks both of you... the content API works perfectly now... 

#187466
Jan 23, 2018 16:10
This topic was created over six months ago and has been resolved. If you have a similar question, please create a new topic and refer to this one.
* You are NOT allowed to include any hyperlinks in the post because your account hasn't associated to your company. User profile should be updated.