The Hands on MVC set of videos: www.learnvisualstudio.net
Routing
In global.asax
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new {controller = "Home", action = "Index", id = ""} // Parameter defaults
);
This means eg
http://localhost:3203/Home/About
will come to the controller called HomeController and run the method called About
public ActionResult About()
{
return View();
}
which in this case will call the view with the same name called About.aspx
What about http://localhost:3203/ which is the same as /Home and /Home/Index
In this case the defaults are being used eg Home, Index , null… as shown above in //Parameter defaults.
It even passes a message from the Home controller, Index method.
ViewData["Message"] = "Welcome to ASP.NET MVC!";
Hardcode /Home to mean something
// for hardcoded /home goto home/member
routes.MapRoute(
"MemberHome",
"home",
new {controller = "Home", action = "Member"}
);
Building UI Form
get is putting in query string
post is putting in the http request body
utility methods for not directly specifying URL
<%Html.BeginForm(); %>
<% Html.EndForm(); %>
Useful to see the Request coming back from /home/member into the HomeController, Member method.
asdf
Yuk!
Post redirect get pattern.
Basically stops the above resend message by getting the browser to do a redirect after the post page, to a nice get page.
[Authorize]
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Member(string status) // this will match up the name status in the forms collection
{
//return View();
return RedirectToAction("Member");
}
The Model using LinqToSQL
Added a simple table with a FK to the aspnet_users UserId column.
The UserId FK is a guid.
The Timestamp has a default value of GETUTCDATE() so that the db writes in the timestamp.
LinqToSQL has:
readonly – shouldn’t allow app to change it
auto Generated Value = true - hmm to do with:
Auto_Sync – so it will auto get the timestamp property back into the model whenever it does an insert.
Can see the class generated that represents our StatusUpdate table:
The View
In the controller:
List updates = db.GetUserUpdates(User.Identity.Name);
return View(updates);
and in the view:
System.Web.Mvc.ViewPage>
we now get a strongly typed object
View Models
Have simply created another class called MemberViewModel in Models:
public class MemberViewModel
{
public List Updates { get; set; }
}
so can pass in more complex stuff than just a list into my view (can’t pass in multiple lists)
So the view now has:
and we can access data like:
foreach (var update in Model.Updates)
Lazy Loading and Eager Loading.
However we’ve hit an interesting issue:
this is because of in the view we’ve called:
Html.Encode(update.User.UserName)
And in the actual query, it returns only the StatusUpdate object and not the User object (even though it does a join).
var updates = from update in this.StatusUpdates
where update.User.UserName == userName
orderby update.Timestamp descending
select update;
in the controller after query is ran and the result put into the ViewModel, then passed to the View… the datacontext is destroyed.
using (var db = new TwixDataContext())
{
ViewData["Stuff"] = "Blah this is a generic object with no intellisense";
MemberViewModel model = new MemberViewModel
{
Updates = db.GetUserUpdates(User.Identity.Name)
};
//List updates = db.GetUserUpdates(User.Identity.Name);
return View(model);
}
So when the view tries to call the User.UserName.. LinqToSQL tries to go and get this data, as it hasn’t been needed yet. This is lazy loading.
We want LinqToSQL to load the User object at the same time as loading the StatusUpdates.. this is Eager Loading.
CSS
Making things look good:
#timeline { list-style: none; padding: 0;}
#timeline li {border-top:dashed 1px #888; padding: 5px 0; }
#timeline li:hover { background-color: #eee; }
#timeline li div.message { font-size: 10pt; }
#timeline li div.message span { font-weight: bold; }
#timeline li div.time { font-size: 8pt; font-style: italic }
for:
Creating a Home/Profile
coppied the get from index, however instead of passing in the current logged in user to GetUserUpdates, am passing in the string ie it will be localhost/nameofperson or localhost/home/profile/nameofperson
Also created a new viewmodel for profile that has username in it (as we’ll need this). Strongly typed as well.
public class ProfileViewModel
{
public string UserName { get; set; }
public List Updates { get; set; }
}
Partial View (#7)
create a viewmodel just for the timeline ie this partial view.
changing MemberViewModel and ProfileViewModel to have a TimeliveViewModel (abstracting it away to get rid of duplication)
public class MemberViewModel
{
public TimelineViewModel TimelineModel { get; set; }
}
public class TimelineViewModel
{
public List Updates { get; set; }
So we reference it in the controller like this:
ProfileViewModel model = new ProfileViewModel
{
UserName = userName,
TimelineModel = new TimelineViewModel{
Updates = db.GetUserUpdates(userName)
}
};
and then in the view:
<%foreach (var update in Model.TimelineModel.Updates) {%>
Hyperlinks, Anonymous Type - create new view as Timeline.ascx
<%Html.RenderPartial("Timeline", Model.TimelineModel); %>
<%Html.ActionLink(update.User.UserName, "profile", new {userName = update.User.UserName}) %>
First property is the text to be displayed, second is the action to be called when they click this link (method on this controller),
We’re not specifying the controller here, so it is going to user the current (home) controller.
The third is a generic object which represents the extra route values that we want.. as the method takes a parameter called userName, we need to pass it. We use an anonymous type
The crowd goes wild 
Seeing Other Peoples Tweets
A common technique when we need a Many to Many relationship.
each user can follow many other users
each user can be followed by many other users
The primary key is both on the FollowerMappings table.
Then in the Mappings:
We have renamed the child and parent properties for clarity.
The first FK relationship is the FollowerId being held constant. So the Child Property is the Followee (all the people the user is following)
The second FK relationship is the FoloweeId being held contact. So the Child Property is the Follower (all people who are following the user).
**TODO refactor and come up with better names.
Adding Methods to The Model DataContext
GetAllUpdates – gets all status update records including those psoted by user that this user is following
IsFollowing – checks if a specific user name is following another user name… so we know whether to display a button
DeleteFollowerMapping
Adding Methods to the HomeController
Changed the get method on Home/Profile to GetUserUpdates.. and to do logic for displaying the Follow/Stop Following button.
Added a post method for Follow (which is called from Profile as a form post), and redirects back to Profile.
[Authorize]
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Follow(string userName)
{
if (!string.IsNullOrEmpty(userName) && !userName.Equals(User.Identity.Name, StringComparison.OrdinalIgnoreCase))
{
using (var db = new TwixDataContext())
{
if (!db.IsFollowing(User.Identity.Name, userName)) // double check they are not already being followed
{
var followerMapping = new FollowerMapping
{
FollowerId = db.GetUserIdForUserName(User.Identity.Name),
FolloweeId = db.GetUserIdForUserName(userName)
};
db.FollowerMappings.InsertOnSubmit(followerMapping);
db.SubmitChanges();
}
}
}
return RedirectToAction("Profile", new {userName = userName});
}
Added a post method for StopFollowing.. same as above.
Adding Properties to the ProfileViewModel
public class ProfileViewModel
{
public string FollowText { get; set; }
public string FollowAction { get; set; }
public bool IsSelf { get; set; }
public string UserName { get; set; }
public TimelineViewModel TimelineModel { get; set; }
}
So when we pass this object to the view, we’ve got lots of strongly typed goodness.
Using in the View
using (Html.BeginForm(Model.FollowAction, "home")) {
%>
<%=Html.Hidden("userName", Model.UserName) %>
<%
}
where the brace ends, is just the same as doing an Html.EndForm.
Best Practise
Use a form post instead of a link if doing database stuff to prevent hacking.
Summary so Far
Why do we have so much business logic in the controller?
next video is #9
why is this line sometimes giving Http Exception in home controller.
var isSelf = userName.Equals(User.Identity.Name, StringComparison.OrdinalIgnoreCase);
#9 – is jQuery.. thats all folks or more stuff.
#10 – is account settings.. name, email, timezone.. form field validation… combo box (dropdown)… nice fade out jquery front end.
#11 – is upload photo
#12 – is search.. ?q=learnvisualstudio… not a post… stuff in his source code that isn’t in video. icon/link delete a post.