Saturday, March 17, 2012

Installer - ASP.NET MVC - Pluggable application modules/components/area/features


This article is in continuation to ASP.NET MVC - Pluggable application modules/components/area/features. Article explains way of creating Installer to install pluggable modules on web server. At present project has all modules, and solution structure looks like below:


Let's consider following three editions of this product are needed. Each edition provides set of modules.  Main application (ProductDemo) is base of product and hence is mandatory in all editions.


Product Edition definition:
Edition           :           Modules
Basic              :           Marketing, Sales
Professional :           Marketing, Sales, Inventory, Billing
Enterprise     :           Marketing, Sales, Inventory, Billing, Warehouse


Creating ‘Basic’ edition Web setup:
Add Web Setup project to solution. Name this project as ‘WebSetup Basic’.

Solution looks like below with just newly created ‘WebSetup Basic’ project. Select File System editor of Web Setup project to get screen like below:

Here we would put Main application and required modules to package them in installer. Binary files needs to go in bin folder and rest content needs to follow same folder as project folder structure. This means all Areas needs to go under ‘Areas’ folder in target machine. 

 



Select ‘Web Application Folder’ node from File System editor (Left most section) and from context menu select AddàWeb Folder. Name this newly created web folder as ‘Areas’.

Under this Areas folder, create two more Web folders with name ‘Marketing’ and ‘Sales’ (Name needs to match the folder name under Areas in application folder structure).


Now add Project output to respected folder. All binaries to add at root level folder (Web Application Folder). Right click on Web Application folder and select ‘Project Output’ option to open below screen:




Select ProductDemo (Main Application) from Project dropdown. Highlight ‘Primary output’ and ‘Content Files’ to add them in setup. Click add button to add.

Now add ‘Primary output’ from Marketing and Sales module to Web Application folder.

Add ‘Content files’ from marketing and Sales modules to its respected Areas folder.



Now setup project must be looking like below:

Compile project and setup is ready. Go ahead and install on target machine.








Creating Professional and Enterprise edition Web setup: 
Once step to create Basic edition (with limited modules) is understood, I think I do not need to explain how to create more such editions. Create another setup project, name it as per your requirement and include modules needed for that edition.

 
What more can be done:
·   Separate setup for each module can be created so that each module can be easily distributed and installed separately.
·   Single master installer can be created which can package all modules and allow options to select required modules while installing project. This like how Visual Studio installer asks to select various components.


Monday, March 5, 2012

Entity Framework - INSERT operation with many to many relations


Consider following example (Figure 1) of many-to-many relationship between Category and Product tables. ProductCategory is a mapping table to maintain many to many relations between tables. This table does not have any payload columns (table has only two foreign keys).


When these tables are added in EDMX, entity framework does not create entity type for ProductCategories. Category tables maintains list of related products and product table maintains list of related categories, which allows handling many to many relations between both tables. See figure 2.


At moment, both Category and Product table has data shown in below figure 3.


Now consider example of adding new product (Product 5) which is associated with Category 1, Category 3 and Category 5. So let’s see how new product can be added using entity framework and associate it with existing categories.



    //Step1 - Creating new product

    var product = new Product();

    product.Name = "Product 5";



    //Step2 - Adding category object with primary key values for existing categories,

    //to associate them with new product

    product.Categories.Add(new Category() { CategoryId = 1 });

    product.Categories.Add(new Category() { CategoryId = 3 });

    product.Categories.Add(new Category() { CategoryId = 5 });





    using (var context = new ProductsEntities())

    {               

        //Step3 - Add new product to context

        context.Products.AddObject(product);



        //Step4 - change EntityState of each category to unchanged

        foreach (var category in product.Categories)

        {

            context.ObjectStateManager

                .ChangeObjectState(category, EntityState.Unchanged);

        }



        //Step5 - Save changes to data store

        context.SaveChanges();

    }





How this works:

·        Step1 is a normal step which creates new Product object and sets its all properties.

·        Step2 create list of associated Categories for this product. Here aim is not to create brand new categories but associate existing categories with new product. Instances of category class are added to product.Categories with CategoryId (Primary key) values which forms foreign key relation in ProductCategory table.

·        Step3 is again a normal step to add new product in entity context to mark this as added object. Remember when AddObject() method is called, it changes a state of entity (product) and all objects in its object tree to Added. After this step if we directly run step5, it will try to INSERT three brand new categories with key 1, 3 and 5 in Category table and will throw primary key violation error since those keys already existing in Category table. If such categories does not existing in Category table, it will create new Categories first and then create new product with categories associated with it.

·        Step4 – At this point all categories in product.Categoires have EntityState added, which we do not want. We just want to associate existing categories with product. We are not making any change to such categories. So this step explicitly sets their EntityState to unchanged.

·        Step5 – It saves new product and its foreign keys in ProductCategogy table

Wednesday, February 29, 2012

ASP.NET MVC - Pluggable application modules/components/area/features

This article discuss about how to develop ASP.NET MVC application which has loosely coupled modules (or features) which can be developed separately without any directly dependency and those modules can be plugged-in to main application without any extra bit of code.

Business Value:
This is specifically useful for product development. Using this approach, each module can be developed separately and deployed/shipped with product separately. This helps in building different version of product like: Basic, professional, premium and enterprise. This loosely coupled feature approach enables to either build separate installer with required features as per different versions or master setup which can install only defined features as per license key used during installation.




Building application:
Below step by step process demonstrates how to build application with pluggable modules/areas in ASP.NET MVC 3.
Suppose we want to build a product which has following modules:
1.      Marketing
2.      Sales
3.      Billing
4.      Inventory
5.      Warehouse
And we want to build a product which allows selling/distribution/deployment of each above modules as separate feature. Typically such products will have some basic features/infrastructure and these modules will fit on top of that. So let’s assume we have basic application with master page, landing page, authentication, security, logging and related resources. We will build application with pluggable modules/component/features for above modules.

Open visual studio and create empty ASP.NET MVC 3 project. Select empty template from below step. (I have named Project/solution as ProductDemo in this example).

You will see empty project in solution explorer like blow:
Now click on Controller to add HomeController, select Empty controller for Template.

Create index view for HomeController to provide landing page of application.

Pluggable modules menu items and expected output:
_Layout.cshtml (Views\Shared folder) is configured to create menus pointing to each above expected modules.

With this, output of application now looks like below:








Creating Areas folder structure and get sample of XXXXAreaRegistration file:
Now click on Project and select Add->Area from context menu. This step will create basic folder structure for Area framework. Specify Marketing as Area name and complete step. This will now create Areas folder and folder structure for Marketing with XXXXAreaRegistration.cs file.
Now take a backup of MarketingAreaRegistration.cs file since we are going to delete Marketing folder in next step. This file will be useful when we will add Marketing module in solution as separate project. (This file can also be created manually once structure is understood).
Now remove Marketing folder.
Adding Pluggable module/area to ASP.NET MVC Project:
Now we are about to start project of added new pluggable module for ‘Marketing’. Right click on solution and choose option to add new project. In below dialog box do following:
·        Select ASP.NET MVC 3 Application
·        Name project as ‘Marketing’
·        Set Location as …\ProductDemo\Area
New project should be created in Areas folder we created in just few seconds before. Create project with empty template.
From newly created “Marketing” project, remove Content folder, scripts folder, view/shared folder and global.ascx file. These are added as part of full MVC project but not needed for us since we are creating this as pluggable module/area.  After this step, solution should look like following:
Since we saved new project marketing under Area folder, it is appearing as hidden folder in ProductDemo/Area folder in solution explorer. DO NOT include this folder in project.
Add MarketingAreaRegistration.cs file at project root in marketing project and change its namespace to match with project name spaces, like below:














Now set output directory of Marketing project to ..\..\bin\ so that compiled dlls are placed in bin directory of ProductDemo (Main application) application.
In marketing project, modify web.config file to remove connection strings, authentication, membership, rolemanager, profile etc sections.

Now create controller Marketing (You can have any other controller as well) in Marketing project. Create index view and place content “Welcome to Marketing Module” like below:


Now application is ready with marketing module. Compile the solution to build all projects in solution. After compilation take a look at ProductDemo\bin folder and you will see compiled dlls like below:


Now run application to see its working. Click Home and Marketing links to see how application is switching between main application shell and marketing module.


Repeat all steps of “Adding Pluggable module/area to ASP.NET MVC Project” section to define rest all modules.


How this works?

ASP.NET MVC Areas structure enables creating separate logical modules and those still resides in same project and binary. Notice xxxAreaRegistration.cs in marketing (or any new project added as per this process) which is inherited from AreaRegistration. This file tells two things to ASP.NET MVC and Routing framework: 1) Area name and Rout for accessing Area and its controller actions.

Global.ascx.cs in Main application (ProductDemo) has following code block. This block registers all areas defined in all assembly present in application bin directory. Remember we have directed output of marketing module to main application bin directory.

Application Master Page - main application has master page (_Layout.cshtml) which is at parent directory in ASP.NET application. As per ASP.NET framework, this layout is inherited to its all sub directories. This way even if marketing is separate project it inherits master page of main application and same applies to all other resources like CSS, image, javascripts and other static resources/pages.


What more can be done?
·        Menus to access such modules can be built dynamically by looking at directories/module directory under Areas folder. If area present in folder, construct menu for its pages and show it on application.
·        Consume WCF service in application. WCF services which are common among main application and module/area project, those needs to be referenced in both the project. Any service which is only required for module, should only be referenced in module.
·        CSS and images can be divvied in such a way that all common resources reside in main application content folder and referenced in _Layout.cshtml. CSS, image and other resources which are specific to module can be placed in module. One more layout page can be introduced at module level which is inherited from main application layout page.
·        Create installer (setup) to package such pluggable modules and install them on web server under IIS. Refer Installer - ASP.NET MVC - Pluggable application modules/components/area/features

Source Code: Download Project

Monday, August 16, 2010

Using enum in Entity - Entity Framework 4.0

Entity framework only allows primitive data types to be used as type while creating property. It is not possible to create property of custom enum type. Many times enum are needed to facilitate developer to choose valid value for property and it also makes developers life simple since Visual Studio provides hints for valid values via intelligence.

 
 

Here I present trick which will help developer to expose enum property from entity and use it during coding to make code more readable. Trick is let entity framework create integer properly for such column but create forwarding property using partial class with type casting and make original property private so that it is not available to programmer. This way programmer will get single enum column to read and write value to entity.

 
 

 
 

Let us take example of Support Ticket system, Ticket table has following structure:

 
 


 
 

Status column has check constraint ( or it could be foreign key to StatusMaster table). For our example let us go with simpler design of having check constraint. Valid status are listed below:

1 - New

2 - Open

3 - On Hold

4 - Closed

5 - Re-Opened

 
 

 
 

Visual Studio work:

Now go ahead and create Empty solutions.

Add Class Library project with name Domain.

Add edmx with name TicketModel. Generate model from database and choose Ticket table.

After edmx designer is shown, it would have Ticket entity with Status as byte property. As shown below:

 
 


 
 

 
 

Here in this example, we would like to represent Ticket Status as enum. To do so add new class file with name Ticket.cs and add below code in class file. Modify class definition to look like below:

 
 


 
 

 
 

Create new public enum TicketStatusCode for above mentioned valid statues. And Create new property TicketStatus of this enum type in ticket class. Write getter and setter of this property as mentioned below:

 
 

 
 


 
 

Since class definition created here is partial class, it adds this code to original Ticket entity which is also a partial class. Getter and Setter of this property refers original Status field which is mapped with database table column. So when application will try to read enum value, it will retrieve value from Status field and return type cast value.

 
 

Now go to designer, select Status column in Ticket entity and open property window. Make Getter and Setter of this property to private.

 
 


 

 
 

 
 

ha

Tuesday, August 10, 2010

Sorting Dictionary using LINQ and using ToDictionary() method to get Sorted Dictionary back

Following code snippet demonstrates:

  1. Using Dictionary to maintain list of items with key
  2. Use LINQ query on Dictionary to sort dictionary
  3. Convert LINQ result to Dictionary back


    class Program
    {
    static void Main(string[] args)
    {
    //Dictionary to maintain list of months
    Dictionary<int,string> months = new Dictionary<int,string>();
    months.Add(1, "January");
    months.Add(3, "March");
    months.Add(8, "August");
    months.Add(12, "December");
    months.Add(2, "February");
    months.Add(4, "April");
    months.Add(5, "May");
    months.Add(6, "June");
    months.Add(7, "July");
    months.Add(9, "September");
    months.Add(10, "October");
    months.Add(11, "November");
    //Original display
    display(months, "Original Dictionary");
    var sortedMonths = (from KeyValuePair<int,string> item in months
    orderby item.Value
    select item).ToDictionary(kvp => kvp.Key, kvp => kvp.Value) as Dictionary<int,string>
    //Sorted display
    display(sortedMonths, "\nDictionary Sorted by Value");
    Console.ReadKey();
    }
    static void display(Dictionary<int,string> list, string displyName)
    {
    Console.WriteLine("{0}:", displyName);
    foreach (KeyValuePair<int,string> item in list)
    {
    Console.WriteLine("{0} {1}", item.Key.ToString().PadLeft(2,' '), item.Value);
    }
    }
    }

Thursday, June 17, 2010

Configure Windows Registry for application Event Logs

System needs to register Application into registry in order to accept event logs. Application Name configured in registry is used as "Source" parameter while writing log entry using .NET code as explained below:

Example assume application name to be "MyApp"


Code snippet to log event:

EventLog.WriteEntry("MyApp", "Page Load is called", EventLogEntryType.Information)


Registry would require entry like below:

[HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\services\eventlog\Application\MyApp]

"EventMessageFile"=hex(2):43,00,3a,00,5c,00,57,00,69,00,6e,00,64,00,6f,00,77,\

00,73,00,5c,00,4d,00,69,00,63,00,72,00,6f,00,73,00,6f,00,66,00,74,00,2e,00,\

4e,00,45,00,54,00,5c,00,46,00,72,00,61,00,6d,00,65,00,77,00,6f,00,72,00,6b,\

00,5c,00,76,00,32,00,2e,00,30,00,2e,00,35,00,30,00,37,00,32,00,37,00,5c,00,\

45,00,76,00,65,00,6e,00,74,00,4c,00,6f,00,67,00,4d,00,65,00,73,00,73,00,61,\

00,67,00,65,00,73,00,2e,00,64,00,6c,00,6c,00,00,00


Important Note: Application running under Administrator context then it would create above registry entry automatically. But in case of deployment on production server this would never be the case. So in this case above registry entry need to be created manually.

Thursday, June 10, 2010

Self Signed Certificates

Steps to create .cer and .pfx files from certificate store

  • Create Self –signed certificate from local machine (.cer file)
    • Open inetmgr
    • Go to Certificates
    • Choose "Create Self-Signed Certificate…"
    • Give Friendly name to certificate and complete step

      (You will see new certificate created in list)

    • Double click on Certificate
    • Click "Copy to File…" button from Details tab
  • Generate .pfx file
    • Open inetmgr
    • Go to Certificates
    • Select certificate from list to export
    • Right click- Export Follow wizard steps