Follow me on Twitter

First time here?

Check out the Archive, and Subscribe to the RSS feed.

ASP.NET Search Engine Optimization

This post was written on March 11, 2009 17:46 by martinhn

I made a few E-commerce websites a few years back when I was self-employed. Ranking well in the search engines is very important for E-commerce websites in order to get relevant visitors, and if your site is designed well for converting, visitors into sales, you will make money. That’s the way it goes.

A lot of naive CEO’s and alike, think that you absolutely have to hire expensive SEO consultants in order to rank well. That is *not* true. Instead, why don’t you just spent your time and money on making original, relevant and great content? That’s a true winner.

Though, there’s still a few technical details you have to get right.

Make sure to set a unique page title on all your pages. Use the same text as a <h1> tag on the page.

Don’t screw up your URLs. You will get punished having the same content on those URLs: www.example.com/producs/computers?sortorder=price&page=2 and www.example.com/producs/computers?page=2&sortorder=price. This is called duplicate content, and search engines doesn’t like that. You can use the new canonical tag for telling search engines which one is original.

Make internal links absolute. Yes. You shouldn’t do this: <a href=”/page1.htm”>Page 1</a>. Do this instead: <a href=”http://www.example.com/page1.htm”>Page 1</a>.

301 permanent redirect example.com to www.example.com or vice versa. Allowing visitors to access your page on both URLs, will also be treated as duplicate content.

If you’re moving your website to a new platform, server, technology and your URLs will change. Make sure not to return 404 on the old URLs. You have to 301 permanent redirect the old URLs to the new ones. Otherwise you will lose all your current search engine carma.

I use those two methods to set page information, and 301 redirect:

  private void SetMetaInformation()
  {
    this.Title = PageTitle;

    HtmlMeta metaKeywords = new HtmlMeta();
    metaKeywords.Name = "keywords";
    metaKeywords.Content = this.MetaKeywords;
    this.Header.Controls.Add(metaKeywords);

    HtmlMeta metaDescription = new HtmlMeta();
    metaDescription.Name = "description";
    metaDescription.Content = this.MetaDescription;
    this.Header.Controls.Add(metaDescription);

    HtmlMeta metaRobots = new HtmlMeta();
    metaRobots.Name = "robots";
    metaRobots.Content = this.MetaRobots;
    this.Header.Controls.Add(metaRobots);
  }

  public void PermanentRedirect(string newUrl)
  {
    Response.Status = "301 Moved Permanently";
    Response.StatusCode = 301;
    Response.AddHeader("Location", newUrl);
  }
 
 

You also want to take a look at the Google SiteMap.

Create a generic handler in ASP.NET and call it sitemap.ashx. Generate and XML string like this:

 
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
   <url>
      <loc>http://www.example.com/</loc>
      <lastmod>2005-01-01</lastmod>
      <changefreq>monthly</changefreq>
      <priority>0.8</priority>
   </url>
</urlset> 

 

Take a further look at the importance of sitemaps, if you want to know why you should use one.

Tags: , ,
Categories: ASP.NET
Actions: E-mail | Permalink | Comments (0) | Comment RSSRSS comment feed

ASP.NET MVC and Classic ASP

This post was written on February 2, 2009 00:09 by martinhn

As I pointed out in my blog post a few days ago, I see several reasons why you should make the switch to ASP.NET MVC. On the other hand, you have to remember that ASP.NET WebForms has been around for many years, and is a very mature technology. We all know that new technology has its initial flaws.

I was fooling a little around with the technology, specifically some databinding where I had a DataTable and wanted to display a Table with the contents. My instant thought was: “This is like databinding back in the days of Classic ASP”…

Take a look at this:

<html>
<head>
<title>Get data from database</title>
</head>
<body>
<%
' Data connection
Set Conn = Server.CreateObject("ADODB.Connection")
DSN = "DRIVER={Microsoft Access Driver (*.mdb)}; "
DSN = DSN & "DBQ=" & Server.MapPath("/cgi-bin/database.mdb")
Conn.Open DSN

' SQL statement
strSQL = "Select * from Employees"
Set rs = Conn.Execute(strSQL)

Do

   Response.Write rs("Test") & "<br>"

   rs.MoveNext

Loop While Not rs.EOF

Conn.Close
Set Conn = Nothing
%>
</body>
</html>

Good old Classic ASP that is. You’ve got to admit that you had seamless control of the HTML that was being rendered!

Which is also the case with ASP.NET MVC. MVC offers you control and simplicity, as opposed to WebForms where you place a server control on the form, and sometimes you ended up with your HTML a complete mess…

Of course, MVC is a lot more maintainable and structured, than Classic ASP was. You instantly get a separation of concerns with the Model, View and Controller design pattern.

So we can fetch data in our Controller:

    public ActionResult Index()
    {
      DataTable dt = new DataTable();
      dt.Columns.Add("Column1");

      dt.Rows.Add("Test 1");
      dt.Rows.Add("Test 1");
      dt.Rows.Add("Test 1");
      dt.Rows.Add("Test 1");
      dt.Rows.Add("Test 1");
      dt.Rows.Add("Test 1");

      ViewData.Model = dt;
      
      return View();
    }


And display it, somewhat like we did back in the days of Classic ASP:

<% foreach (System.Data.DataRow item in ((System.Data.DataTable)ViewData.Model).Rows) 
   { %>
    <%= Html.Encode(item[0].ToString()) %><br />
<% } %>

 

To be honest, I actually likes going back to basics and getting full control of my HTML. Working in WebForms, you sometimes search for control settings to enable or disable the rendering of a certain piece of HTML. I think it is a lot easier to just give you full control.

But on the other hand, I could easily see a lot of the Classic ASP ‘bad habits’ return. Classic ASP can be *ugly* as hell, which means the maintainability is very poor. The same could happen with MVC if you aren’t disciplined enough. WebForms sort of put you in a box, but you could still make it look pretty ugly as well…

ASP.NET MVC Release Candidate links

ScottGu’s release candidate blog post 

Stephen Walther’s guide to learning ASP.NET MVC

Is the use of AJAX on the web voting for a change to ASP.NET MVC?

This post was written on January 30, 2009 00:13 by martinhn

With ASP.NET MVC in Release Candidate now, the technology is getting a lot of attention these days. I personally haven’t given it any greater thought until just recently, but I have to admit that I’m tempted towards a change. I like ASP.NET WebForms. I think the WebForm approach provides us with great manageability and gives us a very rich set of controls.

But one thing that I don’t like about WebForms is, that it abstracts the anatomy of the web and how the web works. This was probably done to lure WinForms developers towards web development and making the change as easy as possible. But I think developers learning ASP.NET WebForms is missing out on the core web technology that is request, responses and headers etc. If you don’t know the core elements of what you’re working with, you can easily make some terrible mistakes.

As user experience is getting much more focus today, especially in the business of SaaS products, you have to deliver a great deal of responsiveness through the use of AJAX. In ASP.NET WebForms I see myself ending up rendering the page once, and doing AJAX callback through the core XmlHttpRequest in JavaScript when the user makes a change to some data. That’s to avoid postbacks. By doing this, I’m missing out on ViewState as well and what is left of ASP.NET WebForms now?

I think the high demand of quality (Testability) and user experience (Responsiveness = AJAX) is making ASP.NET WebForms obsolete.

I see several good reasons to dump WebForms for MVC:

  • - AJAX is used all over, so what good is postback, ViewState and the Page life cycle?
  • - WebForms is making your HTML look very ugly, especially because of the ViewState hidden field.
  • - ViewState can get very large, and slow down your pages.
  • - Performance is better on MVC –> You can handle more requests per second.
  • - The ability to test your code is better on MVC.

Initially, it looks like MVC is missing out on the rich set of controls that ASP.NET WebForms have. But I think this need will be driven by the awesome community that .NET have.

Tags: ,
Categories: ASP.NET
Actions: E-mail | Permalink | Comments (1) | Comment RSSRSS comment feed

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

This post was written on June 19, 2008 23: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

ASP.NET Server Control: FeedList - List most recent items from an RSS feed as links

This post was written on April 21, 2008 21:30 by MartinHN

RSS feeds is everywhere, and rightly so. Just about any modern browser, e-mail client, search engine or mobile phone is compliant with the RSS technology, and it gives web developers an easy and very convenient way to share and consume content across the web. From time to time I need to show a list of recent items form an RSS feed. This is a very easy thing to do, and I always end up copying the same usercontrol around, which becomes nothing but a mess! Eventually I need to change a few things, which means I get different versions of the same control spread throughout my projects.

This mess has got to stop, and from now on I'll put all my common controls in a class library project, which I can reference from all the other projects.

Back to my RSS feed reader - this is the first control I've made into a server control, from one of my many usercontrols around the projects folder. The control downloads the XML at the Url of the RSS feed, and renders a list of links. The controls contains a few properties:

  • FeedUrl: The Url of the RSS feed to download
  • NumberOfItems: The number of items which should be rendered. Defaults to 5 if none is specified.
  • CssClass (Part of the WebControl class, from which FeedList derives): Use this so you have style the control from CSS.

The code

It really is straight forward. Below is the method that renders the links.

private void RenderFeedItems(HtmlTextWriter writer)
{
  if (!String.IsNullOrEmpty(FeedUrl))
  {
    XmlDocument doc = new XmlDocument();
    doc.Load(FeedUrl);

    XmlNodeList items = doc.DocumentElement.SelectNodes("channel/item");

    if (items != null)
    {
      int bounce = NumberOfItems;
      if (items.Count < NumberOfItems)
        bounce = items.Count;

      for (int i = 0; i < bounce; i++)
      {
        XmlNode titleNode = items[i].SelectSingleNode("title");
        XmlNode urlNode = items[i].SelectSingleNode("link");
        XmlNode pubDateNode = items[i].SelectSingleNode("pubDate");

        string title = String.Empty;
        string url = String.Empty;
        DateTime pubDate = new DateTime();

        if (titleNode != null)
          title = titleNode.InnerText;

        if (urlNode != null)
          url = urlNode.InnerText;

        if (pubDateNode != null)
          DateTime.TryParse(pubDateNode.InnerText, out pubDate);

        writer.RenderBeginTag(HtmlTextWriterTag.Li);

        writer.AddAttribute(HtmlTextWriterAttribute.Href, url);
        writer.AddAttribute(HtmlTextWriterAttribute.Title, title);
        writer.RenderBeginTag(HtmlTextWriterTag.A);
        writer.Write(title);
        writer.RenderEndTag();

        writer.RenderEndTag();
      }
    }
    else
    {
      RenderErrorMessage(writer, String.Format("No RSS-feed items found at: {0}", FeedUrl));
    }
  }
  else
  {
    throw new Exception("FeedUrl cannot be empty!");
  }
}

To use the control, put it in your website's App_Code folder, and add the following to the <controls> collection of your <pages> section in web.config:

<add tagPrefix="dnknormark" namespace="dnknormark" />

This should make your <pages> section look like this one:

<pages>
  <controls>
    <add tagPrefix="dnknormark" namespace="dnknormark" />
  </controls>
</pages>
 
And to put it on an aspx page, and show the latest 5 items from this blog, add this line to the page:
 
<dnknormark:FeedList ID="lstFeeds" runat="server" CssClass="feedList" 
NumberOfItems="5" FeedUrl="http://www.dnknormark.net/syndication.axd" />

Use it as you'd like. Feel free to change it, the way you need.


kick it on DotNetKicks.com

Event performance optimization with the EventHandlerList

This post was written on December 25, 2007 22:35 by MartinHN

When using custom events (event delegates) in your ASP.NET pages, user controls and server controls, many developers might define their Events as public fields, like this:

public event EventHandler<EventArgs> MyCustomEvent;


And use this code to fire the event:

protected void OnMyCustomEvent(EventArgs e)
{
  if(MyCustomEvent != null)
  {
    MyCustomEvent(this, e);
  }
}

 

Nothing is wrong with the approach of the latter, but defining the MyCustomEvent as a public field introduces two problems:

    1. Even though no clients have registered any delegates to the invocation list of your event, the compiler generates
      one private delegate field for each event delegate in your class. This is a waste of server memory, especially if
      you have several events inside your class.

    2. One Add and one Remove method is generated for each event field of your class. When clients use += and -= to add
      and remove delegates from the invocation lists of your class' events, these methods are called behind the scenes.
      These two compiler-generated methods are thread-safe, which means they include extra code to synchronize threads
      that are accessing these methods. This means, that everytime a client adds or removes a delegate to or from the
      invocation list of an event, they have to get a lock before they can do the actual work. This introduces an
      unnecessary overhead because most page developers don’t use multiple threads and therefore there is no need for
      thread synchronization.

     

    The EventHandlerList is the answer to our issues. This class comes with the .NET Framework. The EventHandlerList is a linked list of delegates,which is optimized for adding and removing delegates. To use the EventHandlerList in your classes, you need to add a private static key for each event your class exposes. The code bellow defines a key for MyCustomEvent:

    private static readonly object MyCustomEventKey = new object();

    The memory is allocated only once, because the key is static.

    After this, you need to define your events as properties - not fields. These event properties has a different syntax that normal get-set properties. Event properties uses add-remove instead of get-set. The following property, is an event property for MyCustomEvent:

    public event EventHandler<EventArgs> MyCustomEvent
    {
      add { Events.AddHandler(MyCustomEventKey, value); }
      remove { Events.RemoveHandler(MyCustomEventKey, value); }
    }
     

    Every Page, UserControl and WebControl - among others - has a protected property of type EventHandlerList named Events. With this approach, when clients use += to add a delegate to the invocation list of the MyCustomEvent, the add method of the event property calls the AddHandler method of the EventHanderList class, as the code above will tell you. As for -= the RemoveHandler method is called.

    The EventHandlerList class maintains a linked list which can have none or one entry for each event. The AddHandler method checks whether this internal linked list contains an entry for an event with the given event key. If it does, the method calls the Combine method of the Delegate class to add the client delegate to the invocation list of the event. If this internal list doesn’t contain an entry for an event with the given event key, the AddHandler method just adds a new entry.

    With this new approach, we need to update our OnMyCustomEvent Method, which is responsible for firing the event:

    protected void OnMyCustomEvent(EventArgs e)
    {
      EventHandler<EventArgs> handler = Events[MyCustomEventKey] as EventHandler<EventArgs>;
    
      if (handler != null)
        handler(this, e);
    }
     

    This method uses the MyCustomEventKey as an index in the Events list to access the MyCustomEvent-event. The Events list will return null if it does not contain an entry at the specified index. This will happen when no clients has subscribed to the MyCustomEvent.

    Using the EventHandlerList class automatically resolves the two previously mentioned performance problems with the event fields.

    Technorati Tags: ,

     

    kick it on DotNetKicks.com

    Configuring IIS 7 default document from web.config

    This post was written on December 20, 2007 20:14 by MartinHN

    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
    Tags: ,
    Categories: ASP.NET | IIS7
    Actions: E-mail | Permalink | Comments (3) | Comment RSSRSS comment feed

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

    This post was written on December 18, 2007 20: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
    Tags: ,
    Categories: .NET | ASP.NET | C#
    Actions: E-mail | Permalink | Comments (1) | Comment RSSRSS comment feed

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

    This post was written on October 26, 2007 17:57 by MartinHN

    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
    Tags: , , ,
    Categories: ASP.NET | IIS7 | .NET
    Actions: E-mail | Permalink | Comments (4) | Comment RSSRSS comment feed

    Web services and Silverlight 1.1 C# gotchas

    This post was written on September 4, 2007 17: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: , , ,

    Powered by BlogEngine.NET 1.6.0.0

    Disclaimer

    The opinions expressed herein are my own personal opinions and do not represent my employer's view in  anyway.

    © Copyright 2010