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

Posted: October 07, 2017 at 8:35 AM Quote #194323
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
This post/answer is useful
0
This post/answer is not useful

Please login or register
to vote for this post.

(click on this box to dismiss)
Thank You..!
Ajay H. Solanki ( NopCommerce Developer, India )
Email Me : [email protected]
Posted: October 07, 2017 at 1:34 PM Quote #194331
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.
This post/answer is useful
2
This post/answer is not useful

Please login or register
to vote for this post.

(click on this box to dismiss)
- Adam K
www.aperturelabs.biz
Posted: October 07, 2017 at 2:30 PM Quote #194333
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
This post/answer is useful
0
This post/answer is not useful

Please login or register
to vote for this post.

(click on this box to dismiss)
Thank You..!
Ajay H. Solanki ( NopCommerce Developer, India )
Email Me : [email protected]
Posted: October 08, 2017 at 2:06 AM Quote #194336
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
This post/answer is useful
2
This post/answer is not useful

Please login or register
to vote for this post.

(click on this box to dismiss)
Join us at nopCommerce Days. Nov 16-17, New York. http://days17.nopcommerce.com/

Interested in the dedicated Premium support services provided by core developers? Please visit http://www.nopcommerce.com/supportservices.aspx

Regards,
Andrei Mazulnitsyn
Posted: October 08, 2017 at 5:16 AM Quote #194342
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!
This post/answer is useful
0
This post/answer is not useful

Please login or register
to vote for this post.

(click on this box to dismiss)
Thank You..!
Ajay H. Solanki ( NopCommerce Developer, India )
Email Me : [email protected]
Posted: October 10, 2017 at 2:43 PM Quote #194472
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.
This post/answer is useful
1
This post/answer is not useful

Please login or register
to vote for this post.

(click on this box to dismiss)
- Adam K
www.aperturelabs.biz
Posted: October 10, 2017 at 3:02 PM Quote #194473
Thank you Adam!
That's the perfect solution!
+1 from me for this.

Thanks!
This post/answer is useful
0
This post/answer is not useful

Please login or register
to vote for this post.

(click on this box to dismiss)
Thank You..!
Ajay H. Solanki ( NopCommerce Developer, India )
Email Me : [email protected]
Posted: October 12, 2017 at 10:47 PM Quote #194595
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);
        }

This post/answer is useful
0
This post/answer is not useful

Please login or register
to vote for this post.

(click on this box to dismiss)
- Adam K
www.aperturelabs.biz
Posted: October 13, 2017 at 2:41 AM Quote #194603
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 post/answer is useful
0
This post/answer is not useful

Please login or register
to vote for this post.

(click on this box to dismiss)
https://github.com/ilich
Premium support services
  • Dedicated premium support services provided by core developers are intended for persons who run mission critical websites, work on projects with tight deadlines, or want to get dedicated support.
Professional services
  • Want to open a new store? Want to take your store to the next level? Need a custom extension? We can customize nopCommerce to fit your store perfectly. Request a quote to get started.
eCommerce CONFERENCE 2017
Learn more