Controller versus view in MVC .net: is the code in the view as fast as that in the controller? Is it slower?

One of the basic rules of MVC is that views should be only – exactly – views, that is to say: objects that present to the user something that is already “worked and calculated”.

They should perform little, if not none at all, calculation. All the significant code should be in the controllers. This allows better testability and maintainability.

Is this, in Microsoft’s interpretation of MVC, also justified by performance?

We tested this with a very simple code that does this:

– creates 200000 “cat” objects and adds them to a List

– creates 200000 “owner” objects and adds them to a List

– creates 200000 “catowner” objects (the MTM relation among cats and owners) and adds them to a List

– navigates through each cat, finds his/her owner, removes the owner from the list of owners (we don’t know if cats really wanted this, but their freedom on code fits our purposes).

We’ve run this code in a controller and in a razor view.

The result seem to suggest that the code in views runs just as fast as in controllers even if don’t pre-compile views (the compilation time in our test is negligible).

The average result for the code with the logic in the controller is 18.261 seconds.

The average result for the code with the logic in the view is 18.621 seconds.

The performance seems therefore very similar.

Here is how we got to this result.

Case 1: Calculations are in the CONTROLLER

Models:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace WebPageTest.Models
{
public class Owner
{
public string Name { get; set; }
public DateTime DOB { get; set; }
public virtual CatOwner CatOwner { get; set; }
}
public class Cat
{
public string Name { get; set; }
public DateTime DOB { get; set; }
public virtual CatOwner CatOwner { get; set; }
}
public class CatOwner
{
public virtual Cat Cat { get; set; }
public virtual Owner Owner { get; set; }
}
}

Controller:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using WebPageTest.Models;

namespace WebPageTest.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
Stopwatch howLongWillItTake = new Stopwatch();
howLongWillItTake.Start();
List<Owner> allOwners = new List<Owner>();
List<Cat> allCats = new List<Cat>();
List<CatOwner> allCatOwners = new List<CatOwner>();
// create lists with 200000 cats, 200000 owners, 200000 relations
for (int i = 0; i < 200000; i++)
{
//Cat
Cat CatX = new Cat();
CatX.Name = “Cat ” + i.ToString();
CatX.DOB = DateTime.Now.AddDays(i / 10);
//Owner
Owner OwnerX = new Owner();
OwnerX.Name = “Owner ” + i.ToString();
OwnerX.DOB = DateTime.Now.AddDays(-i / 10);
//Relationship “table”
CatOwner CatOwnerXX = new CatOwner();
CatOwnerXX.Cat = CatX;
// Relations
CatOwnerXX.Owner = OwnerX;
CatX.CatOwner = CatOwnerXX;
OwnerX.CatOwner = CatOwnerXX;
//add to list
allCats.Add(CatX);
allOwners.Add(OwnerX);
allCatOwners.Add(CatOwnerXX);
}
// now I remove all the items
foreach (Cat CatToDelete in allCats)
{
Owner OwnerToRemove = CatToDelete.CatOwner.Owner;
allOwners.Remove(OwnerToRemove);
}
// now all cats are free
int numberOfCats = allCats.Count();
int numberOfOwners = allOwners.Count();
howLongWillItTake.Stop();
long elapsedTime = howLongWillItTake.ElapsedMilliseconds;
// give info to the view
ViewBag.numberOfCats = numberOfCats;
ViewBag.numberOfOwners = numberOfOwners;
ViewBag.elapsedTime = elapsedTime;
return View();
}
}
}

View:

<div class=”row”>
<div class=”col-md-12″>
<hr />
<b>Results</b>
<br/>
Cats: @ViewBag.numberOfCats
<br/>
Owners: @ViewBag.numberOfOwners
<br/>
ElapsedTime in milliseconds: @ViewBag.ElapsedTime
<hr />
</div>
</div>

Case 2: Calculations are in the VIEW (pre-compiled)

Models: same as above

Controller:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace WebPageTest.Controllers
{
public class HomeBisController : Controller
{
public ActionResult Index()
{
return View();
}
}
}

View:

@using System;
@using System.Collections.Generic;
@using System.Diagnostics;
@using System.Linq;
@using System.Web;
@using WebPageTest.Models;
@using System.Web.Mvc;
@{
Stopwatch howLongWillItTake = new Stopwatch();
howLongWillItTake.Start();
List<Owner> allOwners = new List<Owner>();
List<Cat> allCats = new List<Cat>();
List<CatOwner> allCatOwners = new List<CatOwner>();
//create lists with 200000 cats, 200000 owners, 200000 relations
for (int i = 0; i < 200000; i++)
{
//Cat
Cat CatX = new Cat();
CatX.Name = “Cat ” + i.ToString();
CatX.DOB = DateTime.Now.AddDays(i / 10);
//Owner
Owner OwnerX = new Owner();
OwnerX.Name = “Owner ” + i.ToString();
OwnerX.DOB = DateTime.Now.AddDays(-i / 10);
//Relationship “table”
CatOwner CatOwnerXX = new CatOwner();
CatOwnerXX.Cat = CatX;
// Relations
CatOwnerXX.Owner = OwnerX;
CatX.CatOwner = CatOwnerXX;
OwnerX.CatOwner = CatOwnerXX;
//add to list
allCats.Add(CatX);
allOwners.Add(OwnerX);
allCatOwners.Add(CatOwnerXX);
}
// now I remove all the items
foreach (Cat CatToDelete in allCats)
{
Owner OwnerToRemove = CatToDelete.CatOwner.Owner;
allOwners.Remove(OwnerToRemove);
}
// now all cats are free
int numberOfCats = allCats.Count();
int numberOfOwners = allOwners.Count();
howLongWillItTake.Stop();
long elapsedTime = howLongWillItTake.ElapsedMilliseconds;
// give info to the view

}
<div class=”row”>
<div class=”col-md-12″>
<hr />
<b>Results</b>
<br />
Cats: @numberOfCats
<br />
Owners: @numberOfOwners
<br />
ElapsedTime in milliseconds: @elapsedTime
<hr />
</div>
</div>

Entity Framework: Database first or code first? Some non-conceptual, very practical differences in real life scenarios

In the last years, Microsoft has promoted Code First as a very comfortable way to make your web (or even client-server) application communicate with your database (I am not talking only about Sql Server. I have had good experience of Entity Framework with Oracle databases as well).

Code First, in contrast with Database first.

Database first is how it has always worked in the IT world:

  1. first you create a DB
  2. then you create objects in your application that are a representation of your DB, and modify the DB contents through the objects.

Code First works the other way around:

  1. first you create (business?) classes in your application
  2. then Entity framework creates the DB tables to hold those objects and keep track of the DB modifications.

There is a third approach (Model First), but I have never really given it a chance because the other two were really sufficient for what I do.

What is better? the Practical Approach

Let us see how the DB-classes link is created in Database First and how this changes in Code First.

The problem:

I am a tie salesperson. I have two entities that are linked:

  1. ties
  2. racks

A tie can be linked to one rack. Racks can hold many ties.

Managing Related Tables in Entity Framework Database First

These are my Racks

CREATE TABLE [dbo].[Rack] (
[Id] INT NOT NULL IDENTITY,
[RackPosition] NVARCHAR (MAX) NULL,
PRIMARY KEY CLUSTERED ([Id] ASC)
);

These are my Ties (linked to my Racks via the RackId, that is a foreign key)

CREATE TABLE [dbo].[Tie] (
[Id] INT IDENTITY (1, 1) NOT NULL,
[TieSerial] INT NULL,
[RackId] INT NOT NULL,
PRIMARY KEY CLUSTERED ([Id] ASC)
);

ALTER TABLE [dbo].[Tie] WITH CHECK ADD CONSTRAINT [FK_Tie_Rack] FOREIGN KEY([RackId])
REFERENCES [dbo].[Rack] ([Id])
GO

These are the tables as you see them in Sql Management Studio:

Image of the two DB tables

The two tables created in the DB

In order to create the classes out of this Database, in Visual Studio we:

  1. Add or update the entity framework package to our web project (why not via NuGet, and why not 6.1, at the beginning of November 2014?)
  2. Add the ADO.NET Entity object to our project (we choose the option “EF Designer from DB”)
  3. We specify the connection string and finally import the DB objects

In Sql Management studio, we add some data to the Rack table, so that – when we create new ties – they can be hung on something!

Racks in the Rack table, Database first

Let us add some racks

Database first: choose what tables you want to import

DB Object to import in database first

We build the solution. At the end, the EF scripts create these class files we take good care of, because we will reuse them in Code First approach:

namespace DatabaseFirst
{
using System;
using System.Collections.Generic;

public partial class Tie
{
public int Id { get; set; }
public Nullable<int> TieSerial { get; set; }
public int RackId { get; set; }

public virtual Rack Rack { get; set; }
}
}

and

namespace DatabaseFirst
{
using System;
using System.Collections.Generic;

public partial class Rack
{
public Rack()
{
this.Ties = new HashSet<Tie>();
}

public int Id { get; set; }
public string RackPosition { get; set; }

public virtual ICollection<Tie> Ties { get; set; }
}
}

Please note: since the foreign key is on the Tie, this means a Tie has a Rack. A Rack has multiple Ties (thus, the ICollection of Ties) in the Rack object

Now, let us see what happens when we create an MVC controller and “scaffold” views

Let us create the views to edit these objects

MVC scaffolding of Database first objects

Below, the code we get for the Ties Controller. Note the bold statements: the scaffolding templates have recognized that, when we create or show a Tie, we also create or show the Rack it is bound to.

Note also that the templates makes use of the RackId field to create and modify the link between the Tie and the Rack.

public class TiesController : Controller
{
private DatabaseFirstDBEntities db = new DatabaseFirstDBEntities();

// GET: Ties
public ActionResult Index()
{
var ties = db.Ties.Include(t => t.Rack);
return View(ties.ToList());
}

// GET: Ties/Details/5
public ActionResult Details(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Tie tie = db.Ties.Find(id);
if (tie == null)
{
return HttpNotFound();
}
return View(tie);
}

// GET: Ties/Create
public ActionResult Create()
{
ViewBag.RackId = new SelectList(db.Racks, “Id”, “RackPosition”);
return View();
}

// POST: Ties/Create
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = “Id,TieSerial,RackId”)] Tie tie)
{
if (ModelState.IsValid)
{
db.Ties.Add(tie);
db.SaveChanges();
return RedirectToAction(“Index”);
}

ViewBag.RackId = new SelectList(db.Racks, “Id”, “RackPosition”, tie.RackId);
return View(tie);
}

// GET: Ties/Edit/5
public ActionResult Edit(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Tie tie = db.Ties.Find(id);
if (tie == null)
{
return HttpNotFound();
}
ViewBag.RackId = new SelectList(db.Racks, “Id”, “RackPosition”, tie.RackId);
return View(tie);
}

// POST: Ties/Edit/5
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit([Bind(Include = “Id,TieSerial,RackId”)] Tie tie)
{
if (ModelState.IsValid)
{
db.Entry(tie).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction(“Index”);
}
ViewBag.RackId = new SelectList(db.Racks, “Id”, “RackPosition”, tie.RackId);
return View(tie);
}

// GET: Ties/Delete/5
public ActionResult Delete(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Tie tie = db.Ties.Find(id);
if (tie == null)
{
return HttpNotFound();
}
return View(tie);
}

// POST: Ties/Delete/5
[HttpPost, ActionName(“Delete”)]
[ValidateAntiForgeryToken]
public ActionResult DeleteConfirmed(int id)
{
Tie tie = db.Ties.Find(id);
db.Ties.Remove(tie);
db.SaveChanges();
return RedirectToAction(“Index”);
}

protected override void Dispose(bool disposing)
{
if (disposing)
{
db.Dispose();
}
base.Dispose(disposing);
}
}

The scaffolding creates not only the controller, but also the views.

This is the Create view (you will notice the creation of a dropdown list that allows us to choose the rack the tie is hung on)

DB First: creation of a new record

Creation of a new record with Entity Framework DB first

And last the index page, which shows what we just created

Tie Table in database first

The ties in our table

Noteworthy:

When the model is sent from the view to the controller, the rack object that is linked to the tie is null (see the breakpoint screenshot). However, the RackId key is not. This allows the DB to keep the link between the new tie and the chosen rack.

model of tie table entry

screenshot of the tie model

Managing Related Tables in Entity Framework Code First

To test how all of this works in the “Code First” world, I will do so:

Create a new Visual Studio project (web application, MVC)

  1. Upgrade EF to 6.1
  2. Prepare a new DB, called CodeFirst
  3. Create model classes from the same classes that were generated automatically by EF in Database First
  4. Add to the project an “Entity framework Code First” ADO.net object. This doesn’t do a lot: basically, it creates a new connection string for you [that you will have to change to make it point to your real DB].
  5. The ADO.net object also adds a DbContext class where you have to specify what classes will be written to the DB (this is another difference from Database First: naturally, Database first asks you where to read data from and what data it should read. Code First does not ask where it should write data and what it should write. You have to write additional code for that. But it’s not a lot.)

This is how the DbContext class looks like after our intervention. In bold, the code we added.

public class CodeFirstModel : DbContext
{
// Your context has been configured to use a ‘CodeFirstModel’ connection string from your application’s
// configuration file (App.config or Web.config). By default, this connection string targets the
// ‘CodeFirst.CodeFirstModel’ database on your LocalDb instance.
//
// If you wish to target a different database and/or database provider, modify the ‘CodeFirstModel’
// connection string in the application configuration file.
public CodeFirstModel()
: base(“name=DefaultConnection”)
{
}

// Add a DbSet for each entity type that you want to include in your model. For more information
// on configuring and using a Code First model, see http://go.microsoft.com/fwlink/?LinkId=390109.

// public virtual DbSet<MyEntity> MyEntities { get; set; }

public virtual DbSet<Tie> Ties { get; set; }
public virtual DbSet<Rack> Racks { get; set; }
}

Now, we ask the scaffolding engine to generate the controller exactly as we did with Database first

Code first in Entity framework: code creation

Code First Controller creation

The created controller is exactly like that of the Database first controller.

public class TiesController : Controller

{
private CodeFirstModel db = new CodeFirstModel();

// GET: Ties
public ActionResult Index()
{
var ties = db.Ties.Include(t => t.Rack);
return View(ties.ToList());
}

// GET: Ties/Details/5
public ActionResult Details(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Tie tie = db.Ties.Find(id);
if (tie == null)
{
return HttpNotFound();
}
return View(tie);
}

// GET: Ties/Create
public ActionResult Create()
{
ViewBag.RackId = new SelectList(db.Racks, “Id”, “RackPosition”);
return View();
}

// POST: Ties/Create
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = “Id,TieSerial,RackId”)] Tie tie)
{
if (ModelState.IsValid)
{
db.Ties.Add(tie);
db.SaveChanges();
return RedirectToAction(“Index”);
}

ViewBag.RackId = new SelectList(db.Racks, “Id”, “RackPosition”, tie.RackId);
return View(tie);
}

// GET: Ties/Edit/5
public ActionResult Edit(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Tie tie = db.Ties.Find(id);
if (tie == null)
{
return HttpNotFound();
}
ViewBag.RackId = new SelectList(db.Racks, “Id”, “RackPosition”, tie.RackId);
return View(tie);
}

// POST: Ties/Edit/5
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit([Bind(Include = “Id,TieSerial,RackId”)] Tie tie)
{
if (ModelState.IsValid)
{
db.Entry(tie).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction(“Index”);
}
ViewBag.RackId = new SelectList(db.Racks, “Id”, “RackPosition”, tie.RackId);
return View(tie);
}

// GET: Ties/Delete/5
public ActionResult Delete(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Tie tie = db.Ties.Find(id);
if (tie == null)
{
return HttpNotFound();
}
return View(tie);
}

// POST: Ties/Delete/5
[HttpPost, ActionName(“Delete”)]
[ValidateAntiForgeryToken]
public ActionResult DeleteConfirmed(int id)
{
Tie tie = db.Ties.Find(id);
db.Ties.Remove(tie);
db.SaveChanges();
return RedirectToAction(“Index”);
}

protected override void Dispose(bool disposing)
{
if (disposing)
{
db.Dispose();
}
base.Dispose(disposing);
}
}

The database is not created, yet.

To create it, either you enable Migrations (in Package console), or – more simply – you launch the application and, via the automatically generated views, create a DB entry that does not depend on other objects (you might as well seed some objects in the database via code, but we want to keep this example as simple as possible),

So, we create new Racks with a Racks controller (we do not start creating Ties because you cannot have a Tie without a Rack). The DB is automatically created. After we create some racks, we can add ties to them.

filling db items in code first

Create racks in Code first

Db created by code first approach

Codefirst database

How does the “automatic” DB look like? Well, it does look identical to the DB first approach: entity framework has indeed created two tables (with a plural name, but there is an option to specify it should remain singular) and the foreign keys.

Now we create our Ties

Tie table creation

We create Ties in CodeFirst now

We have obtained exactly the same result as with Database First.

Bottom line: what is better?

Rarely as in this occasion have I felt entitled to say: it’s the same, it depends on your inclinations and what you have at hand.

Database First is better if you have large databases you need your code to mirror (for smaller DBs, there is the opportunity to do Codefirst importing a part of the DB and creating the rest via .net classes)

Code First is better if you have an empty DB and don’t want to spend too much time switching between two different environments (the code IDE and the DB IDE).

With Code First, you have to learn some new attributes that you will use in your code to specify DB-related attributes as: a certain column has an index, a certain column should not go to the DB at all, and so on.

What do I prefer?

Lately I have gone with Code First because I have always been repaid by investments in technologies that automate certain processes even if at the beginning they seem to confront problems in a simplistic way.

Usually, these technologies improve and take away a lot of programming hassles. Take ORMs: how many developers would have bet they would almost totally replace field-level programming one day? Code First  gives you a more centralized view of your application, with potentially fewer bug to take care of in the future. That did the trick for me.

Static IP addresses for Azure websites that are not hosted as “cloud services” or “VM”s: still impossible for outbound, but workarounds possible

I hope this article is not valid for long and that static IPs will also soon also applicable for Azure websites. At this moment (October 2014), this is not the case.

Microsoft announced at the end of July that you can finally have reserved IPs for VMs and cloud services. Cloud services CAN host websites (the “web” role) but they’re not as easy to deploy as Azure website services (which are elementary).

The details of the procedure to obtain the static IP (for inbound AND outbound traffic) are in this MSDN article here.

The procedure is not very friendly yet: you have to use powershell or Azure’s API. I haven’t seen a graphic interface yet. Moreover, static IPs can – today – only be assigned to newly deployed stuff, not to already-deployed services.

What happens if you still have an Azure “website”, that is the most simple (and agile) way to deploy your own website to the Azure cloud?

Inbound traffic

You CAN have an static IP address for inbound traffic. Here, in an MSDN blog entry, Benjamin Perkins shows how to do it with the help of SSL certificates.

Outbound traffic: there’s the rub

Why would you want your outbound traffic IP to be static? Because there are cases in which your website, in the background, has to call web services which only accept calls from whitelisted IPs. When is this the case?
– financial services (for instance: online payment)
– other paid web services

Should we give up Azure if we need outbound static IP? Not really. There are two ways to overcome the issue of outbound traffic not being static in Azure websites.

1. Azure websites’s IP addresses are not totally dynamic. There IS a range of IPs that your outbound traffic can use. The list is here. If your remote web server needs to know what IP address you’re going to use to make the calls, you can give them the Azure datacenter IP ranges.

What is the problem with this approach? the list is long, whereas web service providers may accept only a few IP addresses.

In October 2014, the West Europe Data Center IP list is long tens of lines. Chances are your web service provider gives you… say ten IPs you can communicate them?

2. You use a static-IP proxy for your websites calls. I have tested this British service called Quotaguard, that I pay for and with whom I have no affiliation whatsoever. It works.

What do they do? they provide you with a proxy server that does have two static IPs you can communicate to your provider as your “whitelisted” IPs. Your Azure traffic that needs whitelisting can pass via Quotaguard.

They have a lot of implementation examples. For .NET, they focus on web forms and http requests that have a proxy property. In case you are using (as it was my case) objects that have no “proxy” porperties, you can create a Proxy object yourself and link it to the .NET framework object “WebRequest”, like this:

using System.Net;

var proxy = new WebProxy(“http://quotaguard_THE_CODE_QUOTAGUARD_GIVES_YOU@eu-west-1-babbage.quotaguard.com:9293“); // you may want to store this URI in the application’s config page in Azure, rather than hardcoding it
            proxy.Credentials = new NetworkCredential(“YourQuotaGuardAccount”, “yourQuotaguardpassword”); // you may want to store credentials in secure config files rather than hardcoding them
            WebRequest.DefaultWebProxy = proxy; // we set the “global” proxy here
            Now you can use your whitelisted webservice call…
Another version of the same code can be found here:
Enjoy!

A simple redirection mechanism using MVC 5 [and IIS8], including “Legacy” URLs

For some hours, I have been looking for good ideas on how to manage redirections in MVC without necessarily configuring IIS (which is not always available to the programmer, unfortunately).

However, very few of the walkthroughs found on the web take into account dealing with “legacy” URLs; primarily, those who have the “.html” extension.

How do I, in MVC, redirect a URL like “oldfolder/oldfilename.html” to a new MVC URL as “/newfolder/beautifulpeople” ?

The answer is not the simplest, because MVC doesn’t manage all that is asked to it by the browser:

A client request is handled, summarily, like this:

– MVC manages the “MVC” routes (as “/area/controller/action”)

– IIS8 manages the static files, which *presumably* don’t need MVC’s intervention.

If we want to manage all the redirections via MVC, we have to tell MVC that it has to take care of BOTH MVC routes AND static files (unless, of course, the latter don’t exist as such. If the static file exists, let IIS transfer it to the user).

The following is a very basic mechanism which uses MVC and a DB table to store redirections. It can be extended to use caching and OR XML files to be faster. It is also true that redirections should be an exception rather than the norm.

The idea is:

– We create a DB table holding old URLs and corresponding new URLs, along with the code we want to use for the redirection (301 is permanent) and an “active” flag.

– We create a set of MVC “catchall” routes (in the RouteConfig file) which will prevent 404 (“not found”) and will call a new controller that does the “redirection” job

– This new controller checks if the DB has an entry equal to the originating URL. If there is, it redirects  the flow to the new URL (permanenty or temporarily, depending on what we wrote in the DB entry); otherwise, it generates a custom 404 page.

– We tell MVC (in web.config) that it has to also take care of the routes that look like files, unless (of course) they are actual static files.

RouteConfig

In our MVC application’s RouteConfig, we create, AFTER the standard routes, a set of “catchall” routes which will direct to a new controller and action (“Error”/”NotFound”) all the requests that could not be routed to an existing controller.

Like this:

//Default, Real route

routes.MapRoute(

name: “Default”,
url: “{controller}/{action}/{id}”,
defaults: new { controller = “Home”, action = “Index”, id = UrlParameter.Optional },
constraints: new { controller=”Home|Search|Account|Show|Other_Controllers|Error”} // CONSTRAINTS are VERY IMPORTANT
);

//catchAll routes to grab the URLs we want to redirect

routes.MapRoute(
name: “Redirect_OrError”,
url: “{*CatchAll1*}”,
defaults: new { controller = “Error”, action = “NotFound” }
);

routes.MapRoute(
name: “Redirect_OrError_two_levels”,
url: “{CatchAll1*}/{CatchAll2*}”,
defaults: new { controller = “Error”, action = “NotFound” }
);

routes.MapRoute(
name: “Redirect_OrError_three_levels”,
url: “{CatchAll1*}/{CatchAll2*}/{CatchAll3*}”,
defaults: new { controller = “Error”, action = “NotFound” }
);
routes.MapRoute(
name: “Redirect_OrError_four_levels”,
url: “{CatchAll1*}/{CatchAll2*}/{CatchAll3*}/{CatchAll4*}”,
defaults: new { controller = “Error”, action = “NotFound” }
);

Attention: The “default, real” MapRoute method must be called with Constraints (Controller=”Home|…. all the controllers you have). If you don’t do so, MVC will not fall through to our “catchall” controllers. It will stop at the default and will give a “404”.

The “Error” Controller

The “Error” Controller does three things:

1. It checks if the request is actually found in the Redirections table as a URL we consider as “old”

2. If it is there, we redirect to the new URL (if 301, “RedirectPermanent”; if others, “Redirect”)

3. If it doesn’t, it generates a custom error page with a 404 status code

public class ErrorController:Controller
{

[HttpGet]
public ActionResult NotFound()
{

string UrlToTest = Request.Url.AbsolutePath.Trim(‘/’);
Redirections redirect = RedirectManager.GetRedirectFromDB(UrlToTest);
if (redirect != null)
{
if (redirect.RedirectType==”301″)
{
return RedirectPermanent(redirect.NewURL);
}
else
{
return Redirect(redirect.NewURL);
}
}
Response.StatusCode = 404;
ViewBag.UrlToTest = UrlToTest;
return View();
}

public ErrorController()
{

}

}

Now what is the RedirectManager we just found in the code?

Very simply, it is a class which contains some helper classes and methods that allow us to examine the DB and look for the URL we want to redirect from.

public class RedirectManager

{

public RedirectManager()

{

}

public class RedirectionsDTO
{

public int Id { get; set; }
public string OldURL { get; set; }
public string NewURL { get; set; }
public string RedirectType { get; set; }
public Nullable<bool> Active { get; set; }

}

public static Redirections GetRedirectFromDB(string OldUrl)
{
RedirectionsDTO redirects;
using (var db = new OurEntitiesDB()) // this is the name of your Entity Framework
{
string URLWithoutSlash = OldUrl;
if (!URLWithoutSlash.StartsWith(“/”))
{
URLWithoutSlash = “/” + URLWithoutSlash;
}

redirects = (from e in db.Redirections
where (e.Active == true && (e.OldURL == URLWithoutSlash))
select new RedirectionsDTO
{
OldURL = e.OldURL,
NewURL = e.NewURL,
RedirectType=e.RedirectType,
Active=e.Active
}).FirstOrDefault();
if (redirects != null)
{
return new Redirections { OldURL = redirects.OldURL, NewURL = redirects.NewURL, Active=redirects.Active, RedirectType=redirects.RedirectType };
}
else
{
return null;
}

}

}

}

}

How is the Redirect table created? It is created by Entity Framework Database first, based on a table maybe similar to this…

MVC redirection storage table

Table which holds redirection data

There should be an index on the OldURL column because we will include it in our queries.

The generated code could be something like this:

namespace YourPreferredNameSpace

{
using System;
using System.Collections.Generic;

public partial class Redirections
{
public int Id { get; set; }
public string OldURL { get; set; }
public string NewURL { get; set; }
public string RedirectType { get; set; }
public Nullable<bool> Active { get; set; }
}
}

Of course, you have to fill this table with the needed redirections! I suggest you create a controller and allow scaffolding templates to automatically generate the CRUD actions and views for these “Redirections” objects.

Web.Config

What we did so far does not help us if we want to redirect *.html files (or *.pdf files, for that matter). This is beacuse IIS will think that the *.html file should not be a part of an MVC route and it will look for it as a static file. To allow MVC to see the *.html URL as a possible MVC route (and redirect it, if needed), we have to allow MVC to examine all the file requests. To do so, we have three suggested paths. There are: an easy+smart way, a smart way and a semi-silly way. The easy+smart way didn’t work for me. The “smart way” did. It happens.

The easy+smart way is to patch the IIS as this article suggests: http://support.microsoft.com/kb/980368

It didn’t work for me because I had no access to IIS.

The “smart one” is suggested by Colin Farr in this old but powerful article:

http://www.britishdeveloper.co.uk/2010/06/dont-use-modules-runallmanagedmodulesfo.html

he suggests that in System.webServers you add

<system.webServer>
<modules >
<remove name=”UrlRoutingModule-4.0″ />
<add name=”UrlRoutingModule-4.0″ type=”System.Web.Routing.UrlRoutingModule” preCondition=”” />

… other modules…

</module>

Now the silly solution instead:

add the runAllManagedModulesForAllRequests=”true” attribute to the “module” section. Colin Farr already explains why this is silly, so I will not.

<modules runAllManagedModulesForAllRequests=”true”>

Using Google and LinkedIn Accounts for Your .net MVC 5 Site’s Authentication: Tips and Tricks and the error externalLoginCallback?error=access_denied

Some months ago, Rick Anderson and Erik Reitan wrote a very good article in the asp.net website (http://www.asp.net/mvc/tutorials/mvc-5/create-an-aspnet-mvc-5-app-with-facebook-and-google-oauth2-and-openid-sign-on)  to show how the ASP.NET Identity authentication / authorization framework can leverage external authentication providers as Google, Facebook, Twitter and LinkedIn.

These providers do frequent changes to their interfaces. I am writing this post to underline a couple of tricky errors that the Google / LinkedIn authentications can present to you even if you seemingly do everything by the books.

Authentication with Google: tricks

Google can give you: “externalLoginCallback?error=access_denied” if you don’t allow your application to use certain – not so intuituive – APIs. In particular, the API that must be checked so that Google gives the green light for authentication is the Google+ API, which is not pre-authorized.

This is documented in MSDN: http://blogs.msdn.com/b/webdev/archive/2014/07/02/changes-to-google-oauth-2-0-and-updates-in-google-middleware-for-3-0-0-rc-release.aspx, however, i thought it was a good idea to replicate the same concept on this site in order to make it easier to find it with a search on the redirect URI error.

Another error one can get for the Google authentication is that the Google “project” has to have a PRODUCT_NAME (however, this is easy to spot because it is Google’s page itself to tell you where the problem lies).

All in all, these are the steps to enable the Google + authentication in MVC 5 (highly suggested to use Visual Studio 2013 Release 2 or superior)

Steps in Visual Studio

Create a new MVC project with “Individual user” authentication

From Nuget, download the Microsoft.Owin.Security.Google package

Google authentication package in NuGet

Google authentication package in NuGet

Enable https for your site (project – properties – set SSL enabled to true)

Copy the location of the SSL site (https://localhost:port_number/, for instance: https://localhost:44302/)

Steps in Google Developer Console

Go to https://console.developers.google.com/project. Click on “Create Project”.

Click on the project name, click on APIs in the left menu and select “Google+ API”. Set it to ON (default is OFF)

Screenshot of the Google+ API we have to check to authenticate users in an MVC project

The Google+ API to enable if you want external authentication to work

Click on “Credentials” – oAuth.

Set the “Authorized Javascript origins” to the localhost SSL root (https://localhost:your_port_number). Set the “authorized redirect URLs” to localhost SSL root + /signin-google: for instance, https://localhost:43202/signin-google. No slash at the end of the URL!

Once you do this step, you have a ClientID and a Secret. You will need those for the Visual Studio application.

Configuring the ClientID in Google

Configuring the ClientID in Google

In the Consent screen, choose a contact email and give the PRODUCT a name!

Google Authentication: Consent screen

Google Authentication: Consent screen

Back to Visual Studio

In the folder App_Start, there is a file Startup.Auth.cs. 

Un-comment the lines releted to the Google authentication and fill the ClientId and ClientSecret fields with those given out by Google (these below are examples, they don’t work.

app.UseGoogleAuthentication(new GoogleOAuth2AuthenticationOptions()
{
ClientId = “xyzsdusidui2uo2380r787285780457284057804.apps.googleusercontent.com”,
ClientSecret = “Uhjhkjhk189r_iGAsMZdHz”
});

Now you should be good to use Google to register and authenticate your users.

 

Authentication with LinkedIn: tricks

Rick and Erik mention this good tutorial for authentication with Yahoo and LinkedIn (+ a set of other OAuth 2.0 providers)

http://www.beabigrockstar.com/blog/introducing-the-yahoo-linkedin-oauth-security-providers-for-owin/

The LinkedIn tutorial is a bit simplified: it does not take into consideration the Redirection URI for localhost.

How to work with Localhost?

As with Google, you need to write down the httpS address of your localhost, for instance: https://localhost:44302/

Beware: for the authentication to work, the OAuth 2.0 redirection URLs must be set to https://localhost:44302/signin-linkedin (or your port of course). 

You have to set this address here:

Linkedin-no-signin-error

In case you don’t append /signin-linkedin, you’ll end up with an “invalid redirect_uri” error:

invalid-redirect-uri-linkedin