Domain Routes and using subdomains in MVC as routes

< 1 min read I came across this when trying to build a website that takes advantage of sub domains in a MVC project.
You can route subdomains in MVC to a particular controller, and specific actions.

In order to do this, you must make your website listen directly on the IP Address, and point a wildcard A record for all subdomains.
In IIS 7, under Bindings, add a binding for http or https, set the IP to the dedicated IP for the site, and leave the Host name empty.

In your DNS Server (GoDaddy or Windows DNS Server), add the following A records

Host: @ or (same as parent)
IP address: (your dedicated IP)
Host: *
IP address: (your dedicated IP)
NOTE: Not all DNS Servers have wildcard options

This will make your website listen for any subdomains on your site.

Reference the classes attached below, and add the following routes to your project

_routes.Add("BlogRouteProfile", new DomainRoute("{url}.example.com", "", new { controller = "Blog", action = "ViewBlog", url = "" }));
_routes.Add("BlogLatestPosts", new DomainRoute("{url}.example.com", "latest-posts", new { controller = "Blog", action = "BlogLatestPosts", url = "" }));
_routes.Add("BlogPost1", new DomainRoute("{url}.example.com", "rss", new { controller = "Blog", action = "BlogRss", url = "" }));
_routes.Add("BlogPost3", new DomainRoute("{url}.example.com", "pages/{pageName}", new { controller = "Blog", action = "BlogPageView", url = "" }));

In your controller, you can now define Actions, just as you normally would for a regular route.

public class BlogController : Controller
{
    public ActionResult BlogPageView(string url, string pageName)
    {
        return View();
    }
}

MVC Execute code on each controller method

< 1 min read The “OnActionExecuting” method is fired before every Action in a MVC controller. You can use this method to set and retrieve variables that are useful throughout your session, such as the person’s name, their profile photo, etc.
The following is an example implementation of this method

public class HomeController : Controller
{
    protected override void OnActionExecuting(ActionExecutingContext ctx)
    {
        base.OnActionExecuting(ctx);
        using (Repository rep = new Repository())
        {
            if (Request.IsAuthenticated)
            {
                var profile = rep.GetProfileByID((Guid)Membership.GetUser().ProviderUserKey);
                ViewBag.Name = profile.DisplayName;
            }
        }
    }
    public ActionResult Index()
    {
        //your code
        return View();
    }
    //rest of your methods
}

Get a string description from an enum

< 1 min read Enums can be very powerful when used appropriately, such as for populating fixed drop-down lists which almost never change

Let’s say you want to save a person’s gender in your database. You could save it as a string, but that’s very wasteful. Ideally you’d save it as tinyint, because it will never have more values, so why waste space and computing time with joins, or other solutions.
Your enum could look like this

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

namespace Data.Enums
{
    public enum Genders : byte
    {
        [DescriptionAttribute("No Answer")]
        NoAnswer = 0,
        [DescriptionAttribute("Male")]
        Male = 1,
        [DescriptionAttribute("Female")]
        Female = 2
    }
}

Log and email errors in MVC 3

3 min read

void Application_Error(object sender, EventArgs e)
{
    ErrorTracking.TrackingInfo info = new ErrorTracking.TrackingInfo();
    info.ErrorPath = "http://" + Request.ServerVariables["HTTP_HOST"] + Request.Path;
    info.RawUrl = Request.RawUrl;
    string strError = "Error in: http://" + Request.ServerVariables["HTTP_HOST"] + Request.Path +
    "nUrl: " + Request.RawUrl + "nn";
    try
    {
        // Get the exception object for the last error message that occured.
        Exception ErrorInfo = Server.GetLastError().GetBaseException();
        if (ErrorInfo != null)
        {
            strError += "Error Message: " + ErrorInfo.Message +
                "nError Source: " + ErrorInfo.Source +
                "nError Target Site: " + ErrorInfo.TargetSite;
            info.ErrorMessage = ErrorInfo.Message;
        }
        strError += "Error Type: " + Server.GetLastError().GetType().ToString();
        info.ErrorType = Server.GetLastError().GetType().ToString();
        if (Server.GetLastError().GetType() == typeof(HttpUnhandledException))
        {
            strError += "Source: " + (Server.GetLastError() as HttpUnhandledException).GetHtmlErrorMessage();
            info.ErrorSource = (Server.GetLastError() as HttpUnhandledException).GetHtmlErrorMessage();
        }
        info.QueryStringData = "";
        strError += "nnQueryString Data:n-----------------n";
        // Gathering QueryString information
        for (int i = 0; i < Context.Request.QueryString.Count; i++)
        {
            strError += Context.Request.QueryString.Keys[i] + ":tt" + Context.Request.QueryString[i] + "n";
            info.QueryStringData += Context.Request.QueryString.Keys[i] + ":tt" + Context.Request.QueryString[i] + "n";
        }
        strError += "nPost Data:n----------n";
        info.PostData = "";
        // Gathering Post Data information
        for (int i = 0; i < Context.Request.Form.Count; i++)
        {
            strError += Context.Request.Form.Keys[i] + ":tt" + Context.Request.Form[i] + "n";
            info.PostData += Context.Request.Form.Keys[i] + ":tt" + Context.Request.Form[i] + "n";
        }
        strError += "nSession Data:n----------n";
        // Gathering Session information
        for (int i = 0; i < Context.Session.Count; i++)
        {
            strError += Context.Session.Keys[i] + ":tt" + Context.Session[i] + "n";
        }
        strError += "n";
        if (User.Identity.IsAuthenticated)
        {
            strError += "User:tt" + User.Identity.Name + "nn";
            info.UserAccountName = User.Identity.Name;
        }
        info.StackTrace = Server.GetLastError().StackTrace;
        strError += "Exception Stack Trace:n----------------------n" + Server.GetLastError().StackTrace +
            "nnServer Variables:n-----------------n";
        // Gathering Server Variables information
        info.ServerVariables = "";
        for (int i = 0; i < Context.Request.ServerVariables.Count; i++)
        {
            strError += Context.Request.ServerVariables.Keys[i] + ":tt" + Context.Request.ServerVariables[i] + "n";
            info.ServerVariables += Context.Request.ServerVariables.Keys[i] + ":tt" + Context.Request.ServerVariables[i] + "n";
        }
        strError += "n";
    }
    catch (Exception s) { strError += s.Message; }
    info.CompleteSource = strError;
    info.SiteKey = Guid.Parse(System.Configuration.ConfigurationManager.AppSettings["ErrorTrackingKey"]);
    info.IsRead = false;
    info.IsResolved = false;
    info.DateLogged = DateTime.Now;
    info.ErrorCause = ErrorTracking.ErrorCauses.Unknown;
    info.UserAccountID = Guid.Empty;
    info.ErrorSource = "";
    info.UserAccountName = "";
    ErrorTracking.ErrorTrackingServiceClient client = new ErrorTracking.ErrorTrackingServiceClient();
    client.SubmitError(info);
    // Send email to developer
    System.Net.Mail.MailMessage m = new System.Net.Mail.MailMessage();
    m.To.Add(System.Configuration.ConfigurationManager.AppSettings["DebugEmail"]);
    m.Subject = "Error occurred on " + Request.ServerVariables["HTTP_HOST"];
    m.IsBodyHtml = false;
    m.Body = strError;
    System.Net.Mail.SmtpClient c = new System.Net.Mail.SmtpClient();
    try
    {
        c.Send(m);
    }
    catch { }
}

MVC Remote Validator

< 1 min read Let’s take a look at a brand new MVC 3 validator – RemoteAttribute. The Remote validator is very simple to use and yet extremely powerful. Remote is for situations where it is impossible to duplicate server side validation logic on the client, often because the validation involves connecting to a database or calling a service. If all your other validation uses javascript and responds to the user’s input immediately, then it is not a good user experience to require a post back to validate one particular field. This is where the remote validator fits in.
In this example, we are going to add remote validation to the username field to check that it is unique.

[Remote("ValidateUserName", "Account", ErrorMessage = "Username is not available.")]
public string UserName { get; set; }

Remote has three constructors allowing you to specify either a routeName, a controller and action or a controller, action and area. Here we are passing controller and action and additionally overriding the error message

public ActionResult ValidateUserName(string username)
{
    return Json(!username.Equals("duplicate"), JsonRequestBehavior.AllowGet);
}

C# Render Razor View as String

< 1 min read The Razor View Engine is very powerful, easy to use, and just overall great for binding data to HTML. But sometimes you need to render HTML for use in Emails, download pages, generate PDF’s, or other purposes, so it would be neat if you could use the Razor View Engine to generate those for you, and just give you the HTML to do with it as you wish. Luckily we have this somewhat built-in, and here’s a wrapper for it.

public string RenderRazorViewToString(string viewName, object model)
{
  ViewData.Model = model;
  using (var sw = new StringWriter())
  {
    var viewResult = ViewEngines.Engines.FindPartialView(ControllerContext, viewName);
    var viewContext = new ViewContext(ControllerContext, viewResult.View, ViewData, TempData, sw);
    viewResult.View.Render(viewContext, sw);
    viewResult.ViewEngine.ReleaseView(ControllerContext, viewResult.View);
    return sw.GetStringBuilder().ToString();
  }
}

You can now use it as in the following example

var htmlPage = RenderRazorViewToString("~/Views/Home/myview.cshtml", siteModel);

You can of course extend the method above, and pass it your own ViewData, TempData, RouteData, change the View Engine, enable or disable ClientValidation and UnobtrusiveJavasript, and so on.