We have customers that often have varying requests. Currently, we do things such as if customer_code = ? then ...
and do something different just for that customer.
I want to employ something similar to how Wordpress/Magento works where you can subscribe to events, receive the data, process it how you want and then return control back to the application.
I want to store this customer specific functionality inside of an assembly specific to that customer. Note, I don't want to completely override the method, just points within the method to do things (in most instances anyway).
Examples are: - Before order placed - After order placed - Before allocation - After allocation - After status change
-
1Take a look on the Template Method patternBart van Ingen Schenau– Bart van Ingen Schenau2017年01月20日 10:02:21 +00:00Commented Jan 20, 2017 at 10:02
2 Answers 2
There are many ways of doing this in C#. Here are a couple of them:
C# events are extensively used by Win Forms and Web Forms. These feel very much like interrupt handlers, but in reality are just delegates (function pointers).
// Declaration
public event EventHandler BeforeOrderPlaced;
public event EventHandler AfterOrderPlaced;
// Raising events
public void ProcessOrder()
{
if (BeforeOrderPlaced != null)
BeforeOrderPlaced(this, new EventArgs());
PlaceOrder();
if (AfterOrderPlaced != null)
AfterOrderPlaced(this, new EventArgs());
}
// Subscribe to events
BeforeOrderPlaced += MyHandler;
private void MyHandler(object sender, EventArgs e)
{
// do something before the order is placed
}
C# virtual methods is the object-oriented approach.
public class OrderSystem
{
// Defaults - do nothing
public virtual void BeforeOrderPlaced() {}
public virtual void AfterOrderPlaced() {}
public void ProcessOrder()
{
BeforeOrderPlaced();
PlaceOrder();
AfterOrderPlaced();
}
}
// Override the defaults to insert code before/after placing the order.
public class MyOrderSystem : OrderSystem
{
public override void BeforeOrderPlaced()
{
// do something before the order is placed
}
}
What you are talking about is known as multitenancy.
Here's one way to do it:
Implement basic functionality in non-sealed classes with key methods defined as virtual. Let's imagine for this example that all of your business logic is in a class called
BaseBusinessLogic
and there is a method calledDoSomething()
.For customer-specific functionality, write classes that inherit from the basic classes, overriding virtual methods as needed. If Kodak is one of your customers, maybe the class might be defined as
class KodakBusinessLogic : BaseBusinessLogic
.Create a database table which maps tenants (perhaps identified by
customer_code
) onto DLLs and classes. There would be 3 columns:customer_code - Whatever datatype you use
custom_assembly - The name of the DLL, e.g.
KodakBusinessLibrary.dll
class_name - The name of the derived class that contains the customer-specific logic (e.g.
KodakBusinessLogic
)When you need to run functionality that may be customer-specific, call a stored procedure to obtain custom_assembly and class_name for that customer_code.
Then you can instantiate the derived class like this:
var asm = Assembly.LoadFile(custom_assembly);
var type = asm.GetType(class_name);
var o = Activator.CreateInstance(type) as BaseBusinessLogic;
if (o == null) throw new Exception("Couldn't load the custom logic.");
And run the customer-specific logic like this:
o.DoSomething();
Even though you have a reference to a BaseBusinessLogic, in reality that pointer is pointing at a KodakBusinessLogic, and calling DoSomething will execute the overridden method that contains the logic for Kodak.
Now, if you don't want to override the method itself, you can define things like this:
class BaseBusinessLogic
{
public void DoSomething() //notice it's not virtual
{
this.OnBeforeProcessing();
//Execute basic logic here
this.OnAfterProcessing();
}
abstract void OnBeforeProcessing();
abstract void OnAfterProcessing();
}
Then in your derived class, you just need to override OnBeforeProcessing and OnAfterProcessing.