Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / ASP.NET

Create Custom Application Block Using .NET Enterprise Library

5.00/5 (1 vote)
21 Oct 2009CPOL7 min read 38.2K   311  
This white paper is written in a view to help developer to create custom application block using .NET Enterprise Library.

Table of Contents

  1. Introduction
  2. Prerequisites
  3. Namespace .NET Enterprise Library 2.0
  4. Technical Design Specification
  5. Create Custom Application Block
  6. Page_Load
  7. Construct Web.config file
  8. Generate Public Key Token for Class Library DLL
  9. Reference
  10. Conclusion

Introduction

I worked on spring.NET framework and have also extensively worked on .NET enterprise library for quite a long time. Every time I work on these frameworks, I experience intense similarity among both. Over the course of time, Microsoft almost targeted everything in .NET Framework that spring.net framework usually provides to a developer. All I can say is that Microsoft keeps pace with the latest technology and wants to be on par with everything that comes their way. It was spring.NET that has actually given the idea and evolution to the master page and similarly we have custom application block which depicts the picture of dependency injection and aspect oriented programming. Here I am not going to describe the similarity or distinguished feature of spring .NET and .NET enterprise library. My intension here is to help the developer implement and develop custom application block using .NET Enterprise library. There may be various articles available online but I would like to demonstrate my findings and discoveries in this part of the syllabus. As all of us are aware of Dependency injection design pattern, it is no longer an old topic. In short, dependency injection ‘Dependency Injection’ isolates the implementation of an object via constructor of objects and it invokes and creates the object at runtime, thus allowing flexibility to operate in the most easiest and loosely coupled way. It is a form of the Inversion of Control Pattern where a factory object holds the responsibility for object creation, linking and loading which in turn shapes up into run time instantiation of object creation and invocation.

What I am going to provide here is the creation of Custom application block using .NET enterprise library that very well demonstrates dependency injection concept. All classes and namespaces will be configured in the configuration file and thus will be linked and loaded at runtime. This is a kind of demand calls invocation method.

Custom Application Block

The best part about this approach is that we can use the same environment for testing and developing module under one project set. In a broader sense, we can configure the custom application block such that the tester can test the application under the same box while the developer will be fixing or developing under the same box as the tester.

Say for example: I have fileUploaderModule package so what will I do here to make my team's life easy. I will create two packages, one is fileUploaderModule_test and fileUploaderModule_Dev and configure them in the configuration file and will help tester and developer to work on the same work package in parallel mode. So this helps me in saving extra test server for my application. The approach will help me to test and develop under the same box for two roles.

Another usage of having this design in place is to make use of competency based asset in organizations across projects/applications thus boon to boost higher end productivity as this can be consumed as one global functional need at the development level.

Say for example, I have a login mechanism used by 10 applications in the organization. So in such a scenario, I will develop one login custom application block which in turn will be used by 10 across applications such that this block is an independent entity and acts as a service provider.

After considering the above sets of measure, we conclude that we will achieve the following advantages in terms of design aspects.

  • Loosely coupled architecture
  • Reusable across projects as library
  • Easy to test as this is custom design packaged
  • Easy to maintain
  • Easy to deploy as custom packaged is configurable in configuration file

Problem Statement: I have files that need to be uploaded into servers through File transfer system ports. So the challenge here is, the server details must be configurable such that this server may change in future. Moreover this package should be developed in such a way as to be consumed across different applications with easy to plug in feature.

Prerequisites

  • .NET Framework 2.0
  • Enterprise library 2.0

Namespace .NET Enterprise Library 2.0

  • Microsoft.Practices.EnterpriseLibrary.Common.dll
  • Microsoft.Practices.ObjectBuilder.dll

Technical Design Specification

Here I am going to demonstrate default setting configuration block and specific setting configuration block section. This works like default constructor and parameterized constructor. If we do not provide provider name, then it picks default setting, else it picks setting as per provider name. For more visibility and understanding, refer to the sequence diagram and text in Red color.

Fig 1.0 Sequence Diagram: Default Setting

XML
<dbConfiguration defaultDBProvider="DefaultFilePath">
    <dbProviders>
	<add name="DefaultFilePath" 
	type="Project.Practice.DB.DBProvider,Project.Practice.DB" userID="admin"
	password="admin" server="militaryZoneArea" database="xyz"/>	
</dbConfiguration>
DefaultDB.JPG - Click to enlarge image

Fig 2.0 Sequence Diagram: Custom setting

XML
<add name="CustomFilePath" 
	type="Project.Practice.DB.DBProvider,Project.Practice.DB" userID="admin" 
	password="admin" server="militaryZoneArea" database="xyz"/>
</dbProviders>
CustomDB.JPG - Click to enlarge image

Create Custom Application Block

Let’s dig up the code for more understanding. We create DBAssembler which implements IAssembler. This is the end point where we create DbProvider object with configurable DBdata settings details. On complete roundtrip, call DBProvider is created and method UploadFile() is called.

Design Configuration Provider

DBAssembler.cs
C#
public class DBAssembler : IAssembler <IDBProvider, DBData>
{
    public IDBProvider Assemble
		(Microsoft.Practices.ObjectBuilder.IBuilderContext context, 
                	 DBData dbData, IConfigurationSource configurationSource, 
                  	 ConfigurationReflectionCache reflectionCache)
   {
        return new DBProvider(dbData.UserId, dbData.Password, 
				dbData.Server, dbData.Database);
    }
} 
DBData.cs

This class is the container object for all configuration settings:

C#
[Assembler(typeof(DBAssembler))]
    public class DBData : NameTypeConfigurationElement
    {
        private const string userIdProperty = "userID";
        private const string passwordProperty = "password";
        private const string serverProperty = "server";
        private const string databaseProperty = "database";

        public DBData()
            : base("DBData", typeof(IDBProvider))
        {
        }
        public DBData(string name, Type type)
            : base(name, type)
        {
        }

        [ConfigurationProperty(userIdProperty, IsRequired = true)]
        public string UserId
        {
            get { return (string)this[userIdProperty]; }
            set { this[userIdProperty] = value; }
        }

        [ConfigurationProperty(passwordProperty, IsRequired = true)]
        public string Password
        {
            get { return (string)this[passwordProperty]; }
            set { this[passwordProperty] = value; }
        }

        [ConfigurationProperty(serverProperty, IsRequired = true)]
        public string Server
        {
            get { return (string)this[serverProperty]; }
            set { this[serverProperty] = value; }
        }

        [ConfigurationProperty(databaseProperty, IsRequired = false)]
        public string Database
        {
            get { return (string)this[databaseProperty]; }
            set { this[databaseProperty] = value; }
        }
    }
DBDataRetriever.cs

DBretriever is written so that the default configuration section can be linked and loaded as per call.

C#
internal class DBDataRetriever : IConfigurationNameMapper
   {
       public string MapName(string name, IConfigurationSource configurationSource)
       {
           return string.IsNullOrEmpty(name) == false ?
           name : ((DBSettings)configurationSource.GetSection(DBSettings.SectionName)).
       DefaultDBProvider;
       }
   }
DBSettings.cs
C#
public class DBSettings : SerializableConfigurationSection
{
    public const string SectionName = "dbConfiguration";
    private const string DBProvidersProviderProperty = "dbProviders";
    private const string DefaultDBProviderProperty = "defaultDBProvider";
    public DBSettings() { }
    
    [ConfigurationProperty(DBProvidersProviderProperty, IsRequired = true)]
    public NameTypeConfigurationElementCollection <DBData> DBProviders
    {
        get { return (NameTypeConfigurationElementCollection 
			<DBData>)base[DBProvidersProviderProperty]; }
    }
    
    [ConfigurationProperty(DefaultDBProviderProperty, IsRequired = true)]
    public string DefaultDBProvider
    {
        get { return (string)base[DefaultDBProviderProperty]; }
    }
}

Design Factory and Provider Class

DBFactory.cs

The DBFactory is the class that creates DBProviderFactory and later it plays an important role when the default setting is invoked and calls CreateDefault() to carry further responsibility or calls Create() for custom setting call. Refer to the sequence diagram for a detailed understanding.

C#
public static class DBFactory
{
    public static IDBProvider CreateProvider()
    {
        try
        {
            DBProviderFactory factory = 
		new DBProviderFactory(ConfigurationSourceFactory.Create());
            return factory.CreateDefault();
        }
        catch (ConfigurationErrorsException configurationException)
        {
            throw;
        }
    }
     public static IDBProvider CreateProvider( string name )
    {
    try
    {
        DBProviderFactory factory = 
		new DBProviderFactory( ConfigurationSourceFactory.Create() );
        return factory.Create( name );
    }
    catch( ConfigurationErrorsException configurationException )
    {
        throw;
    }
 }
}
DBCustomFactory.cs

This can be best explained and can be understood by viewing the sequence diagram. Basically DBCustomfactory is invoked when the default setting is not called. DBCustomFactory fetches all configuration and sets in DBData and returns DBData to DBassembler.

C#
public class DBCustomFactory : AssemblerBasedCustomFactory <IDBProvider, DBData>
{
    protected override DBData GetConfiguration
	(string name, IConfigurationSource configurationSource)
    {
        DBSettings settings = 
	(DBSettings)configurationSource.GetSection(DBSettings.SectionName);
        return settings.DBProviders.Get(name);
    }
}
DBProvider.cs

DBProvider implements IDBProvider and sets values that are configured in config file.The responsibility to set values in the constructor is of the DBsetting class.

C#
[ConfigurationElementType(typeof(DBData))]
public class DBProvider : IDBProvider
{
    protected string m_UserId = string.Empty;
    protected string m_Password = string.Empty;
    protected string m_Server = string.Empty;
    protected string m_DirectoryPath = string.Empty;
    
    private bool m_Disposed = false;
    
    public DBProvider() { }
    public DBProvider
	(string userId, string password, string server, string directoryPath)
    {
        m_UserId    = userId;
        m_Password  = password;
        m_Server    = server;
        m_DirectoryPath = directoryPath;
    }
    
    #region IFTPProvider Interface
    
    bool IDBProvider.UploadFile(string filePath)
    {
        //The functionality is not given here..
        //Intension here is one can make use of these configurable credential
        //Connect to FTP and transfer file.
        //OpenFTP(m_Server,m_UserId,m_Password,m_DirectoryPath,
        //filePath,domain,port) 
        return true;
    }
 }
DBProviderFactory.cs
C#
public class DBProviderFactory : NameTypeFactoryBase <IDBProvider>
{
    protected DBProviderFactory()
        : base()
    {
    }
    
    public DBProviderFactory(IConfigurationSource configurationSource)
        : base(configurationSource)
    { }
}
IDBProvider.cs

IDBProvider basically stores a house of custom attributes where we declare default and custom setting class. It also exposes a list of methods that are available for client interface.

C#
[ConfigurationNameMapper(typeof(DBDataRetriever))] 
[CustomFactory(typeof(DBCustomFactory))]
public interface IDBProvider : IDisposable
{
    bool UploadFile(string filePath );
}

Page_Load

This is an entry point to invoke runtime object instance, here factory object holds the responsibility for object creation. So DBFactory.CreateProvider() is for default setting and DBFactory.CreateProvider("CustomFilePath")) is for Custom setting.

C#
protected void Page_Load(object sender, EventArgs e)
{
    using (IDBProvider dbProvider = DBFactory.CreateProvider())
    {
        Response.Write(dbProvider.UploadFile("abc"));
    }
    
    using (IDBProvider dbProvider = DBFactory.CreateProvider("CustomFilePath"))
    {
        Response.Write(dbProvider.UploadFile("xyz"));
    }
}

Construct Web.config File

If you look into the configuration setting, we have default configuration "DefaultFilePath". If we do not specify the custom file path, then it takes default setting. Such configuration setting is very helpful when it comes to testing the particular module. Due to security reasons in development server, we don't have access similar to the actual production server; in such a scenario, we can configure our setting based on our local box thus helping us to test the module and simulate the actual environment.

XML
<configSections>
    <section name="dbConfiguration" 
	type="Project.Practice.DB.Configuration.DBSettings,Project.Practice.DB,
    	Version=1.0.0.0, Culture=neutral, 
	PublicKeyToken=e6353c372c56789" allowDefinition="Everywhere" 
    allowExeDefinition="MachineToApplication" restartOnExternalChanges="true"/>
</configSections>
<!-- DB Configuration -->
<dbConfiguration defaultDBProvider="DefaultFilePath">
    <dbProviders>
	<add name="DefaultFilePath" 
	type="Project.Practice.DB.DBProvider,Project.Practice.DB" userID="admin" 
	password="admin" server="militaryZoneArea" database="xyz"/>
	<add name="CustomFilePath" 
	type="Project.Practice.DB.DBProvider,Project.Practice.DB" userID="admin" 
	password="admin" server="militaryZoneArea" database="xyz"/>
    </dbProviders>
</dbConfiguration>

Generate Public Key Token for Class Library DLL

One can create a public key for the above configuration using the below given step. Go to Visual Studio command prompt and run the below command:

D:\SampleCustomBlock\bin\Debug>sn -T Project.Practice.DB.dll

Microsoft (R) .NET Framework Strong Name Utility Version 2.0.50727.42 Copyright (c) Microsoft Corporation. All rights reserved.

Public key token is exxx78fd9i34545. Make sure you have created a strong key for the given DLL. If a public key is not required, then one can specify public key=null.

References

Conclusion

If you find a problem running the demo code, please write a note to me. I will try my best to help you out. Any suggestions or updates in this article are most welcome.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)