The purpose of this article is to create a simple Task Management System(TMS) that will help the user to create a task, edit the task and to view the same by using MEF 4.0, WCF , Entity Framework 4.0 with some architectural patterns etc.
Introduction
A Task Management System(TMS) allows managers to track the progress of a task assigned to team member(s).This helps the manager to keep track of the work progress.
This is useful especially when he has to keep track of many tasks assigned to a large number of his staff.
Purpose
The purpose of this article is to create a simple Task Management System(TMS) that will help the user to create a task, edit the task and to view the same by using
MEF 4.0, WCF , Entity Framework 4.0 with some architectural patterns etc.
Technologies In Use
- Dot Net Framework 4.0
- MEF 4.0
- WCF 4.0
- Entity Framework 4.0
- Asp.net 4.0
- Java Script
- Sql Server 2008
- Design Patterns
- Adapter Pattern
What prompted to choose these technologies?
Technology is advancing like the speed of light and so new features are coming into place.
The way we use to view the data modeling in our old ADO.net system has been changed with the advent of the Object Relational Modelling(ORM) framework which on the other hand has gained the momentum with the Entity Framework which was launched by
Microsoft in 2008.It helps developers to create data access applications by programming against a conceptual application model as opposed to the traditional relational storage schema.The objective is to decrease the amount of code and maintenance required for data-oriented applications.
Managed Extensibility Framework is a new framework from Microsoft Corporation to build Extensible applications. Its basic purpose is to plug-in components to an already running application. It is an integral part of .NET 4.0 Framework and it finds its applicability in any kind of .NET applications.In our TMS,
the importance of this framework is immense. Say presently we are using Sql Server 2008 as our DB.So our application model will be Sql Server 2008. Now, after sometime we need to change the application model as Oracle base.In that case we can treat this as a plugable component that can be safetly plugged into the system which on the other hand will extend the capability of TMS.
WCF is the best architecture for any SOA based applications that supports scaling,load balancing provides security, transport, authentication and is a good choice for interoperability
Our presentation layer is Asp.net and we will use JQuery for any client side operation.
The Adapter pattern will help us in transforming the result set return by Entity Framework into our own custom entities.
So keeping these factors in mind, let us go ahead.
N.B.~This article is not for introducing any new technology.Rather it will show as how to build a scalable application by using the aforesaid technologies
Design the Database
So let us start with a simple DB design as depicted as under
The tables tblPriority,tblStatus are lookup tables whose values are as under respectively
The user table(tblUser) contains the information about the current users/team members of the system.In our current TMS, there are 4 users being listed in the table.
The tblTaskActivity table contains the data about the current task being assigned to the user,the status etc.
We also have 5 stored procedures viz
- usp_GetPriority (Gets the record from the Priority table)
- usp_GetStatus (Gets the record from the Status table)
- usp_GetUser (Gets the record from the User table)
- usp_GetTaskList (Gets the tasklist for the users)
- usp_InsertTask (Inserts a new task in the Task Activity table)
- usp_UpdateTask (Updates an already existing task in the Task Activity table)
which will help us in accomplishing our work
Project Structure
It's a simple three tire model with which we are familiar with.
TMSDataService is the data service layer.The MEF will come here which will interact with the EF for accessing the dB operation.Once done, the result will be returned to the TMSService
TMSService is the service layer where WCF will reside.It will accept the data from the TMSDataService layer and will transform the record set to the custom entities via Adapter pattern.Once done , it will return the data back to the TMSView.
TMSView is the view of the TMS system. It will get the record from the TMS Service layer and will display those. The entire system interaction is a two way phenomenon.Since for every client only proxy instance creation is sufficient, henceforth, we can use a singleton pattern in the view layer that will interact with the service layer.
Design the TMSDataService
This layer will interact with the database systems for performing any kind of DB accessing stuff with the help of entity framework.Let us see as how our TMSDataService layer will look like initially
The DataAccessStrategy folder will have the logic for composition of the data and will interact with the EF through the TMSModel.cs class which is a concrete class.The TMSCompositionHelper.cs file is the one that will create the composable parts and will invoke the needed component at runtime depending on the request made by the client.
N.B.~A detailed description of MEF is beyond the scope of this article.For learning how it work, you can refer to my other article on MEF
Now we will create the entity model.For doing so let us follow the below steps
Step 1:Right click on the Components folder->New Item->Ado.Net Entity Data Model. Give the name as "TMSSqlDataModel.edmx"
Click on the Add button
Step 2:From the next screen which says "Choose Model Contents" choose "Generate From Database" and click Next button
Step 3:From the next screen which says "Choose Your Data Connection", give the proper connection, set the focus on the radio button "Yes, include teh sensitive data in the connection string" and check the checkbox "Save entity connection setting in the app.config as" and give a suitable name e.g. "TMSEntities"
Click on the Next button
Step 4:The next screen that will appear is the "Choose your Database objects" screen.From there we will choose only the tables and the store procedures.
N.B.~Expand Tables and select tblPriority,tblStatus,tblTaskActivity,tblUser.Likewise,expand Stored Procedures and select usp_GetPriority,usp_GetStatus,usp_GetTaskList,usp_GetUser,usp_SaveTask
Give a proper namespace to the model and click on the Finish button.The model designer will appear as under
So from the Model Browser we can figure out the tables and the store procedures being choosen from our DB model. At this juncture it will be worth mentioning that, the conceptual EF model treats the store procedures as functions and the tables as entities.
At present we will see how we can import functions to map to Stored Procedures.We will basically use three store procedures for our demonstration purpose viz.usp_GetPriority,usp_GetTaskList,usp_InsertTask,usp_UpdateTask.The other two SP's viz usp_GetStatus,usp_GetUser will follow the pattern as how we will do for usp_GetPriority
Using Import Functions to Map Stored Procedures usp_GetPriority
The usp_GetPriority will return entity of type tblPriority.This procedure selects entire rows from the tblPriority table.Because of this one-to-one mapping, we can create a function import from this stored procedure that returns an tblPriority type.
For that to happen, we need to follow the below steps
Step 1:Right click on the usp_GetPriority and select Add Function Import… from the menu.
The Add Function Import wizard will appear as shown in the below figure
Step 2:Give a proper "Function Import Name" say GetPriority and select Entities under "Returns a Collection Of" where we need to choose the entity "tblPriority"
And then click on the "OK" button.We can figure out that under the "Function Imports" folder, the GetPriority function is listed.
So, let us follow the similar step for the other two SP's viz usp_GetStatus,usp_GetUser
Using Import Functions to Map Stored Procedures usp_GetTaskList - Usage of ComplexType
This store procedure needs some special attention.Let us first see the script
SELECT
t.TaskId
,t.TaskName
,p.PriorityID
,p.PriorityName
,s.StatusID
,s.StatusName
,u.UserID
,u.UserName
,t.TaskCreatedOn
,t.EstimatedTime
,t.ActualTime
From tblTaskActivity t
Join tblPriority p ON t.Priority = p.PriorityID
Join tblStatus s ON t.Status = s.StatusID
Join tblUser u ON t.AssignedTo =u.UserID
We can make out that, this store procedure is accessing three tables viz tblTaskActivity,tblPriority,tblStatus,tblUser for getting teh result set. So we cannot find a single entity as it's corresponding mapping model in the EF framework.
However,the Entity Data Model supports ComplexType. A ComplexType is like an entity without an EntityKey and therefore cannot be change-tracked or updated.
Step 1:From the Model Browser, start the Function Import Wizard for the usp_GetTaskList function.Change the Function Import Name to GetTaskList.
Step 2:Let us click on the Get Column Information button. This will cause the designer to execute the stored procedure so that it can read the schema of the result set. The schema will be displayed in the grid below the button.
Step 3:Let us click on the Create New Complex Type button below the grid.Rename the new complex type in the text box to TaskList.
Step 4:Lastly,click the OK button.
In addition to the new function, we can find the new Complex Type in the Model Browser as shown in the below figure
One of the great advantages of returning a Complex Type rather than an Anonymous Type from a LINQ projection,is that we now have a strongly-typed class to work with.
Mapping Insert Stored Procedures usp_InsertTask to Entities
Let us follow the below steps inorder to accomplish the same
Step 1:In the designer, right click on the "tblTaskActivity" entity and select Stored Procedure mapping.
Step 1:Click <Select Insert Function> and then click the drop down arrow that appears.This exposes the list of all Functions found in the Store metadata.
Step 2:Select usp_InsertTask from the list. The designer will do its best job of matching the stored procedure’s parameters with the entity properties using the names.If it does not, then we have to do so by ourself
The benefit of mapping the stored procedure parameters to the entity properties is that we donot have to write code to give the correct pieces of data to the stored procedure rather the Ef will use these mappings to do that task.Type NewTaskId property into the Result Column Bindings and hook it up to the TaskID property.The stored procedure returns the new ID for the inserted row. This mapping ensures that the new id is pushed back into the entity.
Mapping Update Stored Procedures usp_UpdateTask to Entities
The approach is same here as that of the previous one with the difference that we have to choose the <Select Update Function>
Once our EF model creation, stored procedure to Funstion mapping is over, now we can jump into then MEF model for building the process to accessing the data from the EF.
MEF for accessing the data from EF
As a very first step, we need to add reference to System.ComponentModel.Composition
The IDataAccess interface has the below methods defined
using System.Data.Objects;
using TMSDataService.DataAccessStrategy.Components;
namespace TMSDataService.DataAccessStrategy.Contract
{
public interface IDataAccess
{
ObjectResult GetPriority();
ObjectResult GetStatus();
ObjectResult GetUser();
ObjectResult GetTaskList();
int SaveTask(tblTaskActivity task);
}
}
The concrete class "TMSModel" that implements the above interface is as under
using System.ComponentModel.Composition;
using System.Data.Objects;
using TMSDataService.DataAccessStrategy.Contract;
namespace TMSDataService.DataAccessStrategy.Components
{
[Export(typeof(IDataAccess))]
[ExportMetadata("TMSModelSQLMetaData", "SQLSERVER")]
public class TMSModel : IDataAccess
{
public ObjectResult GetPriority()
{
//Access the entity model from here
var tmsEntities = new TMSEntities();
return tmsEntities.GetPriority();
}
public ObjectResult GetStatus()
{
//Access the entity model from here
var tmsEntities = new TMSEntities();
return tmsEntities.GetStatus();
}
public ObjectResult GetUser()
{
//Access the entity model from here
var tmsEntities = new TMSEntities();
return tmsEntities.GetUser();
}
public ObjectResult GetTaskList()
{
//Access the entity model from here
var tmsEntities = new TMSEntities();
return tmsEntities.GetTaskList();
}
public int SaveTask(tblTaskActivity task)
{
var tmsEntities = new TMSEntities();
tmsEntities.AddTotblTaskActivities(task);
return tmsEntities.SaveChanges();
}
}
}
At runtime [ExportMetadata("TMSModelSQLMetaData", "SQLSERVER")], the compiler will read the value of the metadata from the ExportMetadata attribute where the key is TMSModelSQLMetaData and the value is SQLSERVER.The depending on the client request, the appropriate invocation will happen.
The composition engine looks as under(partial)
public class TMSCompositionHelper : IDisposable
{
[ImportMany]
public System.Lazy>[] DataPlugins { get; set; }
///
/// Assembles the data components
///
public void AssembleDataComponents()
{
try
{
//Step 1: Initializes a new instance of the
// System.ComponentModel.Composition.Hosting.AssemblyCatalog class with the
// current executing assembly.
var catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());
//Step 2: The assemblies obtained in step 1 are added to the CompositionContainer
var container = new CompositionContainer(catalog);
//Step 3: Composable parts are created here i.e. the Import and Export components
// assembles here
container.ComposeParts(this);
}
catch (Exception ex)
{
throw ex;
}
}
///
/// GetPriority
///
///
///
public ObjectResult GetPriority(string dataSourceType)
{
ObjectResult lstPriority = null;
foreach (var dataPlugin in DataPlugins)
{
if ((string)dataPlugin.Metadata["TMSModelSQLMetaData"] == dataSourceType)
{
lstPriority = dataPlugin.Value.GetPriority();
break;
}
}
return lstPriority;
}
}
The export components are being assembled in the AssembleDataComponents() method and are stored in the DataPlugins property.The [ImportMany] attribute helps to store more export components in the DataPlugins property which is a kind of Lazy loader.
The IDataService interface talks to the service layer
Design the TMSService
The TMSService layer looks as under
The "Entities" folder contains the various entities which the View will understand.Let us look into one of the entities say "Priority"
using System.Runtime.Serialization;
namespace TMSService.Entities
{
[DataContract]
public class Priority
{
[DataMember]
public int PriorityID { get; set; }
[DataMember]
public string PriorityName { get; set; }
}
}
The other entities follow the same pattern.The data once received from the TMSDataService will be converted by the "Adaptor".The below is an example for the Adaptor to convert the EF tblPriority entity to the custom Priority collection.
public List GetPriority()
{
//Obtain the data from Adaptee
var lstPriorityData = adaptee.GetPriority();
var lstPriorities = new List();
//Do the transformation
foreach (tblPriority p in lstPriorityData)
{
lstPriorities.Add(new Priority
{
PriorityID = p.PriorityID
, PriorityName = p.PriorityName
});
}
return lstPriorities;
}
The "Adaptee" is the guy who will perform the interaction between these two layers.The service layer exposes it's services to the client through the "ITMSService" interface which has the following operations to offer
Design the TMSView
The TMSView looks as under
It is the TmsClientService that interacts with the WCF service.So the client code(partial) is as under for obtaining the collection of "Priorities"
public List GetPriority()
{
using (TmsServiceClient client = new TmsServiceClient())
{
try
{
return client.GetPriority();
}
catch (Exception)
{
client.Abort();
throw;
}
}
}
The "Create Task" screen looks as under where the users will give the assignments to their resources
Clicking on the "View Task" menu, will take us to the dashboard where the managers can figure out the vaious tasks assigned to the resources, the estimated time and actual time taken for the task completion etc.
The "Edit" button provided will help to edit the records.
Conclusion
In this short article we have seen how to make a scalable 3-Tire application using MEF,EF,WCF,ASP.net technologies.Hope this will help the beginners to start with.
Reference
- An Introduction to Managed Extensibility Framework (MEF) - Part I
- Using Stored Procedures for Insert, Update & Delete in an Entity Data Model