Page 1 of 1

Intermediate ASP.NET - MVC, ViewModels, EF and WebAPI

#1 RexGrammer  Icon User is offline

  • Coding Dynamo
  • member icon

Reputation: 181
  • View blog
  • Posts: 777
  • Joined: 27-October 11

Posted 24 December 2013 - 11:28 AM

Intermediate ASP.NET - MVC, ViewModels, EF and WebAPI


This is meant for people with basic understanding of the ASP.NET technology. It's a quick and dirty primer on some "intermediate" functionalities that should just get you going, it's not written in great detail, but rather just describes the basic usage. This should just teach you the basic usage, so you can get your feet wet. With this knowledge you can start making your own application and then google the problems you encounter (There's a lot of good documentation out there! :) .

Recommended ASP.NET reference:



I'll start off with a lot of theory, and it may seem boring to you, but please bear with me. If you wish to skip some things (only read about the things you're interested in, feel free to just read the relevant parts, although some parts require some background knowledge)

Theory


This might be boring, but it's essential that your understand the technology before you use it. It might seem stupid, but a lot of people actually use it without "knowing" or "understanding" it fully.

1. MVC - Model - View - Controller

The Model - View - Controller pattern (MVC). This pattern basically splits your application into at least 3 tiers/layers. Into the:

  • Presentation layer
  • Buisness layer
  • Data Access Layer (DAL)


The presentation layer is the "View" part of MVC. It's the presentation of the data. In it you define how your page looks and how it presents the data you pass to it (There'll be more talk about passing data to a view later). It's really simple: it's the design of your application.

The Business layer is the "Controller" part of MVC. It's where you perform all of your logic upon the data. If you want to process some long text, which you've pulled from the database, you'd do it here (Uhm, actually... maybe not in the controller directly, but you get the point).

The DAL (Data Access Layer) is the "Model" part of MVC. A model is a class representation of some data, be it data from a database, or data from a flat file, or w/e. Since most of the time you'll use databases for your website, this'll most likely be a EF (More about EF later on) data model.

I know you have many questions now, but please just be a bit more patient. I promise everything'll become clearer when we get to the practical part.

2. ViewModels - Strongly Typed Views FTW

I'll refrain from going into detail here, but there are two types of vies. Dynamically and Strongly typed views. Dynamically typed vies use the ViewBag dynamic object, and strongly typed views use models. You define which type of model a view takes as a parameter, and then, using that model's properties you can define how the view displays the data that's stored into a model. This may sound confusing but it'll become much clearer with examples. Generally it's better to use strongly typed views, and the only time you'll be using dynamic objects is when you need only to pass a little bit of info.

3. EF (Entity Framework) - Your best buddy in the war agains SQL queries

You're tired of querying the database then mapping the data to classes? Then you'll love EF. It let's you define data models, which it automatically populates. You can then manipulate that data and then save the changes, and update the database automatically. You don't have to worry about parametrizing your SQL queries, or did you type the correct table name. It's awesome. Of course, there are situations where it's better not used, but let's forget about that, because it's out of the scope of this tutorial.

4. WebAPI - Giving others the chance to experience the awesomeness of your application

This provides you with the ability to share your applications functionality with others. It follows the MVC pattern, and has the exact same Model and Controller parts, but the "View" is a bit different. It's output is a serialized object (or a IEnumerable<> of objects). It's really easy to use! :)

Practical stuff


Fire up visual studio, it's time to code!!! YAAAY!

Create an empty ASP.NET MVC (5) application, with WebAPI, and the Razor viewengine (You can use ASPX if you want, but all my examples are written using Razor).
SlikaKreiranja

Notice that I'm first creating the database, then writing code.

1. EF (Entity Framework) - Your best buddy in the war agains SQL queries

Create a SqlExpress database, and add the connection string to your web.config file. Name it default connection. Here's my connection string for reference:

<add name="DefaultConnection" connectionString="Data Source=(LocalDb)\v11.0;Initial Catalog=TestDB;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|\TestDB.mdf" providerName="System.Data.SqlClient" />



Let's keep it real simple, and just have 2 tables in our database:

CREATE TABLE [dbo].[User]
(
	[Id] INT NOT NULL PRIMARY KEY IDENTITY, 
    [Username] NCHAR(15) NOT NULL
)

CREATE TABLE [dbo].[Post] (
    [Id]      INT  IDENTITY (1, 1) NOT NULL,
    [Content] TEXT NOT NULL,
    [UserId] INT NOT NULL, 
    PRIMARY KEY CLUSTERED ([Id] ASC)
)




Now make the Post table's UserId a foreign key, that references the user table's id:

CONSTRAINT [FK_Post_User] FOREIGN KEY ([UserId]) REFERENCES [dbo].[User] ([Id])


Note: I have used terribly bad naming, naming both Ids same, but it won't cause trouble in this example. Generally avoid naming two different columns the same name.

Now, let's make a model for our data. Right click models, add, new item, data, ado.net data entity data model and name it BoardModels. Generate it from a database, use the default connection string, and name the entity connection string BoardContext (Please make sure that the connection to the database is closed at this point). Procede, select the two tables that we created, and name the namespace Models, select EF6. Click OK to all the following prompts, and then Yes To All if your web.config file was open.

2. MVC - Model - View - Controller

Right Click your controllers folder, add, Controller. Select an empty ASP.NET MVC controller, and name it Home controller (That's the default routing name for controllers). A Home folder in your views has been created.

Time to use your freshly made models!

Add a new method in your HomeController class called ListUsers, that returns an ActionResult:

public ActionResult ListUsers()
        {
            return View();
        }



The name of the method (Action) is by default the name of the view, and the default routing is {controller}/{action}/{id}.

Change the body of the action to this:

            using (BoardContext db = new BoardContext())
            {
                return View(db.Users.ToList());
            }



So basically with this code, you declare and instantiate a boardcontext which'll we'll use for accessing the db data, and then pass a cached list of posts to our view. Right click the Views/Home folder, add, View. Name it ListUsers, and select an empty template without a model (we'll add it shortly).

In the view add the following code at the top of the file:

@model IEnumerable<EFASPNET.Models.User>



That code basically defines the parameter of the View. It also defines the model that the view uses. In our case it's a IEnumerable of Users.

Add the following code to the view:

<ul>
    @foreach (var user in Model)
    {
        <span>User: @user.Username</span>
    }
</ul>



You should be able to figure out what it does. It just fills the page with users. Now add a row of data to the users table of our application. I've added a row with the username value of "RexGrammer".

Run the application and add this to the url in your browser's address bar: /Home/ListUsers. See what happens? You didn't write a single query yourself but you queried the database and returned the result.

Now add this code to the Index action:

            BoardContext db = new BoardContext();
            return View(db.Posts.ToList());



Now add a view in the /Views/Home folder. Add the following code to it:

@model IEnumerable<EFASPNET.Models.Post>

<h2>Index</h2>

<ul>
    @foreach (var post in Model)
    {
        <span>Author: @post.User.Username</span>
        <br/>
        <p>Content: @post.Content</p>
    }
</ul>



Add a post manually to the database, then run the app. Magic, isn't it? :)

Now let's add one more functionality to our app, and that's to add new posts:

Add a new action called NewPost with the following code

        [HttpPost]
        public ActionResult NewPost(Post post)
        {
            using (BoardContext db = new BoardContext())
            {
                post.UserId = 1;
                db.Posts.Add(post);
                db.Entry(post).State = EntityState.Added;
                db.SaveChanges();
                return RedirectToAction("Index");
            }
        }



Now this is a bit different than our previous actions because it's a POST request and we define it so by adding the [HttpPost] annotation to the action. In our code we add a new post to our collection, tell the context that it's been added, and save the changes, then redirect to the Index action. (We also set the post.UserId to 1, meaning the user that wrote this will always be the one you added to your User table). Now add a view called new post and add the following code:

@model EFASPNET.Models.Post
<h2>NewPost</h2>

@using (Html.BeginForm()){
<fieldset>
    <legend>Group</legend>

    <div class="editor-label">
        @Html.LabelFor(model => model.Content)
    </div>
    <div class="editor-field">
        @Html.EditorFor(model => model.Content)
    </div>
    <p>
        <input type="submit" value="Create" />
    </p>
</fieldset>

}



Here we just create a form that submits the Post object to the NewPost action when you submit the form.

Add a new Action to your home controller:

 public ActionResult NewPost()
        {
            return View();
        }



It's for the GET request which is the default type of request.

Run it. Awesome, innit?

If you want to create an action that deletes specific posts, you can do so by doing something similar to this:

using (BoardContext db = new BoardContext())
            {
                post.UserId = 1;
                db.Posts.Remove(post);
                db.Entry(post).State = EntityState.Deleted;
                db.SaveChanges();
                return RedirectToAction("Index");
            }



BONUS: Using the ViewBag dynamic object to pass data to your view:

You can (In your controller action) write something like this:

ViewBag.TestData = "Santa Claus!";



And then in your view do this:

[span]@ViewBag.TestData[/span]


This way you can actually pass small bits of data between your controller and your view, without having to create a new model just for that.


At the end, I'd like to clarify a bit more about how ASP.NET works with requests. When the server receives a request, it splits the request url like this:

site.com/ControllerName/ActionName/id

You can manipulate the way your application treats these requests by writing controllers and actions. So you write a controller (which is usually a group of related actions like Account or Display) (the default controller is Home, and it's the controller used when there are no controllers specified), then the next part of the request url is used. It's the action name, and actions are stored in your controllers, the default actions is called Index and it's the action that's executed when there's no action specified. Id is a parameters which means you can pass parameters using that notation instead of the standard get requests: Controller/ActionName?username=RexGrammer.

3. ViewModels - Strongly Typed Views FTW

The way we've done so far, is to directly pass the data model to the view. That way we don't have a really good separation of those two layers. Instead there's an option for us to create an additional model, just for the view, that contains just the relevant data, and map the data model to the view model. That way we achieve complete separation.

Create a folder in your project called: ViewModels. In it create a code file named ViewModels.cs and inside it classes called: ListUsersModel, NewPostModel and ListPostsModel.

Now in each of these model classes, add properties that represent the relevant data (for that view). Here's mine:

public class ListUsersModel
    {
        public string Username { get; set; }
    }

    public class NewPostModel
    {
        public string Content { get; set; }
    }

    public class ListPostsModel
    {
        public User Author { get; set; }
        public string Content { get; set; }
    }



Now, go to your views, and instead of using data models as the view models, set them to these instead:

In the Index view, make this the model @model IEnumerable<EFASPNET.ViewModels.ListPostsModel> (You'll have to change the part of the view that displays the author name: Instead of @post.User.Username you should put @post.Author.Username.
In the NewPost view, make this the model @model EFASPNET.ViewModels.NewPostModel
In the ListUsers view, make this the model @model IEnumerable<EFASPNET.ViewModels.ListUsersModel>

All that's left is to map the data models to our view models. We'll add the mapping methods to our controller, although you can do it anywhere you want.

Let's make a method for mapping the post data model to our newpost view model:

        public NewPostModel MapNewPostModel(Post post)
        {
            return new NewPostModel(){Content = post.Content};
        }



Now alter the NewPost HTTP POST action, and make the necessary changes:

[HttpPost]
        public ActionResult NewPost(NewPostModel post)
        {
            using (BoardContext db = new BoardContext())
            {
                db.Posts.Add(new Post() { Content = post.Content, UserId = 1 });
                db.Entry(post).State = EntityState.Added;
                db.SaveChanges();
                return RedirectToAction("Index");
            }
        }



You should create similar methods for other model classes. And now you have perfect separation of data and presentation! Excellent.

4. WebAPI - Giving others the chance to experience the awesomeness of your application

Now to create the API that let's other people see all our posts in a serialized data format. Right click controllers, add, controller. Select WebAPI2 Controller - Empty and name it UserPostController. In the controller create a class called ApiPost and make it have 2 string properties Author and Content:

 public class ApiPost
        {
            public string Author { get; set; }
            public string Content { get; set; }
        }



Add a method to the webapi controller:

public IEnumerable<ApiPost> GetPosts()
        {
            throw new NotImplementedException();
        }



Now just use your database context to get the list of posts and map it to our new ApiPost class then return the list of posts:

BoardContext db = new BoardContext();
            List<ApiPost> posts = new List<ApiPost>();

            foreach (var post in db.Posts.ToList())
            {
                posts.Add(new ApiPost(){Author = "RexGrammer", Content = post.Content});
            }

            return posts;



Then try visiting /api/UserPost/GetPosts. See the output? It's a serialized list of posts. Great!

Outro


Just a few remarks. I've haven't done some things ideally or by the book, but that was to keep the simplicity of this tutorial. Hope you've learnt something new!

Note to Admins: Please remove the annoying />/>/> that are next to my smileys, they just keep coming, no matter what I do :o/>

Is This A Good Question/Topic? 0
  • +

Page 1 of 1