Add handlers
Describes how to add handlers.
Handlers serve as an extension point because you can add new handlers to any handler chain. This allows you to add new or change existing functionality. For example, to the GetCartHandler
chain you could add a handler that selectively displays taxes based on a custom property. In order to add a new handler to an existing handler chain, you must correctly implement and configure the new handler.
For a handler to be used within a handler chain, it must do the following:
- Implement the
IHandler\<TIn, TOut>
interface - Be decorated with the
DependencyName
attribute - Return a result from the
Execute
method - Specify an order via the
Order
property
The IHandler\<TIn, TOut>
interface requires the handler to implement the Execute
method, specify handler order using the Order
property, and associate the handler with a handler chain. The Execute
method is the sole entry point into a handler. This method also contains all of the business logic used by the handler to complete it's work. The Order
property is used to specify the ordering of the handler within the handler chain.
Handlers are ordered in ascending order within a handler chain using the values specified by each handler in the Order
property. Handler chains start with the handler with the lowest order value. By default, all handler chains have at least one handler with an order value of 500. From there, additional handlers usually increment the order by 100. For example, if a handler chain has three handlers, the order values would be 500, 600, and 700. This ordering scheme allows you to both insert a new handler in between existing handlers or add a new handler to the start or end of a handler chain. To insert a new handler into the second position in the handler chain, the handler can be given an order value between 101 and 199. To add the handler to the start or end of the handler chain, the handler can be given an order less than 500 or greater than 700, respectively.
A handler is associated with a handler chain according to the parameter and result object types (TIn
and TOut
). All handlers with the same object types will be executed within the same handler chain. Below is a code sample explaining the association further.
// These two handlers are part of the same handler chain
// because they both declare the same parameter and result
// object types.
public class GetCart : HandlerBase<GetCartParameter, GetCartResult>
public class GetCartLines : HandlerBase<GetCartParameter, GetCartResult>
// This handler is part of a different handler chain because
// it declares different parameter and result object types
// than the two handlers above.
public class ApplyPromotion : HandlerBase<AddPromotionHandler, AddPromotionResult>
Notice that the handlers above do not directly implement the IHandler\<,>
interface. Instead, they inherit from the HandlerBase
class. This base class directly implements the IHandler\<,>
interface while providing utility methods for returning error results and passing execution to the next handler. As a best practice, all handlers should inherit from the HandlerBase class. The association to a handler chain is complete at this point. However, the handler requires a bit more configuration to function within ISC.
The final configuration involves the dependency locator. All handlers must be decorated with the DependencyName attribute. This allows the handler to be retrieved by name via the dependency locator.
Once you have configured the new handler, you can build the solution and run the application. The next time the handler chain executes, your new handler should be included in the chain. To help explain the handler chain further, the following section walks through adding a new handler to a handler chain.
The following procedure adds a new handler to the GetCartHandler chain. This new handler selectively returns the cart taxes based on the current user's role.
Prerequisite
ISC SDK installed
-
In your Extensions project, create a new handler class.
public class ShowTaxAndShippingForBuyer3 { }
-
Implement the IHandler<TIn, TOut> interface by inheriting from HandlerBase<TIn, TOut>. For the TIn and TOut parameter and result object types, specify GetCartParameter and GetCartResult, respectively. Remember, this is how a handler is associated with a handler chain. It's important to specify the correct types. Also remember that the HandlerBase base class provides utility methods for returning error results. You can see these utility methods in action in later steps.
public class ShowTaxAndShippingForBuyer3 : HandlerBase<GetCartParameter, GetCartResult> { }
-
Decorate the handler with the DependencyName attribute. Use the name of the class for the dependency name.
[DependencyName(nameof(ShowTaxAndShippingForBuyer3))] public class ShowTaxAndShippingForBuyer3 : HandlerBase<GetCartParameter, GetCartResult> { }
-
Specify a value for the Order property. Remember, all handler chains start at an order of 100. Within the handler chain, all handlers provided by Configured Commerce Cloud increment the order by 100.
public override int Order => 3000;
-
Implement the Execute method. Remember, this is the sole entry point to the handler. Additionally, this method must either continue the handler chain or break it. Both methods are explained in the sample. This handler logic requires the Order Management > Cart > Show Tax & Shipping Amount in Cart setting to be set to "Yes". This precondition is only for this specific example. You can add a handler regardless of the value of this setting.
public override GetCartResult Execute(IUnitOfWork unitOfWork, GetCartParameter parameter, GetCartResult result) { if (result.Cart == null) { // This is one of the utility methods for returning an error result // provided by the base class. These error results will be propagated // all the way up to the REST API and returned as a result. // Any following handlers will not execute. return this.CreateErrorServiceResult(result, SubCode.CartServiceCartNotFound, "A cart could not be found."); } result.ShowTaxAndShipping = result.UserRoles != null && result.UserRoles.Split(',').Contains(BuiltInRoles.Buyer3); // Once your handler is finished with it's work, if // you execute the next handler, execution will continue // in the handler chain with control being handed over to // the next handler. return this.NextHandler.Execute(unitOfWork, parameter, result); // Returning the result breaks the handler chain. // Any following handlers will not execute. return result; }
For reference, the following is the completed handler class.
[DependencyName(nameof(ShowTaxAndShippingForBuyer3))] public class ShowTaxAndShippingForBuyer3 : HandlerBase<GetCartParameter, GetCartResult> { public override GetCartResult Execute(IUnitOfWork unitOfWork, GetCartParameter parameter, GetCartResult result) { result.ShowTaxAndShipping = result.UserRoles != null && result.UserRoles.Split(',').Contains(BuiltInRoles.Buyer3); return this.NextHandler.Execute(unitOfWork, parameter, result); } public override int Order => 3000; }
-
Build your solution.
If the rest of your application is set up correctly, the next time you add a product to the cart and navigate to the Cart page, you may or may not see the shipping and taxes (depending on your role).
Remove or replace handlers
In previous versions, developers could derive new view models, derive new .NET controllers from existing functionality and override methods to change the behavior of the platform. Configured Commerce 4.0 is prescriptive when it comes to extending the platform. Module handlers are a primary extension point to the platform where developers can add additional business logic and provide additional data to the objects used for the interface.
To remove or replace a handler, give it the same dependency name as an existing handler. By Inversion of Control within Configured Commerce you can create instance that implements HandlerBase with AddAccountParameter and AddAccountResult and same dependency name, your custom class will override the standard Configured Commerce handler. Creating a custom class that derives from HandlerBase and using the same dependency name will void the need for using the Order property.
Note
You cannot "remove" a handler, but you can create an implementation of it that does nothing.
Updated about 1 month ago