Subscribe

Close

Thank you for visiting!

Please consider subscribing to the RSS feed or following me on Twitter.

Archive for ASP.NET:

Real Error Messages from IIS

If you want real error messages for your web application in the development environment, make sure you do this:

In IIS:

  1. Go to your web application under Sites and choose .NET Error Pages in the right pane.
  2. Click Edit Feature Settings in the most right pane.
  3. Make sure Mode is set to Off:
    IIS: Error Pages Off

In Web.Config:

Make sure the following httpErrors element is in the <system.webServer> node for the application, with errorMode set to Detailed and existingResponse set to PassThrough:

<httpErrors errorMode="Detailed" existingResponse="PassThrough">
    <remove statusCode="404" subStatusCode="-1" />
    <error statusCode="404" path="/404" responseMode="ExecuteURL" />
</httpErrors>

The 404 handling in above code is just for good measure.

Now you should once again get real .NET error messages from IIS.

Ajax File Download That Works!

So you want to download a file and show an Ajax loader until the download is complete? Here's a way to do it:

  1. User clicks the download button and a JavaScript function is called.  In this JS function, a number of things will happen:
    • Show an AJAX loader.
    • Create a cookie.
    • Run a check cookie function.
    • Redirect to the file download (in this case an MVC method that returns the file).
  2. If successful, the MVC method removes the cookie and returns the file. When the cookie is removed, disable the Ajax loader (happens in check cookie function).
  3. Done.

Step 1 - JS call

User clicks our download button and we call this export function. Note: just before we create the timer and cookieName variables.

var timer = null;
var cookieName = 'App_FileExportCookie';
	
var export = function () {
    $('#ajax-loader').show();

    var cookie = $.cookie(cookieName);

    if (cookie === undefined) {
        cookie = $.cookie(cookieName, 0);
    }
    checkCookie();

    window.location = '/FileExport';
}

We show the Ajax loader and then check if a cookie exists (using jQuery Cookie plugin). Finally we redirect to the URL where the actual file generation and download will happen.

Step 2 - Check cookie

In step 1, we called the checkCookie function. This one is really important as it will check and see the status of the file download, through checking our cookie.

var checkCookie = function (doClearTimeout) {

    if (doClearTimeout) {
        clearTimeout(timer);
    }
    else {
        timer = setTimeout(function () {

            var cookie = $.cookie(cookieName);

            if (cookie === undefined) {
                $('#ajax-loader').hide();
	        checkCookie(true);
	    } else {
            checkCookie(false);
	    }
	}, 1500);
    }
}

What will happen here, is we set a timeout where we look for our cookie. We call this function until we can't find the cookie we created in step 1 any more. When the cookie doesn't exist, we hide the AJAX loader and clear the timeout.

Finally, we need a place to remove the cookie. Enter step 3.

Step 3 - file generation and remove cookie

In step 1, we redirected the user to the /FileExport url. In ASP.NET MVC, we will have an ActionResult with that name and here we will generate and return the file, plus remove the cookie we created in the beginning. The final code:

private string _fileCookieName = "App_FileExportCookie";
private const string _excelMimeType =
 "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";

public FileResult FileExport()
{
    ExcelFile excel;
	
    try {
        excel = GenerateExcel();
    }
    catch (Exception ex)
    {
        // handle exceptions
    }
    finally
    {
	// same as in JavaScript cookie name
	var cookieName = _fileCookieName;
	var cookie = Request.Cookies[_fileCookieName];

	if (cookie != null)
	{
    	    cookie.Expires = DateTime.Now.AddDays(-1);
	    Response.SetCookie(cookie);
        }
    }
	
    return File(excel.FilePath, _excelMimeType);
}

This is just some pseudo code for generating an Excel file, the important stuff happens in the finally block. We look for our cookie and remove it once the file is generated. Finally, we return the file in proper Excel format.

That's it!

Basic ASP.NET MVC Form Validation

Later versions of ASP.NET MVC utilizes jQuery Validate for form validation, and does it well. A few things must be in place for this to work.

I'm going to show you a simple form with a text field and an email address, both of which are required. The email address will also be validated as an email address.

First of all, make sure you use the needed jQuery files. It's important that we use jQuery validate unobtrusive.

The easiest thing here is to install the Nuget package Microsoft.jQuery.Unobtrusive.Validation using Package Manager Console in Visual Studio:

Install-Package Microsoft.jQuery.Unobtrusive.Validation

(Otherwise, you can find the CDN:s on www.asp.net/ajax/cdn)

Don't forget to add the script elements for jQuery and jQuery unobtrusive. It's up to you if you go with the minified versions or the standard versions.

Next up, we will add two properties to our model, preferably a ViewModel.

ViewModel:

[Required(ErrorMessage = "*")]
[EmailAddress(ErrorMessage = "*")]
public string Email { get; set; }

[Required(ErrorMessage = "*")]
public string Name { get; set; }

First of all, we declare our properties. We decorate them with the Required and EmailAddress attributes. We need to import the System.ComponentModel.DataAnnotations namespace for this.

As you can see, you can define an error message for both the Required attribute as well as the EmailAddress attribute. I've chosen to just use an asterisk for both now.

Next up, we will render our form:

@using (Html.BeginForm("MyForm", null, FormMethod.Post))
{
<fieldset>
     <p>
	@Html.LabelFor(x => x.Email, "Email")
	@Html.TextBoxFor(x=>x.EmailTo, new { @placeholder="Email"})
	@Html.ValidationMessageFor(x => x.EmailTo)
     </p>
     <p>
	@Html.LabelFor(x => x.Name, "Name")
	@Html.TextBoxFor(x => x.Name, new { @placeholder = "Name" })
	@Html.ValidationMessageFor(x => x.Name)
     </p>
     <p>
        <input type="submit" value="Send" />
     </p>
</fieldset>
}

What is important here is that we use the TextBoxFor and ValidationMessageFor HTML helpers. ValidationMessageFor is what helps us display the error messages we configured in the view model.

Finally, you might want to style the HTML rendered when there's a field with a validation error. There are a few options, styling the input-validation-error css class takes you far.

And this is it! It's all you need to get started with form validation in ASP.NET MVC.

Here are a few useful links if you want to dive deeper into this:

Creating an Excel File with EPPlus

EPPlus is a very helpful C# Excel library. You can do almost anything you want with it!

Here's how to create a basic Excel file, fill it with some data, and let a user download it.

First of all, let's create the Excel file. We use a DataTable to create columns and rows with the correct cell information. A DataTable is very convenient to use when you know you're going to export to Excel. You'll see why very soon.

When you've filled your DataTable with the content you want, it's time to create a MemoryStream. We use this MemoryStream as a parameter in a using statement for EPPlus: using (var xlPackage = new ExcelPackage(ms))

You need to create a WorkBook and add a WorkSheet to it. Then we use the LoadFromDataTable method to add data to our Excel cells. Finally we save the Excel file and return the MemoryStream. Now we only need to do something with it :-)

public byte[] ExportExcel(int catalogId)
{
	var dt = new DataTable();
		dt.Columns.Add("ProductId", typeof(string));
		dt.Columns.Add("Price", typeof(string));
	
	var catalog = GetCatalog(catalogId);

	foreach (var product in catalog.Products)
	{
		dt.Rows.Add(product.ProductId, product.Price);
	}

	var ms = new MemoryStream();

	using (var xlPackage = new ExcelPackage(ms))
	{
		var wb = xlPackage.Workbook;
		var ws = wb.Worksheets.Add("WS1");
		ws.Cells.LoadFromDataTable(dt, true);

		xlPackage.Save();
		return ms.ToArray();
	}
	return null;
}

In our MVC controller we call the ExportExcel method and return a FileContentResult, giving the file a name as well:

public ActionResult ExportExcel(int catalogId)
{
	if (catalogId != -1)
	{
		var catalog = _catalogRepository.GetCatalog(Convert.ToInt32(catalogId));
		var file = _exportRepository.ExportExcel(catalogId);

		if (file != null)
		{
			return new FileContentResult(file,
				"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
				{ FileDownloadName = "My Excel File.xlsx" };
		}
	}

	return View("Index");
}

Finally, we need a download button in our View. It calls the ExportExcel action method on our controller and looks like this:

@Html.ActionLink("Download", "ExportExcel", new { catalogId = 7 })

This is all the basics you need to create and fill an Excel file with data using EPPlus.

Adding Additional ViewData

Sometimes you want to add additional View Data to your Partial, while retaining your current View Data. The solutions is very easy. Just use this.ViewData in your constructor overload, and add the new View Data as usual:

@Html.Partial("_Product", prod, new ViewDataDictionary(this.ViewData)
{{ "Addons", "Product Addons"},{ "AdditionalCategory", "Category" }})

404 When Browsing to WCF Service

If you simply get a 404 message when browsing to your WCF service, you probably are missing the necessary components for WCF.

To do this, simply go to Windows Features and Turn Windows features on or off and make sure the correct boxes are ticked under .NET Framework 3.5 and .NET Framework 4.5 Advanced Services:

Activate WCF.

Type Initialization Exception with EPiServer, StructureMap and WCF

Recently I got an error message working with EPiServer, StructureMap and a WCF service:

StructureMap Exception Code: 202\nNo Default Instance defined for PluginFamily

It turns out I forgot some StructureMap configuration. Adding this configuration solved the problem:

For<ISearchService>().Use<SearchService>();

HtmlDecode and MVC

Just a simple tip when you try to use HtmlDecode in MVC (Razor) - don't use WebUtility.HtmlDecode, it won't work.

Just use Html.Raw instead:

@Html.Raw(item.Description)

It really helps to know your Html Helpers.

Html.Raw was introduced in MVC 3.

Web Services And AutoEventWireUp

If you get the NullReferenceException error when dealing with web services, it might have to do with AutoEventWireUp being set to false. If you get an error pointing at something like foreach (ServiceDescription description in serviceDescriptions), it might also have to do with AutoEventWireUp set to false.

For instance, if you set AutoEventWireUp to false globally, in Web.Config, your web services will fail.

There's a fix for this though, although it's a bit of a different one.

You just have to locate the DefaultWsdlHelpGenerator.aspx file in C:\Windows\Microsoft.NET\Framework64\v2.0.50727\CONFIG (or similar) and add this line to the top:

<%@ Page AutoEventWireup="true" %>

How To Solve The "Breakpoint will not currently be hit" Error

When trying to debug in Visual Studio, you get the following error:

Breakpoint will not currently be hit. No symbols loaded for this document.

After some research, I came up with a solution: delete temporary ASP.NET files. This is how you do it:

  1. Locate your temporary ASP.NET files and delete them. They're probably located in C:\Windows\Microsoft.NET\Framework\v2.0.50727\Temporary ASP.NET Files.
  2. Rebuild your project and attach to process and you should be fine - the breakpoint should be hit.

Fixing "The file exists" IOException Error

If you get the System.IO.IOException: The file exists error, it's probably because your C:/Windows/Temp folder is absolutely full with temporary files. In fact, if you have more than 65535 files in your temp folder, the method GetTempFileName will throw this error.

To resolve this, just remove all temporary files in C:/Windows/Temp. It might take some time, but it will solve the problem.

Here's the stack trace I got:

System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
   System.IO.Path.GetTempFileName()

How To Name Controls in ASP.NET Web Forms

When working with ASP.NET, particularly Web Forms up until version 4 of the ASP.NET framework, one might want to think a bit at the naming of the controls used.

I often stumble upon control names looking like this: ChoicesPlaceHolder, MessageLabel and similar. That's clear and decent naming, but it can be improved.

Me and fellow developers have found a simple yet useful and clear naming convention, based on a prefix and a descriptive control name (using Hungarian notation).

This is how it can look instead: phChoices, lblMsg

Naming is particularly important when dealing with nested controls and ContentPlaceHolders. Using better naming, you can improve and reduce this rendered control id:

ctl00_ContentPlaceHolderAllColumns_ContentPlaceHolderAllContentColumns_
ContentPlaceHolderSubNavigation_SubNavigation_RepeaterNavigation_ctl06_
HyperLinkNavigationPageLink

To this id:

ctl00_cphAllColumns_cphAllContentColumns_cphSubNav_SubNav_rptNav_
ctl06_hlNavLnk

Pretty much better, huh? Plus it saves bandwidth.

Also, think twice about where you put runat="server". Is it needed on all controls?

Here's a table with common controls, their prefix and an example naming.

Table: Common ASP.NET Web Forms control and naming
ASP.NET Control Prefix Example
Button btn btnSend
CheckBoxList cbl cblColourCombinations
ContentPlaceHolder cph cphMain
DropDownList ddl ddlNavChoices
Label lbl lblMsg
Literal lit litMsg
Panel pnl pnlLogin
PlaceHolder ph phArticles
RadioButtonList rbl rblPaymentChoices
TextBox tb tbUserName

Get Started Using Solr for Search with ASP.NET

Solr is an advanced search coming from Apache's Lucene project. Thanks to SolrNet, a .NET library for Solr, it is quite convenient to use Solr for search in ASP.NET. I'll show you how. (You can also download the sample app right off, if you'd like to)

Install Apache Tomcat and Solr

First of all, make sure you get the latest version of Apache Tomcat and Solr. (I installed Tomcat 7 and Solr 1.4.1 (zip version) as of September 2010.)

Tomcat installation

When installing Tomcat, make sure to remember the port you specify (normal for Solr is 8983). After installation, the Apache Tomcat Properties window should popup. If not, find Configure Tomcat in the start menu and make sure the web server's started. If it's started, you should find the default Tomcat startpage if you browse to http://localhost:8983.

Solr installation

Before you install Solr, stop the Tomcat web server (through the Configure Tomcat window).

When you've downloaded the Solr zip file (make sure it's the zip version!), unzip the archive and find the dist folder. In the dist folder, find the apache-solr-1.4.1.war file and copy it to C:\Program Files\Apache Software Foundation\Tomcat 7.0\webapps, renaming it to solr.war.

Now, we also need to create the Solr folder, which will host our Solr configuration files, indexes and so on. I created C:\Solr. You'll also need to copy the contents of the apache-solr-1.4.1\example\solr folder to your newly created Solr folder. When you're done, you should at least have the bin and conf folder.

Finally, we need to tell Tomcat where our Solr folder is located. Open up the Configure Tomcat window, navigate to the Java tab and add this row to Java Options:

-Dsolr.solr.home=c:\solr

It should look like this:

Solr folder in Tomcat

Now, you should first start the web server and then be able to navigate to http://localhost:8983/solr. Installation and basic configuration done!

Quick look at the configuration files

The Solr configuration files are important - you will use them to tell Solr what should be indexed and not. The most important config files are schema.xml and solrconfig.xml. These are located in the C:\Solr\conf folder.

Easier use of Solr with the SolrNet library

SolrNet is a great .NET library for Solr, making it all easier. Download assemblies (and samples) on the SolrNet Google Code page.

Sample ASP.NET app with SolrNet for download

I've developed a sample web application for you, using SolrNet for search in ASP.NET.

Basically, you've got some data in the SQL Server database and use SolrNet to find items in the search index and present all you want from the database.

Here's what you should know:

  • Map fields to Solr using attributes (Player.cs in the classes folder)
  • This is sample code, not everything might be suitable for a production environment
  • You should use Linq to Sql, NHibernate or similar for better scaling and easier data access

Download the sample Solr app

I'd like to thank A. Friedman for his contribution to the Solr and ASP.NET world. Here's his great blog post on Solr and SolrNet.

Code snippets using SolrNet

Here's some code snippets from my Solr app. You can find them in the source code, although I found it being a good idea to post code for a couple of common actions using Solr and SolrNet for search.

Search the index and bind to Repeater:

var search = new DefaultSearcher()
	.Search(query, 10, 1);

rptResults.DataSource = search.Result;
rptResults.DataBind();

Re-index all data:

new SolrBaseRepository.Instance<Player>().Start();
			
var solr = ServiceLocator.Current.GetInstance<ISolrOperations<Player>>();
var players = new PlayerRepository().GetPlayers();

solr.Add(players);
solr.Commit();

Remove from index:

new SolrBaseRepository.Instance<Player>().Start();

var solr = ServiceLocator.Current.GetInstance<ISolrOperations<Player>>();
var specificPlayer = new PlayerRepository().GetPlayer(id);

solr.Delete(specificPlayer);
solr.Commit();

Adding multiple fields to the search index

By standard, Solr lets you index one field only, thanks to the defaultSearchField in schema.xml. It's easy to turn on indexing of multiple fields though, using copyField and an additional field which takes multi values.

What you have to do is to edit schema.xml a bit:

  1. Setup the fields you want to get indexed, using field.
  2. Create an additional field called "text", setting its multiValued property to true.
  3. Use copyField to copy data to this additional field.
  4. Use this additional field, "text", as the defaultSearchField.
Here's how:
<fields>
	<field name="id" type="int" indexed="true" stored="true" required="true" /> 
	<field name="firstname" type="text" indexed="true" stored="false" required="false" /> 
	<field name="lastname" type="text" indexed="true" stored="false" required="false" />
	<field name="text" type="text" indexed="true" stored="false" multiValued="true" />
</fields>
<copyField source="firstname" dest="text" />
<copyField source="lastname" dest="text" />
<uniqueKey>id</uniqueKey>
<defaultSearchField>text</defaultSearchField>
<solrQueryParser defaultOperator=uot;AND" /> 

Debugging Solr

If you encounter any problems with Solr, try this to get it working:

  • Turn off elevate.xml handler (comment appropriate lines in solrconfig.xml).
  • Case sensitive configuration files - make sure you spell copyField, multiValued etc correctly.
  • In solrconfig.xml, make sure you use matching data types to those you've defined in your ASP.NET app.

More Solr

Solr is really powerful and gives you a lot of options. I recommend the Solr Wiki for more information on what actually is possible.

Make Forms Authentication work in IIS 7

When experiencing Forms Authentication problems in IIS 7, step through this list to make sure everythings in place for Forms Authentication to work:

  1. Make sure Forms Authentication is enabled for your website in IIS.
    In IIS 7, browse to your website, go to Authentication and make sure Forms Authentication is set to Enabled, like this:

    Forms Authentication set to enabled in IIS 7

  2. Make sure your Web.Config settings looks something like this:

    <authentication mode="Forms">
    	<forms name="aspxFormsAuth" defaultUrl="/Admin"
    		   loginUrl="/Admin/Signin.aspx" protection="All" timeout="90" />
    </authentication>

    Also, make sure you've not missed the location element:

    <location path="admin">
    	<system.web>
    		<authorization>
    			<deny users="?"/>
    		</authorization>
    	</system.web>
    </location>
  3. Make sure <modules> in <system.webServer> has the runAllManagedModulesForAllRequests attribute set to true:

    <system.webServer>
    	<modules runAllManagedModulesForAllRequests="true" />
    </system.webServer>

    If you got the 403.14 error, this might solve it.

  4. Finally, you could try the ordinary iisreset or aspnet_regiis -i command. Make sure you run aspnet_regiis -i on c:\Windows\Microsoft.NET\Framework\v4.0.30319 (or Framework64 if you're running a 64 bit OS).

How to Keep Visual Studio Debugging Alive

If you, like me, like to take a deep plunge into Visual Studio debugging, you might've encountered the dreaded Web server process termination error, "The web server process that was being debugged has been terminated by Internet Information Services":

Visual Studio web server process termination during debugging

It's very irritating when you're in the middle of a debugging session and it's stopped by the error message above. But - there's a solution! As the error message tells us, you can configure application pool settings in IIS.

This is how to do make sure you'll be able to debug forever:

  1. Pop up your IIS and find the correct Application Pool
  2. Right click and choose Advanced Settings
  3. Below Process Model, find Ping Enabled and make sure it says False:
    Disable ping for application pool in IIS 7
  4. Done!
If you have trouble finding the correct application pool, have a look at resolving w3wp processes by Daniel Berg. It'll help!

How to solve unknown server tag 'asp:ListView'

Recently I got this error message, basically telling me I was using an unknown server control. In this case, the ListView control. First thing that comes to mind is if the control itself exists in .NET 3.5, which I was using. Of course it does!

Next thing, I was reading about the solution to this being to completely reinstall .NET Framework 3.5 and .NET Framework 3.5 SP1.

However, I solved it by adding two lines to the Web.Config controls element instead, saving me a lot of time.

Here's the code:

<pages>
  <controls>
	  <add tagPrefix="asp" namespace="System.Web.UI" assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
	  <add tagPrefix="asp" namespace="System.Web.UI.WebControls" assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
  </controls>
</pages>

ASP.NET and improving web standards

Developers often ask me what they can do to improve web standards on their ASP.NET website. There are a number of tricks available and I thought I'd share them with you.

Web.Config magic

First thing to do, is to make sure you've got your Web.Config ready for web standards. This is really simple, as there's only some copy and paste needed. What you want to do is setting xhtmlConformance mode to Strict and make use of browserCaps, both in the system.web section, like this:

<system.web>
<xhtmlConformance mode="Strict" />
<browserCaps>
  <case match="W3C_Validator*">
    TagWriter = System.Web.UI.HtmlTextWriter
    W3CDomVersion = 1.0
  </case>
</browserCaps>

....
</system.web>

Use the best .NET controls at your disposal

Countless of times you see .NET controls such as Label and Panel used. Not good, since both render extra HTML output. A label renders an extra span and a Panel renders an extra div, thus affecting the markup of the website - and at worst even breaking the layout.

To the rescue, comes the Literal and PlaceHolder controls. The Literal and PlaceHolder don't render extra markup, and I've never ran into a single problem using them instead.

Also, I like to use the Repeater as often as I can, since I don't always trust third party controls.

Set CSS class on the first listing item

You might have seen class="first" set on the first item in a listing, a number of times. For some, this is a pain to address. However, nothing could be easier than setting a specific CSS class on the first item in a listing. You just have to use the onItemDataBound event on your control. Here's how it might look:

<asp:Repeater ID="rptListing" OnItemDataBound="cssAddons" runat="server">
<ItemTemplate>
	<li<asp:Literal ID="cssClass" Visible="false" runat="server"> class="first"</asp:Literal>>
		<%#Eval("Item") %>
	</li>
</ItemTemplate>
</asp:Repeater>

And the codebehind:

protected void cssAddons(object sender, RepeaterItemEventArgs e)
{
	ListItemType type = e.Item.ItemType;

	if (type == ListItemType.Item)
	{
		if (e.Item.ItemIndex == 0)
		{
			Literal litCssClass = (Literal)e.Item.FindControl("cssClass");
			if (litCssClass != null) litCssClass.Visible = true;
		}
	}
}

You should never be forced to rebuild a project when doing layout changes - that's why one would prefer to use a hidden Literal with the CSS class in the ItemTemplate, and toggling the visibility if it's the first item in a listing.

Customized web controls and CompositeControl

A lot of you are perhaps writing your own web controls. Take note though, inheriting from CompositeControl will make your web controls render an extra span. There's a simple solution to this - just override the Render method in your control, like this, and you're set:

protected override void Render(HtmlTextWriter writer)
{
	RenderContents(writer);
}

Some reminders - naming conventions and id's

When it comes to ASP.NET and naming web controls, things can get messy. You want to name your controls as short as possible, without making the name totally meaningless. You particularly want to name your ContentPlaceHolders in a smart way, to avoid id's like this:

ctl00_ContentPlaceHolderContent_ContentPlaceHolderMain_ContentPlaceHolderWidePageWithoutTopImage_ContentPlaceHolderWidePage_ContentPlaceHolderPageContent_ButtonSend

I regularly prefix my web controls, rpt for Repeater, cph for ContentPlaceHolders and so on.

Another problem in ASP.NET Web Forms is id's. If you use runat="server" with a HTML element, the id might look like in the example above. You must avoid this as much as you can, since the altered id's might break the layout. Some might say you should use CSS classes instead, but that's not the way to go. CSS classes aren't meant to be used instead of id's.

In ASP.NET 4, you can actually choose how your id's are rendered, through the ClientIdMode property. A welcome change!

More reading

Remember this is aimed at ASP.NET Web Forms websites, even though part of the tricks can be used in an MVC environment too!

Add controls and namespaces to Web.Config for easier maintenance

Very often you add a namespace or register an assembly on top of an ASP.NET page, like this:

<%@ Page Language="C#"
	AutoEventWireup="True"
	CodeBehind="Default.aspx.cs"
	Inherits="ProjectName._Default" %>
<%@ Import Namespace="System.IO" %>
<%@ Import Namespace="ProjectName.Classes" %>
<%@ Register Assembly="ProjectName" Namespace="ProjectName.Controls" TagPrefix="cc" %>

It works to do once or twice, but when you need to add these namespaces and controls on several different pages, it quickly becomes maintenance hell.

What to do? Use Web.Config instead.

You just have to declare your namespaces and controls in the system.web section, like this:

<system.web>
	<pages>
		<namespaces>
			<add namespace="System.IO" />
			<add namespace="ProjectName.Classes" />
		</namespaces>
		<controls>
			<add assembly="ProjectName" namespace="ProjectName.Controls" TagPrefix="PN" />
		</controls>
	</pages>
</system.web>

Then you will be able to write code like

<%=Toolbox.FormatHtml("....") %>

instead of code like

<%=ProjectName.Classes.Toolbox.FormatHtml("....") %>

A clear improvement. You also won't need to register your controls everywhere to use them.

If you haven't been using this feature (it already came in ASP.NET 2.0!) it's time now!

How to add a control to a StringBuilder

When you want to put together a number of strings in ASP.NET, the StringBuilder is very handy. Instead of using concatenation, StringBuilder both looks better and improves performance. You can simply append a set of strings to the StringBuilder and return it, like this:

public static string BuildNumbers()
{
	StringBuilder sb = new StringBuilder();
		sb.Append("<ul>");
	
		for (int i=0; i<500; i++)
		{
			sb.Append(string.Format("<li>{0}</li>",i));
		}

		sb.Append("</ul>");

	return sb.ToString();
}

However, sometimes you might want to add a web control to the StringBuilder. A bit more tricky.

What you want to do, is to dynamically create the control and use StringWriter together with HtmlTextWriter to render the control inside the StringBuilder. Like this, when you want a LinkButton:

public static string BuildNumbers()
{
	StringBuilder sb = new StringBuilder();
		sb.Append("<ul>");
	
		for (int i=0; i<500; i++)
		{
			sb.Append(string.Format("<li>{0}</li>",i));
		}

		LinkButton lnkBtn = new LinkButton();
			lnkBtn.ID = "lnkBtnSubmit";
			lnkBtn.Text = "Submit";

		using (StringWriter sw = new StringWriter(sb))
		{
			using (HtmlTextWriter tw = new HtmlTextWriter(sw))
			{
				lnkBtn.RenderControl(tw);
			}
		}
	
	sb.Append("</ul>");

	return sb.ToString();
}

How to install .NET developer tools on Windows 7

I ran into problems with IIS and ASP.NET after I installed Windows 7, so I thought I'd share my then successful installation process with you.

Clean install of Win 7

First of all, a clean installation of Windows 7 is preferred. I recommend you this because it's otherwise possible you encounter a strange problem which has to do with an upgrade.

A clean installation of Windows 7 will always minimize your problems, plus it's the best start you can get.

First instructions

To minimize upcoming problems, the best thing to do is to install the .NET developer tools before you install anything else.

Make sure you install these tools before anything else and your installation will probably as smooth as anything.

My successful installation process went like this:

  1. Enable IIS and ASP.NET
  2. Install SQL Server 2008 + Management Studio (plus ASP.NET MVC)
  3. Install Visual Studio 2008 + Service Pack 1

Enable IIS and ASP.NET

After the installation of Windows 7 is done, it's first time for IIS and ASP.NET.

You'll have to enable these features through Windows Features which is located under Programs and Features in the Control Panel.

In the Windows Features window, make sure the following options are ticked:

  • Internet Information Services (you can also activate IIS 6 under Web Management Tools->IIS 6 Management Compatibility)
  • ASP.NET (under World Wide Web Services->Application Development Features)

Turning on IIS and ASP.NET in Windows Features

Use Web Platform Installer to install SQL Server and ASP.NET MVC

Microsoft Web Platform Installer is an awesome tool which will help you install programs, modules and much more related to .NET development. Use Web Platform Installer to install the following:

  • SQL Server 2008 + SP1
  • SQL Server Management Studio Express
  • ASP.NET MVC

Once you've downloaded and installed Web Platform Installer, activate the features under the Web Platform tab:

Microsoft Web Platform Installer, the Web Platform tab

The installer will automatically download and install all your selected features. Gone are the days with meaningless steps in the SQL Server installation process!

Mind, you can also choose to install a whole lot of other features, but I recommend you to save them for later.

Install Visual Studio 2008 + SP1

Final step is to install Visual Studio 2008 and Service Pack 1.

Once this is done, you should be set and everything should be fine.

If problems occur

If you encounter any problems, it's always a good shot to use your search engine to find anything related to the problem.

I tried a lot of different solutions though and no solution helped me - a clean installation and the above installation flow solved my problems.

Twitter module in ASP.NET for download

If you've been looking for an ASP.NET Twitter module, look no further. I've coded a C# module in order to consume a given Twitter feed. Download the Twitter module for easy use in your own project.

How to use

You can either show the latest Twitter message or any given number of Twitter messages with this module - all you have to do is to point out a Twitter username and the number of messages to show.

Here's a code example on how you can show 5 Twitter messages in a Repeater.

Code-behind:

string userName = "dileno";

List<TwitterFeed> messages = TwitterFeed.Fetch(userName, 5);

if (messages.Count>0)
{
rptTwitterMessages.DataSource = messages;
rptTwitterMessages.DataBind();
}

Repeater in code-front (aspx/ascx):

<asp:Repeater ID="rptTwitterMessages" runat="server">
<HeaderTemplate><ul></HeaderTemplate>
<FooterTemplate></ul></FooterTemplate>
<ItemTemplate><li><%#((TwitterFeed)Container.DataItem).Message %> (<a href="<%#((TwitterFeed)Container.DataItem).Link %>"><%#((TwitterFeed)Container.DataItem).PubDate %></a>)</li></ItemTemplate>
</asp:Repeater>

Download the Twitter module

You can download the C# module with complete source code and code examples:

Download Twitter module (zip, 16 kB)

Short instructions for implementation

  • Drop the TwitterFeed.cs file into your project.
  • Use the code in Default.aspx.cs to customize the Twitter messages.
  • Update your appSettings.Config with the key TwitterFeedUrl, which you find in this project's appSettings.Config file.

Dealing with different website and database locales

Using different locales

When I developed this blog, I wanted to use the en-GB locale to be able to present correct formats for dates and numbers.

It is simple to set a specific locale for a whole web application - you just have to add the <globalization> tag under <system.web> in Web.Config and set the culture and uiCulture attributes, like this:

<globalization culture="en-gb" uiCulture="en-gb"/>

So far - so good. Dates and numbers have the correct format and everything looks well - until in comes the database.

I use Sweden's best web hosting provider (according to a study published in 2009) and this means the SQL Server database's collation is set to Finnish_Swedish_CI_AS - as it should be.

Having a website and a database with different locales gives you problems. Trust me.

Out-of-range datetime value

The main problem I ran into was a database insert of an incorrect date - the date I tried to insert obviously had an en-GB format while my database expected a date with sv-SE format. This generated an out-of-range datetime value error.

At first, I tried rewriting the date to a correct format, but quickly realized it was wrong to do so. I ended up with one of the two correct solutions for this problem.

Solutions

The two solutions are:

  • Update database collation to match your website locale
  • Update SQL query to use parameters

I ended up with the latter one - updating my SQL query to use paremeters instead.

This is also how you want to insert and update your database tables - by using parameters with your command object (unless you use any ORM solution like NHibernate, but that's another blog post).

Here's a SQL query where parameters are used (disregard the lousy error handling):

string strSQL = "INSERT INTO mytable(user,password) VALUES(@user,@password)";

SqlConnection objConn = new SqlConnection(connString);
SqlCommand objCmd = new SqlCommand(strSQL, objConn);

objCmd.Parameters.AddWithValue("@user", "username");
objCmd.Parameters.AddWithValue("@password", "secretpassword");

try
{
objConn.Open();
objCmd.ExecuteNonQuery();
}
catch { }
finally
{
objConn.Close();
}

Conclusion

When developing a database driven website, make sure you know the website's locale and the database's collation. Also, use parameters with your SQL queries, not only when dealing with dates and numbers, but always. You don't know what input might try to sneak into the database otherwise.

Improve performance by using explicit cast instead of DataBinder.Eval

The DataBinder.Eval method is often used when you want to bind data to a control's template. What DataBinder.Eval really does is to cast Container.DataItem to its specific type, like this:

<%# DataBinder.Eval(Container.DataItem, "Heading") %>

DataBinder.Eval uses .NET reflection in order to cast Container.DataItem to its specific type. The use of reflection in this method will give you a performance loss.

If performance is of importance in your application or if you know what type Container.DataItem is, it's better to use an explicit cast instead:

<%# ((DbDataRecord)Container.DataItem)["Heading"] %>

To the top