Task Management System(TMS)

Niladri.biswas
Posted by in .NET Framework category on for Beginner level | Points: 250 | Views : 31784 red flag

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.


 Download source code for Task Management System(TMS)

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

  1. Dot Net Framework 4.0
  2. MEF 4.0
  3. WCF 4.0
  4. Entity Framework 4.0
  5. Asp.net 4.0
  6. Java Script
  7. Sql Server 2008
  8. Design Patterns
    1. 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

  1. usp_GetPriority (Gets the record from the Priority table)
  2. usp_GetStatus (Gets the record from the Status table)
  3. usp_GetUser (Gets the record from the User table)
  4. usp_GetTaskList (Gets the tasklist for the users)
  5. usp_InsertTask (Inserts a new task in the Task Activity table)
  6. 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

  1. An Introduction to Managed Extensibility Framework (MEF) - Part I
  2. Using Stored Procedures for Insert, Update & Delete in an Entity Data Model
Page copy protected against web site content infringement by Copyscape

About the Author

Niladri.biswas
Full Name: Niladri Biswas
Member Level: Platinum
Member Status: Member
Member Since: 10/25/2010 11:04:24 AM
Country: India
Best Regards, Niladri Biswas
http://www.dotnetfunda.com
Technical Lead at HCL Technologies

Login to vote for this post.

Comments or Responses

Posted by: Vishvvas on: 1/4/2012 | Points: 25
Nice but exhaustive article. Could you elaborate on MEF in the current context?
Posted by: Ck.kislay on: 1/24/2012 | Points: 25
I just run you script and all the tables and sp are created in my database. Then I point this database in web.config file and fire the applicaion in vs2010 but it throws exception in service code.

will you explain to run the code??

Thanks,
Chandan
Posted by: Niladri.biswas on: 1/24/2012 | Points: 25
What exception you received?Please explain....Ideally, the data source /connection string will be in the Service project's config file. You generate the model and will receive an entry in the config file of your EF. Copy that and paste the same in the config file of your Service application and it should work.
Posted by: Ck.kislay on: 2/16/2012 | Points: 25
I am getting this error. May be some change required in increase the service timeout.
----------
System.TimeoutException was unhandled by user code
Message=The request channel timed out while waiting for a reply after 00:01:00. Increase the timeout value passed to the call to Request or increase the SendTimeout value on the Binding. The time allotted to this operation may have been a portion of a longer timeout.
Source=mscorlib
StackTrace:
Server stack trace:
at System.ServiceModel.Channels.RequestChannel.Request(Message message, TimeSpan timeout)
at System.ServiceModel.Channels.ClientReliableChannelBinder`1.RequestClientReliableChannelBinder`1.OnRequest(TRequestChannel channel, Message message, TimeSpan timeout, MaskingMode maskingMode)
at System.ServiceModel.Channels.ClientReliableChannelBinder`1.Request(Message message, TimeSpan timeout, MaskingMode maskingMode)
at System.ServiceModel.Channels.ClientReliableChannelBinder`1.Request(Message message, TimeSpan timeout)
at System.ServiceModel.Security.SecuritySessionClientSettings`1.SecurityRequestSessionChannel.Request(Message message, TimeSpan timeout)
at System.ServiceModel.Dispatcher.RequestChannelBinder.Request(Message message, TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)
at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)
Exception rethrown at [0]:
at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
at TMSView.TmsServiceReference.ITmsService.GetPriority()
at TMSView.TmsServiceReference.TmsServiceClient.GetPriority() in E:\RnD\DotNetfunda\Task Management System(TMS)\Niladri.biswas_Articles_634611484349258594_TaskManagementSystem\TaskManagementSystem\TMS\TMSView\Service References\TmsServiceReference\Reference.cs:line 582
at TMSView.TmsViewService.TmsClientService.GetPriority() in E:\RnD\DotNetfunda\Task Management System(TMS)\Niladri.biswas_Articles_634611484349258594_TaskManagementSystem\TaskManagementSystem\TMS\TMSView\TmsViewService\TmsClientService.cs:line 20
at TMSView.Controls.TaskUC.PopulateLookups(TmsClientService client, DropDownList ddl, Int32 fillOrder) in E:\RnD\DotNetfunda\Task Management System(TMS)\Niladri.biswas_Articles_634611484349258594_TaskManagementSystem\TaskManagementSystem\TMS\TMSView\Controls\TaskUC.ascx.cs:line 102
at TMSView.Controls.TaskUC.PopulateLookups() in E:\RnD\DotNetfunda\Task Management System(TMS)\Niladri.biswas_Articles_634611484349258594_TaskManagementSystem\TaskManagementSystem\TMS\TMSView\Controls\TaskUC.ascx.cs:line 92
at TMSView.Controls.TaskUC.Initialize() in E:\RnD\DotNetfunda\Task Management System(TMS)\Niladri.biswas_Articles_634611484349258594_TaskManagementSystem\TaskManagementSystem\TMS\TMSView\Controls\TaskUC.ascx.cs:line 71
at TMSView.Controls.TaskUC.Page_Load(Object sender, EventArgs e) in E:\RnD\DotNetfunda\Task Management System(TMS)\Niladri.biswas_Articles_634611484349258594_TaskManagementSystem\TaskManagementSystem\TMS\TMSView\Controls\TaskUC.ascx.cs:line 24
at System.Web.Util.CalliHelper.EventArgFunctionCaller(IntPtr fp, Object o, Object t, EventArgs e)
at System.Web.Util.CalliEventHandlerDelegateProxy.Callback(Object sender, EventArgs e)
at System.Web.UI.Control.OnLoad(EventArgs e)
at System.Web.UI.Control.LoadRecursive()
at System.Web.UI.Control.LoadRecursive()
at System.Web.UI.Control.LoadRecursive()
at System.Web.UI.Control.LoadRecursive()
at System.Web.UI.Control.LoadRecursive()
at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)
IInnerException: -----
Thanks
Posted by: Bhupendra on: 2/14/2013 | Points: 25
i am getting the below error, reply me as soon as possible
No connection could be made because the target machine actively refused it 127.0.0.1:8732

Login to post response

Comment using Facebook(Author doesn't get notification)