Address inserted event handling from plugin not working / IConsumer<EntityInserted<Address>> event not working

This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.
6 years ago
Hi nopCommerce Forum Members,

I am developing a plugin which consumes Address insert/update event. I need to perform custom actions when customer add address OR update existing address from My Account -> Addresses. I developed plugin and it is working fine for address update event, but it is not working for address inserted event.

Here is my plugin code.

namespace Nop.Plugin.Misc.MyPlugin
{
    public class AddressInsertEventConsumer :
        IConsumer<EntityUpdated<Address>>,
        IConsumer<EntityInserted<Address>>        
    {
        private readonly AddressLogsSettings _addressLogsSettings;
        private readonly IPluginFinder _pluginFinder;
        private readonly IOrderService _orderService;
        private readonly IStoreContext _storeContext;
        private readonly IWorkContext _workContext;

        public AddressInsertEventConsumer(AddressLogsSettings addressLogsSettings,
            IPluginFinder pluginFinder,
            IOrderService orderService,
            IStoreContext storeContext,
            IWorkContext workContext
            )
        {
            this._addressLogsSettings = addressLogsSettings;
            this._pluginFinder = pluginFinder;
            this._orderService = orderService;
            this._storeContext = storeContext;
            this._workContext = workContext;
        }

        #region :: Updated ::
        
        public void HandleEvent(EntityUpdated<Address> eventMessage)
        {
            //Code which I need when Address is Updated
        }
        
        #endregion

        #region :: Inserted ::
        
        public void HandleEvent(EntityInserted<Address> eventMessage)
        {
      //Code which I need when Address is Inserted
    }
        #endregion
    }
}



I tried to debug code from customer controller to plugin code I find that it is not actually adding event for address inserted.

See code of
Customer Controller -> Add new Address

[HttpPost]
        [PublicAntiForgery]
        [ValidateInput(false)]
        public ActionResult AddressAdd(CustomerAddressEditModel model, FormCollection form)
        {
            if (!_workContext.CurrentCustomer.IsRegistered())
                return new HttpUnauthorizedResult();

            var customer = _workContext.CurrentCustomer;

            //custom address attributes
            var customAttributes = form.ParseCustomAddressAttributes(_addressAttributeParser, _addressAttributeService);
            var customAttributeWarnings = _addressAttributeParser.GetAttributeWarnings(customAttributes);
            foreach (var error in customAttributeWarnings)
            {
                ModelState.AddModelError("", error);
            }

            if (ModelState.IsValid)
            {
                var address = model.Address.ToEntity();
                address.CustomAttributes = customAttributes;
                address.CreatedOnUtc = DateTime.UtcNow;
                //some validation
                if (address.CountryId == 0)
                    address.CountryId = null;
                if (address.StateProvinceId == 0)
                    address.StateProvinceId = null;
                customer.Addresses.Add(address);
                _customerService.UpdateCustomer(customer);

                return RedirectToRoute("CustomerAddresses");
            }


Here I can see that it is adding new address to customer.Addresses list and calling _customerService.UpdateCustomer service. If we go to UpdateCustomer service it is adding event for customer updated but not for address update.


While customer address update code is actually calling service _addressService.UpdateAddress(address);
Customer Controller -> Edit Address

        [HttpPost]
        [PublicAntiForgery]
        [ValidateInput(false)]
        public ActionResult AddressEdit(CustomerAddressEditModel model, int addressId, FormCollection form)
        {
            if (!_workContext.CurrentCustomer.IsRegistered())
                return new HttpUnauthorizedResult();

            var customer = _workContext.CurrentCustomer;
            //find address (ensure that it belongs to the current customer)
            var address = customer.Addresses.FirstOrDefault(a => a.Id == addressId);
            if (address == null)
                //address is not found
                return RedirectToRoute("CustomerAddresses");

            //custom address attributes
            var customAttributes = form.ParseCustomAddressAttributes(_addressAttributeParser, _addressAttributeService);
            var customAttributeWarnings = _addressAttributeParser.GetAttributeWarnings(customAttributes);
            foreach (var error in customAttributeWarnings)
            {
                ModelState.AddModelError("", error);
            }

            if (ModelState.IsValid)
            {
                address = model.Address.ToEntity(address);
                address.CustomAttributes = customAttributes;
                _addressService.UpdateAddress(address);

                return RedirectToRoute("CustomerAddresses");
            }

            //If we got this far, something failed, redisplay form
            model.Address.PrepareModel(
                address: address,
                excludeProperties: true,
                addressSettings: _addressSettings,
                localizationService: _localizationService,
                stateProvinceService: _stateProvinceService,
                addressAttributeService: _addressAttributeService,
                addressAttributeParser: _addressAttributeParser,
                loadCountries: () => _countryService.GetAllCountries(_workContext.WorkingLanguage.Id),
                overrideAttributesXml: customAttributes);
            return View(model);
        }


So here it is not actually calling _addressService.Insert method from customer address address add method.

Address Service -> Add new Address

        /// <summary>
        /// Inserts an address
        /// </summary>
        /// <param name="address">Address</param>
        public virtual void InsertAddress(Address address)
        {
            if (address == null)
                throw new ArgumentNullException("address");
            
            address.CreatedOnUtc = DateTime.UtcNow;

            //some validation
            if (address.CountryId == 0)
                address.CountryId = null;
            if (address.StateProvinceId == 0)
                address.StateProvinceId = null;

            _addressRepository.Insert(address);

            //cache
            _cacheManager.RemoveByPattern(ADDRESSES_PATTERN_KEY);

            //event notification
            _eventPublisher.EntityInserted(address);
        }


So _eventPublisher.EntityInserted(address); is not called when customer adds address the same scenario is happening in admin panel address add function as well. So Ultimately no address add event is fired and my plugin is not able to catch this event.

Can anybody please provide me solution?
How can I actually get EntityInserted Event for Address entity?

Thanks,
Ajay
6 years ago
If you don't want to modify the source to trigger the event where you need, you can hook into AddressAdd post OnActionExecuted method using an ActionFilter.
6 years ago
AdamK wrote:
If you don't want to modify the source to trigger the event where you need, you can hook into AddressAdd post OnActionExecuted method using an ActionFilter.


Thanks Adam K, I will look into this and return with results.

But what you think, There should be event initiated by code as it does for address update. Right?

Thanks
6 years ago
Hi Ajay,

It's not only about addresses. There are a lot of other entities without appropriate events because we use EF navigation properties. Solutions could be the following ones:

1. Do not use navigation properties in EF. But it'll require huge change in source code. We're not ready to do it just for events
2. When some entity is saved we could check all its navigation properties (entire grath) and see what has been changed. Then raise appropriate events
3. Use workarounds like Adam suggested
6 years ago
a.m. wrote:
Hi Ajay,

It's not only about addresses. There are a lot of other entities without appropriate events because we use EF navigation properties. Solutions could be the following ones:

1. Do not use navigation properties in EF. But it'll require huge change in source code. We're not ready to do it just for events
2. When some entity is saved we could check all its navigation properties (entire grath) and see what has been changed. Then raise appropriate events
3. Use workarounds like Adam suggested


Thank you very much Andrei & Adam.

I followed Adam's method and able catch event at

  public override void OnActionExecuted(ActionExecutedContext filterContext)
        {
          //here I need to get newly added address id.
          

        }


But I am not able to get address id of new added address id.

Can you please help me how can I get here that lastly added address id?

Thank you in Advance!
6 years ago
ajaysolanki wrote:


Thank you very much Andrei & Adam.

I followed Adam's method and able catch event at

  public override void OnActionExecuted(ActionExecutedContext filterContext)
        {
          //here I need to get newly added address id.
          

        }


But I am not able to get address id of new added address id.

Can you please help me how can I get here that lastly added address id?

Thank you in Advance!


Probably something like:

var newestAddress = EngineContext.Current.Resolve<IWorkContext>().CurrentCustomer.Addresses.OrderByDescending(a => a.CreatedOnUtc).FirstOrDefault();


Also, make sure you're catching POSTs only.
6 years ago
Thank you Adam!
That's the perfect solution!
+1 from me for this.

Thanks!
6 years ago
a.m. wrote:
Hi Ajay,

It's not only about addresses. There are a lot of other entities without appropriate events because we use EF navigation properties. Solutions could be the following ones:

1. Do not use navigation properties in EF. But it'll require huge change in source code. We're not ready to do it just for events
2. When some entity is saved we could check all its navigation properties (entire grath) and see what has been changed. Then raise appropriate events
3. Use workarounds like Adam suggested


Hi Andrei,

#1 is the best option in my opinion - I appreciate the extra control and consistency.

Overall, I think things like the product updated event when a review is submitted can be confusing. Customers probably should not be able to trigger product updates.  I haven't looked into #2 and this seems clunky, but for the short term an overload similar to this might be helpful:


//used if navigation property is updated
public virtual void UpdateCustomer<T>(Customer customer, T updatedNavEntity) where T : BaseEntity
        {
            if (customer == null)
                throw new ArgumentNullException("customer");

            _customerRepository.Update(customer);

            //event notification
            _eventPublisher.EntityUpdated(updatedNavEntity);
        }

6 years ago
Hi Ajay,

You can also use database triggers on Address table and move your logic there. It might be easier to implement. Plugin can install a trigger, it is just plain SQL.
This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.