ASP.NET MVC tips

A list of tips, involving Entity Framework, Extension Methods, programming patterns and others, that has been built up from recent ASP.NET MVC 3 programming journeys.

Introduction

From the moment I saw ASP.NET MVC I knew that this is not only useful but highly powerful. However, with great power comes great responsibility (and in technology: great requirements), resulting in a steep learning curve. This article is not focused on professional ASP.NET MVC developers (I suppose they do know everything I will write in this article), but is dedicated to people who just started developing in ASP.NET MVC (3) or plan to do so.

Most tips and source codes will be focused on the MVC core while others are focusing on techniques that could be used in combination like the Entity Framework or the jQuery validation helper. This article will also contain more specialized topics like IoC with the Unity dependency resolver or working with MySQL databases instead of Microsoft SQL ones. Even though some tips might be irrelevant for some people and other tips might be known by other people, I considered them all worth to be written down.

This article will not try to teach you MVC, HTML, JavaScript or CSS. In this article I will give you a series of (not-connected) tips, which could be helpful while dealing with ASP.NET MVC. Some of those tips might become obsolete with time, however, every tip will contain a lesson (or did contain one for me when I've been caught!).

Background

ASP.NET MVC is probably the best approach for building dynamic webpages. Now this is kind of a strong sentence and there will be people, who will strongly disagree with that opinion. What makes ASP.NET MVC so good? On the one hand you can write it using C# (or VB.NET - but I strongly prefer C#). C# is a static type language which started as a kind of Java clone, but contains a lot more state of the art features nowadays. Even though the language is static you can access powerful features like reflection, dynamic variables and anonymous objects. Even anonymous methods, so called lambda expressions, are possible. All in all the language is very fast (for a managed language) and is JIT-compiled. Even though it is not possible to reach the level of performance as with C, you can easily reach a far better performance than with any scripted language.

Now one might argue that performance is either not everything, or if it is, then you can always cross-compile to a binary. Here comes the second argument for ASP.NET MVC: since it is building up on the .NET / VisualStudio stack you can access a really good debugger and very good tools to get the job done. Now for years this has also been a kind of counter argument, since ASP.NET (now without the MVC) was a synonym to WebForms for most people. The WebForms stack is really powerful and deep, but had the disadvantage to eat performance and put you (the programmer) out of control. You just clicked around, programmed a bit etc., but the end-result (i.e. the markup) was really different from your input. And finding out what was going on in the backend was also quite a tidious task.

Here is where ASP.NET MVC comes in. The MVC concept is quite old and really useful for building graphical user interfaces. Nowadays the MVC pattern has gained a lot of acceptance for building web applications with Ruby on Rails. Since Ruby is a dynamic language with a non-C-like synthax we really needed to have ASP.NET MVC as a counter part. So what do we get in the end?

  • Total control of our output
  • Object-oriented design
  • Building upon the powerful ASP.NET core stack
  • A very modern and elegant style of writing views with the Razor view engine
  • Access to other .NET libraries, and C/C++ libraries
  • A (JIT) compiled source for performance boost
  • Excellent debugging support
  • A static language with dynamic types and other modern features

If you haven't tried out ASP.NET MVC, but you do know C# or the .NET-Framework (or even ASP.NET), then you should give it a shot right away! This article is the right choice if you did this, have a clue right now whats going on and if you want to learn more just in case.

Tip 1: Seperate entities and models

This first tip seems trivial (or not) at first, but I needed a long time to understand this. When we talk about entities in this case we mean a row from a database table. What most beginners / intermediate MVC users will do wrong is that they mix models and entitites. Those two things should always be seperated and every view should just get the minimum required data. Let's consider the case where I do have the following view:

@model TestModel
<p>Hallo @Model.Name!</p>
<p>You are back online!</p>

This (strongly typed) view seems trivial. Here we just need the property Name of the given instance of TestModel. So TestModel might look like:

public class TestModel
{
	public string Name { get; set; }
}

Maybe TestModel has more properties, which aren't set in this case. This does not matter as long as TestModel is not a database entity like:

public class TestEntity
{
	public int Id { get; set; }
	public string Name { get; set; }
	public string FirstName { get; set; }
	public string Street { get; set; }
	public string City { get; set; }
	public DateTime Birthday { get; set}
}

Making a query to request (and fill) all those properties seems like to much work in this case. But even though the performance issue might be a good argument, there is an even better one for using only real models to feed the view. Consider the case that we want one more property in our view, but one that is not directly in the entity TestEntity. It could be one that is related to the primary key Id. The interesting effect is that we would need to change the argument, since we cannot extend the entity (this one is related to the table layout - which will of course stay as it was). So the problem here is the extensibility. While using a specific (or more general) model for each view, we gain extensibility and can reduce work of the entity framework.

More information regarding the models / entity objects discussion can be found at Stackoverflow.

Tip 2: Revalidate updated content

A cool feature of MVC is validation. Webdesigners had one dream: Validate on the server and the client. If somebody has extensive HTML5 features (and/or JavaScript enabled) a smooth validation without any page request is executed on the client. This results in less traffic, faster page execution and more benefits. However, some users might not have such capabilities or will turn them off to search for possible exploits in the server side validation. Therefore one must always have at least the same validation rules on the server as on the client. With ASP.NET MVC every user gets a working copy of jQuery and some powerful plugins. One of those plugins is responsible for the validation, while another one is responsible for executing the former one unobtrusively. This means that the plugin scans the loaded document for special tags and attributes. After this search the proper elements will be supplied with the required event handlers.

This procedure works great if we want a single page to load and unload, but does not work so great if we update a part of the content. The update could be done with a AJAX request or by our own JavaScript modifications. The problem is that we need a way to set up validation for a certain region (the updated region) after the page has been loaded. In this case we need to call a method of the validator script. This method then hooks up the live validation on the selected element(s). The following code snippet illustrates the usage:

$.validator.unobtrusive.parse('#Content');

Here the element with the ID Content will be parsed and set up with live validation. More information can be found at Stackoverflow.

Tip 3: Securing your website

Security is always a big issue. Of course exploits are easier to find when the source is public. Since this is the case with websites we need to be extra careful with the data we are receiving on your server. Possible exploits come either by a malicious user, or a hijacked user. The last scenario could happen due to cross site scripting (XSS), a malicious program like a virus or a problem with the user's software itself. In any case we need to be prepared. Most XSS cases can actually be prevented by using the AntiForgeryToken. In order to use it on a website we just have to insert one statement in the form:

@using(Html.BeginForm())
{
	@Html.AntiForgeryToken()
	<!-- Rest Of Our Form -->
}

This statement will create a hidden form element with a random sequence that is only known on the server (a temporary variable). When the user submits the form we still need to validate the token. The validation can be done over an attribute, the [ValidateAntiForgery] attribute. Let's have a look how to protect the action ProtectMe of the Test controller:

public class TestController : Controller
{
	/* ... */

	//
	// POST: /Test/ProtectMe

	[HttpPost]
	[ValidateAntiForgery]
	public ActionResult ProtectMe(ProtectMeModel data)
	{
		if(ModelState.IsValid)
		{
			/* What to do if valid? */
			return RedirectToAction("Protected");
		}

		/* What to do if invalid ? */
		return View(data);
	}
}

It is crucial to call the Model.IsValid property here, since the attribute signals a wrong anti forgery token by adding a model error statement. Therefore, even if the data submitted seems to be valid for this model, we will have an invalid submission.

Cross site scripting is probably the biggest threat anyway, so we should not only prevent any hijacked requests, but we should also prevent that responses from our server do contain malicious scripts and requests. If user generated content cannot contain any HTML at all we are basically safe, however, this is not always possible. If users are allowed to input HTML we have two ways to deal with it:

  1. Black list all tags and attributes with malicious potential
  2. White list all tags that are needed and cannot contain malicious content

The first method seems appealing to most users (since they do not think about all the possibilities). We will always prefer the second method since it is usually not required to grant users too many tags (what do they need to do anyway?) and since this list can be controlled more easily (think about a growing number of tags with future specifications of the HTML standard for instance).

Unfortunately a sanatizer library has not been included in MVC 3 out of the box. Luckily somebody else put a lot of effort into writing one resulting in the AntiXSS library. To convert (HTML) input with using a list of standard (and safe) tags, all we need to call is Sanatizer.GetSafeHtmlFragment(). The usage of such libraries is strongly recommended since they will usually be updated regularly, resulting in a state of the art sanatizer. The library can be downloaded at Microsoft.

The last point to mention in this section is the need for proper authorization. Authorization is quite important and should be regarded as a mandatory element for every website. All our actions need to be public (in the code) and are therefore accessible via a HTTP request. This means that every, the good, the bad and the ugly, user can access those actions. A good start is the usage of the [Authorize] attribute. It can be used for controllers and actions. If we use it for a controller, like:

[Authorize]
public class TestController : Controller
{
	/* ... */
}

This will protect ANY action inside from access by users which are not logged in. If we do want unauthorized users to access any of the actions we need to place the [Authorize] attribute above the other actions like:

public class TestController : Controller
{
	/* ... */

	[Authorize]
	public ActionResult ShouldBeProtected()
	{
		/* ... */
	}

	[Authorize]
	public ActionResult AlsoOnlyForLoggedInUsers()
	{
		/* ... */
	}

	public ActionResult AccessibleByEveryone()
	{
		/* ... */
	}
}

Maybe we need even to distinguish between those authorized users. Basically we can do by using their usernames or (more general) their roles. Every user has one username assigned, plus zero or more roles. One very important role could be called Administrators. If we want a certain action to be accessed only by users, who are in the role Administrators, we need to have the following attribute:

public class TestController : Controller
{
	/* ... */

	[Authorize(Role = "Administrators")]
	public ActionResult ShouldBeProtected()
	{
		/* ... */
	}
}

It is possible to enter more roles or mix roles and usernames. However, usually using a fixed username is a unflexible coding technique, which will result in problems sooner or later.

Tomas Jansson goes even one step further and reverses the direction of securing our web application. Therefore everything is locked in the beginning and we have to explicitly unlock the access (to specific groups or all users). This is certainly a good way to prevent accidently left open doors. The artile of Tomas is called Securing your ASP.NET MVC3 application.

Tip 4: Always work with a DAL

A data access layer is a layer which provides simplified access to data stored in persistent storage of some kind, such as an entity-relational database. This definition shows us already that a DAL is basically another abstraction between our web application and the database. Our access to the database should already be simplified by using the Entity Framework. This method gives us the advantage of writing (compiler checked) queries with LINQ. Another advantage is to obtain the data in C# objects compared to pure string / objects from the database.

All in all using the database directly is like boiling water directly on the cooking top instead of using a cooking vessel as layer in between. The later provides several advantages such as a cleaner, more robust way to achieve the same goal. Now in order to have a real DAL we need more than just the Entity Framework. Actually the Entity Framework would be more like an electric cooking top in the analogy above (while fire would be using SQL directly). So how does our cooking vessel look like in reality (or let's say in our virtual programming world):

  • Needs access to the Entity Framework, i.e. the database entities.
  • Provides only the (already abstracted) methods, i.e. it does not provide complete tables or direct queries, but already premade queries.
  • Returns models and not entities.

We could abstract the entity framework using the repository pattern. A very basic example is given by Scott Gu in the Nerddinner Book Pt 3. But first, let's see how we can set up our database with the Entity Framework:

public class MyDatabase : DbContext
{
	public DbSet<EntityType> TableName { get; set; }

	protected override void OnModelCreating(DbModelBuilder modelBuilder)
	{
		modelBuilder.Conventions.Remove<IncludeMetadataConvention>();
	}
}

All we need to do is creating a class that inherits from DbContext. The name of the new class must be equal to the name of the connection in the web.config (more on that later). Here we just have one table of types EntityType. The table name would then be TableName. Usually the convention is that the table name is the plural of the entity's class name.

Another feature is the metadata convention. The entity framework can also construct the database, if the database tables are not available and the user rights are sufficient. To remember the state during the last database build a special table is set up. This table will contain the metadata. Since this is something for test / non-productive systems, we usually want to remove this convention.

Having set up our database connection by defining the entities over classes and combining them together in the MyDatabase class, we are ready to construct our real DAL.

public class DatabaseRepository
{
	// The database is a member variable
	MyDatabase db;
	
	// List methods
	public SomeSpecificModel ListAllEntities()
	{
		/* ... */
	}
	
	// Insert methods
	public void AddNewEntity(AnotherSpecificModel data)
	{
		/* ... */
	}
	
	// Save method - just one example
	public void Save()
	{
		db.SaveChanges();
	}
}

Now that we are using a proper DAL with the class DatabaseRepository we could also use a more sophisticated method to construct it. One way would be to make an interface out of the DatabaseRepository, called IRepository. The next tip will now handle the IoC pattern, which is based upon such implementation rules.

Tip 5: Setting up a proper IoC dependency resolver

The Inversion of Control (IoC) pattern is one of the most used programming patterns in ASP.NET MVC. Its main advantage is that objects can be constructing without needing to know all the dependencies. In order to use this style of programming for our own controllers and others we need to set up a proper dependency resolver.

There are already some well tested and good working dependency resolver solutions around. One of the most used ones is called Unity. All we need to do is to tell the service locator, which dependency can be resolved by passing which object. Therefore we can built a class like the following (RepositoryResolver):

public class RepositoryResolver : IDependencyResolver
{
	readonly IUnityContainer _container;

	public RepositoryResolver(IUnityContainer container)
	{
		this._container = container;
	}

	public object GetService(Type serviceType)
	{
		try
		{
			return _container.Resolve(serviceType);
		}
		catch
		{
			return null;
		}
	}

	public IEnumerable<object> GetServices(Type serviceType)
	{
		try
		{
			return _container.ResolveAll(serviceType);
		}
		catch
		{
			return new List<object>();
		}
	}
}

Next we integrate the class to resolve dependencies. Therefore we implement the following code in the global.asax.cs file.

public class MvcApplication : HttpApplication
{
	/* ... */
	
	public static void RegisterDependencies()
	{
		var container = new UnityContainer();
		container.RegisterType<IDependency, ImplementionOfIDependency>();
		DependencyResolver.SetResolver(new RepositoryResolver(container));
	}

	protected void Application_Start()
	{
		/* ... */
		RegisterDependencies();
	}
}

This will automatically construct controllers like the following:

public class DependentController : Controller
{
	public DependentController(IDependency someDependency)
	{
		/* Do Something With Dependency */
	}
}

So all we need to do in order to have a fully working dependency resolver in ASP.NET MVC is to implement IDependencyResolver and to add the (or a) resolver in the available DependencyResolver object by calling the SetResolver() method.

More information can be found at Brad Wilson's blog.

Tip 6: Use the MVC 4 Bundling feature in MVC 3

Of course ASP.NET MVC 4 offers more features than MVC 3. The question now is: do we really need all those features? Certainly one could work with ASP.NET MVC (1) today, but some features are just so convinient that it's hard to miss those two iterations. Now let's assume we cannot move to MVC 4, which feature would still be nice to have in MVC 3 for general web application development? In my opinion it is the bundling feature of MVC 4, that we should not miss in MVC 3. It is quite easy to get by using Nuget. If you haven't checked out Nuget yet, you should probably read one or the other article (either here on Codeproject or on the official website). Nuget is a really nice packet manager, which offers a lot of awesome features. One of the tasks that Nuget is aiming to do is checking for updates of the installed packages. This helps us keeping track of our third party libraries (or even our own ones accross many systems).

With a fully installed Nuget package manager we just have to type the following command into the Nuget powershell console:

Install-Package Microsoft.Web.Optimization -Pre

This will install the ASP.NET MVC 4 Bundling feature in the solution. Installing means, that the binary will be referenced and all dependencies will also be resolved and installed. Now we are ready to use the prelimary version of the bundling manager. The bundling process will take all found or given JavaScripts or CSS files and combine them into one. This will result into fewer page requests, bringing our users a faster page load and us less server load. Additionally the bundling class offers minification features, i.e. it will even reduce the file size of JavaScript and CSS files. The bundling manager is extensible, which means we could also write support for our own file types or use it as a transpiler (like CoffeeScript to JavaScript and such). To enable the bundling manager we need to set it up in the global.asax.cs file:

public class MvcApplication : HttpApplication
{
	/* ... */
	
	protected void Application_Start()
	{
		BundleTable.Bundles.EnableDefaultBundles();
		/* ... */
	}
}

Let's first use it directly from a View:

<p>Here is some view content!</p>
<p>Some more content and now our JavaScripts ...</p>
<script src="@System.Web.Optimization.BundleTable.Bundles.ResolveBundleUrl("~/Scripts/js")"></script>

Here we just specify a directory and a file extension (for JavaScript in this case). The bundling manager will then search for all *.js files in the directory, combine them, minimize them and respond to any requests regarding this url. The bundling manager is quite smart in most cases, placing files like jQuery above possible jQuery plugins and leaving out vsdoc files (documentation of JavaScript files without the vsdoc in their name). However, sometimes we do only want specific files in a specific order of a directory. In this case we can create our own bundle. The best way to do this, is (again) in the global.asax.cs file. Here we just insert the following code:

public class MvcApplication : HttpApplication
{
	/* ... */
	
	void RegisterBundles(BundleCollection bundles)
	{
		/* Now we create a specialized jQuery bundle */
		var jQueryBundle = new Bundle("~/Scripts/jquery", new JsMinify());
		jQueryBundle.AddDirectory("~/Scripts", "jquery*.js", searchSubdirectories: false, throwIfNotExist: true);
		bundles.Add(jQueryBundle);
		
		/* Now we create a very special bundle */
		var specialBundle = new Bundle("~/Scripts/special", new ScopeMinify());
		specialBundle.AddFile("~/Scripts/game/main.js");
		specialBundle.AddFile("~/Scripts/editor/scroll.js");
		specialBundle.AddFile("~/Scripts/editor/editor.js");
		specialBundle.AddFile("~/Scripts/site/ready.js");
		bundles.Add(specialBundle);
	}
	
	protected void Application_Start()
	{
		RegisterBundles(BundleTable.Bundles);
		/* ... */
	}
}

The ScopeMinify is our own class, which looks like:

public class ScopeMinify : JsMinify
{
	public override void Process(BundleContext context, BundleResponse response)
	{
		response.Content = "(function() {" + response.Content + "})();";
#if !DEBUG
		base.Process(context, response);
#endif
	}
}

So this class scopes all included JavaScript files with an anonymous method aka the IIFE (Immediately Invoked Function Expression) pattern. If we are productive then the JavaScript will also be minified (harder to debug since objects will be renamed) otherwise it will just be bundled and scoped.

Having set up a bundling package we only need to call the corresponding url. Let's do it again from a View:

<script src="@System.Web.Optimization.BundleTable.Bundles.ResolveBundleUrl("~/Scripts/special")"></script>

Bundling is a nice way to gain more performance and secure our web applications a little bit better, since we could scope productive JavaScript by using anonymous methods. It will also prevent most users from trying to read our JavaScript source (of course the most evil fishes will still be in the water).

Some more information can be found at Joey Iodice's Blog. Jef Claes does also write about the bundling feature in his article ASP.NET MVC 4 Bundling in ASP.NET MVC 3.

Tip 7: Using MySQL as database (together with memberships)

Sometimes people complain about .NET or ASP.NET (MVC) that they can only use MSSQL or SQL Express (or other Microsoft products) as their database system. This is totally not true, since ASP.NET MVC builds upon ASP.NET, which uses the name .NET for a certain reason. One of the core components of .NET is called ADO.NET and enables programmers to use any database system in principle. All we need is connector. In the case of MySQL we can choose between several available connectors. The official and widely accepted connector is called MySQL .NET Connector.

Using the official package brings us the most complete implementation. In this tip we will investigate what we need to download and set up in the web.config file to have access to a fully featured MySQL database with the standard ASP.NET membership provider (writing our own membership provider can always be done - see tip 21). First of all we download the MySQL Connector/NET from mysql.com/downloads/connector/net/. After the installation process (which should also be done on our server), we need to change a few lines the web.config file:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <connectionStrings>
    <add name="DatabaseConnection" connectionString="Server=localhost;Port=3306;Database=yourdbname;Uid=yourusername;Pwd=yourpassword;" providerName="MySql.Data.MySqlClient" />
  </connectionStrings>
  <!-- ... -->
</configuration>

This enables the usage of DatabaseConnection as our primary database connection. By convention (for the Entity Framework) we need to name the class which inherits from the DbContext to DatabaseConnection. Now the Entity Framework will work together with MySQL as a database. So what about the membership provider? Well, in this case we need to extend the web.config file even more. Let's have a look:

<configuration>
	<!-- ... -->
	
	<system.web>
		<!-- ... -->

		<authentication mode="Forms">
		  <forms loginUrl="~/Account/LogOn" timeout="2880" />
		</authentication>

		<roleManager enabled="true" defaultProvider="MySQLRoleProvider">
		  <providers>
			<clear/>
			<add name="MySQLRoleProvider" autogenerateschema="true" type="MySql.Web.Security.MySQLRoleProvider, MySql.Web, Version=6.5.4.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d"
				 connectionStringName="MarioDB" applicationName="/" />
		  </providers>
		</roleManager>

		<membership defaultProvider="MySQLMembershipProvider">
		  <providers>
			<clear />
			<add name="MySQLMembershipProvider" autogenerateschema="true" type="MySql.Web.Security.MySQLMembershipProvider, MySql.Web, Version=6.5.4.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d"
				 connectionStringName="MarioDB" enablePasswordRetrieval="false" enablePasswordReset="true" requiresQuestionAndAnswer="false" requiresUniqueEmail="true"
				 maxInvalidPasswordAttempts="5" minRequiredPasswordLength="6" minRequiredNonalphanumericCharacters="0" passwordAttemptWindow="10" applicationName="/" />
		  </providers>
		</membership>

		<!-- ... --->
	</system.web>
</configuration>

Now there is one important attribute that is being set here. If we do not set the autogenerateschema to true, we need the propriate tables to be ready for the membership and role provider. This is unecessary work, which means we should always start with the generation of the table being allowed. Why should we remove it afterwards? On the one side the database user should not have too many rights, so granting the table create should be removed as soon as the job is done. On the other hand the extra check if the table exists is not required, saving us a little bit of time.

Tip 8: Detect jQuery AJAX requests

Sometimes we want to fetch resources by AJAX requests. This technique saves us resources by limiting the output and gives the user a better experience. Even though we are performing an AJAX request we still need to include users with older browsers or disabled JavaScript engines. For this tip we will leave out the actual implementation on the client and focus on the server side. The scenario is the following: A page is requested and whether the request was done by jQuery or directly we server a partial result with the part that should be updated or a complete page.

Lucky for the us the writers of jQuery have been quite smart about this scenario and added a certain header name and value for every AJAX request. The creators of ASP.NET MVC recognized this and have included an extension method to signal us if AJAX was used or not. The extension method IsAjaxRequest() is located in the Request object that can be accessed by any controller.

Therefore we could write a controller action like the following:

public ActionResult UploadImage()
{
	/* Do general work */
	
	//Just to distinguish between an AJAX request (for: modal dialog) and normal request
	if (Request.IsAjaxRequest())
		return PartialView(data);

	return View(data);
}

In case of AJAX we would just update the content region of the webpage. In any other case we would update the whole page by responding with the full content.

Tip 9: Precompile the views to minimize errors

Once we finished writing our MVC application we will probably publish the code in release mode. We have probably written a lot of code, generated many views and even had a lot of tests created. The tests are all fine and everything seems in place, however, what can not be tested are the views. If any view contains an error we will only realize this by accessing it. This is a huge problem - but it can be avoided. By compiling the views we have a basic check if we forgot or missed something.

To set up compilation we need to change the project's XML file. Here is how we do it:

  1. Right click on the project inside the solution explorer
  2. Click unload project
  3. Now right click on the project again
  4. This time click edit
  5. Change the following line
    <MvcBuildViews>false</MvcBuildViews>
    
    to result in
    <MvcBuildViews>true</MvcBuildViews>
    
  6. Save the file and close it
  7. Right click on the project again
  8. Click on reload project

That's all! Now our views are being compiled like the rest of the source code. Note, that this will probably not satisfy everyone. Usually activating the compilation option for release builds is sufficient.

More information on that topic is available over the Stackoverflow question. Also David Ebbo did write the post Turn your Razor helpers into reusable libraries on MSDN, which is quite related. A full guide for setting up compilation is given on Tugberk Ugurlu's blog.

Tip 10: Using the TagBuilder

Constructing perfectly valid (HTML) tags is super easy. We do not need to mess around with HTML (which will maybe contain a typo like a missing quotation mark) for producing HTML output. The first thing most people recognize is that every string will be HTML-encoded. Let's consider the following view:

@{
	var title = "<span class=mark>HI, Mum!</span>";
}
<p>@title</p>

The (partial) response from the server is not the expected

<p><span class=mark>HI, Mum!</span></p>
but rather the following:
<p>&lt;span class=mark&gt;HI, Mum!&lt;/span&gt;</p>

This is actually one of the features of MVC (or the Razor view engine). If we want HTML, we have to tell this the system explicitly. There are two ways for this:

  1. We use the Raw() extension method of the HTML helper, which takes a string and outputs it as it has been passed.
  2. We do not output string values, but only MvcHtmlString or such instances, which implement IHtmlString.

Since we do not have the option to use always scenario number one we will have to deal with the second one. Luckily constructing a MvcHtmlString is really easy, since the constructor just wants a string as argument or since we could just use the static Create() method of the MvcHtmlString class.

Now the construction process of an HTML string could begin using many string concatenations or a StringBuilder instance. The best method, however, is the usage of the TagBuilder. All we need to do for creating one is to tell the constructor what the name of the tag is. Then we can add or remove custom attributes as we want to. We can also add or remove CSS classes. Another feature is the set up of a valid ID. Since some signs are forbidden, the TagBuilder will automatically sanatize the ID string with valid ones. Finally we can also set the inner text or HTML.

Now we consider the case of building our own image link. We could do the following:

public static MvcHtmlString ImageLink(string link, string src)
{
	var tag = new TagBuilder("a");
	tag.Attributes.Add("href", link);
	var img = new TagBuilder("img");
	img.Attributes.Add("src", src);
	img.Attributes.Add("alt", string.Empty);
	tag.InnerHtml = img.ToString(TagRenderMode.SelfClosing);
	return MvcHtmlString.Create(tag.ToString(TagRenderMode.Normal));
}

Here we are using two TagBuilder (one for each tag) instances. One of the features of this class is the overloaded ToString() method. Here we can set the final look. Since an image tag is self closing we are picking this one for the image tag. The image tag is then being set as the inner HTML of the anchor-tag. In the last step we create the final MvcHtmlString.

Tip 11: Include very useful extension methods

ASP.NET MVC was built using the newer features of C# / VB.NET extensively. One of the features used the most is the possibility of extension methods. In theory you could rewrite all available extension methods (e.g. the ones from the Html object inside a view) and replace all existing calls by your own versions, just by replacing the proper namespace. This means that we have an easy way of exchanging existing methods with our own ones just by changing the used namespace.

Most of the time we are happy with the existing extension methods, but we want to have additional ones. A good practice is to make a directory with files for each class that we want to extend with our own methods. We could call that subdirectory Extensions and add static classes like HtmlExtensions, UrlExtensions and others there. Some of the more standard like extension methods (which aren't provided out of the box) are:

  • A <form> directive, which can be used for submitting files
  • The file input element
  • An action link, which surrounds an HTML image tag instead of simple text
  • Some HTML5 form input elements, e.g. the <input type=email> element

A possible way of providing those extension methods could look like this:

public static class HtmlExtensions
{
	public static MvcForm BeginFileForm(this HtmlHelper html)
	{
		return html.BeginForm(null, null, FormMethod.Post, new { enctype = "multipart/form-data" });
	}

	public static MvcHtmlString File(this HtmlHelper html, string name, bool multiple)
	{
		var tb = new TagBuilder("input");
		tb.Attributes.Add("type", "file");
		tb.Attributes.Add("name", name);
		tb.GenerateId(name);

		if (multiple)
			tb.Attributes.Add("multiple", "multiple");

		return MvcHtmlString.Create(tb.ToString(TagRenderMode.SelfClosing));
	}

	public static MvcHtmlString File(this HtmlHelper html, string name)
	{
		return html.File(name, false);
	}

	public static MvcHtmlString MultipleFileFor<TModel, TProperty>(this HtmlHelper<TModel> html, Expression<Func<TModel, TProperty>> expression)
	{
		string name = GetFullPropertyName(expression);
		return html.File(name, true);
	}

	public static MvcHtmlString FileFor<TModel, TProperty>(this HtmlHelper<TModel> html, Expression<Func<TModel, TProperty>> expression)
	{
		string name = GetFullPropertyName(expression);
		return html.File(name);
	}

	public static MvcHtmlString ActionImage(this HtmlHelper html, string imageUrl, string action, string controller, object routeValues, object htmlAttributes)
	{
		var urlHelper = new UrlHelper(html.ViewContext.RequestContext);
		var link = new TagBuilder("a");
		link.Attributes.Add("href", urlHelper.Action(action, controller, routeValues));
		var img = new TagBuilder("img");
		img.Attributes.Add("src", imageUrl);
		img.Attributes.Add("alt", action);
		link.InnerHtml = img.ToString(TagRenderMode.SelfClosing);
		return MvcHtmlString.Create(link.ToString(TagRenderMode.Normal));
	}

	public static MvcHtmlString Email(this HtmlHelper html, string name)
	{
		return html.Email(name, string.Empty);
	}

	public static MvcHtmlString Email(this HtmlHelper html, string name, string value)
	{
		var tb = new TagBuilder("input");
		tb.Attributes.Add("type", "email");
		tb.Attributes.Add("name", name);
		tb.Attributes.Add("value", value);
		tb.GenerateId(name);
		return MvcHtmlString.Create(tb.ToString(TagRenderMode.SelfClosing));
	}

	public static MvcHtmlString EmailFor<TModel, TProperty>(this HtmlHelper<TModel> html, Expression<Func<TModel, TProperty>> expression)
	{
		var name = GetFullPropertyName(expression);
		var value = string.Empty;

		if(html.ViewContext.ViewData.Model != null)
			value = expression.Compile()((TModel)html.ViewContext.ViewData.Model).ToString();

		return html.Email(name, value);
	}

	static string GetFullPropertyName<T, TProperty>(Expression<Func<T, TProperty>> exp)
	{
		MemberExpression memberExp;

		if (!TryFindMemberExpression(exp.Body, out memberExp))
			return string.Empty;

		var memberNames = new Stack<string>();

		do
		{
			memberNames.Push(memberExp.Member.Name);
		}
		while (TryFindMemberExpression(memberExp.Expression, out memberExp));

		return string.Join(".", memberNames.ToArray());
	}

	static bool TryFindMemberExpression(Expression exp, out MemberExpression memberExp)
	{
		memberExp = exp as MemberExpression;

		if (memberExp != null)
			return true;

		if (IsConversion(exp) && exp is UnaryExpression)
		{
			memberExp = ((UnaryExpression)exp).Operand as MemberExpression;

			if (memberExp != null)
				return true;
		}

		return false;
	}

	static bool IsConversion(Expression exp)
	{
		return (exp.NodeType == ExpressionType.Convert || exp.NodeType == ExpressionType.ConvertChecked);
	}
}

The ActionImage() method uses the creation of a UrlHelper instance to generate the corresponding hyperlink. This can be done by using the ViewContext.RequestContext property of the current HtmlHelper object.

Sometimes very useful extensions are provided as extension methods. Let's have a look at a very useful extension called Captcha MVC. The extension is available over Nuget or on the official Codeplex page. This extension gives us the possibility of including Captchas (a kind of verification that the user is actually a human and not a computer program) to prevent automatic usage of our webpage. This will reduce form submissions by non-human users. In order to use the extension we just have to do the following steps:

  1. Download Captcha MVC
  2. Install the extension in our solution
  3. Set up the encryption key in the web.config file
  4. Include the namespace of the extension method for the views
  5. Insert the @Html.Captcha("YourText", 5) directive in forms, where we want to place the Captcha
  6. Insert the CaptchaVerify attribute above actions, where we want to check for the Captcha

The extension method for the Captcha MVC should be slightly modified to provide everything for OUR webpage as we need it. The standard implementation looks like:

public static class HtmlExtensions
{
	public static MvcHtmlString Captcha(this HtmlHelper htmlHelper, string textRefreshButton, int length)
	{
		return CaptchaHelper.GenerateFullCaptcha(htmlHelper, textRefreshButton, length);
	}

	public static MvcHtmlString Captcha(this HtmlHelper htmlHelper, int length)
	{
		return CaptchaHelper.GenerateFullCaptcha(htmlHelper, length);
	}
}

We will also need to set up some CSS styles in the end to adjust the Captcha to the look and feel of our web application.

Tip 12: Attributes you should never forget

As we've already seen attributes are a very important feature of ASP.NET MVC (or to be more correct: are a feature of C# that has been used extensively in ASP.NET MVC). However, the controller is not the only place where we need to have the right attributes in place. By MVC constructed data models are validated by given attributes. If no attribute is specified then MVC assumes some default values. This can result in unwanted sideeffects. A very popular misbelief is that strings can be as long as the maximum in string length in the .NET-Framework. This is, however, not true. The default value for string length is 128. This sometimes leads to confusion as discussed on the StackOverflow page.

To exceed this default string length we need to specify a maximum length. If we want to use the maximum size possible, we just need the attribute MaxLength. Other options are the usage of the StringLength attribute. Considering strings we need also to think about (any) HTML input. If HTML input is detected the ASP.NET runtime will usually throw an error. This can be avoided by telling MVC that a certain parameter is allowed to submit HTML values to our server. All we need is the AllowHtml attribute in this case. Let's have a look at a sample model with those attributes:

public class SubmitDataModel
{
	[MaxLength]
	[AllowHtml]
	public string LongHTMLInput { get; set; }
}

Also, if we want that a specific value is provided, then we need to add the Required attribute. A special case are the elementary (value) types like Int32, Bool and others (basically all structures). Here we are not able to distinguish between a given (requirement fulfilled) and a default (like 0 for an Int32 type) value, due to the construction process. To avoid any wrong computations we should change the datatype from a value type to a reference type. This can be done by appending a ? after the value type's type. So public int Id { get; set; } would be changed to public int? Id { get; set; }. Here the Required attribute makes sense again.

A good resource about Validation in MVC is Henry He's article on Codeproject. Scott Gu also writes about ASP.NET MVC 2: Model Validation in his blog.

Tip 13: Be cautious with ForeignKeys

One of the most popular scenarios when working with ASP.NET MVC / the Entity Framework is that we have a set up database that needs to be used from the Entity Framework. Therefore we need to construct a code that maps to the existing database. This can be tricky since the Entity Framework follows its own conventions, which can be changed of course, but are set to a standard value. Another trap could be that certain mappings are not supported or tricky to implement.

There are a lot of tips and tricks how to handle mappings and such, so we will just go (as an example) into detail of implementing an existing one-to-one mapping in the code. Let's start with the (dominant) table. We call the corresponding entity DominantEntity. We assume the following structure:

public class DominantEntity
{
	[Key]
	public virtual int Id { get; set; }
	
	/* Some other properties */
}

Now we construct the minor entity - simply called MinorEntity:

public class MinorEntity
{
	[Key]
	[ForeignKey("DominantEntity")]
	public virtual int DominantEntityId { get; set; }
	
	public virtual DominantEntity DominantEntity { get; set; }
}

We use the virtual keyword here to allow the Entity Framework to go for lazy loading in possible scenarios. Therefore the foreign key is always loaded, but the related foreign entity is only loaded when requested (saving us some database resources).

What could be really useful in this scenario, is to know how to set properties (i.e. columns) of entities (i.e. tables) to their real names (if the convention cannot be applied). The following snippet shows how the entity EntityType is being re-mapped over the OnModelCreating() method.

public class MyDatabase : DbContext
{
	/* ... */
	public DbSet<EntityType> EntityTypes { get; set; }

	protected override void OnModelCreating(DbModelBuilder modelBuilder)
	{
		/* ... */
		modelBuilder.Entity<EntityType>().Prop(model => model.Property).HasColumnName("SampleColumnName");
		modelBuilder.Entity<EntityType>().ToTable("SampleTableName");
	}
}

The disadvantage of such a specialized mapping is that it can result in errors like the Multiplicity is not valid in Role ***. Because the Dependent Role refers to the key properties, the upper bound of the multiplicity of the Dependent Role must be '1'. To avoid such problems we need to remember to call the SaveChanges() method of our MyDatabase class between insertation. We can only insert an entity of type MinorEntity, if the referenced DominantEntity has already been added to the database.

More about foreign keys in the Entity Framework can be found in the Codeproject article about EF Code first - Add a foreign key relationship.

Tip 14: Working with localization - chances and pitfalls

Localization is one of the most awesome features of the .NET-Framework, since it is included out of the box. All we need to do is following a certain convention for naming resource files and we'll end up with a complete localizable application. This can be applied to any .NET-Application: WinForms, WPF, WebService, WebForms, ... and of course ASP.NET MVC!

To use localization we need two things:

  1. Proper entries in the web.config file
  2. Localized versions of our resource files (therefore we have to gather all resources that need to be localized from the resoucrce file(s))

The naming convention of resource files is such that if we original (default) resource file is called String.resx then a proper German one would be called String.de.resx. If we want a more specialized version for Austria we would name it String.de-at.resx. Now every value that is missing the version for Austria is taken from the generalized German version. If the generalized German version is missing some values the default value from the General resource file is taken. Since the resource file is compiled and then accessed by the views we need to set the access modifier of any resource file to public.

The web.config file could look like this:

<configuration>
	<!-- ... -->
	<system.web>
		<!-- ... -->
		<globalization culture="en-us" uiCulture="auto" />
	</system.web>
</configuration>

The important attribute is the uiCulture. With this attribute we set the language of the user interface. The value auto tells the ASP.NET runtime to use the (from the browser submitted) language header. If the submitted language is not found, the default language (from the default resource file(s)) will be taken.

More interesting is the usage of the value en-us in the culture attribute. Here we set the internal language to English in the US-format. This will handle numbers with a dot as decimal seperator, which is the reason for applying this value. Unfortunately JavaScript is not localized (even though there are libraries). Therefore JavaScript validation might bring up errors when trying to validate decimal values such as 4,5. This is a perfectly valid value from some perspective, e.g. the German system, however, it will not be validated (by default). In order to avoid such pains we guide the user to use the US-format. This is where our trouble would start. Now the client is validated in US English, but the server is validated in German (if we would use auto for the culture). Therefore we avoid this trouble by using US English on the server as well.

Tip 15: Constraints for better routing

Routing is one of the primary features of ASP.NET MVC. To boost error handler and avoid bugs we should use constraints for URL parameters. If we want the id to be a numeric value (like an int) we should specify a constraint like \d+. Constraints are set up as regular expressions. Regular expressions are one of the most useful techniques in the IT industry, since they allow us to make fast and exact text searches based on patterns. This article will not give an introduction to regular expressions.

Let's see how some route without any constraint could look like:

routes.MapRoute(
	"BlogArchive",
	"Archive/{entryDate}",
	new { controller = "Blog", action = "Archive" }
);

Obviously all links to /Archive/... will be taken to the Archive action of the controller Blog. Now this includes Urls like /Archive/5 as well as /Archive/abc. Clearly, here we have no constraint, besides the requirement of having a parameter specified.

Since we are talking about a date we might want the parameter to be in a very specific date format, like the ISO 8601 standard (YYYY-MM-DD). By adding a forth argument (the constraint), we are able to tell the ASP.NET MVC routing engine our requirement for a valid Url. Here is the final method call:

routes.MapRoute(
	"BlogArchive", // Route name
	"Archive/{entryDate}", // Route design
	new { controller = "Blog", action = "Archive" }, // Route mapping / default route values
	new { entryDate = @"d{4}-d{2}-d{2}" } // Route constraints
);

We can also built up route constraints based on the HTTP method, language and other properties. If we are not satisfied with the existing solutions, we can even built our own route constraints. All we need to do is to construct a class that implements the IRouteConstraint interface. Now we can use an instance of that class as a constraint on some property.

A good article on the whole topic can be found at Stephan Walther's blog.

Tip 16: Be careful when using names for action parameters

Automatic model generation is awesome if we play by the rules. As an example we could take the post by Shawson's Code Blog, dealing with null returns on post back. To reconstruct the example we could consider the following controller:

public TestController : Controller
{
	//
	// GET: /Test/Method/5
	
	public ActionResult Method(int id)
	{
		/* ... */
	}

	//
	// POST: /Test/Method/5
	
	[HttpPost]
	public ActionResult Method(int id, DataModel specialName)
	{
		/* ... */
	}
}

The ModelState will always be invalid, even if the data is all valid and fine. Here is how this concret model could look:

public class DataModel
{
	public int specialName { get; set; }
	
	/* ... */
}

Here we see the problem. We already submit a Name-Value pair that has the name specialName. Therefore MVC will try to generate the attribute of type DataModel from the value in specialName. This will not work. If we change the signature to Method(int id, DataModel otherSpecialName) we will be free from trouble.

Sometimes we would like to use existing routes with different parameter names to look like an already set up route. This is possible with the Bind attribute. Let's compare those two methods:

public MyController : Controller
{
	public ActionResult MyMethod([Bind(Prefix = "id")] string parameter)
	{
		/* ... */
	}

	public ActionResult MyMethod(string parameter)
	{
		/* ... */
	}
}

If we just set up the default route (remember: {controller}/{action}/{id}), we'll end up with the following Urls to reach both actions:

  1. The first one could be: My/MyMethod/hello
  2. The second one could be: My/MyMethod?parameter=hello

Parameter names do not have many restrictions. If we think about the example above and the rule that parameter names are not allowed to have the same name as the action we know almost everything to prevent errors concerning the parameter name.

Tip 17: Adding namespaces to views

Views use a different web.config than the application itself, since this results in some benefits. If we want to use a certain namespace across all views (e.g. since our models are placed in this namespace, or to use a certain extension method for the HtmlHelper instance) we need to add it in the web.config placed in the Views subdirectory.

The following web.config shows how namespaces can be added:

<configuration>
	<!-- ... -->
	<system.web.webPages.razor>
		<pages pageBaseType="System.Web.Mvc.WebViewPage">
			<namespaces>
			<add namespace="System.Web.Mvc" />
			<!-- ... -->
		</pages>
		<!-- ... -->
	</system.web.webPages.razor>
</configuration>	

If we need a single namespace more often in one view, we could just add it for this one view. The following view demonstrates this:

@using System.Web.MyOwnNamespace
<p>Here is some paragraph!</p>

The topic is also debated on StackOverflow. The question how to import a namespace in Razor view page? concerns the usage of a namespace in a single view. Also how to add extra namespaces to razor pages? discusses the possibility of adding namespaces in the web.config file.

Tip 18: Internal actions

Sometimes we need to make a method (from a controller) public or to be exposed as an action without actually wanting it to be called directly from the browser. This can be done again by using attributes. One of the most useful attributes is the ChildOnlyAction attribute. It will make the action or controller (when applied to a controller) be invisible for requests. The only way to access the action is from inside the web application.

[ChildOnlyAction]
public ActionResult CanOnlyBeCalledInternal()
{
	/* ... */
	return PartialView();
}

Usually we will return partial views from such actions. The biggest advantage from such actions is the ability to call them (similar to method calls) from views. Therefore you could insert blocks of HTML (partial views) inside a view, just by using @Html.Action() (and passing the action, controller and maybe route value data). This is in contrast to calling @{Html.RenderPartial()}, where we render directly a partial view. The difference is that the first case calls an action, which then calls a partial view for rendering. The model generation and database queries are done in the controller's action. The second case directly calls the partial view.

More information about the Action() extension method can be found at Phil Haack's blog.

Tip 19: Performance boost through caching

In the world of HTTP there are several performance best practices. If we want to stay at the simple (but very effective) tips, we will first focus on request reduction. If we can reduce the number of requests we will end up with a much faster webpage and less server load. The next step would be to reduce actual computation. This could be done by extensive analysis of our algorithms, database queries and such, but for a very effective and robust solution we could also aim for caching. The disadvantages of caching are of course in the required memory and the expiration of content. The memory is handled automatically by ASP.NET, which is one problem less. If memory is getting short, resources will be released and re-computation will be required. On the other side we need to think about a good expiration date, as well as what to cache exactly.

The easiest way to add caching is to use the OutputCache attribute for a controller (therefore it will be applied to all actions), or a specific action. This will result in a cached action result. If ASP.NET MVC detects the action being called, it will just display the already cached output. Usually we would also specify the number of seconds that the computed content should be stored in memory. Let's just say we want to cache an action for 45 seconds:

public CachedController : Controller
{
	[OutputCache(Duration = 45)]
	public ActionResult Index(int id)
	{
		/* ... */
	}
}

The problem with the current directive is still that different parameter values (e.g. id = 2, id = 9, ...) will be treated equal. This means that if the action is cached with id being set to 2, the output for the same action with the id being 9 will be the same, if requested while the action is still in the cache.

Of course we can attack such problems by using other parameters, like VaryByParam. A better solution for the problem above would therefore be:

public CachedController : Controller
{
	[OutputCache(Duration = 45, VaryByParam = "id")]
	public ActionResult Index(int id)
	{
		/* ... */
	}
}

We can specify list of parameters by using semicolons to seperate different parameter names. If we want to include every parameter in this list we could just use a *. Now we might still get some trouble if the result is localized. Here we must also distinguish between different languages that are served from this action. This can be implemented by using the VaryByHeader parameter. The language is passed in the Accept-Language header key.

public CachedController : Controller
{
	[OutputCache(Duration = 45, VaryByParam = "*", VaryByHeader = "Accept-Language")]
	public ActionResult Index(int id)
	{
		/* ... */
	}
}

Sometimes we want to take a specific setting for multiple actions (across various controllers). Here we might get into the hell of copy and paste. To avoid this (and using DRY: don't repeat yourself) we could use the so called cache profiles. Those work like global variables from the web.config file - just specicialized for caching. Let's add the previously defined output cache configuration in the configuration file:

<configuration>
	<!-- ... -->
	<system.web>
		<!-- ... -->
		<caching>
		<outputCacheSettings>
			<outputCacheProfiles>
				<add name="Cache45Seconds" duration="45" veryByHeader="Accept-Language" varyByParam="*"/>
			</outputCacheProfiles>
		</outputCacheSettings>
		</caching>
	</system.web>
</configuration>

Now we can use the created cache profile by referring to the name Cache45Seconds. The attribute call becomes:

public CachedController : Controller
{
	[OutputCache(CacheProfile = "Cache45Seconds")]
	public ActionResult Index(int id)
	{
		/* ... */
	}
}

This all sounds very easy and appealing, however, one must always think of some rules. First of all: Is the cache duration really already too long? Then: Should I really vary by all / those parameters? And finally you should always think about security! Never cache a restricted output. Stephen Walther devotes a whole post to this topic.

More information can be found on the Official ASP.NET Page article. Scott Hanselmann also discussed this topic in his article Caching in ASP.NET - VaryByParam may need VaryByHeader.

Tip 20: Override methods of your controllers

Sometimes we have quite simple actions, which will just end up serving a previously written view. No data needs to be populated and nothing needs to be queried. Nevertheless we will end up with a controller that looks similar to:

public class CustomerController : Controller
{
	public ActionResult Index()
	{
		return View();
	}

	public ActionResult Details()
	{
		return View();
	}
	
	public ActionResult Help()
	{
		return View();
	}

}

That's a lot of typing for so many actions that are not doing something besides calling the view with the same name. By overriding the method HandleUnknownAction() of the Controller class we are able to merge them all:

public class CustomerController : Controller
{
	protected override void HandleUnknownAction(string actionName)
	{
		this.View(actionName).ExecuteResult(this.ControllerContext);
	}
}

Since other unknown actions will result in errors anyway, this method can be used without thinking in most cases.

The example can be found at Stephen Walther's blog.

Tip 21: Your own membership provider

If we want to use a more specialized membership scheme we need to write our own membership provider. This is the only way to work with an existing database scheme, which is not set up in the ASP.NET membership scheme. There are already some articles on the Codeproject to write our own membership provider. In this tip we will just go into detail of the most interesting points.

All we need to do is to inherit from the abstract class MembershipProvider. Now that we have our own implementation we just need to write actual code for the methods that are interesting for us.

public class MyMembershipProvider : MembershipProvider
{
	/* ... */
	
	public override bool ChangePassword(string username, string oldPassword, string newPassword)
	{
		/* This one should really be implemented */
	}
	
	public override MembershipUser CreateUser(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status)
	{
		/* This is also quite important */
	}
	
	public override string ResetPassword(string username, string answer)
	{
		/* This method could be useful */
	}
	
	public override bool ValidateUser(string username, string password)
	{
		/* This is certainly the most important method */
	}
}

Writing our own membership provider is one thing, but without a proper RoleProvider quite obsolete. Here is, where we have the possibility to actually distinguish between groups of users.

public class MyRoleProvider : RoleProvider
{
	/* ... */
	
	public override bool IsUserInRole(string username, string roleName)
	{
		/* This is certainly the most important method */
	}
}

The concept is analogous to the one used with the MembershipProvider. We are again inheriting from an abstract class. Now that we've written our own implementations we just need to tell the runtime to use our providers. This is again done with the web.config file. The following lines will enable our membership provider:

<configuration>
	<!-- ... -->
	
	<system.web>
		<!-- ... -->

		<authentication mode="Forms">
		  <forms loginUrl="~/Account/LogOn" timeout="2880" />
		</authentication>

		<roleManager enabled="true" defaultProvider="MyRoleProvider">
			<providers>
				<clear/>
				<add name="MyRoleProvider" type="OurNameSpace.MyRoleProvider" />
			</providers>
		</roleManager>
		<membership defaultProvider="MyMembershipProvider">
			<providers>
				<clear />
				<add name="MyMembershipProvider" type="OurNameSpace.MyMembershipProvider" />
			</providers>
		</membership>

		<!-- ... --->
	</system.web>
</configuration>

A really good tutorial on writing own membership providers can be found at The Integrity.

Created . Last updated .

References

Sharing is caring!