Saturday, December 6, 2008

8 simple SEO tips for blogs

Follow these simple rules for search engine optimization and your blog will rank much higher in Search Engines.

  1. Use your primary keyword in your blog domain

    The first thing to do is to ensure that your blogs URL contains the primary keyword you want to optimize for. Using the targeted keyword in subdomains also helps.

    For example, if you want to start a HTML tutorial site then the primary keyword you want your URL to contain is html. So choose a URL like www.htmlhelp.com.

    You can also use the keyword in subdomains like www.web-design.com/htmlhelp

  2. Use your primary keyphrase in the title of your posts

    If your primary key phrase is html help make sure that the word html and help appear in your blog headers such as H1 and H2 tags as well as the title of each of your posts.

  3. Use your secondary keywords in the body of your post

    If you want to get listed for secondary keywords use them infrequently in the body of your post. The theory is that the more times a keyword appears within a Webpage, the more relevant the page is likely to be for someone searching those keywords.

    But do not overdo this by repeating the same keywords over and over again. Google bots can find out if a keyword is too frequent on a page and might just remove your site from their index.

  4. Use your keywords in the anchor text of links

    Use your primary and secondary keywords in the anchor text of links when linking to other blog posts or to other pages of your blog. Keyword in links have more importance than simple text.

  5. Make sure search engines can spider your blog easily

    Make sure your navigation bar is present on all pages of your blog. Your previous posts or atleast the popular ones should be linked to all pages so they get spidered easily.

  6. Get backlinks from other blogs

    You need as many links as possible to link back to your posts or blog because it will help you build pagerank and get your blog to rank higher in search engines. The more links you have the higher your blog is ranked in Technorati helping your blog to be found easily.

    So how do you get backlinks?

    • The first thing to do to get high-quality links is to submit your blog and RSS feed to blog search engines and directories.

      Start by submitting your blog to all the directories listed on this page:
      http://www.masternewmedia.org/rss/top55/

    • Link exchanging with other similarly-themed blogs will help you to form richly interlinked networks or communities.
    • If you find an interesting article on another blog, link to it generously. The trackback will become a link back to your blog.
    • Lastly posting legitimate comments in response to posts on other blogs will help you get backlinks. Regularly post legitimate comments in similarly-themed blogs with high traffic to get many backlinks.
  7. Update your blog frequently

    Update your blog frequently using all the rules mentioned above and your blog will surely get top rankings in a short time.

  8. Stick with your blog

    Once you start posting on your blog, stick with the same domain or you could end up losing a lot of your traffic and regular readers.

    Also stick with the topic you selected for your blog. If it’s about pets don’t suddenly switch to another topic such as Gadget’s because you will loose traffic.

For tips on building traffic to your blog, read the series- How to increase your blogs traffic

Instantiating components on template markup

All client-side template engines enable you to create HTML (feel free to go ‘duh’). What they don’t all allow is the creation of event handlers and components over the markup they generate. The general approach with those engines is to do a second pass of code over the markup to create handlers and components. This is quite unfortunate as this generally requires some knowledge of the markup (which plays against separation of concerns) or something like the introduction of marker CSS classes into the markup.

For our own template engine, we wanted event handler creation and component instantiation to be first class scenarios, and we wanted both to be possible from imperative code as well as declarative code.

Imperative code blocks

Let’s start with the imperative approach. The template engine enables the introduction of code blocks right inside the template and also exposes a $element that is a reference to the last created element from the point in the template where it is referenced. So a first approach might be to just hook events and create components from there. Sure enough, that works:

<img sys:id="{{ $id('photo') }}"      sys:src="{{ 'Images/' + Photo }}"      alt="{binding FirstName}" />  <br /> <input type="text" id="{{ $id('firstName') }}"        class="editInPlace name"        value="{binding FirstName}" />  

Please note that the comment-based code block syntax is on its way out and will be replaced in the next preview with an attribute-based alternative. I will give more details when that is available. Suffice it to say for now that this mixing of component instantiation code into markup is not what we intended code blocks for. Code blocks are there to make simple scenarios like conditional rendering and looping over markup as easy as possible.

Unobtrusive and imperative

The better imperative approach looks very much like how you would do things outside of a template if you’re into unobtrusive JavaScript. The way you add event handlers and components over the page’s markup is by subscribing to some page ready event (pageLoad or Sys.Application.add_init in Microsoft Ajax, $(document).ready in jQuery). From that handler, you query the DOM for specific elements and add event handlers and components on those.

To do the same over a template, you handle the itemCreated event of DataView, query the DOM that was just created for the data item and then add event handlers and components.

One problem with repeating markup is to create unique IDs for the generated elements. This is relevant to the problem at hand because referencing elements by ID is by far the most common way. Wouldn’t it be nice to be able to just use getElementByID? Well, in client templates, we provide you with an easy way to both generate unique IDs and to reference elements by ID.

Unique IDs can be generated by the $id function that is part of the execution environment of templates (along with $dataItem, $element, etc.). $id takes a string parameter, which is an ID that is unique within the template, and combines it with the current data item’s index to generate an ID that can be unique within the page:

<img sys:id="{{ $id('photo') }}" sys:src="{{ 'Images/' + Photo }}"      alt="{binding FirstName}" />

To reference those elements -even if you don’t know the pattern $id uses to globalize the id-, you can use the getElementById method that is provided by the template context, which is conveniently available from the event arguments of itemCreated:

args.get_templateContext().getElementById("photo")

Here’s what the code to add an event handler and a behavior looks like:

function onItemCreated(sender, args) {     var context = args.get_templateContext(),         dataItem = args.get_dataItem();     $addHandler(context.getElementById("photo"), "click", function() {         alert("You clicked " + dataItem.FirstName + "'s photo.");     });     $create(Bleroy.Sample.EditInPlace,            { cssClass: "editing" }, {}, {},            context.getElementById("firstName")); }

 

Note: there is a known bug in Preview 3 that prevents getElementByID from working correctly outside of IE. We fixed that bug already for the next preview.

Fully declarative

Of course, if you prefer a fully declarative approach, we allow that too. The template engine understands DOM-0 event handlers in pretty much the same way that the browser does outside templates (we tried to apply the principle of least surprise here). This means that if you specify for example an onclick attribute on an element, it is understood as a string that is the source code for the body of a function that will act as a handler for the click event. The template engine also supports binding expressions on attributes and this is no exception. That means that you can actually build that string expression that will become the body of the handler dynamically using the current data item:

<img sys:id="{{ $id('photo') }}" sys:src="{{ 'Images/' + Photo }}"   alt="{binding FirstName}"   onclick="{{'alert(\'You clicked '+FirstName+'\\\'s photo.\');'}}"/>

Important note: you should be super-careful about building such handler strings on the fly with embedded data: there is potential for injection here, if the FirstName data came from the user or an uncontrolled source. In a real application, you'd want to encode FirstName to escape any quotes. You may useSys.Serialization.JavaScriptSerializer.serialize(FirstName) for example. 

Then, to instantiate the components, you can use our declarative syntax (which will also be the subject of a future post):

<input type="text" id="{{ $id('firstName') }}" class="editInPlace name"        value="{binding FirstName}"        sys:attach="inplace" inplace:cssclass="editing"/>

Conclusion

There are plenty of options to add event handlers and components over template-generated markup in Microsoft Ajax, catering to different coding styles, but we hope we succeeded in keeping the system as simple as possible while keeping all relevant scenarios possible.

Download the full code fro the demo here: 
http://weblogs.asp.net/blogs/bleroy/Samples/EventsAndBehaviorsInTemplates.zip

ASP.NET MVC on IIS 6 Walkthrough

I’ve seen a lot of reports where people have trouble getting ASP.NET MVC up and running on IIS 6. Sometimes the problem is a very minor misconfiguration, sometimes it’s a misunderstanding of how IIS 6 works.

In this post, I want to provide a definitive guide to getting ASP.NET MVC running on IIS 6. I will walk through using the .mvc or .aspx file extension for URLs first, then I will walkthrough using extension-less URLs.

If you’re running into problems with IIS 6 and ASP.NET MVC, I recommend trying to walk through all the steps in this post, even if you’re not interested in using the .mvc or .aspx mapping. Some of the lessons learned here have more to do with how ASP.NET itself works with IIS 6 than anything specific to ASP.NET MVC.

Initial Setup

To make this easy, start Visual Studio and create a new ASP.NET MVC Web Application Project on the machine with IIS 6. If your IIS 6 machine is on a different machine, you can skip this step. We can deploy the site to the machine later.

After you create the project, right click the project and select Properties. The project properties editor will open up. Select the Web tab and selectUse IIS Web Server. Click on the image for a full size view.

Project Properties Editor

In the project URL, I gave it a virtual application name of Iis6DemoWeb and then checked Create Virtual Directory. A dialog box should appear and you should now have an IIS virtual application (note this is different than a virtual directory, as indicated by the gear looking icon) under your Default Web Site.

IIS 6 Virtual Web Application

Using a URL File Extensions

When you run the ASP.NET MVC installer, it will set up an ISAPI mapping in IIS 6 to map the .mvc extension to the aspnet_isapi.dll. This is necessary in order for IIS to hand off requests using the .mvc file extension to ASP.NET.

If you’re planning to use extension-less URLs, you can skip this section, but it may be useful to read anyways as it has some information you’ll need to know when setting up extension-less URLs.

Mapping .mvc to ASP.NET

If you plan to use the .mvc URL extension, and are going to deploy to IIS 6 on a machine that does not have ASP.NET MVC installed, you’ll need to create this mapping by performing the following steps.

One nice benefit of using the .aspx extension instead of .mvc is that you don’t have to worry about mapping the .aspx extension. It should already be mapped assuming you have ASP.NET installed properly on the machine.

For the rest of you, start by right clicking on the Virtual Application node (IIS6DemoWeb in this case) and select Properties. You should see the following dialog.

Website Properties

Make sure you’re on the Virtual Directory tab and select Configuration. Note that you can also choose to make this change on the root website, in which case the tab you’re looking for is Home Directory not Virtual Directory.

This will bring up the Application Configuration dialog which displays a list of ISAPI mappings. Scroll down to see if .mvc is in the list.

application mappings

In the screenshot, you can see that .mvc is in the list. If it is in the list on your machine, you can skip ahead to the next section. If it’s not in the list for you, let’s add it to the list. You’re going to need to know the path to the aspnet_isapi.dll first. On my machine, it is:

c:\windows\microsoft.net\framework\v2.0.50727\aspnet_isapi.dll

It might differ on your machine. One easy way to find out is to find the .aspx extension in the list and double click it to bring up the mapping dialog.

extension mapping

Now you can copy the path in the Executable text box to your clipboard. This is the path you’ll want to map .mvc to.

Click Cancel to go back to the Application Configuration dialog and then click Add which will bring up an empty Add/Edit Application Extension Mapping dialog.

Fill in the fields with the exact same values as you saw for .aspx, except the extension should be “.mvc” without the quotes. Click OK and you’re done with the mapping.

Specifying Routes with an Extension

Before we run the application, we need to update the default routes to look for the file extension we chose, whether it be .mvc or .aspx extension. Here is the RegisterRoutes method in my Global.asax.cs file using the .mvcextension. If you want to use the .aspx extension, just replace{controller}.mvc with {controller}.aspx.

public static void RegisterRoutes(RouteCollection routes) {   routes.IgnoreRoute("{resource}.axd/{*pathInfo}");    routes.MapRoute(     "Default",     "{controller}.mvc/{action}/{id}",     new { action = "Index", id = "" }   );    routes.MapRoute(     "Root",     "",     new { controller = "Home", action = "Index", id = "" }   ); }

Note that because the second route, “Default”, has a literal extension as part of the URL segment, it cannot match a request for the application root. That’s why I have a third route named “Root” which can match requests for the application root.

Now, I can hit CTRL+F5 (or browse my website) and I should see the following home page.

Home Page

And about page.

About Page

Notice that the URLs contain the .mvc extension.

Uh oh, Houston! We have a problem

Of course, you’re going to want to be able to navigate to the web root for your project. Notice what happens when you navigate to /Iis6DemoWeb.

Root Home Page

This is a bug in the Default.aspx.cs file included with our default template which I discovered as I was writing this walkthrough. We’ll fix it right away, but I can provide the fix here as it’s insanely easy.

Note: If you received a File Not Found error when visiting the root, then you might not have Default.aspx mapped as a default document. Follow these steps to add Default.aspx as a default document.

As I’ve written before, this file is necessary for IIS 6, IIS 7 Classic Mode, and pre SP1 Cassini, but not IIS 7 Integrated. So if you’re using Cassini with Visual Studio 2008 SP1 and deploying to IIS 7 Integrated, you can delete Default.aspxand its sub-files.

In the meanwhile, the fix is to make the following change.

Change:

HttpContext.Current.RewritePath(Request.ApplicationPath);

To

HttpContext.Current.RewritePath(Request.ApplicationPath, false);

If you created your website in the IIS root rather than a virtual application, you would never have noticed this issue. But in the virtual application, the URL to the stylesheet rendered contained the virtual application name, when it shouldn’t. Changing the second argument to false fixes this.

IIS6 Extension-less URLs

Ok, now we’re ready to try this with extension-less URLs using the infamous “Star mapping” or “Wildcard mapping” feature of IIS 6. I say infamous because there is a lot of concern over the performance implications of doing this. Of course, you should measure the performance of your site for yourself to determine if it is really a problem.

The first step is to go back to the Application Configuration Propertiesdialog like we did when configuring the .mvc ISAPI mapping (see, I told you that information might come in useful later).

application mappings

Next to the Wildcard application maps section, click the Insert… button.

wildcard extension mapping

This brings up the wildcard application mapping dialog. Enter the path to theaspnet_isapi.dll. You can follow the trick we mentioned earlier for getting this path.

Don’t forget to uncheck the Verify that file exists checkbox! This is one of the most common mistakes people make.

If you’ve been following along everything in this post, you’ll need to go back and reset the routes in your Global.asax.cs file to the default routes. You no longer need the .mvc file extension in the routes. At this point, you can also remove Default.aspx if you’d like. It’s not needed.

Now when you browse your site, your URLs will not have a file extension as you can see in the following screenshots.

Home page without extension

About page without extension

 

Final Tips

One thing to understand is that an ASP.NET project is scoped to the Website or Virtual Application in which it resides. For example, in the example I have here, we pointed a Virtual Application named IIS6DemoWeb to the directory containing my ASP.NET MVC web application.

Thus, only requests for that virtual application will be handled by my web application. I cannot make a request for http://localhost/ in this case and expect it to be handled by my application. Nor can I expect routing in this application to handle requests for another root directory such ashttp://localhost/not-my-app/.

This might seem like an obvious thing to say, but I know it trips some people up. Also, in the example I did here, I used a virtual application for demonstration purposes. It’s very easy to point a root Website in IIS to my application and run it in http://localhost/ rather than a virtual application. This is not a problem. I hope you found this helpful.

Did you know… How to add a Linked Item to a project

This feature is helpful when you need a common file shared among projects, like a bitmap, readme, set of common tools and utilities, etc.

To add, right-click on the project in the solution explorer, and select Add – Existing Item.  You’ll notice the “Add” button is one of those button-dropdowns.  Drop down the Add button and select Add a link

Add Existing Item with Add as Link selected

Now you have a linked item in your project as indicated by the shortcut icon.

Linked item in Solution Explorer

The location of the linked file is saved in the project file.  The location must be via the file system, either on different drives or UNC.  No http or ftp.

Tracking User Activity

Introduction

I like data. I go gaga over measurable metrics. Nothing makes me happier than storing information and then seeing it expressed in tables of numbers and colorful charts. Whenever I work on a web application I am always looking for interesting data to record and analyze, and the most interesting (and potentially profitable) data that every website owner should track are usage statistics. Web server log files and online tools like Google Analytics provide an array of useful metrics, including how many unique visitors accessed your site, what pages were the most popular, what days of the week and hours of the day represent peak demand, and so forth.

Many ASP.NET web applications support user accounts, enabling visitors to create an account and sign in to the site. With a little bit of effort you can track the activity of your logged on users. This can include recording activities such as what pages were visited as well as what actions were performed. Consider a page that allows a user to manage his profile. When first arriving at this page the activity log might add an entry like "Visiting the User Profile page." After updating his e-mail address, the activity log might record, "Changed e-mail address from scott@example.com to mitchell@example.com." Such usage tracking offers a deeper level of analysis than is possible with log files or online visitor statistic tools. Instead of data that report total number of visitors or how the average user is interacting with the site, user activity tracking can provide a very detailed view of how a particular individual is using the application and what actions he is performing while signed on to the site.

This article examines how to record your users' activities in a database table and display this information in a web page. A complete, working demo application that shows these techniques in action is available for download.

ASP.NET's Membership system makes it easy to create and manage user accounts. Many websites that use Membership are configured to use SqlMembershipProvider, a Membership provider that ships with the .NET Framework and stores user account information in a Microsoft SQL Server database. The demo application for this article uses SqlMembershipProvider, storing user account information along with the user activity log in a Microsoft SQL Server 2008 Express Edition database file (ASPNETDB.mdf), which you will find in the application's App_Data folder. For more information on using the Membership system refer to my ASP.NET Web Security tutorial series.

A Look at the Membership System's User Tracking Implementation

One of the lesser known features of ASP.NET's Membership system is that it has a built-in mechanism to track the last date and time each user has accessed the system. Each user account has a LastActivityDate property that records this information; the SqlMembershipProvider stores this value in the aspnet_Users table's LastActivityDate column in Coordinated Universal Time (UTC). This LastActivityDate value is automatically updated whenever a user signs in and whenever their user account information is accessed via the Membership.GetUser method. The LastActivityDate is used by the Membership system to determine how many users are online - theMembership.GetNumberOfUsersOnline method returns the number of users whose LastActivityDate is within a certain window of the current date and time (15 minutes, by default).

The Membership system's user tracking implementation is pretty limited as it only specifies the last date and time a user was active on the site. It does not indicate what the user was doing at that time or provide any sort of activity history. The activity logging system presented in this article overcomes these limitations.

Designing the User Activity Log Database Table

The first step in building any analysis tool is to determine what information to track. Different website usage analytic tools capture different information: web server logs typically record the filename of each requested page, the querystring, the date and time, and the HTTP status code, whereas online tools capture information of interest to the sales and marketing departments: visit durations, the geographical locations of the site's visitors, the number of unique visitors, entry and exit pages, and so on.

What information do we need to record when tracking the online activity of a website's logged on users? At a minimum we would need to log:

  • The activity being performed
  • The user performing the activity
  • The date and time of activity
  • The page being visited when the activity is performed

This information can be modeled in a single database table. Figure 1 shows such a table, which I've named ActivityLog. This table contains one record for each activity recorded for each user on the site.

Figure 1: The ActivityLog table models the activity log.

The ActivityLog table models the activity log.

The ActivityLogID is of type uniqueidentifier and uniquely identifies each log entry. The UserId column identifies the user who performed the activity. (The UserId column in the aspnet_Users table is what uniquely identifies each user in the SqlMembershipProvider user store.) The Activity column describes the activity performed; PageUrl is the URL of the page where the activity was performed. Finally, ActivityDate is the date and time (in UTC) that the activity was performed.

The ActivityLog table is designed to have a record added for each activity performed by the user. Depending on the popularity of your website, this table can grow to include tens of thousands if not millions of records. You may want to consider implementing some mechanism to remove records older than a certain date, such as all activity log entries more than three months old. This could be accomplished by a SQL Job that executes nightly.

Logging User Activity

Web server log files automatically record each requested page; online usage statistic tools use cookies to track the pages users visit. Both of these logging mechanisms, once setup and configured, track visits to the site automatically without needed intervention from the web developer. The activity log is more flexible as it can be used to track any "activity," which may be page visits or user-instigated actions. Therefore, logging a user activity to the database involves writing code.

To help facilitate this process I created a custom base page class named BasePage that extends the System.Web.UI.Page class. BasePage includes a method named LogActivity(activity, recordPageUrl) that writes a record to the ActivityLog table with the specified activity and, if specified, the URL of the currently visited page.

The LogActivity method's code follows:

  1. protected void LogActivity(string   
  2.   
  3. activity, bool recordPageUrl)  
  4. {  
  5.     if (Request.IsAuthenticated)  
  6.     {  
  7.         // Get information about the currently logged on user  
  8.         MembershipUser currentUser = Membership.GetUser(false);  
  9.         if (currentUser != null)  
  10.         {  
  11.             Guid userId = (Guid)currentUser.ProviderUserKey;  
  12.             // Log the activity in the database  
  13.             using (SqlConnection myConnection = new  
  14.                SqlConnection(ConfigurationManager.  
  15.                ConnectionStrings["MembershipConnectionString"].ConnectionString))  
  16.             {  
  17.                 SqlCommand myCommand = new SqlCommand();  
  18.                 myCommand.CommandText = "usp_LogUserActivity";  
  19.                 myCommand.CommandType = CommandType.StoredProcedure;  
  20.                 myCommand.Connection = myConnection;  
  21.                 myCommand.Parameters.AddWithValue("@UserId", userId);  
  22.                 myCommand.Parameters.AddWithValue("@Activity", activity);  
  23.                 if (recordPageUrl)  
  24.                     myCommand.Parameters.AddWithValue("@PageUrl", Request.RawUrl);  
  25.                 else  
  26.                     myCommand.Parameters.AddWithValue("@PageUrl", DBNull.Value);  
  27.                 myConnection.Open();  
  28.                 myCommand.ExecuteNonQuery();  
  29.                 myConnection.Close();  
  30.             }  
  31.         }  
  32.     }  
  33. }  

The method starts by determining if the user visiting the page is authenticated. If so, it gets the user's information via the Membership class's GetUser method. If a user is returned, the stored procedure usp_LogUserActivity is called, passing in values for the @UserId@Activity, and @PageUrl parameters. Note that if recordPageUrl is false, the @PageUrl parameter is set to a database NULL value; if it is true, the @PageUrl parameter is assigned the raw URL of the currently requested page, which includes the directories, filename, and querystring of the requested web page (i.e., /MyApp/Users/Default.aspx?ID=2).

The usp_LogUserActivity stored procedure starts by updating the LastActivityDate column in the aspnet_Users table. As a result, adding an entry to the user activity log is tantamount to retrieving the user's information through the Membership system. Following the update to the LastActivityDate, the usp_LogUserActivity stored procedure inserts a record into the ActivityLog table. This update and insert are atomic as they are performed under the umbrella of a transaction. For background on transactions and using SQL Server's TRY...CATCH blocks seeMaintaining Database Consistency with Transactions and TRY...CATCH in SQL Server.

  1. ALTER PROCEDURE dbo.usp_LogUserActivity  
  2. (  
  3.     @UserId     uniqueidentifier,  
  4.     @Activity   nvarchar(100),  
  5.     @PageUrl    nvarchar(100)  
  6. )  
  7. AS  
  8. BEGIN TRY  
  9.    BEGIN TRANSACTION    -- Start the transaction  
  10.     DECLARE @CurrentTimeUtc datetime  
  11.     SET @CurrentTimeUtc = getutcdate()  
  12.    -- Update the LastActivityDate in aspnet_Users  
  13.     UPDATE  dbo.aspnet_Users  
  14.     SET     LastActivityDate = @CurrentTimeUtc  
  15.     WHERE   @UserId = UserId  
  16.     -- Insert activity record for user  
  17.     INSERT INTO ActivityLog(UserId, Activity, PageUrl, ActivityDate)  
  18.     VALUES(@UserId, @Activity, @PageUrl, @CurrentTimeUtc)  
  19.    -- If we reach here, success!  
  20.    COMMIT  
  21. END TRY  
  22. BEGIN CATCH  
  23.   -- Whoops, there was an error  
  24.   IF @@TRANCOUNT > 0  
  25.      ROLLBACK  
  26.   -- Raise an error with the details of the exception  
  27.   DECLARE @ErrMsg nvarchar(4000), @ErrSeverity int  
  28.   SELECT @ErrMsg = ERROR_MESSAGE(),  
  29.          @ErrSeverity = ERROR_SEVERITY()  
  30.   RAISERROR(@ErrMsg, @ErrSeverity, 1)  
  31. END CATCH  

With the BasePage class complete, the final step is to have the ASP.NET pages in the site derive from BasePage (rather than from System.Web.UI.Page). Once this has been done you can call the LogActivity method from any page. For example, the homepage (~/Default.aspx) has the following code for its Page_Load event handler:

  1. protected void Page_Load(object sender, EventArgs e)  
  2. {  
  3.     if (!Page.IsPostBack)  
  4.     {  
  5.         base.LogActivity("Visiting the homepage..."true);  
  6.     }  
  7. }  

The LogActivity method can be called from any event handler in those ASP.NET pages that derive from BasePage. Call LogActivity from the Page_Load event handler to log information when a page is first visited. You can additionally call LogActivity when the user clicks a button to log that they've performed a particular action (such as editing or deleting a record from some database table). All of the ASP.NET pages in the demo application derive from the BasePage class, and most include at least one call to the LogActivity method.

Displaying a Particular User's Activity History

The activity log provides a complete history of each user's activity on the site. The demo application includes a page named ActivityHistoryByUser.aspx that displays the complete history in a paged grid for a particular user. Figure 2 shows a screen shot of ActivityHistoryByUser.aspx in action.

The GridView controls used in the web pages in this tutorial use default paging, which is easy to implement but inefficient when paging through a large number of records. Because the activity log may contain thousands of records for each user account, the GridView controls should be retooled to use custom paging unless some mechanism is put into place to periodically cull old activity log entries from the table or if only recent activity log records are displayed in the grid. For more information on custom paging see Custom Paging in ASP.NET.

Figure 2: Scott's activity history is displayed in a grid.

Scott's activity history is displayed in a grid.

Each activity in the left column is displayed as a hyperlink that links to the activity's PageUrl value (if such a value exists). The Last Updated column shows the date the activity was performed. For activities older than a week, the date the activity was performed is displayed. If the activity occurred more recently then a human-friendly message, such as "2 days ago" or "6 minutes ago," is displayed in place of the date and time. (This display formatting is handled by the FormatLastUpdatedDate method in the BasePage class.)

The grid is populated by the records returned from the usp_GetUserActivityLog stored procedure. As the following markup shows, this stored procedure returns the ActivityLog entries for a particular user ordered from the most recent entries to the oldest.

  1. ALTER   
  2.   
  3. PROCEDURE dbo.usp_GetUserActivityLog  
  4. (  
  5.     @UserID uniqueidentifier  
  6. )  
  7. AS  
  8. DECLARE @CurrentDateUtc datetime  
  9. SET @CurrentDateUtc = getutcdate()  
  10. SELECT ActivityLogID, Activity, PageUrl, ActivityDate, @CurrentDateUtc as CurrentDate  
  11. FROM ActivityLog  
  12. WHERE UserID = @UserID  
  13. ORDER BY ActivityDate DESC  

Viewing the Online Users and their Last Performed Activity

Many websites that support user accounts have a page that lists what users are currently online and what activity they last performed. As noted earlier, the Membership system automatically records each user's last active date and time and provides a method for determining how many users have been active within a specified time window. The Membership's built-in system does not include what activity the user last performed, but this information is captured by the ActivityLog table.

I added a stored procedure to the database named usp_GetCurrentActivityForOnlineUsers that returns the list of users who are currently online along with their most recent activity. This stored procedure takes in a single input parameter, @MinutesSinceLastInactive, which is the number of minutes that has elapsed since a user has been active in the system and is still considered "online." For example, a value of 15 means that any user whoseLastActivityDate is within 15 minutes of the current date and time is considered "online," whereas those users whose LastActivityDate is outside of this window are considered "offline."

The usp_GetCurrentActivityForOnlineUsers stored procedure starts by determining what time is the cutoff for a user to be considered "online." It then queries the aspnet_Users and ActivityLog tables to retrieve the username of online users along with information about their last activity.

  1. ALTER PROCEDURE   
  2.   
  3. dbo.usp_GetCurrentActivityForOnlineUsers  
  4. (  
  5.     @MinutesSinceLastInactive   int   
  6. )  
  7. AS  
  8. DECLARE @CurrentTimeUtc datetime  
  9. SET @CurrentTimeUtc = getutcdate()  
  10. DECLARE @DateActive datetime  
  11. SELECT  @DateActive = DATEADD(minute,  -(@MinutesSinceLastInactive), @CurrentTimeUtc)  
  12. SELECT  act.UserId,  
  13.         u.UserName,   
  14.         act.Activity,  
  15.         act.PageUrl,  
  16.         act.ActivityDate,  
  17.         @CurrentTimeUtc as CurrentDate  
  18. FROM    dbo.aspnet_Users u(NOLOCK)  
  19.     INNER JOIN dbo.ActivityLog act(NOLOCK) ON  
  20.         act.UserId = u.UserId  
  21. WHERE   u.LastActivityDate > @DateActive AND  
  22.         act.ActivityDate = u.LastActivityDate  
  23. ORDER BY act.ActivityDate DESC  

The WhoIsOnline.aspx page displays the results from this stored procedure in a grid (see Figure 3).

Figure 3: Those users currently online are listed along with their most recent activity.

Those users currently online are listed along with their most recent activity.

Conclusion

Web server log files and online usage analysis tools are helpful in determining and evaluating macroscopic usage patterns. Unfortunately, these tools cannot provide a detailed view of how a particular user is interacting with your site. Nor can they provide live, up to the second activity information that can be used in your application to show who is currently online and what they are doing. Such deep usage analysis and real-time statistics are possible on websites that support user accounts.

ASP.NET's Membership system greatly simplifies the process of setting up, creating, and managing user accounts. However, the Membership system only tracks when each user's last activity was on the site; it does not log the activity performed or maintain an activity history. As examined in this article, it is possible to build your own user activity log with a little bit of elbow grease.

Happy Programming!

References

Tuesday, December 2, 2008

Nokia N79








Device Description:

The Nokia N79 is a GSM/WCDMA dual-mode multimedia computer supporting WCDMA/HSDPA, EGSM, and WLAN. Change the Xpress-on smart covers that automatically change the theme when attached. Use the Nokia N79 to connect to mobile broadband using WLAN or HSDPA (3.5G). Find directions and locations with the integrated A-GPS and included maps. Take photos with a 5-megapixel autofocus camera with a dual LED flash and automatic location tagging of images and videos. Enjoy videos, music, and graphics on the 2.4” QVGA display with orientation sensor. Additional features include UPnP, Bluetooth 2.0 +EDR, and USB 2.0 High-Speed.

Technical Specs

Developer PlatformS60 3rd Edition, Feature Pack 2
Operating SystemSymbian OS v9.3
Resolution240 x 320

General

Resolution240 x 320
Color Depth24 bit
Size110 x 49 x 15 mm
Weight97 g
Input Methods3 Labeled Soft Keys
5-way Scrolling
Grid Key Mat
Navi Wheel

Frequency BandGSM 1800
GSM 1900
GSM 850
GSM 900
WCDMA 2100
WCDMA 900

Regional AvailabilityAfrica
Americas
Asia-Pacific
Europe
Middle East

Data BearerCSD
Dual Transfer Mode (MSC 11)
EGPRS
GPRS
HSCSD
HSDPA
WCDMA

CPU CountSingle CPU
CPU TypeARM 11
CPU Clock Rate369 MHz

Extra Features

Extra FeaturesAccelerometer Sensor
FOTA Firmware over the Air
Flight Mode
Internet Radio
Nokia Maps
OMA Domain Name System (DNS) 1.0
On-Demand Paging
Open C API's
PoC (Push-to-talk over Cellular)
Stereo FM Radio
Stereo Handsfree Speakers
Still Image Editor
SyncML
TV Out
Themes
Visual Radio

GPS FeaturesA-GPS

APIs

Java TechnologyCLDC 1.1
IAP Info API
JSR 135 Mobile Media API
JSR 172 Web Services API
JSR 177 Security and Trust Services API
JSR 179 Location API
JSR 180 SIP API
JSR 184 Mobile 3D Graphics API
JSR 185 JTWI
JSR 205 Wireless Messaging API
JSR 226 Scalable 2D Vector Graphics API
JSR 234 Advanced Multimedia Supplements
JSR 248 Mobile Service Architecture for CLDC
JSR 75 FileConnection and PIM API
JSR 82 Bluetooth API v1.1
MIDP 2.1
Nokia UI API
eSWT UI API

Symbian CertificatesSymbian A
Symbian B
Symbian C
Symbian D
UTI Root

Product ID0x2000DA64

Browser & Flash

BrowserHTML over TCP/IP
OSS Browser
WAP 2.0
Web Runtime
XHTML over TCP/IP

Flash LiteFlash Lite 3.0

Multimedia

Camera Resolution2584 x 1938
Digital Zoom20 x
SensorCMOS 5.0 Megapixel
Focal length5.2 mm
F-Stop/Aperturef/2.8
Focus range10 cm to infinity
Image FormatJPEG/Exif
Camera FeaturesAuto Focus, Carl Zeiss Optics, Flash, Red-Eye Reduction, Self Timer, Video Stabilization
Video Resolution640 x 480
Video Frame Rate30 fps
Video Zoom8 x
Video FormatH.263, MPEG-4
Secondary Camera Resolution352 x 288
Secondary Camera Digital Zoom2 x
Secondary Camera Image FormatJPEG
Secondary Camera Video FormatH.263
Video FeaturesVideo Player
Video Recorder
Video Ringtones
Video Sharing
Video Streaming

Video Formats3GPP formats (H.263)
Flash Video
H.264/AVC
MPEG-4
RealVideo 7,8,9/10
WMV

Graphics FormatsBMP, EXIF, GIF87a, GIF89a, JPEG, JPEG 2000, PNG, WBMP
Audio FeaturesAudio Equalizer
Audio Recording AAC stereo, 48kHz
Audio Streaming
FM Transmitter (88.1 - 107.9 MHz)
Music Player
Stereo

Audio FormatsAAC, AAC+, AMR-NB, AMR-WB, M4A, MIDI Tones (poly 64), MP3, MP4, Mobile XMF, RealAudio 7,8,10, SP-MIDI, True tones, WAV, WMA, eAAC+

Memory Functions

Max User Storage50 MB
Memory CardMicro SD
Max Memory Card Size8 GB
Maximum Heap SizeUnlimited
Maximum JAR SizeUnlimited

Connectivity

Local ConnectivityBluetooth 2.0 +EDR
Bluetooth Stereo Audio
DLNA Certification
Micro USB
Mini Stereo Plug 3.5mm
Nokia AV 3.5mm
Nokia microUSB Cable CA-101
UPnP
USB 2.0 High-Speed
USB Mass Storage
WLAN

WLAN support802.11b/g
WEP
WPA
WPA2 (AES/TKIP)

Bluetooth ProfilesA2DP, AVRCP, BIP, BPP, DI, DUN, FTP, GAP, GAVDP, GOEP, HFP, HID, HSP, OPP, PBAP, SAP, SDP, SPP

Messaging

MessagingIM
MMS+SMIL
SMS

Email ProtocolIMAP4
POP3
SMTP

Email SolutionMail for Exchange
Nokia Intellisync Wireless Email
OMA E-mail Notification v1.0

Messaging FeatureOMA Instant Messaging and Presence Service v1.1
OMA Instant Messaging and Presence Service v1.2.1
OMA Multimedia Messaging Service v1.2

Document FormatsExcel, PDF, Powerpoint, Word, Zip

Power Management

Power Management2.0mm Charger Connector
BatteryBL-6F 3.7V 1200 mAh
GSM Talk Time5.5 hours
WCDMA Talk Time3.5 hours
VoIP Talk Time4 hours
GSM Standby Time372 hours
WCDMA Standby Time406 hours

Other

OMA Device ManagementOMA Client Provisioning v1.1
OMA Device Management v1.1.2

OMA Data SynchronizationOMA Data Synchronization v1.2
Digital Rights ManagementOMA DRM Forward Lock
OMA DRM v1.0
OMA DRM v2.0
OMA DRM v2.1
Windows Media DRM 10

DRM Delivery MethodHTTP Download
MMS
OMA Download v1.0

Web ServiceLiberty Alliance ID-WSF 1.1
OMA WS Network Identity 1.0
WS-I Basic Profile 1.0/1.1

Tools

The main Tools & SDKs page www.forum.nokia.com/tools provides all the tools & SDKs in an easy to access form. The page also includes Getting Started -articles giving an overview of the tools and SDKs.

Carbide.c++ v1.3, the development tool for C++ for Symbian OS and Open C developers
Carbide.c++ is a powerful family of mobile development tools built on the Eclipse framework. Family members include: Carbide.c++ OEM Edition for device creation users Carbide.c++ Professional Edition for developers working with pre-production devices Carbide.c++ Developer Edition for application development on production phones Carbide.c++ Express, available as a no-cost download to introduce developers to mobile experience

Carbide.ui Theme Edition
Carbide.ui Theme Edition enables the creation and editing of themes for S60 and Series 40 devices. Carbide.ui provides professionals and hobbyists alike with access to more than 1,000 customizable elements in the S60 UI, which makes the S60 UI the most customizable UI available.

Recommended SDK(s)

OpenGL ES 1.1 Plug-in
This early technology plug-in for S60 3rd edition SDK for Symbian OS, for C, enables the development of OpenGL ES 1.1 applications with N93. N93 is the first Nokia multimedia computer with HW accelerated 3D graphics/OpenGL ES 1.1 support. This plug-in enables the full usage of the native 3D graphics features supported in N93. The content of this plug-in will be a part of S60 R3.1 SDK.

S60 Platform SDKs for Symbian OS, for Java™
S60 Platform SDK for Symbian OS, for Java™ MIDP allows developers to quickly and efficiently run and test Java applications for devices built on the S60 platform. Using a Java™ IDE, development with the SDK is hosted on a PC.

S60 Platform SDKs for Symbian OS, for C++
The S60 SDKs for Symbian OS enable application development for devices based on the S60 platform using C++. The SDK includes all key resources needed for application development, such as documentation, API reference, and an emulator. An IDE is not included; Carbide.c++ is recommended.

Friday, November 28, 2008

Hosting an “Indigo” (WCF) Service


Table of Contents:

Introduction

Hosting Environments

· ASMX Services

· WCF Services

· Different Hosting Environments

WAS Hosting Environment

· A Simple Hosted Service

· Service Configuration

· Service Addressing

· Service Activation

· Service Recycling

Let me start with something simple for this topic. I first make a few assumptions here:

  • You have used Microsoft Internet Information Services (IIS).
  • You are familiar with .NET Programming with Common Language Runtime (CLR) and ASP.NET.
  • You have heard of Microsoft Windows Communications Foundation (WCF), code-named “Indigo”.

That is all what you need to know before reading the following!

Below I will write about how to host a WCF service in a special “hosting” environment.

Introduction

There are tons of articles and blogs out there talking about Service Oriented Architecture (SOA), Web Services, and WCF. Never mind if you have not read them. So I will make things as simple as possible. Basically, WCF, as the next generation of service-oriented programming platform, provides a set of .NET APIs in its Version 1 (V1) for developers to use. It will be released as part of a larger package WinFX. You can download the most recent PDC release fromhere.

Every service-oriented programming model is also a client-server model. WCF has no difference. Both the client and service counterparts of WCF are based on the so-called “ABC” model: Address, Binding, and Contract. People also call this as “WHW” model: Address stands for “Where”, Binding stands for “How”, and Contract stands for “What”. Among the three, Binding is the most complicated part in this model. It tells WCF how to transfer data between the client and the service:

  • What transport do we use: HTTP, TCP, MSMQ, Named Pipe, etc?
  • What encodings do we use for the data to be encoded?
  • What security mechanisms do we use for the underlying channels?
  • Etc.

For each WCF service, there is a host that manages service instances and lifetime. The base type of the host is System.ServiceModel.ServiceHost. It provides events and extensibility points for you to programming with. Actually I am lying here. ServiceHost is not the most basic service host type, but System.ServiceModel.ServiceHostBase is. However, you will never touch this base type if you do not want to re-create the whole channel stack to replace WCF underlying implementations.

How far have I diverted from my topic? Aren’t we talking about WCF service hosting? No, not yet! The concept of “host” has relative meanings in different programming plug-in layers. The “host” that I will talk about here has a broader meaning: it is application-level “host”. That is, a host is an application that loads one or more WCF ServiceHosts, which we will call services in the following, and manages their lifetime. In some cases, it provides an environment that works as the entry point for all messages that are routed to the services. Am I clear here? If not, please read on.

Hosting Environments

There are different types of hosting environments for WCF services. To understand how these hosting environments work, we need to go back to see how the predecessor of WCF, a.k.a., ASMX works.

ASMX Services

When ASP.NET V1 was shipped, a Web service framework known as ASMX was introduced. It is tightly integrated with the ASP.NET HTTP pipeline. This pipeline provides the hosting environment to ASMX services and it is almost always hosted inside ASP.NET applications which are managed by IIS. In this way, ASMX services can leverage the advantages of ASP.NET applications such as process management, application recycling, configuration and deployment, and extensibility points provided by ASP.NET HTTP pipeline.

It is not required to have IIS to run ASMX services. For example, you can use the ASP.NET Cassini sample web server to host an ASP.NET application and thus ASMX services. You also build a web server into your own application to host the ASP.NET pipeline using the managed HTTP.sys utility System.Net.HttpListener(see Run ASMX without IIS).

However, ASMX services cannot run without the ASP.NET HTTP pipeline. Even though you can integrate the pipeline into your application as above, you still need to think of the ASMX services as being in a different world, that is, they have their own configuration and deployment which are quite web-flavored. In this sense, WCF services go one big step beyond to make service-oriented programming real.

WCF Services

The fact that ASMX services are tightly coupled with ASP.NET HTTP pipeline is due to the flat and passive HTTP request handling mechanism. HTTP requests received from IIS are passed on to ASP.NET pipeline and then are consumed by service methods.

WCF services, however, are based on a strictly layered model to break this paradigm. Roughly speaking, there are three different layers for WCF services:

  • ServiceModel layer
  • Channel layer
  • Transport layer

This layered model is similar as the Open System Interconnection (OSI) model. The lowest Transport layer is responsible for listening and receiving requests and converting the received data into “Messages”. A Message is a cross layer data packet that travels all of the layers to the top. The Channel layer provides a Message processing framework for different service requirements. In the real implementation, it actually consists of one or more sub-channel layers such as Basic layer, Security layer, ReliableMessaging layer, Transaction layer, etc. The top ServiceModel layer is the application layer that manages service instances and invokes service operations. It provides Object Models (OM) for people to program with.

In this layered model of WCF services, the top SericeModel layer is also responsible for building the lower level layers in different hosting environments. It also makes the Transport layer to be freely chosen among different communication protocols. This is why it does not rely on the ASP.NET HTTP pipeline as ASMX services does.

Different Hosting Environments

As mentioned above, WCF services are hosted by a ServiceHost. Ideally, any application or environment can create ServiceHost to host WCF services. In reality, we classify the different environments into three categories:

  • Windows Activation Service (WAS) hosting environment
  • EXE applications
  • NT Services

The WAS hosting environment is a broad concept that extends the ASP.NET HTTP pipeline hosting concept for ASMX services. WAS is an NT service inWindows Vista. It is separated out from the legacy IIS as a standalone Windows component that provides protocol-agnostic activation mechanism for different protocols besides HTTP. The IIS, Cassini, and any custom Web Server flavored hosting environments also fall into this category since they share the same WCF service deployment and activation mechanism. I will talk about this more in later sections.

You can easily host a WCF service in an EXE application so that you can control the lifetime of the ServiceHost. Furthermore, the WCF service shares the same configuration file with the application. This hosting environment is mostly useful when you also build your client code into the application. A good example is that a sophisticated business application with 3-tier architecture can have WCF services to pass messages between different layers.

NT Services are special EXE applications which are long running and the lifetime is controlled by Service Control Manager (SCM). Because of the long running behavior, you need to be careful of all of different requirements for writing an NT Service: security, reliability, and performance. You can use Microsoft Visual Studio to create managed NT Services with “Windows Service” project type.

WAS Hosting Environment

As mentioned above, a WCF service works similarly as an ASMX Service. For HTTP protocol, it relies on ASP.NET HTTP pipeline to transfer data. For non-HTTP protocols such as Net.Tcp, Net.Pipe, and Net.Msmq etc, it relies on the extensibility points, i.e., ProcessProtocolHandler (PPH) and AppDomainProtocolHandler (ADPH), of ASP.NET to transfer data. We will use the term “protocol handler” interchangeably to refer to either the ASP.NET HTTP pipeline or the ADPH. In the context of this writing, WCF services hosted in this environment are called WAS hosted services or simply called hosted services, and in contrast, services hosted in NT Services or EXE applications are calledself-hosted services.

Let’s first take a look at a simple hosted service and see how it works.

A Simple Hosted Service

A hosted service requires a physical service file with extension “.svc” to be associated with it. Here is the content of a simple service file HelloWorld.svc:

<%@Service Language="C#" Class="HelloWorld.HelloService" %>

using System;

using System.ServiceModel;

namespace HelloWorld

{

[ServiceContract]

public interface IHelloContract

{

[OperationContract]

string Hello(string greeting);

}

[ServiceBehavior]

class HelloService : IHelloContract

{

public string Hello(string greeting)

{

return "You said: " + greeting;

}

}

}

This file has a Service directive which is enclosed in the “<% %>” block and it also contains inline C# code. The Service directive tells the hosting environment which Service this file points to.

Just as ASMX services, the code can be compiled into a DLL that is deployed to the Global Assembly Cache (GAC), or to the application’s “\bin” directory, or it can be put in a C# file under the application’s “\App_Code” directory.

With this service file, we have defined a service contract IHelloContract and the service implementation. However, the service still does not have an endpoint defined yet. You can either define an endpoint declaratively through the configuration file “web.config” or imperatively through code. The latter will be covered separately in a different blog later.

Service Configuration

The service configuration inside the web.config file is very similar as that of self-hosted services. Here is an example of web.config file for the above service:

xml version="1.0" encoding="utf-8"?>

<configuration>

<system.serviceModel>

<services>

<service type="HelloWorld.HelloService">

<endpoint binding="basicHttpBinding"

contract="HelloWorld.IHelloContract" />

service>

services>

system.serviceModel>

configuration>

I need to point out a few points for this config file:

1) Service Type Attribute: The service type “HelloWorld.HelloService” specified here plays as a lookup key for the corresponding HelloWorld.svc. It tells the hosting environment which service will take the configuration specified here.

2) Endpoint Address: I did not specify the “address” of the endpoint here. The endpoint address is the same as the base address in this case. I will expand this in the next section.

3) Nested web.config: The service configuration can be specified in nested web.config files. For ASP.NET applications, a web.config file can locate in any sub-directories or virtual web directories as well as the root of the virtual application. All of the settings in the nested web.config files are merged for the virtual ancestors of the directory where the .svc file locates.

Service Addressing

The .svc file does not contain much useful information except for providing the service type which is also specified in the web.config file. Then why do we need .svc files after all? The main reason is that we need them to provide intuitive addressing model.

Suppose that we drop the above HelloWorld.svc file into a virtual application “/test”, we automatically have the following base address for the service:

http://localhost/test/HelloWorld.svc

You can enable different protocols for a single ASP.NET application. Each protocol needs to have a protocol binding for the whole web site. For IIS6 and below, the only protocol that is supported is HTTP (which implies HTTPS).

The WAS hosting environment looks up all enabled protocols for the ASP.NET application and generates the base addresses for the hosted service. Each base address has the following format:

://[:]//.svc

You can access the base addresses through the ServiceHostBase.BaseAddresses property. In WCF V1, only one base address is supported for each protocol. So if there are two or more bindings for HTTP is specified, you will get an ArgumentException on the service side with error message “Collection already contains an address with scheme http”.

Note: It is NOT recommended to specify an absolute address (with protocol scheme included) to the endpoint in web.config, otherwise, you will get AddressAccessDeniedException on the service side if the address does not match the service base address up to the application path () or EndpiontNotFoundException if you happen to match the application path but not the service path.

Service Activation

The WAS hosting environment is represented by the public type System.ServiceModel.ServiceHostingEnvironment. It is responsible for service activation, that is,

· It finds the service compilation information from System.Web.Compilation.BuildManager

· It creates the ServiceHost instance to host the service with compilation information.

· It then calls ServiceHost.Open to build the layers.

· It then caches the service and manages its lifetime inside the AppDomain.

This whole process is exposed through the following public method:

public static void EnsureServiceAvailable(stringvirtualPath)

This API is protocol agnostic. It is an extensible point for a custom protocol handler to activate the service.

The service activation process has the following characteristics:

1) Activation on demand: A WCF service is activated in the AppDomain only if EnsureServiceAvailable is called by one protocol handler for that service. Once it is activated, it will be active in the AppDomain until the AppDomain is unloaded.

2) Activation once for all: A WCF service can be activated from any protocol handler. Once it is activated, it is activated for all endpoints and thus all protocols. So when a second protocol handler calls EnsureServiceAvailable, it is a no-op as the service has been activated.

Service Recycling

Hosted WCF services enjoy all of the features for ASP.NET applications. One of the major feature is application recycling including AppDomain recycling and process (or AppPool) recycling. There are different factors that can cause application recycling to happen. See “Recycling Application Pool Settings” for more information about process recycling. When the worker process is recycled, all of the AppDomains in that process are also recycled. The configuration setting HostingEnvironmentSection.IdleTimeout can control AppDomain recycling. Also an AppDomain can be recycled if critical files for the application are changed. These files include web.config, assemblies in the “\bin” directory, code files in the “\App_Code” directory, etc.

Whenever a .svc file is modified in application, the AppDomain is also recycled. This is to simplify the service recycling mechanism. When the AppDomain is recycled, the ServiceHostingEnvironment tries to close all of the cached WCF services in a timely manner. Services not closed timely are aborted at the end. The timeout is controlled by the HostingEnvironmentSection.ShutdownTimeoutproperty.

The drawback of service recycling is that all of the sessionful data is lost. This means that Security and ReliableMessaging break when recycling happens. The workaround is to leverage the ASP.NET state service features by enabling AspNetCompatibility mode. I will expand on this in a later blog.