Friday, December 21, 2012

MVC and Routing Basics

Routing is located in the System.Web.Routing namespace

Can be used in MVC and also ASP.NET web forms apps.

Routing is used to map incoming URLS to action methods in a controller and optionally pass arguments to the action methods.

Generates outgoing urls (links in pages; generally used for navigation).

check out www.useit.com for guidelines on url best practices.

Routing is similar to URL Rewriting (been around for a long time in ASP.NET web forms). URL rewriting is geared towards pages and doesn’t really apply to MVC. Now you should almost always use Routing, but in MVC it is hard not to use routing.

 

Defining a route

  • URL Pattern
  • Default values
  • Constraints
  • Arbitrary properties

Look in the Global.asax.cs file under the RegisterRoute() method. In the RegisterRoute method you will use the MapRoute() method to specify any of the 4 items listed above. There are many overloads for this method.

routes.MapRoute(
“Default”, // route name
”{controller}/{action}/{id}”, // url pattern
new { controller = "Home”, action = “Index”, id = “”} // parameter defaults
);

The order or the MapRoute method is very important. They are processed from top to bottom. so put routes from more specific to more general.

URL Pattern

Default URL pattern: {controller}/{action}/{id}

Consists of segments. Each segment has URL parameters or placeholders and are shown as {…} and some static text.

The RouteValueDictionary holds the values that the parameters take based on the current url. For example if the url is http://localhost/Dog/Edit/3 then when the route is matched the RouteValueDictionary will contain keys and values as shown below.

controller = Dog
action = Edit
id = 3

You don’t have to use / to separate the placeholders. You can use just about anything, but it should be something that is not the data

Some other examples of url patterns:

“Brent/{controller}-{action}-{id}” –> Brent/Dog-Edit-3

“Brent/{controller}-{action}/{id}” –> Brent/Dog-Edit/3

 

Default Values

Works much like optional parameters in methods. If the parameter is not specified routing looks to see if there is a default value for that parameter (placeholder). They are passed to the routes.MapRoute() method.

http://localhost - default values for controller and action

http://localhost/Dog/List - default values for id

 

Constraint

What will happen if your url is http://localhost/Dog/Edit/3? In this example, let’s assume that the id is an int and 3 will be assigned to it. This matches the routing pattern fine, but what would http://localhost/Dog/Edit/Jewel do? It would try to assign Jewel to an int and that would not work and would result in the YSOD. To make it so the pattern is not matched we can introduce a constraint to only match numbers. Constraints can use regular expressions to control accepted values.

We could change the route to something like this using an overloaded Map method.

routes.MapRoute(
“Default”, // route name
”{controller}/{action}/{id}”, // url pattern
new { controller = "Home”, action = “Index”} // parameter defaults – notice no id default
new { id = @”\d{1,6} }

);

routes.MapRoute(
“Default”, // route name
”{controller}/{action}”, // url pattern
new { controller = "Home”, action = “Index”} // parameter defaults,
);

The first route is to accomplish what we were looking for. Unfortunately, it doesn’t allow for things like http://localhost and http://localhost/Dog/List. So, we had to add the second route to allow those routes.

Handling an Arbitrary Number of Parameters

Can use a catch-all parameter that:

  • match URLS with arbitrary number of parameters
  • Ignores slashes
  • Captures everything after what matches the url pattern to the end of the url
  • Excludes the query string parameters (doesn’t capture)
  • MUST be the last parameter in the url
  • Can only have one in url pattern

To do this, just a * to the beginning of the parameter name.

routes.MapRoute(
“CatchAll”, // route name
”Catch/All/Params/{*randomParams}”, // url pattern
new { controller = "Dog”, action = “CatchAll” } // parameter defaults
);

There needs to be a method in the Dog controller called CatchAll. It would look something like this

public ActionResult CatchAll(string randomParams) {… }

A matching url might look like this: http://localhost/Catch/All/Params/and/more/stuff/goes/here.

In this example, “and/more/stuff/goes/here” would be passed to the CatchAll method.

It is up to you to do something with the string. In most cases you will parse it and do something interesting with the values in the url.

 

Blocking Routes

You may want to block users from particular urls. To do that you would typically put a new route as shown below as one of the first routes in your routes collection so that it matches first.

routes.IgnoreRoute(“{resource.axd/{*pathInfo}”)

or

routes.Add(new Route(“{resource}.axd/{*pathInfo}”, new StopRouteHandler()));

Both of these do the same thing.

 

Generating URLS

You could hardcode urls in your MVC application, but is difficult to maintain and messy. Luckily MVC makes it easy when you use the routes to automatically generate urls. To access this automatic generation you would typically use HtmlHelper.ActionLink and HtmlHelper.RouteLink helper methods.

ActionLink – the url is based on the combination of the controller and action name, and any other parameters

RouteLink – relies on the route definitions and parameters

Works just like incoming routes regard order, so Routing will match the first one it finds which is based on the order you add it to the collection of routes.

 

Named Routes

If you want to force a particular route to be used. This works because the route name is basically a key into the list of routes in the route collection. This affects both the automatically generated urls (see ActionLink and RouteLink) and incoming urls. This would be useful if you want to use a route that is farther down the list of routes and thus would not usually be matched. Some people say that you should not use named routes, but the choice is yours.

Monday, December 10, 2012

Intro to MVC

URLS have the default format: /domain/controller/method/ID

Controller

They are generally light, or that should be the goal. (I need to verify this comment)

Class must end in Controller suffix and implement IController interface. However, we usually inherit from the Controller class since it inherits from IController and provides more common code we need. It cannot be abstract and cannot use generic parameters.

index() is the name of the Action Method for the default method that will be called.

Unless a method says otherwise, it looks in the Views folder. It then looks for a folder named the same as the class (minus the suffix Controller). Then it looks for the page with the name that matches the method name.

Action Methods can be overloaded, but also have attributes that determine how it is used.

Requirements to be an Action method:

  • Public (and not static)
  • Must be defined on System.Object or Controller
  • Cannot be a special method
  • Cannot have a NonAction attribute.


Security Warning: Anybody on the internet can invoke any public Method in your controller unless decorated as NonAction. However, you would usually only include methods that should be invoked based on a url.

Use the [ActionName("MyNewActionNameHere")] attribute on the Action method to change how it is used in the url. This changes what is matched for routing the request.

HttpGet, HttpPost, HttpPut, HttpDelete attributes to affect what overloaded method is called based on how it is being called. The verb is based on the verb in the current request.

use HandleUnknownAction() method to change what happens when a bad url is passed (i.e. 404 error). We could redirect to error page for example.

Controllers should be simple. Logic should be in the model. A controller's job is to translate user's actions to the model, then provide a response.

It is not uncommon to directly reference the Model (including the Entity Framework) here using Linq. If you do, it is best to instantiate the Entity Framework model in the action method rather than creating it at the controller level.

Action Filters

Implemented as attributes that can be used on Action Methods in the controller or the controller itself. Can use more than one, and specify order.
AsyncTimeout, NoAsyncTimeout, Authorize, ChildActionOnly, HandleError, OutputCache, RequiresHttps, ValidateAntiforgeryToken, ValidateInput

View

use Html.RenderPartial() to render a user control

<%: ... %> will automatically html encode (if not already) what is being rendered. This is a better choice than <% ... %>

Should be as simple as possible. No calculations, etc. Logic and calculations should be in the Model.

Give the View the raw data, let it format it the way the user should see it.

Can use open source view engines such as: Spark, Nvelocity, Brail, Nhaml

Generally will want to put files, etc in the Content directory.

Use ContentResult object as a return type in the Action to send a file to the browser.

Use ViewData object for use with strongly-typed model and Dictionary (name/value pairs). Allows code nuggets (<% %>)
Code Nuggets: <% %>

<%= %> not recommended anymore, use <%: %> due to data not being encoded.

<%: %> always use instead of = nuggest since it will do same thing, but safely encode content.

HTML Helper Methods help with writing tedious common html. They are really just shortcuts to generate HTML. Examples: Html.Label(), Html.LabelFor(), Html.TextBox(), Html.TextBoxFor(), Html.BeginForm(), Html.ValidationSummary(), Html.ValidateMessageFor(), Html.ActionLink(), Html.RenderPartial(), Html.TextArea(), Html.Hidden(), Html.RouteLink(), Html.Action()

Partial Views (implemented as user Controls). They allow you to reuse html and view code. They can be handy for Create and Edit views that share nearly all the html. Generally they should be in the Views/Shared if they will be used by multiple controllers. If they will be used by one controller, but multiple views then put it in the Views/<Controller Folder>. use the Html.RenderPartial() to include the partial view on the part of the page you want it to display. Shares data with parent model by default, but this can be changed to use a different model by passing different model via Html.RenderPartial(). Partial views can be nested.

Use RenderAction() to call controller action methods from a view and display the result of that action method. useful to show data that is not part of the model of the view. Similar to using a RenderPartial(), but instead of inserting a partial view, you can insert virtually anything because most anything you return from an Action method. Beware of using this too often because the calls between controller and views becomes difficult to maintain. Only use it when needed.

HtmlHelper - AntiForgeryToken(), Encode(), EnableClientValidation(), AttributeEncode(), HttpMethodOverride(). 3 classes of helpers: 1. Untyped or regular helper methods, 2. Strongly typed helper methods, 3. templated helper methods. Lots of overloads. Searches for fields in the following order: ModelState, ViewData, and view model. Can put @ before a custom attribute that is passed to a helper. You can also use a Dictionary.
The Html.Label() helper method will use the Display() attribute from the Model to determine what text to display.

There are some high level helpers for Grids and Charts. For those, check out, WebGrid() with getHtml() and Chart with AddTitle(), AddSeries(). These are used with Razor.

Can supply a default value via an extra parameter on helper methods.

Most common Templated Helper Methods

  • Display, DisplayFor, DisplayForModel
  • Edit, EditorFor, EditorForModel

Models

Most any kind of technology or conventions can be used here. This would include things like Entity Framework, etc.
Encapsulates data and business rules, logic, validation, calculations, etc. This is where all the heavy lifting is done.
Use directly in conrollers and views
It is NOT required to add the suffix Model to the classes, but it does generally make it easier to follow in code.

Monday, December 3, 2012

Truncating a table from a Linked Server

Let’s assume you are using SQL Server and you want to truncate a table on a Linked Server but are getting errors about permissions.

Msg 4701, Level 16, State 1, Line 1

Cannot find the object "MyTable" because it does not exist or you do not have permissions.

The first thing to check is that you have the proper permissions. MSDN says

“The minimum permission required is ALTER on table_name. TRUNCATE TABLE permissions default to the table owner, members of the sysadmin fixed server role, and the db_owner and db_ddladmin fixed database roles, and are not transferable. However, you can incorporate the TRUNCATE TABLE statement within a module, such as a stored procedure, and grant appropriate permissions to the module using the EXECUTE AS clause.”

Assuming you do actually have permission the problem is probably in the syntax.

My guess is that you tried the same thing I did to start with, which is the following.

truncate table MyLinkedServer.MyDB.dbo.MyTable

You can use truncate table statement on a Linked Server, but not with that syntax and you have to know a trick. Here is the same statement, but using a different syntax.

EXEC MyLinkedServer.MyDB.sys.sp_executesql N'truncate table dbo.MyTable'

It seems a bit convoluted to me, but it works.

NOTE: If you get a message about … is not configured for RPC then click here to read how to fix that issue.

How to resolve: Msg 7411, Level 16, State 1, Line 1 Server 'MyServer' is not configured for RPC.

 

If you get the following error when trying to execute a stored procedure on a Linked Server in MS SQL Server you need to enable RPC on the server that has the linked server defined (I called my linked serverMyServer in this example)

Msg 7411, Level 16, State 1, Line 1

Server 'MyServer' is not configured for RPC.

The solution is simple, just execute the following SQL on the server where you have defined the Linked Server. You will need to change MyServer to the name of your linked server.

EXEC master.dbo.sp_serveroption @server=N'MyServer', @optname=N'rpc', @optvalue=N'true'
GO

EXEC master.dbo.sp_serveroption @server=N'MyServer', @optname=N'rpc out', @optvalue=N'true'

You need RPC enabled on the Linked Server so that you can called stored procedures. In most cases you only need to do the second one that allows out, but if you have problems you can do the first one also. Most people recommend doing both even though both are not needed in many cases.