A quick introduction to ASP.NET MVC3 and WCF-based 
        services architecture (30 min)
My intention with this work is to introduce the basics of 
        ASP.NET MVC3 and WCF; the main goal will be to draw a picture of a solution 
        using WCF services in the back-end as well as Entity Framework to handle the 
        Data Access, and ASP.NET MVC3 in the front-end. This work obeys to the need to 
        demonstrate/answer some basic questions to friends (who are proficient in other technologies) about an end to end application using ASP.NET MVC and WCF Services.
To achieve our goal the reader has to follow a series of 
        steps in order to create a simple app to perform the basic CRUD operations for a 
        user’s table. 
    
Minimum Requirements
- Visual C# 2010 Express (minimum)
- SQL server express
A. Creating the first project (UI 
        project). 
    
1) Open 
        Visual Studio
2) Create 
        New Project, Name it MyFirstMVC3App
3) 
        From Installed Templates select Web >
        ASP.NET MVC3 Web Application
4) 
        Select template: empty and
        View Engine: Razor
B. Creating a project to host our 
        Business objects
1) Add new 
        project to the solution, right click on the solution icon and Select Add > New 
        Project
2) From 
        Installed Templates select Class library and Name the project as 
        Domain
3) Remove Class1.cs
4) Create a folder for this project and name it Entities
5) Right click on the Entities folder and select Add > 
        Class and name it User.cs
6) Replace the code inside User.cs with the code block below 
        containing WCF and MVC attributes; will make sense later when we run the app.
using System.Web.Mvc;
using System.ComponentModel.DataAnnotations;
using System.Runtime.Serialization;
using System.Data.Objects.DataClasses;
 
 
namespace Domain.Entities
{
[DataContract]
public class User
    {
[DataMember]
[HiddenInput(DisplayValue = false)]
[EdmScalarPropertyAttribute(EntityKeyProperty = true, IsNullable = false)]
public int UserID { get; set; }
 
[DataMember]
[Required(ErrorMessage = "Please enter a user name")]
public string Name { get; set; }
 
[DataMember]
[Required(ErrorMessage = "Please enter a description")]
[DataType(DataType.MultilineText)]
public string Description { get; set; }
 
[DataMember]
[Required(ErrorMessage = "Please enter your email address")]
[RegularExpression(".+\\@.+\\..+", ErrorMessage = "please enter a valid email")]
public string Email { get; set; }
 
[DataMember]
[Required(ErrorMessage = "Please enter your phone number")]
public string Phone { get; set; }
 
public string PrivateField { get; set; }
    }
}
7) You might notice that some attributes are red-highlighted, 
        to correct the problem add the following references to your project, right click 
        at the References node of the current project created and then select 
        Add reference
Copy & paste one by one the following references in the 
        text box at the bottom of the window just opened and then click add (Don’t copy 
        the dot at the beginning of the path !!).
- c:\Program Files (x86)\Microsoft ASP.NET\ASP.NET MVC 3\Assemblies\System.Web.Mvc.dll
- C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\System.Runtime.Serialization.dll
- C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\System.Data.Entity.dll
- C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\System.ComponentModel.DataAnnotations.dll
C. Creating a project to host the Data access layer (Entity framework)
0) Create a database, execute the following script in sql 
        server management studio. 
    
(Make sure that you have a folder C:\Temp as shown in 
        the script)
( NAME
        = N'DBTest', FILENAME
        = 
        N'C:\Temp\DBTest.mdf' , SIZE
        = 
        3072KB, MAXSIZE
        = UNLIMITED, FILEGROWTH = 
        1024KB )
        LOG ON
        
    
( NAME
        = N'DBTest_log', FILENAME
        = 
        N'C:\Temp\DBTest_log.ldf' , SIZE
        = 1024KB , 
        MAXSIZE = 2048GB , 
        FILEGROWTH = 10%)
GO
USE DBTest
CREATE
        TABLE [dbo].[Users]( [UserID] [int] 
        IDENTITY(1,1) NOT
        NULL,
[Name] 
        [nvarchar](150)
        NOT NULL,
        [Description] [nvarchar](100) NULL,
[Email] 
        [nvarchar](100)
        NULL,
[Phone] 
        [nvarchar](100)
        NULL,
        [PrivateField] [nvarchar](100) NULL, 
    
CONSTRAINT [PK_users]
        PRIMARY KEY
        NONCLUSTERED 
    
(
[UserID]
        ASC
)WITH
        (PAD_INDEX
        = OFF, 
        STATISTICS_NORECOMPUTE =
        OFF,
        IGNORE_DUP_KEY =
        OFF,
        ALLOW_ROW_LOCKS =
        ON,
        ALLOW_PAGE_LOCKS =
        ON)
        ON [PRIMARY]
)
        ON [PRIMARY]
GO
1) Add a new 
        project to the solution, right click on the solution icon and Select Add > New 
        Project
2) From 
        Installed Templates select Class library and Name the project as 
        Domain.EF
3) Remove 
        Class1.cs
4) Right 
        click on Domain.EF (new project) select Add >New Item… from
        Installed Templates select Data > ADO.NET Entity Data 
        Model and name it EFModel and then click Add
5) Select Generate from Database
6) Select your Database connection (You will have to 
        configure a new connection, Click in New Connection, Select MS SQL Server…Server 
        Name: localhost most of the time would be enough, Database name:DBTest) and then
        Next
7) Select Tables/Finish
8) Right Click on the model you just created, select 
        Properties and change Code Generation 
        Strategy to None (Save and 
        close the file)
9) Right click on the project (Domian.EF), select Add
        > Class and name the class EFContext.cs
    
10) By now we can test our Wcf service using the tool WcfTestClient.exe, so, let’s compile the solution pressing F6
Considerations
10) Replace the next code in the EFContext file
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Objects;
 
namespace Domain.Entities
{
public class EFContext : ObjectContext
    {
public EFContext() : base("name=DBTestEntities", "DBTestEntities")
        {
_users = CreateObjectSet<User>("Users");
        }
 
public ObjectSet<User> Users
        {
get
            {
if (_users == null)
                {
_users = base.CreateObjectSet<User>("Users");
                }
return _users;
            }
        }
private ObjectSet<User> _users;
 
    }
}
11) You will notice that VS will prompt that User type or 
        namespace could not be found, right click on references then select Add 
        references > Projects, double click on Domain and then 
        close the window
D. Creating a WCF-based services 
        architecture project
1) Add a new project to the solution
2) Select Installed Templates > WCF > WCF Service 
        Application and name it WcfService
3) Now add Domain and Domain.EF references to this project (Add 
        references > Projects, double click on Domain and double click 
        on Domain.EF and close the window)
4) Open the file IService1.cs, remove the content inside 
        interface IService1 (what is inside the brakets { }), also remove completely the 
        class CompositeType and insert the following code inside the interface IService1
 
[OperationContract]
List<User> GetUsers(int page);
5) Again VS, 
        prompts an error in User type, right click and Resolve.
6) Open 
        Service1.svc, remove the code inside the class Service1 and insert the following 
        code:
 
 
public List<User> GetUsers(int page)
{
int PageSize = 5;
EFContext context = new EFContext();
return context.Users.AsQueryable().OrderBy(u => u.UserID).Skip((page - 1) * PageSize).Take(PageSize).ToList();
}
 
 
 
7) Right 
        click on User type (List<User>) and resolve
8) Add a new 
        reference, this time from Assemblies select System.Data.Entity 
        (Don’t forget to resolve EFContext missing reference)
9) Open 
        the file app.config from Domain.EF and copy the ConnectionString section into 
        Web.Config from WcfService 
    
10) By now we can test our Wcf service using the tool WcfTestClient.exe, so, let’s compile the solution pressing F6
Testing the WCF service.
11) Right 
        clicking on WcfService project, select Debug > Start new instance.
12) In the 
        System Tray you will see ASP.NET Development server is running our WCF service, 
        double click on the icon , a window will popup, click in the Root URL 
        link, a browser will list the content of the project, click on Service1.svc file 
        and copy the link exposed there (that should be similar to
        
        http://localhost:50178/Service1.svc?wsdl).
, a window will popup, click in the Root URL 
        link, a browser will list the content of the project, click on Service1.svc file 
        and copy the link exposed there (that should be similar to
        
        http://localhost:50178/Service1.svc?wsdl).
 
 , a window will popup, click in the Root URL 
        link, a browser will list the content of the project, click on Service1.svc file 
        and copy the link exposed there (that should be similar to
        
        http://localhost:50178/Service1.svc?wsdl).
, a window will popup, click in the Root URL 
        link, a browser will list the content of the project, click on Service1.svc file 
        and copy the link exposed there (that should be similar to
        
        http://localhost:50178/Service1.svc?wsdl).
13) Now we 
        need to find and run Wcftestclient to test our service (should be found in the 
        folder "C:\Program Files (x86)\Microsoft Visual Studio 
        10.0\Common7\IDE\WcfTestClient.exe").
14) Once 
        WcfTestClient is open, select file>Add Service, paste the end point 
        address (http://localhost:50178/Service1.svc?wsdl) 
        and click Ok, now you can test your service by double clicking GetUsers(), 
        setting the request value parameter to one (1) (a value of zero will crash !!!) 
        and click invoke.
(Go to SQL Server Management studio to insert a record in 
        your table to see your service returning something other than an empty 
        Domian.Entities.User[])
E. Working the front-end
        Let’s go back to the main project (MyFirstMVC3App), we will create the structure 
        to present a web page with a list of users and two buttons to add and update 
        users.
1) Start by 
        adding a new reference to this project, right click on References >Add 
        Reference>Projects>Solution Double click on Domain project and close 
    
2) Lets wire 
        up our first WCF service, by adding a service reference to our project, 
        right click on References > Add Service Reference, copy again the 
        address of your web service (something similar to
        
        http://localhost:50178/Service1.svc?wsdl *port may be different) click Go 
        and make sure that your Namespace is: ServiceReference1 and click OK
3) Now Add 
        our first controller, Right click on the folder “Controllers”, Select Add > 
        Controller and change the name of the 
        controller from Default1Controller to UserController, click Add and replace the 
        code inside the class User Controller with the following:
public ViewResult List(int page = 1)
{
ServiceReference1.Service1Client myService = new ServiceReference1.Service1Client();
return View(myService.GetUsers(page));
}
 
4) Add a 
        Model to provide the list of users to 
        our view (will create the view in the next step), Right click in Models > Add 
        > Class name it UsersViewModel.cs and click Add.
Our View Model should look like this:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Domain.Entities;
 
namespace MyFirstMVC3App.Models
{
public class UsersViewModel
    {
public List<User> Users { get; set; }
    }
}
(Don’t forget to resolve before building the solution)
Press F6 to build solution!!
5) Switch to 
        UserController.cs, Right click within the method List and select Add
        View, select create a strongly typed 
        view Model class UsersViewModel (MyFirstMVC3App.Models)
6) Replace 
        the content inside the body tags for the view you’ve just generated with the 
        following content 
    
<div>
<table width="90%" align="center">
<tableheader>
<tr>
<th align="left">Name</th>
<th align="left">Description</th>
<th align="right">Phone</th>
<th align="right">Email</th>
</tr>
</tableheader>
<tablebody>
@foreach (var u in Model.Users)
            {
<tr>
<td align="left">@u.Name</td>
<td align="left">@u.Description</td>
<td align="right">@u.Phone</td>
<td align="right">@u.Email</td>
</tr>
            }
</tablebody>
</table>
 
</div>
 
7) Now that 
        we have a model (UsersViewModel), lets update our UserController method named 
        List which should look like the following :
(Don’t forget to resolve UsersViewModel)
 
public 
        ViewResult List(int page = 1)
{
        ServiceReference1.Service1Client myService =
        new 
        ServiceReference1.Service1Client();
        UsersViewModel usersViewModel 
        = new 
        
        UsersViewModel();
        usersViewModel.Users = myService.GetUsers(page).ToList();
return 
        View(usersViewModel);
}
8) Open the 
        file global.asax and replace RegisterRoutes method with the following code:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
 
routes.MapRoute(
"Default", "{controller}/{action}/{id}", new { controller = "User", action = "List", id = UrlParameter.Optional } );
 
}
 
 
 
9) If you 
        have a user in your database, press F5 to see how it looks.
--Here a 
        quick insert to have something to view.
INSERT
        INTO [DBTest].[dbo].Users(Name, Description, Email, Phone, PrivateField)
        VALUES('Dwyane 
        Wade',
        'American basketball player', 'test@email.com', '333 333 3333', 'private note')
F. Adding the last pieces of the 
        work
1) Stop the 
        project, and add a the following method to UserController
/// 
        <summary>
/// GET: /User/Add
/// 
        </summary>
/// 
        
        <returns></returns>
public 
        
        ActionResult Add()
{
User userModel =
        new 
        User();
return View("Detail", userModel);
}
2) Right 
        click on the previous method added and select Add View, select “Create a 
        strongly-typed view, Model: User, Scaffold template: Create and click Add 
        button.
3) The 
        following code shows the completed code generated by the wizard except for the 
        code that has been removed related to the “PrivateField”, 
    
also I have added some parameters to Html.BeginForm to 
        trigger the Save method from UserController in the form of a POST request and 
        @Html.HiddenFor 
    
        @model 
        
        Domain.Entities.User
        @{
Layout =
        null;
        }
<!DOCTYPE 
        html>
<html>
<head>
<title>Add</title>
</head>
<body>
<script 
        src="@Url.Content("~/Scripts/jquery-1.5.1.min.js")" 
        type="text/javascript"></script>
<script 
        src="@Url.Content("~/Scripts/jquery.validate.min.js")" 
        type="text/javascript"></script>
<script 
        src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" 
        type="text/javascript"></script>
        @using 
        (Html.BeginForm("Save",
        
        
        "User",
        
        
        FormMethod.Post))
{
        @Html.HiddenFor(model => model.UserID)
        @Html.ValidationSummary(true)
<fieldset>
<legend>User</legend>
<div 
        class="editor-label">
        @Html.LabelFor(model => model.Name)
</div>
<div 
        class="editor-field">
        @Html.EditorFor(model => model.Name)
        @Html.ValidationMessageFor(model => 
        model.Name)
</div>
<div 
        class="editor-label">
        @Html.LabelFor(model => 
        model.Description)
</div>
<div 
        class="editor-field">
        @Html.EditorFor(model => 
        model.Description)
        @Html.ValidationMessageFor(model => 
        model.Description)
</div>
<div 
        class="editor-label">
        @Html.LabelFor(model => model.Email)
</div>
<div 
        class="editor-field">
        @Html.EditorFor(model => model.Email)
        @Html.ValidationMessageFor(model => 
        model.Email)
</div>
<div 
        class="editor-label">
        @Html.LabelFor(model => model.Phone)
</div>
<div 
        class="editor-field">
        @Html.EditorFor(model => model.Phone)
        @Html.ValidationMessageFor(model => 
        model.Phone)
</div> 
    
<p> 
    
<input 
        type="submit" 
        value="Create" 
        />
</p>
</fieldset>
}
<div>
        @Html.ActionLink("Back to 
        List",
        
        "List")
</div>
</body>
</html>
4) We need 
        the user to be able to request the previous view (Detail.cshtml) throughout our 
        List.cshtml view, so let’s open List.cshtml view and add the following code 
        before the ending tag 
        
        </body>
        
    
        @using 
        (Html.BeginForm("Add", 
        "User", 
        FormMethod.Get)) 
    
{
<input 
        type="submit" 
        value='Add' 
        />
}
Adding a new service:
5) We will 
        now add new service to our interface at the back end layer, Open IService1 and 
        insert the following code beneath GetUsers 
    
[OperationContract]
bool SaveUser(User user);
6) Open 
        Service1.svc and insert the following code below GetUsers 
    
/// 
        <summary>
/// Stores/Update 
        user data into table dbo.Users
/// 
        </summary>
/// 
        <param 
        name="user"></param>
/// 
        
        <returns></returns>
public 
        bool SaveUser(User user)
{
try
{
EFContext context =
        new 
        EFContext();
if (user.UserID 
        == 0)
{
        context.Users.AddObject(user);
}
else
{
        context.Users.Attach(new 
        User() { UserID = 
        user.UserID });
        context.Users.ApplyCurrentValues(user);
// Update a 
        existing user
}
        context.SaveChanges();
}
catch
{
return 
        false; 
        //something 
        wrong happened
}
return 
        true; 
        //save 
        successfully
}
7) The 
        previous changes to our Service1 requires to update the service reference…from
        MyFirstMVC3App -> Service references, right click at 
        ServiceReference1 and select Update Service Reference
  
    
8) Open 
        UserControler.cs and add the following method to the class UserController, this 
        method will take action when the user clicks the Create button from our 
        view Detail.cshtml
[HttpPost]
public 
        
        ActionResult Save(User userModel)
{
        ServiceReference1.Service1Client myService =
        new 
        ServiceReference1.Service1Client();
var response = 
        myService.SaveUser(userModel);
if (!response)
{
//do 
        something (show a user friendly message!)
}
        ActionResult redirectResult 
        = RedirectToAction("List");
return 
        redirectResult;
}
9) Run your 
        app and add a user, your Add view should look like the follow one (try entering an invalid email account):
Adding the necessary functionality to Update a 
        user profile.
10) Open 
        IService1.cs and add the following code to the interface IService1 (beneath 
        SaveUser)
[OperationContract]
User Get(int userId);
11) Open 
        Service1.svc.cs and add the implementation of the method Get to Service1 class 
        (beneath SaveUser)
public 
        User Get(int userId)
{
EFContext context =
        new 
        EFContext();
var user = 
        context.Users.Where(u => u.UserID == userId).FirstOrDefault();
return user;
}
12) Update 
        service references from MyFirstMVC3App -> Service 
        references, right click at ServiceReference1 and select Update 
        Service Reference
13) Open 
        UserControler.cs and add the following method to the class UserController, this 
        method will take action when the user clicks on an existing user name
public 
        
        ActionResult Get(int userId)
{
        ServiceReference1.Service1Client myService =
        new 
        ServiceReference1.Service1Client();
var userModel = 
        myService.Get(userId);
return View("Detail", userModel);
}
14) Open 
        List.cshtml and replace @u.Name ( located at the foreach loop )with the 
        following code:
        @Html.RouteLink(u.Name,
        new { controller =
        
        "User", 
        action = 
        "Get", userId = 
        u.UserID })
15) Open 
        Detail.cshtml and replace 
        
        <input
        
        
        type="submit"
        
        
        value="Create"
        
        
        /> with the following code:
if (Model.UserID 
        == 0)
{ 
    
  <input 
        type="submit" 
        value="Create" 
        />
}
else
{ 
    
  <input 
        type="submit" 
        value="Update" 
        />
}
16) Run the 
        app and play with it… the missing operation is to remove a user and I thought it 
        would be good to leave it up to the reader as an exercise to give it shot. 
    
Considerations
· Code 
        refactoring (i.e. a thin layer to implement the services).
· Adopt 
        Test driven development and Inversion of Control (IoC).
· Use 
        proper comments and validations.
· At the 
        Back End
Code refactoring is required
Web services transport protocols: HTTP (or HTTPS), TCP, Named 
        Pipes, and MSMQ.
· At the 
        Front End
Model binders.
Css
JQuery
Javascript.










 
No comments:
Post a Comment