Articles‎ > ‎

[ Article ] How to create an extensible modular application in .Net

Introduction

This article will help you in creating a plug-in model application in .net which can be extended without modifying the main application. All new features are added to the main application as a plug-in. In this post we will see how to discover and use extensions with no configuration required. Also we will see how to encapsulate code and avoid fragile hard dependencies. We will create new features as class libraries and will drop it into a predefined folder and the main application will pick it from that location and will use it.

Background

When we are creating an application which is sure to evolve with time, we have to design it in such a way that enhancements are simple to achieve. If we fail to do so then we will end up rewriting the entire application from scratch when modifications or new requirement comes. We have to design our system in such a way that features are independent modules and each module is plugged into the main application. When a change comes, we only have to modify the module which has change or if it is a new feature then we should be able to create a module for it and plugged into the main application. When we model our system as modules, we have the luxury of reusing the module in another system also we are designing the system in such a way that it is open for extension, but closed for modification. In this fashion we are following open-closed principle. This kind of design is called modular design. Modular design is an approach that subdivides a system into smaller parts that can be independently created and then used in different systems to drive multiple functionalities.

Here in this article we will examine a simple calculator example to see how this modular plug-in model design works. Here in this application, we will have a simple calculator which has 4 main features which are Add, Subtract, Divide and Multiply. We will divide each feature into separate module and will drop this module into a predefined folder and the main application will pick it up and will display the feature in its main window. Users can then use the calculator as if it is a single application.

A downside to modularity is that modular systems are not optimized for performance. This is usually due to the cost of putting up interfaces between modules. If you are designing a performance critical application then avoid using this approach.

System Requirements

  1. For this application we need PRISM 4.1. Download PRISM 4.1 from here.

Download file will be an exe file. Run the exe file which will ask you for a folder to where it need to copy PRISM files to. Point a folder of your choice. Now when the installation is complete, you can find PRISM files in the folder which you have pointed to. Find the Bin folder inside. Inside Bin folder you can find Desktop folder. This folder will have PRISM dll’s which we need in our project. Remember we are going to create a desktop application. This is why we choose the desktop folder inside the bin folder.

NOTE:- For creating a modular application, PRISM is not a requirement. MEF which comes with .Net 4.0 is good enough. But having PRISM in application will give more flexibility. We will be using MEF in PRISM to create this application. 

Table of contents


Using the code

To begin with a composite pluggable modular application lets create a sample Calculator. This calculator will have four main features which are Add, Subtract, Divide and Multiply. We will create these features as separate class library (dll) or so called modules. We will then deploy these modules to a folder which the main application is configured to pick modules from. We will use WPF application as the main application to show the calculator feature. We are using WPF as just a main application. We are not going to follow any WPF standards to create this application. We are concentrating on the business part of this application not the presentation. Below shown image is what we are expecting as a final output of this example.

Calculator example for the plug-in model extensible application



Build extensible application
To start with, let’s create a new project. We are going to create the calculator application UI using WPF.
So select WPF application from new project wizard. Open visual studio instance and go to
File -> New -> Project and select WPF application.
                                           How to build extensible application in .net

                                              How to create extensible application in .net


Now create a new class library project to the solution and name it as Framework. This will be a common dll which we will refer to most of the project that we will use.  Common stuffs go here. Now to the framework project, add one interface and name it as “ICalculatorFeature.cs”

                                           How to build extensible application in .net
                                               How to create extensible application in .net

In our calculator example, we are going to implement four basic features of a calculator, Addition, Subtraction, Division and Multiplication. We will create module or component for each feature. These features will then act as pluggable components or modules. i.e. when this module is available, then only the feature is available in the calculator.

For our example, the interface ICalculatorFeature should contain one property and a method as given below.

string Symbol { get; }
long PerformCalculation(int valueOne, int valueTwo);

Those entire pluggable components which we use in this example should be inherited from this interface. When they implement the interface property and methods, the property “Symbol” will give the unique symbol associated with the module and method “PerformCalculation” will perform the calculation associated with that module.

Now we need four class library projects. Create a solution folder with the name “CalculatorFeatures” and add these projects into it and name it as Add, Divide, Multiply, Subtract respectively.
Add one class each to all these projects.
Add “ActionAdd.cs” to Add project
Add “ActionDivide.cs” to Divide project
Add “ActionMultiply.cs” to Multiply project

Add “ActionSubtract.cs” to Subtract project

                                                    How to build extensible application in .net
                                                     How to create extensible application in .net

The classes must inherit from “ICalculatorFeature”. Add a reference to Framework project and Inherit the class from “ICalculatorFeature” and implement the property and methods.

public class ActionAdd : ICalculatorFeature
{
        #region ICalculatorFeature Members
        /// <inheritdoc />
        public string Symbol
        {
//This property will return the symbol associated with each module or dll. This will be the text to display in the feature button for calculator.
            get { return "+"; }
        }
        /// <inheritdoc />
        public long PerformCalculation(int valueOne, int valueTwo)
        {
          //this method will do the calculation. The calculation logic for each module goes here. Here as the           //module is for addition, addition logic goes here
            return valueOne + valueTwo;
        }
        #endregion
}

Similar way you have to implement other modules.

Project Configurations

Now we need some configuration for the projects. We are going to change the output folder for the projects. First create a folder called “Executables” along with the main calculator project folder

           How to build extensible application in .net
              How to create extensible application in .net


Right click on the project and select properties. Go to Build tab. Now change output path. For the modules Add, Divide, Multiply and subtract, point it to the “Features” folder inside Executables folder. This is important.
                                                                                How to build extensible application in .net

                                        How to create extensible application in .net

And for all other projects, make it point to executables folder
                                                                               How to build extensible application in .net

                                       How to create extensible application in .net


Now if you build the solution, the output dll for Add, Divide, Multiply and Subtract should go to Features folder inside executables folder and for projects Framework and Calculator, it should go to executables folder. In the main calculator application add one app.config file and in the appSettings section add the below settings

<appSettings>
    <add key="ModulesFolder" value="Features" />
</appSettings>

This setting is needed to find out the folder where the module dll’s will be. Remember we have set the output path of the projects to “Executables\Features” folder.
Now the basic setups for the example projects are over. Now let’s make it pluggable with the help of mef.
MEF
For making a module pluggable, we have to export it via mef. For this add a reference to System.ComponentModel.Composition
                                                                               How to build extensible application in .net
                                          How to create extensible application in .net


And add Export attribute to the classes that we created as shown below.

using System.ComponentModel.Composition;
using Framework;
namespace Substraction
{
    [Export(typeof (ICalculatorFeature))]
    public class ActionSubtract : ICalculatorFeature
    {
        #region ICalculatorFeature Members
        /// <inheritdoc />
        public string Symbol
        {
            get { return "-"; }
        }
        /// <inheritdoc />
        public long PerformCalculation(int valueOne, int valueTwo)
        {
            return valueOne - valueTwo;
        }
        #endregion
    }
}

Do exports for all the pluggable modules or features (Add, Divide, Multiply, Subtract). Once you are done with this, the modules are pluggable. We can now detect these modules from our main application. Let’s see how this is done.
In the main calculator project we have to configure MEF Container to export all those class objects which have Export attribute defined. For this we have to do some jugglery like

var catalog = new DirectoryCatalog(@".\");
var container = new CompositionContainer(catalog);
container.Composeparts(this);

and then expose the container using our own logic so that the class which require the object can get it either via querying the container or by defining Import attribute. We can simplify this jugglery by using Microsoft’s framework which is called Prism. Prism is a superset of features which include MEF. Now prism will handle all the jugglery of creating container and make it available when in need. Let’s see how this is done.
For this we need prism, Prism is available as a download and you can get it from http://www.microsoft.com/en-us/download/details.aspx?id=28950. Download it and run it as administrator and it will unzip the library into a folder of your choice. Now in that folder you can see a bin folder inside that you can see a desktop folder within that folder you can find all prism related dll’s. Now add a reference to these dll’s to the main calculator project.

Microsoft.Practices.Prism
Microsoft.Practices.Prism.MefExtension
Microsoft.Practices.ServiceLocation

                                                                                 How to build extensible application in .net
                                             How to create extensible application in .net

Now we need a class file. Add new class file and name it as “Bootstrapper.cs”. This will be the entry point of the application. We will see later how to make this file as our entry point file.

           How to build extensible application in .net

               How to create extensible application in .net

Now the bootstrapper class has to inherit “MefBootstrapper”. MefBootstrapper comes along with Prism and is in the namespace Microsoft.Practices.Prism.MefExtension. Now we have to override some methods from  MefBootstrapper. These methods are as below.

protected override DependencyObject CreateShell()
protected override void InitializeShell()
protected override void ConfigureAggregateCatalog()
protected override void ConfigureContainer()

Now your bootstrapper should look like this

public class Bootstrapper : MefBootstrapper
    {
        protected override DependencyObject CreateShell()
        {
            return new MainWindow();
        }
        protected override void InitializeShell()
        {
            base.InitializeShell();
            Application.Current.MainWindow = (Window) Shell;
            Application.Current.MainWindow.Show();
        }
        protected override void ConfigureAggregateCatalog()
        {
            base.ConfigureAggregateCatalog();
            string modulesFolder = ConfigurationManager.AppSettings["ModulesFolder"];
            AggregateCatalog.Catalogs.Add(
                new DirectoryCatalog(
                    Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), modulesFolder),
                    "*.dll"));
            AggregateCatalog.Catalogs.Add(new AssemblyCatalog(typeof (MainWindowModel).Assembly));
        }
        protected override void ConfigureContainer()
        {
            base.ConfigureContainer();
            Container.ComposeExportedValue(AggregateCatalog);
        }
    }

Here CreateShell() method will return the main window to be shown when application starts up. InitializeShell() will do all initialization process for your main window. ConfigureAggregateCatalog() will give us provision to configure the catalogue. This catalogue will then be used to configure container. Catalogue will tell from where to fetch objects of the classes which we have exported. Here we used two catalogues one is Module catalogue, to discover modules form the modules folder which is “Features” folder inside Executables folder. And Assembly catalogue to find the assembly which has the class “MainWindowModel”. ConfigureContainer() will help to configure container with the help of catalogue which we configured and will fetch all objects of the class which have export attribute on it and will the container will have those object. Later we can query container to give the object of our choice. Now let’s see how to make this file as the entry file. In the App.xaml file, there is an entry called StartupUri we have to remove it completely.

                                                                                    How to build extensible application in .net
        How to create extensible application in .net

Now in the codebehind file for App.xaml, we have to tell it to run our bootstrapper.
   
/// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App : Application
    {
        protected override void OnStartup(StartupEventArgs e)
        {
            base.OnStartup(e);
            var bootstrapper = new Bootstrapper();
            bootstrapper.Run();
        }
    }

Now if you run the application, you can see your main window. Now let’s do some wpf coding to display the Symbols from the dll or component in the main window. Let’s do it in the MVVM way. MVVM is a very famous design pattern for creating wpf application. Here let’s not go into details of MVVM and lets simplify it by view and model. For this let’s create a folder in the calculator project and name it as “Model”. Now let’s create a class file in that folder and name it as “MainWindowModel.cs”. Content for the file should be as below.

namespace CalculatorWithMEF.Model
{
    public struct ButtonProperty
    {
        public String Symbol { get; set; }
        public ICommand Command { get; set; }
    }
    [Export]
    public class MainWindowModel : Control
    {
        public static DependencyProperty CalculatorTextProperty =
            DependencyProperty.Register("CalculatorText", typeof (int),
                                        typeof (MainWindowModel), new FrameworkPropertyMetadata(0));
        private ObservableCollection<ButtonProperty> _buttons;
        public String PreviousButtonValue { get; set; }
        public int BackupValue { get; set; }
        // Dependency Property
        public int CalculatorText
        {
            get { return (int) GetValue(CalculatorTextProperty); }
            set { SetValue(CalculatorTextProperty, value); }
        }
        public ObservableCollection<ButtonProperty> ButtonsViewModelColl
        {
            get { return _buttons ?? (_buttons = new ObservableCollection<ButtonProperty>()); }
            set { _buttons = value; }
        }
        [ImportMany(typeof (ICalculatorFeature))]
        public IEnumerable<ICalculatorFeature> CalculatorFeature { get; set; }
        public ICommand ButtonClick
        {
            get
            {
                /*DelegateCommand use it as an alternative(add reference to Microsoft.Practices.Prism)*/
                return new RelayCommand(PerformClickAction);
            }
        }
        public bool CanClickButtonandPerformAction(object parameter)
        {
            // Add any logic here to enable / disable the button);)
            return true;
        }
        public void PerformClickAction(object parameter)
        {
            switch ((string) parameter)
            {
                case "On/Off":
                    if (0 == ButtonsViewModelColl.Count)
                    {
                        foreach (ICalculatorFeature feature in CalculatorFeature)
                        {
                            ButtonsViewModelColl.Add(new ButtonProperty {Symbol = feature.Symbol, Command = ButtonClick});
                        }
                        ButtonsViewModelColl.Add(new ButtonProperty {Symbol = "=", Command = ButtonClick});
                    }
                    else
                    {
                        ButtonsViewModelColl.Clear();
                    }
                    break;
                case "=":
                    ICalculatorFeature calculatorFeature =
                        CalculatorFeature.FirstOrDefault(val => (string) parameter == val.Symbol);
                    if ((null == calculatorFeature) && string.Empty != PreviousButtonValue)
                    {
                        calculatorFeature = CalculatorFeature.FirstOrDefault(val => PreviousButtonValue == val.Symbol);
                    }
                    if (null != calculatorFeature)
                    {
                        CalculatorText = (int) calculatorFeature.PerformCalculation(BackupValue, CalculatorText);
                    }
                    BackupValue = CalculatorText;
                    break;
                default:
                    BackupValue = CalculatorText;
                    PreviousButtonValue = (String) parameter;
                    break;
            }
        }
    }
}


Note that here we are Exporting the class itself
[Export]
public class MainWindowModel : Control
    {}

Now pay attention to the line
[ImportMany(typeof (ICalculatorFeature))]
        public IEnumerable<ICalculatorFeature> CalculatorFeature { get; set; }
Here we say ImportMany(typeof (ICalculatorFeature)) now mef will load all objects of classes which is derived from ICalculatorFeature and will assign to the property CalculatorFeature. Now we are going to make this file as the code behind file for the MainWindow. Let’s see how this is done, go to the code behind file of MainWindow

/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
  {
     public MainWindow()
     {
       InitializeComponent();
       this.DataContext = ServiceLocator.Current.GetInstance(typeof (MainWindowModel));
     }
  }

Here we are binding the data context of the xaml file. When we say ServiceLocator.Current.GetInstance, mef will return you an instance of the MainWindowModel now when mef try to resolve all dependency in the MainWindowModel class. Here when mef tries to resolve the dependence, it will fill the enumerator with exported values and the property will now contain 4 objects. Now this is the content of xaml file within the window tag
<Window.Resources>     
<DataTemplate x:Key="DataEnumTemplate">        
<Button Height="37" Width="75" Command="{Binding Command}"             CommandParameter="{BindingPath=Symbol}" Content="{Binding Path=Symbol}">
</Button>     
</DataTemplate>
</Window.Resources>
<Controls:Grid>     
<Controls:TextBox Height="41" Margin="127,37,0,0" Width="212"
HorizontalAlignment="Left"Name="textBox1" Text="{Binding Mode=TwoWay, 
Path=CalculatorText}" VerticalAlignment="Top" />     
<ListView Margin="59,84,106,12" Width="338" BorderThickness="0" 
HorizontalAlignment="Center"ItemsSource="{Binding Path=ButtonsViewModelColl}" ItemTemplate="{StaticResource DataEnumTemplate}">     
</ListView>     
<Button Height="37" Margin="416,98,0,0" Width="75" Command="{Binding 
ButtonClick}"CommandParameter="On/Off" Content="On/Off" HorizontalAlignment="Left" 
Name="button1"VerticalAlignment="Top" />
</Controls:Grid>

This will now bind to the miainwindowmodel. Now run the application and you can see the application running. Now try to unload one project say Addition module and clean the output folder and try to run the application and you can see the calculator does not have the addition button!


Comments