This post was inspired by this question: http://world.episerver.com/forum/developer-forum/Episerver-Commerce/Thread-Container/2016/9/commerce-manager-contacts-events/
and is an excerpt from my book: https://leanpub.com/proepiservercommerce
You might notice the lacking of events in some parts of the system. We have events for catalog system, for order system, for prices and inventories changes, but that’s not enough. You might want to have events – or at least – the ability to know when something happens. For example, when a customer contact is changed, or edited, or deleted, it would be very nice to do some extra actions.
Sending emails, updating external systems, etc.
Such events are not available out-of-box, so we have to implement our own. How? We don’t have ICustomerContactService (or something similiar) interface where we can write our implementation to replace the default service (and even if there is, it would be a big task to do so). So there’s no “ordinary”, framework-way to do that. However, CustomerContact is built on Business Foundation system, and BF, at its core, is all above extensible and pluggable. We don’t have ICustomerContactService interface, but we have IPlugin
who can do the same, and even more. As we learned in previous chapter, CustomerContact is just another EntityObject and all operations are still done via BusinessManager.Execute(Request) – even we have some nice wrapper methods to make working with it easier. And when Execute(Request) is called, it also runs all registered IPlugin modules.
That’s it, we can simply write an IPlugin implementation to inject our code when some changes are done to a customer contact.
public class CustomerContactHandler : IPlugin { public void Execute(BusinessContext context) { var request = context.Request; if (request.Target.MetaClassName != "Contact") { return; } if (request.Method == RequestMethod.Delete) { //Do something } } }
IPlugin has only one method Execute(BusinessContext) – which we implement here. We care about context.Request: if the target object being “handled” is of actual CustomerContact type – otherwise we just skip it to avoid any performance overload, because our module will be called for every Business Object of any types. Here’s we are “listening” to Delete action, but you’re free to
listen to other actions, here’s the full list:
public const string InitializeEntity = "InitializeEntity"; public const string Create = "Create"; public const string Update = "Update"; public const string Delete = "Delete"; public const string Load = "Load"; public const string List = "List"; public const string Import = "Import"; public const string InitializeMappingDocument = "InitializeMappingDocument";
Normally, the CRUD operations are the ones we care about. Now build your project contains the class above and drop the assembly into bin folder of Commerce Manager, then try to delete a contact.
Wait a minute. Nothing happens.
Well, yes, because we haven’t registered it. BusinessManager only cares about the plugins registered in Configs\baf.data.manager.config. Let’s go there and add this simple line to <businessManager><plugins> section:
<add method="*" metaClass="Contact" eventStage="PostMainOperation" type="EPiServer.Commerce.Sample.CustomerContactEvent, EPiServer.Commerce.Sample" />
The attributes speak for themselves – here we are listening to all methods for Contact meta class. Of course, you can change the method to listen to, “Create” only, for example. You can also change to listen to all meta classes, by changing “Contact” to “*”, or listen to another Business Foundation MetaClass.
[quads id=2]
evenStage is an interesting attribute – here we define when we want our plugin executed. There are 4 possible values:
PreMainOperation = 1 PreMainOperationInsideTranasaction = 2, PostMainOperationInsideTranasaction = 8, PostMainOperation = 16,
As the names might suggest, the stages are before the main action, before or after the main action, but within same transaction (if your module does some data modification, it should be here), or after the main action. In our example, we just want to have some notifications when a contact is deleted, so we register our plugin to be PostMainOperation.
There’s a caveat here, we don’t really know the result of the main operation. The only thing we know is there was no error (no exception thrown). So we just assume that it completes successfully.
The final attribute is only the full name of our little type, with the assembly name.
And now, try deleting a contact again. And this time, magic should happen!
Does this work with Commerce 14?
Yes it should still work. Untested by me but in theory, it should
Hello Quan,
Do you know how to register IPlugin in the new Commerce 14?
I believe baf.data.manager.config files are not working anymore.
I will response on my own comment. Maybe someone will find it useful 🙂
I dug into the Optimizely code and I have found the solution.
services.Configure(options =>
{
options.Plugins.Add(new RequestPlugin()
{
MetaClass = “Contact”,
Method = “Update”,
EventStage = EventPipeLineStage.PreMainOperation,
TypeName =
“Mediachase.Commerce.Customization.Plugins.CustomPageNormalizationPlugin, Mediachase.Commerce”
});
});