Windows Live Development Part 1 - Authenticating users via Windows Live ID

June 20, 2008 00:01 by MartinHN

With so many online services out there, you nearly claim a new online identity somewhere every day. You tend to use the same username and password couple for all of them, but sometimes your preferred username is taken, or your password does not meet the sites' password policy. I reckon I have well over 100 online identities out there - I rely on the same 3-4 combinations of username and password, and it's really messy. In this blog post I'm going to cover how to authenticate users of an ASP.NET application against Windows Live, using the SDK for Windows Live ID. I'm going use the built-in ASP.NET Membership mechanism and go from there.

I could have gone the OpenID way, but Scott Hanselman already did an example on OpenID, so I thought I'd try out Windows Live. Jeff Atwood asked the question, if we really need another username and password in the pursuit of the best authentication model for stackoverflow.com.

Conceptual idea of using Windows Live ID

tmpF3A8Windows Live ID, is the technology you should be quite familiar with when authenticating on Microsoft web sites, and Messenger. If you want to know more about what Windows Live ID is, take a look at this video from Channel9.

When using Windows Live ID, you redirect the user to the Windows Live login-page, with a return URL specified. When the user authenticates, Windows Live will redirect the user back to your site, and delivering a user id and a token. Here's an overview of how it looks:

image

Getting your application ID to use Windows Live ID

To get started using Windows Live ID, first of all you need an application ID. Basically you need to sign up for one, and register your application. Go to https://msm.live.com/app/default.aspx and click 'Register an Application'. (You can find Microsoft's guide to this here: http://msdn.microsoft.com/en-us/library/cc287659.aspx)

tmpF3F5

Leave Domain Name blank. Save your self-chosen Secret Key for later use, and submit the form. When you see the confirmation page, your application ID is shown. Copy that, as we're going to use it later.

In the return URL field, type the URL of your application followed by a name of a page to handle the Windows Live ID communication, like http://localhost/demoapp/webauth-handler.aspx.

Download and install Windows Live ID Web Authentication SDK

Microsoft has made it quite easy for ASP.NET developers to get started, by providing an SDK. Download it here: http://www.microsoft.com/downloads/details.aspx?FamilyId=24195B4E-6335-4844-A71D-7D395D20E67B&displaylang=en and install the SDK. By default it is installed here: C:\Program Files\Windows Live ID\WebAuth

From the SDK we need a class called WindowsLiveLogin, located in the App_Code folder of the sample. Keep this file in mind for a few seconds.

Start coding - create a new website

Open Visual Studio 2005/2008 and create a new ASP.NET website the way you want it. First we will add the WindowsLiveLogin.cs file to our App_Code folder. (C:\Program Files\Windows Live ID\WebAuth\Sample\App_Code\WindowsLiveLogin.cs).

Leave the default.aspx page, and put the following HTML in the markup:

<html>
<head>
  <title>Windows Live&trade; ID</title>
</head>
<body>
  <iframe id="WebAuthControl" name="WebAuthControl" src="http://login.live.com/controls/WebAuth.htm?appid=<%=AppId%>&style=font-size%3A+10pt%3B+font-family%3A+verdana%3B+background%3A+white%3B"
    width="80px" height="20px" marginwidth="0" marginheight="0" align="middle" frameborder="0"
    scrolling="no"></iframe>
  <%
   1:  if (UserId == null)
   2:      { 
%> <p> This application does not know who you are! Click the <b>Sign in</b> link above.</p> <%
   1:  }
   2:      else
   3:      { 
%> <p> Now this application knows that you are the user with ID = "<b><%
   1: =UserId
%></b>".</p> <%
   1:  } 
%> </body> </html>

And this piece of code in its code-behind class.

using System;
using System.Web;
using System.IO;
using WindowsLive;

public partial class DefaultPage : System.Web.UI.Page
{
  const string LoginCookie = "webauthtoken";

  // Initialize the WindowsLiveLogin class.
  private static WindowsLiveLogin wll = new WindowsLiveLogin(true);
  
  protected static string AppId = wll.AppId;
  protected string UserId;

  protected void Page_Load(object sender, EventArgs e)
  {
    HttpRequest req = HttpContext.Current.Request;
    HttpApplicationState app = HttpContext.Current.Application;

    HttpCookie loginCookie = req.Cookies[LoginCookie];

    if (loginCookie != null)
    {
      string token = loginCookie.Value;

      WindowsLiveLogin.User user = wll.ProcessToken(token);

      if (user != null)
      {
        UserId = user.Id;
      }
    }
  }
}

Default.aspx will work as a login page. Users click the Sign-in button, and is redirected to Windows Live. When they authenticate, they're redirected back to our handler page, that we will now create...

Create a new aspx page, and name it webauth-handler.aspx. Recall that this was the page we provided as the return URL when we registered the application at Windows Live.

This page will serve as the handler page for the user, when Windows Live redirects back to your site. Windows Live will post authentication specific values for you to use. Because this page is never seen by a user (we will send the user back where they began), we can simply remove all the markup.

Put the following code in your code-behind class for webauth-handler.aspx.

using System;
using System.Web;
using System.IO;
using WindowsLive;

public partial class HandlerPage : System.Web.UI.Page
{
  // Relative path to the login- and logoutpage.
  const string LoginPage = "default.aspx";
  const string LogoutPage = "default.aspx";
  const string LoginCookie = "webauthtoken";
  static DateTime ExpireCookie = DateTime.Now.AddYears(-10);
  static DateTime PersistCookie = DateTime.Now.AddYears(10);

  // Initialize the WindowsLiveLogin class.
  static WindowsLiveLogin wll = new WindowsLiveLogin(true);

  protected void Page_Load(object sender, EventArgs e)
  {
    HttpRequest request = HttpContext.Current.Request;
    HttpResponse response = HttpContext.Current.Response;
    HttpApplicationState application = HttpContext.Current.Application;

    string action = request["action"];

    if (action == "logout")
    {
      HttpCookie loginCookie = new HttpCookie(LoginCookie);
      loginCookie.Expires = ExpireCookie;
      response.Cookies.Add(loginCookie);
      response.Redirect(LogoutPage);
      response.End();
    }
    else if (action == "clearcookie")
    {
      HttpCookie loginCookie = new HttpCookie(LoginCookie);
      loginCookie.Expires = ExpireCookie;
      response.Cookies.Add(loginCookie);

      string type;
      byte[] content;
      wll.GetClearCookieResponse(out type, out content);
      response.ContentType = type;
      response.OutputStream.Write(content, 0, content.Length);

      response.End();
    }
    else if (action == "login")
    {
      HttpCookie loginCookie = new HttpCookie(LoginCookie);

      WindowsLiveLogin.User user = wll.ProcessLogin(request.Form);

      if (user != null)
      {
        loginCookie.Value = user.Token;

        if (user.UsePersistentCookie)
        {
          loginCookie.Expires = PersistCookie;
        }
      }
      else
      {
        loginCookie.Expires = ExpireCookie;
      }

      response.Cookies.Add(loginCookie);
      response.Redirect(LoginPage);
      response.End();
    }
    else
    {
      response.Redirect(LoginPage);
      response.End();
    }
  }
}

The first login test

Now we've got everything setup as we should, and it should be possible to login using Windows Live. View the default.aspx page in your favorite browser, and let's try.

First you will see out login page:
tmp9D6

Click the Sign in link, which will take you to Windows Live:
wlive_signin

Sign in with your Windows Live ID, and you will return to our login page - which will now welcome you and show your user ID:
wlive_signin 

Now your user has authenticated against Windows Live, using the Windows Live ID. Now it's up to you to handle the user's Windows Live ID, which is what I'm going to blog about in part 2 of the Windows Live Development series here...


kick it on DotNetKicks.com

Currently rated 4.3 by 3 people

  • Currently 4.333333/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Add to: Facebook Add to: Digg Add to: Del.icio.us Add to: Reddit Add to: Furl Add to: Yahoo Add to: Spurl Add to: Google Add to: Technorati

Designing for Internationalization

June 10, 2008 21:17 by MartinHN

Web sites and Web Applications today, are very often exposed beyond the borders of your home country, and therefore users speak different languages, and has a different currency, date- and time formats etc. ASP.NET provides you with an entire namespace for handling things like this. That is the System.Globalization namespace, where you will find a lot of classes for handling your every day globalization tasks. I'm not going to cover anything in this namespace now, if you want to get your hands dirty take a look at this video: http://asp.net/learn/videos/video-40.aspx where you will see how to use local and global resources for your application.

Using a global resource file for a place to store display text on buttons, labels, validation controls etc. is fine. But if you have an e-commerce site, selling products in multiple regions with different languages, you need an extra level. That level is a way to globalize e.g. the name and description of your product. When a user changes language, the name and description of your "display product details page" should change accordingly.

For me, the ideal solution should not result in extra database columns like name_us, name_da, name_es. This would be a very static solution, as you would have to change your database whenever a new language is added to your application. Nor should it require extra tables, so you need to join like hell, when you need to select a product.

I've decided to store e.g. the Name values as XML in the database, and parse that XML into a Dictionary<string, string> property on my Product object, with the key of the Dictionary being the language code (en-US for US English). To me this seems to work just fine. My database design is not getting more complex, and I can get and set values quite easily.

The XML string that goes into my ProductName column in the database table, looks like this:

<cultures><culture code="en-us">Logitech SmartCam 124</culture></cultures>

On my Product object, the Name property is a Dictionary:

private Dictionary<string, string> _Name;

public Dictionary<string, string> Name
{
  get { return _Name; }
  set
  {
    if (_Name != value) MarkDirty("Name");
    _Name = value;
  }
}

When I need to get the US English value of the Product.Name property, I call:

p.Name["en-us"]

To get a Dictionary from my XML, I use this helper method:

public static Dictionary<string, string> GetDictionary(string xml)
{
  if (String.IsNullOrEmpty(xml))
    return new Dictionary<string, string>(owner);
    
  XmlDocument doc = new XmlDocument();
  doc.LoadXml(xml);
    
  Dictionary<string, string> dic = new Dictionary<string, string>();

  foreach (XmlNode node in doc.DocumentElement)
  {
    dic.Add(node.Attributes["code"].Value, node.InnerText);
  }

  return dic;
}

And when I want to update my database, after I change the Name of the Product, I convert to Dictionary to an XML string using this helper method:

public static string GetXmlDocument(Dictionary<string, string> dic)
{
  XmlDocument doc = new XmlDocument();
  XmlNode docElement = doc.CreateNode(XmlNodeType.Element, "cultures", "");

  foreach (string key in dic.Keys)
  {
    XmlNode node = doc.CreateNode(XmlNodeType.Element, "culture", "");
    XmlAttribute att = doc.CreateAttribute("code");
    att.Value = key;
    node.Attributes.Append(att);

    node.InnerText = dic[key];

    docElement.AppendChild(node);
  }

  doc.AppendChild(docElement);

  return doc.OuterXml;
}

If you need more information on this huge topic, take a look at the ASP.NET Wiki: http://wiki.asp.net/page.aspx/55/internationalization/


Currently rated 5.0 by 1 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Add to: Facebook Add to: Digg Add to: Del.icio.us Add to: Reddit Add to: Furl Add to: Yahoo Add to: Spurl Add to: Google Add to: Technorati

Adding a Website to IIS7 programmatically

April 16, 2008 20:32 by MartinHN

Some time ago, I blogged about Adding an Application Pool to IIS7 programmatically. The result was a new Application Pool, that uses the Integrated Pipeline in IIS7.

In this post I will show you how to add a new Website, that uses the Application Pool from the other blog post.

I've loaded the Console Application I used to add the Application Pool, and moved the logic from Main to a method called AddApplicationPool, to split it up nicely. It is actually even easier to add a website programmatically. Below is the code for doing just that:

private static void AddWebSite()
{
  ServerManager mgr = new ServerManager();

  if (!Directory.Exists(@"c:\inetpub\wwwroot\iis7test"))
  {
    Directory.CreateDirectory(@"c:\inetpub\wwwroot\iis7test");
  }

  // Add a new Site to the server, configured to use our the iis7test home directory.
  Site site = mgr.Sites.Add("MyWebSite", @"c:\inetpub\wwwroot\iis7test", 80);
  
  // Set the application pool name of the site, to use the MyAppPool application pool.
  site.ApplicationDefaults.ApplicationPoolName = "MyAppPool";

  // Clear all bindings.
  site.Bindings.Clear();

  // Make the site listen to incoming HTTP requests using host header iis7test, on port 80.
  site.Bindings.Add("*:80:iis7test", "http");
  
  // Set auto start to true.
  site.ServerAutoStart = true;

  // Commit the changes
  mgr.CommitChanges();
}

Notice how we add Bindings to the website. Bindings is the information that tells IIS7 when to serve our website. We use this string to configure bindings: *:80:iis7test. The first * tells IIS to listen on all IP addresses on your system. 80 is the port number, and iis7test is the host header value for this site.

To browse our website, we need to add iis7test to the computers hosts file (located in %WINDIR%\System32\Drivers\etc), and point it to 127.0.0.1.


kick it on DotNetKicks.com

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Add to: Facebook Add to: Digg Add to: Del.icio.us Add to: Reddit Add to: Furl Add to: Yahoo Add to: Spurl Add to: Google Add to: Technorati

Adding an Application Pool to IIS7 programmatically

April 16, 2008 20:21 by MartinHN

Internet Information Services 7 (IIS7) has a great new set of features regarding configuration. Using the Integrated Configuration system, you can configure your server from XML files, the IIS7 manager, the command prompt using the APPCMD tool, but IIS7 also lets you manage your server from managed code in a very intuitive manner.

If you navigate to the %WINDIR%\System32\InetSrv folder in Windows Vista, you'll find all the executables, DLL's and XML (.config) configuration files you need. It doesn't matter if you use the IIS7 Manager, managed code or the APPCMD command-line based tool to manage your server - at the end of the day you are changing an XML file. That is applicationHost.config which is located here: C:\Windows\System32\inetsrv\config\applicationHost.config.

The integrated configuration system on IIS7 is great news for hosters, and web developers. Hosters can easily automate server management through managed code, and as a web developer, you can configure your server from your web.config file, which makes it easy to move your web application from development, to test, and further up towards production. Read my post on how to set a websites default document, from within the web application's web.config file.

In this post, I will create an application pool on my local IIS7.

Launch Visual Studio 2005 or 2008 - whatever you've got will work.

Create a new Console Application, and give it a name of your own choice.

Right click References in the Solution Explorer and add a new reference.

image

Locate Microsoft.Web.Administration.dll from the C:\Windows\System32\inetsrv folder.

To access IIS7, we use the ServerManager class, as shown below.

   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.Linq;
   4:  using System.Text;
   5:  using Microsoft.Web.Administration;
   6:   
   7:  namespace Iis7ManagementTest
   8:  {
   9:    class Program
  10:    {
  11:      static void Main(string[] args)
  12:      {
  13:        ServerManager mgr = new ServerManager();
  14:   
  15:        // Add a new application pool called MyAppPool
  16:        ApplicationPool myAppPool = mgr.ApplicationPools.Add("MyAppPool");
  17:        
  18:        // Configure my new app pool to start automatically.
  19:        myAppPool.AutoStart = true;
  20:   
  21:        // What action should IIS take when my app pool exceeds 
  22:        // the CPU limit specified by the Limit property
  23:        myAppPool.Cpu.Action = ProcessorAction.KillW3wp;
  24:   
  25:        // Use the Integrated Pipeline mode
  26:        myAppPool.ManagedPipelineMode = ManagedPipelineMode.Integrated;
  27:   
  28:        // Set the runtime version of ASP.NET to 2.0
  29:        myAppPool.ManagedRuntimeVersion = "V2.0";
  30:   
  31:        // Use the Network Service account
  32:        myAppPool.ProcessModel.IdentityType = ProcessModelIdentityType.NetworkService;
  33:        
  34:        // Shut down after being idle for 5 minutes.
  35:        myAppPool.ProcessModel.IdleTimeout = TimeSpan.FromMinutes(5);
  36:   
  37:        // Max. number of IIS worker processes (W3WP.EXE)
  38:        myAppPool.ProcessModel.MaxProcesses = 1;
  39:   
  40:        // Commit the changes
  41:        mgr.CommitChanges();
  42:      }
  43:    }
  44:  }
 

After the application has been executed, we will see our new application pool inside the IIS7 Manager:

image

That's all for now. In another blog post I will show how to add a new web site, that will use our new application pool - also from managed code.


kick it on DotNetKicks.com

Currently rated 5.0 by 2 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Add to: Facebook Add to: Digg Add to: Del.icio.us Add to: Reddit Add to: Furl Add to: Yahoo Add to: Spurl Add to: Google Add to: Technorati

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

December 18, 2007 21:05 by MartinHN

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

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Add to: Facebook Add to: Digg Add to: Del.icio.us Add to: Reddit Add to: Furl Add to: Yahoo Add to: Spurl Add to: Google Add to: Technorati
Tags: ,
Categories: .NET | ASP.NET | C#
Actions: E-mail | Permalink | Comments (1) | Comment RSSRSS comment feed

Web services and Silverlight 1.1 C# gotchas

September 4, 2007 18:51 by MartinHN

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: , , ,

Currently rated 5.0 by 1 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Add to: Facebook Add to: Digg Add to: Del.icio.us Add to: Reddit Add to: Furl Add to: Yahoo Add to: Spurl Add to: Google Add to: Technorati

Fiddler2 and the ASP.NET Development server (Cassini)

August 29, 2007 23:33 by MartinHN

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.


Currently rated 5.0 by 2 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Add to: Facebook Add to: Digg Add to: Del.icio.us Add to: Reddit Add to: Furl Add to: Yahoo Add to: Spurl Add to: Google Add to: Technorati
Tags:
Categories: ASP.NET | C# | Cassini | Fiddler2
Actions: E-mail | Permalink | Comments (0) | Comment RSSRSS comment feed

Set the DataTable.Locale property - or get weird sorting

August 29, 2007 21:02 by MartinHN

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: , , ,

Currently rated 5.0 by 1 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Add to: Facebook Add to: Digg Add to: Del.icio.us Add to: Reddit Add to: Furl Add to: Yahoo Add to: Spurl Add to: Google Add to: Technorati
Tags:
Categories: ASP.NET | C#
Actions: E-mail | Permalink | Comments (1) | Comment RSSRSS comment feed

Some very cool project on CodePlex

August 9, 2007 23:41 by MartinHN

As a .NET developer, you must be familiar with CodePlex. There are some great projects there - I like to surf around and see what other developers are doing, and try some of the products. I've found a lot of things from CodePlex useful, so here are my top 3...

The patterns & practices team at Microsoft has several project on CodePlex. A lot of software factories - but the main one is the Enterprise Library which is a huge help, when you want something done without having to re-invent a lot of stuff... Also listen to this .NET Rocks podcast with David Hayden, explaining the Enterprise Library in more detail.


Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Add to: Facebook Add to: Digg Add to: Del.icio.us Add to: Reddit Add to: Furl Add to: Yahoo