Search

Categories

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Send mail to the author(s) E-mail

# Thursday, April 01, 2010

elevator

use cases

paper prototype:

put mock up live

http://pubtricks.heroku.com/

image

changes to scaffold:

background colour in all games in \views\layouts\games.html.erb

Comments [0] | | # 

 

heroku - free
dreamhost - 9us per month
railsplayground - 5us per month
slicehost - vps.. bout 20 per month

gem install heroku

image

create git repo

heroku create

  it then added ssh key to remote

git remote – has added in the remote

git push heroku master

image

hmmm…

tried:  heroku keys:add c:\id_rsa.pub

remembered i’d put my key in

c:\home\.ssh\id_rsa.pub

heroku rake db:migrate

image

The crowd goes wild!

Comments [0] | | # 
# Friday, March 26, 2010

After an awesome mountainbike ride I’ve managed to break my collar bone.  I wouldn’t recommend this :-)

So I though I’d make the most of the ‘opportunity’ and have a look at RoR..

image

Goal:  To deploy an application live

Downloading RoR

rubyonrail.org – download.  Getting 1.8.7 of Ruby.. tick add to executable, and associate .rb

rubygems – package manager..extract, run setup.rb

gem install rails 2.3.5

rails --version

http://docs.heroku.com/windows – good install screencast

Hello World App

rails blog

cd blog

ruby script/server

localhost:3000

uses WEBrick 1.3.1

am getting strange error:

image

Install SQLite3

http://akitaonrails.com/2009/01/13/the-best-environment-for-rails-on-windows

http://www.sqlite.org/download.html

sqlite3.exe into \windows

sqlite3.dll into system32

 

http://allaboutruby.wordpress.com/2009/07/20/installing-rails-on-windows-3-years-later/

gem install sqlite3-ruby

cd \code\ruby\hello

ruby script\server

the crowd goes wild!!!

image

ruby script/generate scaffold post title:string description:text

This command creates a Post scaffold, a model/controller/view ready to be updated.  The post model will have 2 fields – a title which is a single line of text and a description, which is multi-line text.

image

Run the database migration. This is because after creating the scaffold, the database is not automatically updated with the new blog model, you need to do it manually by running a rake db:migrate command

image

cool – now have db integration and working scaffold

http://railsforum.com/

http://railscasts.com/episodes/archive

http://allaboutruby.wordpress.com/2009/08/08/5-minute-project-in-rails/ – very simple app.

http://ruby-toolbox.com/categories/rails_form_builders.html

 

http://guides.rubyonrails.org/getting_started.html#installing-rails

in c:\code

rails blog   - this creates a rails app that uses sqllite.

 

The Blog from Guide

http://guides.rubyonrails.org/getting_started.html

rails blog

rake db:create

rake –T  shows all rake (general purpose command runner) commands

ruby script/generate controller home index  -creates files including app/views/home/index.html.erb. This is the template that will be used to display the results of the:

 index action (method) in the home controller.

image

edit index.html.erb to <H1>Hello Rails</H1>

ruby script\server

http://locahost:3000/home/index

image

Change home page

config\routes.rb

map.root :controller => "home"

Scaffolding

Rails scaffolding is a quick way to generate some of the major pieces of an application. If you want to create the models, views, and controllers for a new resource in a single operation, scaffolding is the tool for the job.

ruby script/generate scaffold Post name:string title:string content:text

This creates a model, a db migration ruby file, views and changes the config\route.rb file.

rake db:migrate

put in:  <%= link_to "My Blog", posts_path %>

image

then

image

image

 

Validation

The model file, app/models/post.rb is about as simple as it can get:

validates_presence_of :name, :title

validates_length_of :title, :minimum => 5

image 

View

<% @posts.each do |post| %>
  <tr>
    <td><%=h post.name %></td>
    <td><%=h post.title %></td>
    <td><%=h post.content %></td>
    <td><%= link_to 'Show', post %></td>
    <td><%= link_to 'Edit', edit_post_path(post) %></td>
    <td><%= link_to 'Destroy', post, :confirm => 'Are you sure?', :method => :delete %></td>
  </tr>
<% end %>

h is a rails helper

lik_to buils hyperlinks

edit_post_path

new_post_path

Layout

These are containers for views

app\views\layouts\posts.html.erb

<body style="background: #EEEEEE;">

now all views for posts have a grey background

Entering Data and Wiring Up

<% form_for(@post) do |f| %>
  <%= f.error_messages %>

form_for block is used to create an html form..

<%= f.label :name %><br />

<%= f.text_field :name %>

wires up a text box with name attribute

DRYing up Code - Partials

scaffold generated views for new and edit are largely identical.. so can use partials template.

_form.html.erb  underscore is convention for partial

<% form_for(@post) do |f| %>
  <%= f.error_messages %>

  <p>
    <%= f.label :name %><br />
    <%= f.text_field :name %>
  </p>
  <p>
    <%= f.label :title %><br />
    <%= f.text_field :title %>
  </p>
  <p>
    <%= f.label :content %><br />
    <%= f.text_area :content %>
  </p>
  <p>
    <%= f.submit 'Save' %>
  </p>
<% end %>

and in new.html.erb

<h1>New post</h1>

<%= render :partial => "form"  %>

<%= link_to 'Back', posts_path %>

****7.2 Using Filters to eliminate controller duplication

 http://wiki.devchix.com/index.php?title=Server_2003

The Blog App Video

From front page of rails site:

http://www.opensourcerails.com/

http://www.softwaredeveloper.com/features/best-ruby-on-rails-061307/

http://www.3months.com/technology

http://blog.obiefernandez.com/content/

 

Auth

http://wiki.rubyonrails.org/howtos/authentication-authorization

Authentication is what you do when you let a user identify itself. This is needed when you want to offer a login access to your application.

Authorization is what you do when you check the credentials of a user before letting him/her interact with specific sections of your system. This is needed when you have restricted areas/actions.

Comments [0] | | # 
# Tuesday, February 23, 2010

This is useful to get checking on the aspx page at compile time. 

Right click on the project, and select unload.

Edit the .csproj file

image

Set MvcBuildViews to true

Save, reload, compile.

Comments [0] | | # 
# Monday, February 22, 2010

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.

image

asdf

image

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.

 

image


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:

image

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:
 
System.Web.Mvc.ViewPage

and we can access data like:
 
foreach (var update in Model.Updates)

 

Lazy Loading and Eager Loading.

However we’ve hit an interesting issue:

image

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:

    "timeline">
    <%foreach (var update in Model.Updates) {%>


  • class="message">
    <%= Html.Encode(update.User.UserName) %>:
    <%=Html.Encode(update.Message) %>

    class="time">
    <%=update.Timestamp.ToLocalTime().ToString() %>


  • <%} %>
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

 

image

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

image

The primary key is both on the FollowerMappings table.

Then in the Mappings:

image

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?

DSCN1403

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.

Comments [0] | | # 
# Friday, February 19, 2010

Pairing with Michael, we did our first spike from here:

http://ericdotnet.wordpress.com/2009/04/09/jquery-search-box-and-aspnet-mvc/

worked fine locally, but then going live getting some issues.  Looked like it was mateerit.co.nz/search

<script src=" <%=Url.Content("~/Scripts/jquery-1.3.2.js") %>" type="text/javascript"></script>

The above seemed to work, however the javascript seemed harder:

<script type="text/javascript">

$(document).ready(function() {
$("#searchTerm").autocomplete("/Home/getAjaxResult/");
});

</script>

so I cheated and went onto a subdomain:  jsearch.mateerit.co.nz

All works now with firebug too.

image

Comments [0] | | # 

I use vimperator, however sometimes I like to have the normal menus back.  The command I have to type is:  set guioption+=mT

Trick is to download autohotkey:

Run Autoscriptwriter (recorder)

save the file, and assign a hotkey.  In this case Windows Key, s

#s::  - set guioptions
Send, {SHIFTDOWN};{SHIFTUP}set{SPACE}guioptions{SHIFTDOWN}={SHIFTUP}=m{SHIFTDOWN}t{SHIFTUP}{ENTER}

#t:: – tv on

#8:: - 85hz

Then put the files in the startup script directory: C:\Documents and Settings\dave\Start Menu\Programs\Startup

Comments [0] | | # 
# Tuesday, February 16, 2010

http://www.mikesdotnetting.com/Article/128/Get-The-Drop-On-ASP.NET-MVC-DropDownLists

1) SelectListItem – Value and Text property assigned

var db = new northwindDataContext();
IEnumerable<SelectListItem> items = db.Categories.Select(c => new SelectListItem {Value = c.CategoryID.ToString(), Text = c.CategoryName});
ViewData["CategoryID"] = items;
 
in the view:
<%= Html.DropDownList("CategoryID") %>

which is the pretty much the same as:
 
<%= Html.DropDownList("CategoryID", (IEnumerable<SelectListItem>)ViewData["Categories"]) %>

2) SelectList is tidier, and gives a couple of overloads including selected (in the case below number 3):
 
var query = db.Categories.Select(c => new {c.CategoryID , c.CategoryName});
ViewData["Categories"] = new SelectList(query.AsEnumerable(), "CategoryID", "CategoryName",3);

image
 

PostBack

On the product view:

<% using (Html.BeginForm(null, null, FormMethod.Post, new { id = "TheForm" })) {%> 
<%=Html.DropDownList("CategoryID", (SelectList)ViewData["Categories"], new { onchange="this.form.submit();"})%>
<%}%>

and the product controller:

public ActionResult Index(int? categoryid)
{
var db = new northwindDataContext();

var query = db.Categories.Select(c => new {c.CategoryID, c.CategoryName});
ViewData["Categories"] = new SelectList(query.AsEnumerable(), "CategoryID", "CategoryName");

List<Product> products;
if (categoryid == null)
products = db.Products.ToList();
else
products = (from p in db.Products
where p.CategoryID == categoryid
select p).ToList();
return View(products);
}

which gives:
 
image 
Used LinqToSQL.
ASP.NET MVC version 1
 

Run Implementation

Found I had to sort a generic list
 
The crowd goes wild:
image
Comments [0] | | # 
# Monday, February 15, 2010

Both my asp.net web hosters advertised they are hosting on Server 2008, which meant IIS7.  This makes things much easier when publishing an MVC site (to do with routing apparently).  As I’d hosted my sites for years with these providers, they were still on Win2003 (IIS6) boxes.  I just had to ask for an upgrade and it was done.

Upload to IIS7 and it just works!

The first step was to see if routing worked on the default website.. it did!

The next step – to publish up the NerdDinner app I’ve been going through on Rob Conery, and get the database connected live.

image

The crowd goes wild  - this is live and connecting to a database :-)

Changing Namespace / Solution Name / Project Names

I had lots of namespace errors.. forgot to change default assembly namespace, which meant linq to sql was in the wrong space.  However it is possible to change everything very quickly – solution, project names, directly.  One gotcha was remembering to delete everything on the live server so there weren’t 2 dll’s with the same object names inside..

Changing Default Page to Activity / upload live trick

routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Activity", action = "Index", id = "" } // Parameter defaults
);

Change controller = “Home” to controller = “Acitivity”

I also implemented the copyifnewer.bat trick for easily going from dev to live which is here: http://www.programgood.net/2010/01/28/WebConfigAutomatingDevAndLive.aspx

Run application working live with Create, Read, Update and Delete working.  Also validation, which comes for free in the standard templates.

image

DropDownList and Postbacks – covered in another post on this site.

http://www.mikesdotnetting.com/Article/128/Get-The-Drop-On-ASP.NET-MVC-DropDownLists

Summary Page

I created another link in the master page for menu item:

www.mateerit.co.nz/run

image

For the summary page I’m considering a ViewModel approach.

Lets see if a simple new Summary controller will suffice

Got to a stage.. was going to upload to github, but had db connection strings in there which didn’t want in the history.. so looked at rebase in git.. want to make sure before I play too much :-)

 

Creating a SQL View then Displaying it

Michael came up with an elegant solution which I’m trying.. so creating a SQL View:

SELECT     personid,    
DATEPART(week, date) AS Week,
DATEPART(year, date) AS Year,
SUM(kilometres) AS kilometres,
SUM(hours) AS hours
FROM dbo.Activity
GROUP BY personid, DATEPART(week, date), DATEPART(year, date)

which gives a good summary output:

image

The aim is to be able to see the summary of each week like this (this is the old webforms version)

image

linq to sql generated classes off the view.

Michael did it like this:

image

I’m on this:

image

Passing Complex Data – Lists within Lists

As each person has data associated to them, a list within a list is good.

image

public class WeekSummary
{
public int Personid;
public string Personname;
public List<WeekSummaryData> Persondata;
}

public class WeekSummaryData
{
public double? Hours;
public double? Kilometers;
public int? Week;
public int? Year;
}

Then in the repository:

public IQueryable<WeekSummary> GetWeeklySummary()
{
var weekSummaries = from p in db.Persons
orderby p.personname
let data = GetWeeklySummaryData(p.personid)
select new WeekSummary {Personid = p.personid,
Personname = p.personname,
Persondata = new List<WeekSummaryData>(data)};
return weekSummaries;
}

IQueryable<WeekSummaryData> GetWeeklySummaryData(int personid)
{
var summary = from w in db.weekly_summaries
where w.personid == personid
orderby w.Year , w.Week
select new WeekSummaryData {Hours = w.hours,
Kilometers = w.kilometres,
Week = w.Week,
Year = w.Year};
return summary;
}

** understand this linq

Then in the controller:

public ActionResult Index()
{
var summaries = _repository.GetWeeklySummary();
return View(summaries);
}

and the view:

** understand this rendering

<% foreach (WeekSummary weekSummary in Model) {
%>
<table>
<tr>
<th></th>
<% foreach (WeekSummaryData weekSummaryData in weekSummary.Persondata) { %>
<th><%=Html.Encode(weekSummaryData.Week) + "/" + Html.Encode(weekSummaryData.Year) %></th>
<%}%>
</tr>
<tr>
<td>
<%=Html.Encode(weekSummary.Personname) %>
</td>
<% foreach (WeekSummaryData week in weekSummary.Persondata) { %>
<td><%=Html.Encode(week.Hours) + "hrs" %><br /><%=Html.Encode(week.Kilometers) + "kms" %></td>
<%}%>
</tr>
</table>
<% } %>

 

refactor code with an interface for testing

unit test do

automock

jquery

tooling to help make form crud websites faster…

reproting tooling

mvc2

Comments [0] | | # 
# Friday, February 12, 2010

While pair programming with some great developers I asked the question:  How would you refactor this project (the running project website).

When both of the guys I paired with suggested that it would be smart to move to MVC and to use an ORM, I listened.

After downloading ASP.NET MVC version 1.  I’ve been working through the tutorial from Rob Conery:

http://www.asp.net/learn/mvc-videos/video-8143.aspx (about 80mins)

Creating a New MVC Project

Ctrl Shift N

Linq to SQL

image

going to use Membership for login / users.

In models, add new item: LINQ to SQL

drag on the two tables

image

Original table name was dinners.  The class is called dinner

Renamed NerddinnerDataContect to DB, and context namespace to Nerddinner

however Entity Namespace to Nerddinner.Model – which didn’t work!  I left it blank.

Adding a Controller

The default Index controller

var db = new DB();
var dinners = db.Dinners;
return View(dinners);

Adding a View

right click inside method in controller

Create strongly-typed view

View content – code gen nice.

Tooling figured out which is the primary key (as linq to sql)

 

ModelBinders - Add

When creating a new Dinner, we use ModelBinders.. mvc looks at the Object and sees if it can bind to the linqtosql.

Could have used the existing FormCollection, but this is easier!

If ModelState.IsValid   .. nice.

image

And by changing the Html.ValidationMessage

image

Editing Data

In the controller:

public ActionResult Edit(int id)
{
var db = new DB();
var dinner = db.Dinners.SingleOrDefault(x => x.DinnerID == id);
return View(dinner);
}

This is another way of expressing the above:

public ActionResult Edit(int id)
{
var db = new DB();
//var dinner = db.Dinners.SingleOrDefault(x => x.DinnerID == id);
var dinner = (from a in db.Dinners
where a.DinnerID == id
select a).SingleOrDefault();

return View(dinner);
}

<% gator tags

Other view engines – Spark or nHaml

image

So we’ve built scaffolding.

Routing

We want /dinners to the be root of the site.

Routing is the thing that listens for URL’s and passes them to a controller.

eg Dinner / Edit /5

eg { controller} / {action} / {id}

To change to be /dinners as the route in global.asax

static public void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new {controller = "Dinner", action = "Index", id = ""} // Parameter defaults
);
}

We changed from Home to Dinner above.

Deleting

image

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Delete(int id)
{
var db = new DB();
var dinner = (from a in db.Dinners
where a.DinnerID == id
select a).SingleOrDefault();
db.Dinners.DeleteOnSubmit(dinner);
db.SubmitChanges();

return RedirectToAction("Index");
}

are wired up on the view:

<% using (Html.BeginForm("delete", "dinner")) {%>
<%=Html.Hidden("id",Model.DinnerID) %>
<input id="Submit1" type="submit" value="delete" />
<% } %>

We are not using any authentication to stop people doing this at the moment. For that we need:

[AcceptVerbs(HttpVerbs.Post)]
[Authorize(Roles="Administrator")]
public ActionResult Delete(int id)
{
var db = new DB();

Testing / Repository Pattern

Because the controller has database connection inside of it, this isn’t good from a testing point of view, so we’re going to refactor this out.

Speed would be a problem with lots of tests, as would not knowing what is in the database.

Created an IDinnerRepository interface:

public interface IDinnerRepository
{
IQueryable<Dinner> FindAllDinners();
Dinner GetDinner(int id);
void Add(Dinner dinner);
void Update(Dinner dinner);
void Delete(Dinner dinner);
void Save();
}

Then a SqlDinnerRepository that implements this, where the constructors calls the DB:

public class SqlDinnerRepository : IDinnerRepository
{
DB db;

public SqlDinnerRepository()
{
db = new DB();
}

public IQueryable<Dinner> FindAllDinners()
{
return db.Dinners;
}

public Dinner GetDinner(int id)
{
var dinner = (from a in db.Dinners
where a.DinnerID == id
select a).SingleOrDefault();

return dinner;
}

Then in the Dinner Controller:

public class DinnerController : Controller
{
IDinnerRepository _repository;

public DinnerController()
{
_repository = new SqlDinnerRepository();
}

// this will be the one that the test uses
public DinnerController(IDinnerRepository repository)
{
_repository = repository;
}

//
// GET: /Dinner/
public ActionResult Index()
{
//var db = new DB();
//var dinners = db.Dinners;
var dinners = _repository.FindAllDinners();
return View(dinners);
}

The plan is that we will make up something that implements IDinnerRepository when testing to make our life easier.

DSCN1379_dave

Made: FadinnerkeDinnerRepository

Made: DinnerControllerTests

Ctrl R T – Run tests

Found error in test code:

 //var data = result.ViewData.Model as IList<Dinner>; // this didn't work.. hmmmmmmm.
var data = result.ViewData.Model as IEnumerable<Dinner>;

TDD

Made a test that dinners should only return dates that are today or later:

[TestMethod]
public void Index_Should_Return_Dinners_For_Today_Or_Later() {
// Arrange
var controller = new DinnerController(new Fakes.FakeDinnerRepository());

// Act
//ViewResult result = (ViewResult) controller.Index();
var result = controller.Index() as ViewResult;

// Assert
var data = result.ViewData.Model as IEnumerable<Dinner>;
Assert.IsFalse(data.Where(x=>x.EventDate<DateTime.Now).Count()>0);
}

Failed the test, then wrote the code which was in the DinnerController:

var dinners = _repository.FindAllDinners().Where(x=>x.EventDate >= DateTime.Now);

 

Lambda expressions

var dinners = _repository.FindAllDinners().Where(x=>x.EventDate >= DateTime.Now);

 

Presentation Model Pattern or View Model

45min into Rob’s video.

Comments [0] | | #