Cool Privilege Control System Part 2 -- asp.net MVC with WCF





5.00/5 (8 votes)
Privilege Control System based on MVC and WCF.
Cool Privilege Control System Part 1 -- asp.net MVC
Cool Privilege Control System Part 2 -- asp.net MVC with WCF
Introduction
Cool Privilege Control is a standalone access right system. It resolves many complex access right problems. such as user with different access level problem, unlimited function hierarchy and easily add function type. And also contains many common mechanisms, such as support multilingual environment, support three type logs(error log, trace log, audit log). It helps you easily and quickly create asp.net mvc demo project with powerful privilege. For details please see my last artical. In here I want to take some times to thanks who vote 5 and ask questions. Thanks again. Today I want to go more deeply into system extension. What is this and how? Please follow my mind.
Background
WCF which full name is Windows Communication Foundation. It is a framework for building service-oriented applications. I known some smart guys had guessed. Yeah. you got it! In this artical I will seperate Cool Privilege Control System into two different part. One is MVC project which is responsible for UI maintenance (Send request to Service/Respond received from Service) , the other is WCF project(Hosted by IIS) which is responsible for executing operations (Receive and perform request from caller/Send response to caller). Let's go ahead I will to How and Why use WCF to perform system extension.
Design Pattern
First of all, I want to describe the mvc design pattern. Yeah. MVC design pattern. I known many guys have more ideas and experiences than me. I don't want to fool who go further than me. But I think the advanced is come from the cons. of original one and new requirements.
There are many mvc image in google search engine. Which help me to reduce many times to draw the diagram. Thanks Google.
MVC composed of Controller, View and Model. MVC design pattern can be used in not only web application but also client application(P.S. I prefer use MVVM design pattern in client application development. I mean WPF application). But Cool Privilege Control is a web application, so I will focus on web application MVC. A web request send to the app server. A url router receive the request and select suitable controller to perform the request(P.S. I ignored many steps before url router received the reuqest. such as HttpHandler. Because my topic is not to overwrite MVC design pattern.). Controller is an intermedialy between model and view. It handles the requests, call model methods, bind data and return views. Model contains db operations, business logic. View is a presentation layer, present layout with data.
MVC Pros and Cons
Pros: |
|
Cons: |
|
Note: Above pros and cons is my personal opinions. If there are anything wrong. Please feel free and contact me.
As pervious diagram discribed, after Controller received the request from URL Router, Controller will call wcf service instead of Model modification method. Then WCF service receive request and call Model modification method. Model perform the action and return View Model to the caller. Controller receive the responded View Model and bind to the View.
MVC With WCF Pros and Cons
Pros: |
|
Cons: |
|
After previous sections, as smart as you are, I guess you would like to use WCF to extend yours' products. Below sequence diagram illustrate the flow about "Get HR Info." in ASP.NET MVC, I hope this diagram give you some ideas when you design your complex system with cool privilege control.
Description
1. | Client user sent a request to get Employee Basic Info from HR Module. |
1.1 | HR Module redirect browser to login page. |
1.1.1 | Reponse login page to client. |
2. | Client user input login name and password, click submit button. |
2.1 | MVC controller invoke WCF authenticate method. |
2.1.1 | WCF execute validation method. |
2.1.2 | WCF Service issue token or return valication error to the caller. |
2.1.3 | Client side cache token for advance process. |
3. | Client user sent a request with token to get HR Info. |
3.1 | HR Module invoke WCF service with function key and token in order to check access right. |
3.1.1 | WCF execute validation method. |
3.1.2 | If it is successful, return success to HR Module. |
3.1.2.1 | HR Module return HR Info to client user. |
3.1.3 | If it is fail, return fail to HR Module. |
3.1.3.1 | HR Module return fail message to client user. |
There was no doubting that you can replace the UI to different technology. I just give you an example, you can control the flow by yourself via call WCF service.
Settings
For MSSQL/MYSQL database user, I publish two versions to fulfill your case. If you use MSSQL as default, please download “CoolPrivilegeControl.Community.WCF.MSSQL.zip”, otherwise, please download “CoolPrivilegeControl.Community.WCF.MYSQL.zip”. And I list my development environment for your reference.
1. Microsoft Visual Studio 2013
2. .NET Framework 4.5.1
3. MSSQL Server 2012 or MYSQL 5.6.26
4. MVC 5.2.3
5. Entity Framwork 6.0
Mandatory Step: After opened solution, please right click solution and select “Enable NuGet Package Restore”.
Mandatory Step: Please right click solution and click "Properties", set projects "CoolPrivilegeControl" and "CoolPrivilegeServiceHost" as startup projects.
4.1 EDIT WEB.CONFIG FILE
4.1.1 For MYSQL user
Please pay attention to the appSettings node, change DBSource/DBName/DBPort/LoginName/LoginPWD value based on mysql server settings.
Property | Description |
DBSource: | IP address of the server host |
DBName: | Database name |
DBPort: | Server TCP/IP port |
LoginName: | DB user name |
LoginPWD: | DB user password |
IsDebug: | If you set true, More details tracer info. will be shown. |
Property | Description |
IsDebug: | If you set true, whatever exception will be show on the page, and vice versa. |
4.1.2 For MSSQL user
As the same as mysql user, change DBSource/DBName/LoginName/LoginPWD value based on mssql server settings, except DBPort. In mssql server, the port number can be specified after the server name or server ip address with comma. E.g.
WELLSCHEUNG\MSSQLSERVER2012,49287 49287 is the port.
Property | Description |
DBSource: | IP address of the server host With Port Format: server name or ip address,port |
DBName: | Database name |
LoginName: | DB user name |
LoginPWD: | DB user password |
IsDebug: | If you set true, More details tracer info. will be shown. |
For UI web.config settings, Please refer to above section 4.1.1.2
4.1.3 Enable or Disable DB initializer
That is the feature of entity framework code first design pattern. And it is the most useful I ever heard before. When you executed the project, if database instance did not exist in the server and the flag is enabled, entity framework mechanism would help you to initialize it. For more information please refer to MSDN(https://msdn.microsoft.com/en-us/data/ee712907). If you do not want entity framework to initial database and cover your database, you can set the attribute named “disableDatabaseInitialization” on context element to true.
Alternative, you can initialize DB via sql script or database backup. I prepare sql script for mysql user to execute and database backup for mssql user to restore.
4.2 Edit Log4Net.config file
We used log4net to help us record trace info and error info. About how to use log4net, I though most of you had more experiences than me, so I do not want to spend many times on repeat. I only specified that all functions in the system enable trace log as default (i.e. Include both input and output information). That is an easy way to trace error even if we cannot run visual studio debug when onsite support. In WCF version, we can config two log4net.config. The one is for asp.net mvc, the other is for wcf service.
It is easy to turn off or change another type of info you wanted to capture. There are seven levels type which is pre-set in log4net.
The following levels are defined in order of increasing priority:
- ALL
- DEBUG
- INFO
- WARN
- ERROR
- FATAL
- OFF
You can replace the value of the attribute named “level” to what you wanted in log4net.config.
SysLog: Log all info of the system. |
ErrorLog: Only log exception of the system. |
In mvc project, previous setting is a global setting. If you want to disable one or any functions to log information automated. You can mark the function you wanted as “[UnTracerAction]” function. After you do like that all info will not be log.
[HttpPost]
[ValidateAntiForgeryToken]
[UnTracerAction]
public ActionResult Create(FunctionVM functionVM)
{
//Message Box Title -- When Error occured, Message Box would be showed.
string str_MsgBoxTitle = MultilingualHelper.GetStringFromResource(languageKey, "FManage_Create");
If you only want to log the main thread enter the function whether or not, except the detail information input or output. You can mark the function you wanted as “[TracerActionWithDetails(EnableTracer=false)]” function.
[HttpPost]
[ValidateAntiForgeryToken]
[TracerActionWithDetails(EnableTracer=false)]
public ActionResult Create(FunctionVM functionVM)
{
//Message Box Title -- When Error occured, Message Box would be showed.
string str_MsgBoxTitle = MultilingualHelper.GetStringFromResource(languageKey, "FManage_Create");
After previously settings. System is ready for your use. Press “F5” in Visual Studio and double check if it has any compile errors or not. If any error came out, you can send the error to me for inspection or search solution in google by yourself.
Using the code
In coming section, I want to illustrate how to call wcf service in Cool Privilege Control.
UI Operation
Here is a full view of method Index in FTManageController. which will be called when user enter selection criteria and click search button in Function Type Management.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Index(FunctionTypeVM selectionCriteria)
{
//Message Box Title -- When Error occured, Message Box would be showed.
string str_MsgBoxTitle = MultilingualHelper.GetStringFromResource(languageKey, "FTManage");
//Declare output variable(recordCount && entityList_Result)
int recordCount = 0;
List<FunctionTypeVM> entityList_Result = new List<FunctionTypeVM>();
//Declare wcf output object;
FTSerListResult entity_FTSerListResult = null;
//Instantiate WebCommonHelper in order to call wcf service
WebCommonHelper webCommonHelper = new WebCommonHelper();
webCommonHelper.CallWCFHelper<IFunTypeMgtSer>(this, this.HttpContext, postOffice.FunTypeMgtSerPath, (entity_IFunTypeMgtSer, entity_WCFSessionVM) =>
{
entity_FTSerListResult = entity_IFunTypeMgtSer.GetListWithPaging(entity_WCFSessionVM, selectionCriteria, 1, PageSize, null, null, CustomFilter(selectionCriteria));
});
//Assign data to local variable
if (entity_FTSerListResult != null)
{
recordCount = entity_FTSerListResult.Int_TotalRecordCount;
entityList_Result = entity_FTSerListResult.EntityList_FunctionTypeVM;
}
//Set paging bar info (Total Record Count and Page Index)
StorePageInfo(recordCount, 1);
//Cache selection criteria
StoreSelectionCriteria<FunctionTypeVM>(selectionCriteria);
//Pass Error To UI
string strError = "";
if (entity_FTSerListResult.StrList_Error.Count() > 0)
strError = string.Join("<br/>", entity_FTSerListResult.StrList_Error.ToArray());
//Fail
if (entity_FTSerListResult.StrList_Error.Count > 0)
{
MsgInfo errorMsgInfo = new MsgInfo();
errorMsgInfo.MsgTitle = str_MsgBoxTitle;
errorMsgInfo.MsgDesc = strError;
errorMsgInfo.MsgType = MessageType.ValidationError;
ViewBag.ActionMessage = errorMsgInfo;
}
//Success
return View(entityList_Result);
}
Step by step code spec
- Set message box title.
//Message Box Title -- When Error occured, Message Box would be showed. string str_MsgBoxTitle = MultilingualHelper.GetStringFromResource(languageKey, "FTManage");
- Declare variables recordCount and entityList_Result, which used to receive total record count and Function Type list after service call.
//Declare output variable(recordCount && entityList_Result) int recordCount = 0; List<FunctionTypeVM> entityList_Result = new List<FunctionTypeVM>();
- Declare WCF output object
//Declare wcf output object; FTSerListResult entity_FTSerListResult = null;
- Instantiate WebCommonHelper and call service.
//Instantiate WebCommonHelper in order to call wcf service WebCommonHelper webCommonHelper = new WebCommonHelper(); webCommonHelper.CallWCFHelper<IFunTypeMgtSer>(this, this.HttpContext, postOffice.FunTypeMgtSerPath, (entity_IFunTypeMgtSer, entity_WCFSessionVM) => { entity_FTSerListResult = entity_IFunTypeMgtSer.GetListWithPaging(entity_WCFSessionVM, selectionCriteria, 1, PageSize, null, null, CustomFilter(selectionCriteria)); });
- Assign data to local variableS
//Assign data to local variable if (entity_FTSerListResult != null) { recordCount = entity_FTSerListResult.Int_TotalRecordCount; entityList_Result = entity_FTSerListResult.EntityList_FunctionTypeVM; }
- Cache paging info in order to set paging bar.
//Set paging bar info (Total Record Count and Page Index) StorePageInfo(recordCount, 1);
- Cache selection criteria.
//Cache selection criteria StoreSelectionCriteria<FunctionTypeVM>(selectionCriteria);
- Return error info to UI if error occured or bind entity list to the view.
//Pass Error To UI string strError = ""; if (entity_FTSerListResult.StrList_Error.Count() > 0) strError = string.Join("<br/>", entity_FTSerListResult.StrList_Error.ToArray()); //Fail if (entity_FTSerListResult.StrList_Error.Count > 0) { MsgInfo errorMsgInfo = new MsgInfo(); errorMsgInfo.MsgTitle = str_MsgBoxTitle; errorMsgInfo.MsgDesc = strError; errorMsgInfo.MsgType = MessageType.ValidationError; ViewBag.ActionMessage = errorMsgInfo; } //Success return View(entityList_Result);
Service Operation
In above code snip, I illustrated the operation of UI controller. In blew code snip, I will show you the process after wcf received a call.
Here is a full view of wcf method called by above controller
public FTSerListResult GetListWithPaging(WCFSessionVM entity_WCFSessionVM, FunctionTypeVM entity_SearchCriteria, int int_CurrentPage, int int_PageSize, string str_SortColumn, string str_SortDir, List<string> str_CustomFilter)
{
try
{
//Restore Server Session by token
RetrieveServerSideSession(entity_WCFSessionVM);
//Flag Success or Fail
bool ret = false;
//Define error list
List<string> strList_Error = new List<string>();
//Instantiate FTSerListResult
FTSerListResult returnResult = new FTSerListResult();
CoolPrivilegeControlContext dbContext = CoolPrivilegeControlContext.CreateContext();
FunctionTypeRespository entityRepos_FT = new FunctionTypeRespository(dbContext, entity_ServerSideSession.ID);
#region [ Check Privilege ]
ret = CheckAccPrivilege(entity_ServerSideSession.ID, entity_WCFSessionVM.RequestFunKey, entity_WCFSessionVM.RequestFunTypeKey, ref strList_Error);
#endregion
//Initialize FTSerListResult instance
returnResult.StrList_Error = strList_Error;
returnResult.Int_TotalRecordCount = 0;
returnResult.EntityList_FunctionTypeVM = new List<FunctionTypeVM>();
//Success
if (ret)
{
int recordCount = 0;
List<FunctionTypeVM> vmList = entityRepos_FT.GetEntityListByPage(entity_SearchCriteria, int_CurrentPage, int_PageSize, str_SortColumn, str_SortDir, out recordCount, str_CustomFilter);
//Assign data to FTSerListResult instance
returnResult.EntityList_FunctionTypeVM = vmList;
returnResult.Int_TotalRecordCount = recordCount;
}
return returnResult;
}
catch (Exception ex)
{
throw new FaultException<WCFErrorContract>(new WCFErrorContract(ex), ex.Message);
}
}
Step by step code spec
- Restore wcf session by token
//Restore Server Session by token RetrieveServerSideSession(entity_WCFSessionVM);
- Define a flag and error list
//Flag Success or Fail bool ret = false; //Define error list List<string> strList_Error = new List<string>();
- Instantiate FTSerListResult object which is used to reply the request.
//Instantiate FTSerListResult FTSerListResult returnResult = new FTSerListResult();
- Instantiate dbcontext object and function type respository that contains data access logic and is used to get data.
CoolPrivilegeControlContext dbContext = CoolPrivilegeControlContext.CreateContext(); FunctionTypeRespository entityRepos_FT = new FunctionTypeRespository(dbContext, entity_ServerSideSession.ID);
- Check Privilege based on Function Key, Function Type Key and current user id.
#region [ Check Privilege ] ret = CheckAccPrivilege(entity_ServerSideSession.ID, entity_WCFSessionVM.RequestFunKey, entity_WCFSessionVM.RequestFunTypeKey, ref strList_Error); #endregion
- Initialize FTSerListResult instance
//Initialize FTSerListResult instance returnResult.StrList_Error = strList_Error; returnResult.Int_TotalRecordCount = 0; returnResult.EntityList_FunctionTypeVM = new List<FunctionTypeVM>();
- Get data from service and set it to the output object
//Success if (ret) { int recordCount = 0; List<FunctionTypeVM> vmList = entityRepos_FT.GetEntityListByPage(entity_SearchCriteria, int_CurrentPage, int_PageSize, str_SortColumn, str_SortDir, out recordCount, str_CustomFilter); //Assign data to FTSerListResult instance returnResult.EntityList_FunctionTypeVM = vmList; returnResult.Int_TotalRecordCount = recordCount; } return returnResult;
After previous recipes, assume you have basic knowledge of Cool Privilege Control. And it is hard for me to decribe all functions in the system. you can trace the program via set debug point. If you have any question about the project or find any bugs, feel free and contact me. I hope all of you can get ideas from my project which make our's world more and more beautiful.
Test-driven development
Cool Privilege Control System is using the test-driven development (TDD) approach. For many companys, TDD is a mandatory approach in repetition development cycle. Cool Privilege Control System contains 40 test cases in all Controllers, you can easily test all functions by clicking "Run All" in Test Explorer. Certainly, you can add new test case or add new conditions into the orignial test case based on your requriements. Cool Privilege Control System uses Xunit and Mock. Xunit is a test framework inject into Visual Studio, as the same as MSTest and Nunit. For more infos, you can visit the official site.
History
2016-02-21 Initial publication
2016-03-01 Add Testing Project