Category Archives: ASP.NET

Configuring IIS 7 default document from web.config

IIS 7 has a lot of changes regarding the configuration model. Using the Integrated Configuration System in IIS 7 you can set configuration of your server on different levels, compared to IIS 6, where the ASP.NET developer were more restricted. Configuring IIS 7 from an ASP.NET Web Application’s web.config file works from both ASP.NET 2.0 and ASP.NET 3.5. Note that the server can be restricted to lock certain configuration settings from above the application level, you might encounter this if your website runs in a shared hosting environment.

Setting up the website

To have a test website, I will setup a new website on my Vista machine running IIS 7. I don’t want the website to run under the Default Web Site, so first thing, I’ll add a hostname to my hosts file. This is located in the drivers\etc folder of your windows directory’s system32 folder. Add the following line:

127.0.0.1         iis7test

Open up Internet Information Services (IIS) Manager, and create a new Application Pool. Name it iis7test, and leave the settings as default:

image

Add a new website with the following settings:

image

This should get us started. Open up Visual Studio 2008 (2005 will also work). Open the website you just created inside IIS.

image

The only item in the website is a web.config. Add a new Web Form called Test.aspx.

Right-click the Test.aspx file and click View in browser. You could get an HTTP Error 500.00, like this:

image

To get rid of it, set impersonate to false in the web.config file by adding this line to the system.web section:

<identity impersonate=”false” />

Now your site should work fine. Edit the Test.aspx file, so it has some content. Just write Test IIS 7 or something in the HTML mark-up.

If you right-click the website icon in the Solution Explorer, and click View in browser, you will get an error like the one bellow:

image

That is because our website does not have any pages that match the name of the default documents on IIS. If you don’t want to be limited by the few default documents that comes with IIS out-of-the-box, you can add your own from web.config by adding these lines:

<system.webServer>
<defaultDocument>
<files>
<clear />
<add value=”Test.aspx” />
</files>
</defaultDocument>
</system.webServer>

Maybe the system.webServer section already existed in your web.config file. If so, just add the defaultDocument section. Here you can specify all the filenames that should act as a default document in IIS 7. The <clear /> line is optional. If you want to keep the default settings, and only add your own – just delete that line.

Now when we save the new web.config file, and refresh the browser we se our Test.aspx file:

image

This setting is just one of many that you can specify in your application’s web.config file. In the near future I’ll be blogging more about specific settings you can use to configure IIS 7 from ASP.NET.

Technorati Tags: ,

kick it on DotNetKicks.com

The Integrated Programming model in IIS 7

IIS 7 comes with a bunch of improvements for developers. You can do a whole lot of exciting things even from web.config, but also from code.

To take advantage of the powerful integrated programming model, you need to set your application pool to use the Integrated pipeline mode. There’s no limit to what you can do.

I found a lot of great stuff in the book called Professional IIS 7 and ASP.NET Integrated Programming and learned a lot of useful stuff.

IIS 7 (or 7.5) is still the preferred way to develop, test and deploy ASP.NET applications, and as a developer you must stay up-to-date on what your tools and framework has to offer!

Sending e-mails from your ASP.NET app and maintaining a list of recipients

Making your application send e-mails, is something you do almost in every application. It’s easy – all you need is an SMTP server to relay your mail through. Sending e-mail messages to several recipients at once can also be done, by adding multiple recipients to either of the MailMessage.To, MailMessage.Cc or MailMessage.Bcc properties in C#. But if you send a newsletter every month, week or day, it is very important to maintain your list of recipients, so that whenever people get a new e-mail address (and they do!) and the old one stop working – you avoid those undelivered mails everytime you send your newsletter.

I’ve made this simple class called BulkSender, that takes three parameters in the constructor:
mailMessage (System.Net.Mail.MailMessage), recipients (List<string>), mailSender (System.Net.Mail.SmtpClient).

It has one method: SendEmailMessage, and what it does is, that it adds all recipients to the Bcc collection on the MailMessage, and sends the mail in a try-catch block. Whenever the SmtpFailedRecipientsException or SmtpFailedRecipientException is handled, it adds the e-mail address to a local List<string> and returns the e-mail addresses with delivery failures as a List<string>.

Code snippet:
image

When you get the List<string> of failed recipients, you can add your own logic to take care of maintaining your list of recipients. Maybe an e-mail address has to fail 3 times, before it is deleted, or maybe you want to delete it the first time it fails.

Another good thing to have in mind when sending e-mails to multiple recipients at once, is to add the mail addresses to the Bcc collection, and NOT the To or Cc collection. This way the e-mail addresses is hidden, you only get to see ‘undisclosed-recipients’ in you mail client:
image

Note that the BulkSender class automatically clears the To, Cc and Bcc collections before adding the recipients to the Bcc collection. This way you know for sure, that you do not have any e-mail addresses publicly available when you send the e-mail.

You can download the code below. Feel free to tweak the class – and let me know if you have some interesting things regarding this topic.

Technorati Tags: ,

BulkSender.zip (818.00 bytes)

kick it on DotNetKicks.com

How to handle IIS Event id 1009 (Event id 5011 on IIS 7)

Recently at work we had some serious production environment issues. Our web application basically made the IIS application pool terminate unexpectedly. And we didn’t get a single stack trace or a line of code. The only thing that was logged inside of the Windows Event Viewer, was event id 1009, which basically told that the application pool serving our app terminated unexpectedly. And that was it. All active user sessions were wiped, and the users had to login again.

The error started after we did a major upgrade. A lot of our code had changed since the previous version, so we set up an new website and application pool inside IIS 6.0 for the new version, so we could upload the new application, and then turn off the old one, to get as seamless an upgrade as possible. It worked fine, and we were happy. Until a few hours later, when we saw our users were logged off.

At first we thought it was IIS settings we had done wrong, so we did a complete comparison with the old website and application pool to see if we forgot anything. There was no difference at all. After a while, we realized that the only thing that could make the IIS crash like that, was our own code. But with no clue of where the danger in our code were – we were lost.

After some time, we got WinDbg attached to the worker process of IIS, serving our application. We caught a few memory dumps, but those were not of the exception we were looking for.

Later I found an HttpModule for ASP.NET. Basically the .Net framework 2.0 has changed the way it handles exceptions from other threads in IIS. In ASP.NET 1.1, unhandled exceptions from asynchronous threads inside IIS were ignored. But in ASP.NET 2.0, those unhandled exceptions makes the IIS application pool crash.

Make the IIS crash yourself

So try to do this. Create a new ASP.NET 2.0 website running on your local IIS. You only need a single page, so you’re fine with the default.aspx Visual Studio creates for you. Add a button to the page, and create an event handler for the buttons OnClick event.

Add this to your code-behind:

  /// <summary>
  /// Handles the Click event of the btnMakeCrash control.
  /// </summary>
  /// <param name="sender">The source of the event.</param>
  /// <param name="e">The EventArgs instance containing the event data.</param>
  protected void btnMakeCrash_Click(object sender, EventArgs e)
  {
    ThreadPool.QueueUserWorkItem(new WaitCallback(MakeIisCrash));
  }

  /// <summary>
  /// Makes the IIS crash.
  /// </summary>
  /// <param name="stateInfo">The state info.</param>
  private void MakeIisCrash(object stateInfo)
  {
    // Instantiate the DataSet to null
    DataSet ds = null;

    // Make an unhandled NullReferenceException
    ds.CaseSensitive = true;
  }

The buttons click event handler uses the ThreadPool to execute the MakeIisCrash() method. This method instantiates a DataSet as null, and the sets a property. Since the DataSet is null, this throws a NullReferenceException which is not handled, as you can see.

Click the button, and see how IIS will crash.

You can sense that your computer is working harder – that is because IIS terminates the application pool. If you go and check the Windows Application log, you can see that is has added an error:

image

Now this doesn’t tell you much. Imagine if you had hundreds of thousands line of code – how would you find the cause of the error?

Inside Visual Studio, add a new class to the website – call it UnhandledExceptionModule, and apply this code:

#region Using

using System;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Web;

#endregion

/// <summary>
/// Handles all unhandled exceptions from in the current AppDomain. 
/// 
/// Works great to catch unhandled exceptions thrown by IIS's child threads, /// which will make the application pool terminate unexpectedly 

/// without logging. This makes sure your Exception /// is logged to the Application event log.
/// </summary>
public class UnhandledExceptionModule : IHttpModule
{
  #region Fields

  private static int _UnhandledExceptionCount = 0;
  private static string _SourceName = null;
  private static object _InitLock = new object();
  private static bool _Initialized = false;

  #endregion

  #region IHttpModule members

  public void Init(HttpApplication app)
  {

    // Do this one time for each AppDomain.
    if (!_Initialized)
    {
      lock (_InitLock)
      {
        if (!_Initialized)
        {
          string webenginePath = Path.Combine(RuntimeEnvironment.GetRuntimeDirectory(), "webengine.dll");

          if (!File.Exists(webenginePath))
          {
            throw new Exception(String.Format(CultureInfo.InvariantCulture,                 "Failed to locate webengine.dll at '{0}'.  This module requires .NET Framework 2.0.", webenginePath));
          }

          FileVersionInfo ver = FileVersionInfo.GetVersionInfo(webenginePath);
          _SourceName = string.Format(CultureInfo.InvariantCulture, "ASP.NET {0}.{1}.{2}.0", ver.FileMajorPart,                 ver.FileMinorPart, ver.FileBuildPart);

          if (!EventLog.SourceExists(_SourceName))
          {
            throw new Exception(String.Format(CultureInfo.InvariantCulture,                 "There is no EventLog source named '{0}'. This module requires .NET Framework 2.0.", _SourceName));
          }

          AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(OnUnhandledException);

          _Initialized = true;
        }
      }
    }
  }

  public void Dispose()
  {
  }

  #endregion

  #region UnhandledException event handler

  public void OnUnhandledException(object o, UnhandledExceptionEventArgs e)
  {
    // Let this occur one time for each AppDomain.
    if (Interlocked.Exchange(ref _UnhandledExceptionCount, 1) != 0)
      return;

    StringBuilder message = new StringBuilder("\r\n\r\nUnhandledException logged by UnhandledExceptionModule:\r\n\r\nappId=");

    string appId = (string)AppDomain.CurrentDomain.GetData(".appId");
    if (appId != null)
    {
      message.Append(appId);
    }

    Exception currentException = null;
    for (currentException = (Exception)e.ExceptionObject; currentException != null; currentException = currentException.InnerException)
    {
      message.AppendFormat("\r\n\r\ntype={0}\r\n\r\nmessage={1}\r\n\r\nstack=\r\n{2}\r\n\r\n",
                           currentException.GetType().FullName,
                           currentException.Message,
                           currentException.StackTrace);
    }

    EventLog Log = new EventLog();
    Log.Source = _SourceName;
    Log.WriteEntry(message.ToString(), EventLogEntryType.Error);
  }

  #endregion
}

Modify the httpModules section inside web.config to look lige this:

        <httpModules>            <add type="UnhandledExceptionModule" name="UnhandledExceptionModule"/>        </httpModules>

This is your new friend. It is a must for any ASP.NET 2.0 application running in production environment. This will catch your exception, and write the message and the stack trace to the Windows Application Event log, and now you can see what caused the crash:

image

So after we applied this HttpModule to our production environment, we get a nice entry in our Application event log from ASP.NET. It includes a stack trace, which makes us able to find the problem, and fix it!

Further reading regarding ASP.NET production environment issues:

Hope this will help someone.

Technorati Tags: , , ,

kick it on DotNetKicks.com

Web services and Silverlight 1.1 C# gotchas

As anyone must know by now, Silverlight is Microsoft's cross-browser, cross-platform RIA (Rich Internet Application) technology – like Adobe's Flash. With Silverlight, they say the sky is the limit, and it's only up to yourself what to invent. Silverlight offers you a big chunck of the .NET CLR in the browser, when using Silverlight 1.1 Alpha (Refresh). As always, there are do's and don'ts when you work with new technology and in this not-known-yet-part series, I'm going to recap my experience using Silverlight 1.1 Alpha and later Silverlight 1.1 Alpha Refresh to develop some sophisticated charting components.

Before you begin – general info

For the current release of Silverlight (Silverlight 1.1 Alpha Refresh) you don't have the System.Data namespace (that means no DataSets nor DataTables). Also the System.Xml namespace is very small, you don't get the XmlDocument which would have been useful. I haven't heard anything reliable about whether or not those namespaces and classes will make it into the final release of Silverlight, but it doesn't seem like it.

Maybe 3rd party components will come to help here, but that's a drawback because then the users have to install something new on their machines to be able to use your app.

Mark your webservice as a ScriptService

If you're going to use any kind of data in your Silverlight apps, and you probably are, then you have to use a webservice. Calling a webservice from Silverlight is a bit different from calling a webservice from another ASP.NET page.

First of all, you have to mark your webservice as Scriptable. The Scriptable attribute excists in the System.Web.Script.Services namespace, and is part of the ASP.NET AJAX framework (also included in the .NET Framework 3.5). If you are developing on ASP.NET 2.0, you have to make sure you have ASP.NET AJAX installed on the server, and reference the correct assemblies in your ASP.NET 2.0 application.

This is how your class definition will look like:

[WebService(Namespace = http://tempuri.org/)]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.Web.Script.Services.ScriptService]
public class SilverchartDataService : System.Web.Services.WebService{
} 


Now your webservice can be called from Silverlight – and also JavaScript / AJAX.

Don't call a webservice on another domain

The security model of Silverlight, and also other client-side technologies, deny calling a webservice on another domain. That is also called cross-domain webservice calls, and if you do, you will get this exception when #debugging your Silverlight app using Visual Studio 2008:

image

I've seen ways to get around this problem, but it is beyond the purpose of this blog post. If you need info on this topic, try this: Using a proxy to access remote APIs (The article is for AJAX – should work for Silverlight)

JSON Serialization compatibillity with .NET

Whenever you use a webservice, all the objects transfered gets serialized. (Remember to mark your own classes as Serializable if you want to use them in your webservice.) And Silverlight uses the JSON Serialization 'engine'. This can result in some nasty errors, yet difficult to debug. I've found out, that some .NET classes cannot be used – these include:

  • decimal
  • enum (So don't use enum's with Silverlight – at least not until it is supported)

And there are probably more than those two…

Technorati Tags: , , ,

Fiddler2 and the ASP.NET Development server (Cassini)

If you’re debugging ASP.NET apps, it can sometimes be an advantage to be able to see the actual requests, and analyze file sizes, run-time rendered code and being able to ‘fiddle’ with the code on run-time. Well. Fiddler2 provides extremely useful features for doing so, but for people using the ASP.NET development server (Cassini) you have to do a few workarounds…

I’m running IE7 on Windows Vista (a great OS). IE7 automatically bypasses proxies for localhost, which is our main problem. When using the ASP.NET development server (Cassini), the URL looks like this one: http://localhost:49950/app/Default.aspx. When you open Fiddler2, you see that it hasn’t monitored the traffic to the localhost address. This can be fixed by applying a period (.) after localhost. So change the URL to this:  http://localhost.:49950/app/Default.aspx

In my case, that wasn’t enough. I got an error from Fiddler2 in my browser. If I disabled Fiddler2, it worked just fine. Then I added a rule to Fiddler2. To do this, in Fiddler2 go to Rules > Customize rules. (or hit CTRL + R). Find the OnBeforeRequest event-handler, and add the following code:

if (oSession.host.substr(0, 10)==”localhost.”)
{
         oSession.host=oSession.host.replace(“localhost.”, “127.0.0.1”);
}

Now – when you want to use Fiddler, just add a dot right after localhost in the URL, and Fiddler will start working!

That worked for me – hope it works for you too, if you’re having problems.

Set the DataTable.Locale property – or get weird sorting

If you have a monster of an ASP.NET app, and your user come from different places in the world, they are most likely to have different locales as well. I’m danish, and we have three special characters in our alphabet – æ, ø, å. If I have a DataTable with – let’s say firstname and lastname columns. I create my DataTable like this:

      DataTable dt = new DataTable();
      dt.Columns.Add(new DataColumn("Firstname", typeof(string)));
      dt.Columns.Add(new DataColumn("Lastname", typeof(string)));
      dt.Rows.Add(new object[] { "Øjvind", "Jensen" });
      dt.Rows.Add(new object[] { "John", "Nielsen" });
      dt.Rows.Add(new object[] { "Åse", "Østergaard" });

This is how I DataBind my GridView:

      if (!Page.IsPostBack)
      {
        GridView1.DataSource = dt.DefaultView;
        GridView1.DataBind();
      }

With my regional settings on my machine (which in this case is also the webserver) we get the correct sorting:

image
The GridView sorted by Firstname ascending.

But if I go ahead and change the settings of my machine, to English (United States), my GridView will look like this, when I sort it by Firstname ascending:

image

This is because the server compares the strings wrong. So if you’re dealing with users from around the globe – you have to set the Locale property of the DataTable – this is done like this:

dt.Locale = System.Globalization.CultureInfo.CurrentCulture;

Now this I cannot test on a single machine. This is because the above code takes the CultureInfo of your machine, and since I just changed my regional settings, I get the GridView sorted wrong. So I fired up my old laptop, this time with FireFox the GridView looks like this:

image

From a user perspective, a GridView sorted incorrectly is very bad. So to add this single line of code is really not a big deal.

Technorati Tags: , , ,