Maarten Balliauw {blog}

ASP.NET, ASP.NET MVC, Windows Azure, PHP, ...

NAVIGATION - SEARCH

Authenticating users with RPXNow (in ASP.NET MVC)

ASP.NET MVC RPX RPXNow Don’t you get sick of having to register at website X, at website Y, at website Z, …? It is really not fun to maintain all these accounts, change passwords, … Luckily, there are some large websites offering delegated sign-in (for example Google Accounts, Live ID, Twitter OAuth, …). You can use these delegated sign-in methods on your own site, removing the need of registering yet another account. Unfortunately, not everyone has an account at provider X…

A while ago, I found out about the free service JanRain is offering: RPXNow. This (free!) service combines the strengths of several major account providers (Google Accounts, Live ID, Twitter, Facebook, OpenID, …) into one simple API. This means that people are able to log in to your website if they have an account at one of these providers! Here’s how to use it in ASP.NET MVC…

Download sample code: Rpx4Mvc.zip (246.97 kb)

kick it on DotNetKicks.com

Creating some HTML helpers

After registering at RPXNow, you will receive an application name and API key. This application name should be used when embedding the login control that is provided. To simplify embedding a login link, I’ve created two HtmlHelper extension methods: one for embedding the login control, another one for providing a login link:

[code:c#]

public static class HtmlHelperExtensions
{
    public static string RpxLoginEmbedded(this HtmlHelper helper, string applicationName, string tokenUrl)
    {
        return "<iframe src=\"https://" + applicationName + ".rpxnow.com/openid/embed?token_url=" + tokenUrl + "\" scrolling=\"no\" frameBorder=\"no\" style=\"width:400px;height:240px;\" class=\"rpx-embedded\"></iframe>";
    }

    public static string RpxLoginPopup(this HtmlHelper helper, string applicationName, string tokenUrl, string linkText)
    {
        return "<script src=\"https://rpxnow.com/openid/v2/widget\" type=\"text/javascript\"></script><script type=\"text/javascript\">RPXNOW.overlay = true; RPXNOW.language_preference = 'en';</script>" +
            "<a class=\"rpxnow\" onclick=\"return false;\" href=\"https://" + applicationName + ".rpxnow.com/openid/v2/signin?token_url=" + tokenUrl + "\">" + linkText + "</a>";     
    }
}

[/code]

I can now add a login link in my views more easily:

[code:c#]

<%=Html.RpxLoginPopup("localtestapp", "http://localhost:1234/Account/Login", "Login") %>

[/code]

Creating the RPX implementation

The RPX implementation is quite easy. When a user logs in, a token is posted to your web application. Using this token and the API key, you can query the RPX service for a profile Here’s a simple class which can take care of all this:

[code:c#]

public class RpxLogin
{
    protected string apiKey = "";

    public RpxLogin(string apiKey)
    {
        this.apiKey = apiKey;
    }

    public RpxProfile GetProfile(string token)
    {
        // Fetch authentication info from RPX
        Uri url = new Uri(@"https://rpxnow.com/api/v2/auth_info");
        string data = "apiKey=" + apiKey + "&token=" + token;

        // Auth_info request
        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
        request.Method = "POST";
        request.ContentType = "application/x-www-form-urlencoded";
        request.ContentLength = data.Length;

        StreamWriter requestWriter = new StreamWriter(request.GetRequestStream(), Encoding.ASCII);
        requestWriter.Write(data);
        requestWriter.Close();

        HttpWebResponse response = (HttpWebResponse)request.GetResponse();
        TextReader responseReader = new StreamReader(response.GetResponseStream());
        string responseString = responseReader.ReadToEnd();
        responseReader.Close();

        // De-serialize JSON
        JavaScriptSerializer serializer = new JavaScriptSerializer();
        RpxAuthInfo authInfo = serializer.Deserialize<RpxAuthInfo>(responseString);

        // Ok?
        if (authInfo.Stat != "ok")
        {
            throw new RpxException("RPX login failed");
        }

        return authInfo.Profile;
    }
}

[/code]

Note that the RPX service returns JSON data, which I can deserialize using the JavaScriptSerializer. That’s really all it takes to get the logged-in user name.

Plumbing it all together

All of the above can be plumbed together in a new AccountController. This will have to be extended for your application (i.e. for storing the logged in username in a membership database, … Simply add these two action methods in a blank AccountController and you are ready to RPX!

[code:c#]

[HandleError]
public class AccountController : Controller
{
    public ActionResult Login(string token)
    {
        if (string.IsNullOrEmpty(token)) {
            return View();
        } else {
            IRpxLogin rpxLogin = new RpxLogin("b2e418e8e2dbd8cce612b829a9234ed4a763b2c0");
            try
            {
                RpxProfile profile = rpxLogin.GetProfile(token);

                FormsAuthentication.SetAuthCookie(profile.DisplayName, false);
            }
            catch (RpxException)
            {
                return RedirectToAction("Login");
            }

            return RedirectToAction("Index", "Home");
        }
    }

    [Authorize]
    public ActionResult Logout()
    {
        FormsAuthentication.SignOut();
        return RedirectToAction("Index", "Home");
    }
}

[/code]

Download a sample application: Rpx4Mvc.zip (246.97 kb)

kick it on DotNetKicks.com

How we built TwitterMatic.net - Part 5: the front-end

TwitterMatic - Schedule your Twitter updates“After having found a god-like guardian for his application, Knight Maarten The Brave Coffeedrinker found out that his application still had no functional front-end. It’s OK to have a guardian and a barn in the cloud, but if there’s nothing to guard, this is a bit useless. Having asked the carpenter and the smith of the village, our knight decided that the so-called “ASP.NET MVC” framework might help in his quest.”

This post is part of a series on how we built TwitterMatic.net. Other parts:

kick it on DotNetKicks.com

The front-end

In part 2 of this series, we have already created the basic ASP.NET MVC structure in the web role project. There are few action methods and views to create: we need one for displaying our scheduled tweets and one for scheduling a tweet. We’ll concentrate on the latter in this post.

Action methods

The Create action method will look like this:

[code:c#]

// GET: /Tweet/Create

public ActionResult Create()
{
    TimedTweet tweet = new TimedTweet();

    ViewData["SendOnDate"] = tweet.SendOn.ToShortDateString();
    ViewData["SendOnTime"] = tweet.SendOn.ToShortTimeString();

    return View(tweet);
}

// POST: /Tweet/Create

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(int UtcOffset, string SendOnDate, string SendOnTime, FormCollection collection)
{
    TimedTweet tweet = new TimedTweet(this.User.Identity.Name);
    try
    {
        tweet.SendOn = DateTime.Parse(SendOnDate + " " + SendOnTime).AddMinutes(UtcOffset);

        // Ensure we have a valid SendOn date
        if (!TimedTweetValidation.ValidateFutureDate(tweet.SendOn))
        {
            ModelState.AddModelError("SendOn", "The scheduled time should be in the future.");
        }

        if (this.TryUpdateModel(tweet, new string[] { "Status" }, collection.ToValueProvider()) && ModelState.IsValid)
        {
            // ...
            Repository.Insert(this.User.Identity.Name, tweet);

            return RedirectToAction("Index");
        }
        else
        {
            // ...
            return View(tweet);
        }
    }
    catch
    {
        // ...
        return View(tweet);
    }
}

[/code]

As you can see, we’re doing the regular GET/POST differentiation here: GET to show the Create view, POST to actually do something with user-entered data. Nothing too fancy in the code, just passing some data to the repository we created in an earlier post.

Views

The view for our Create action is slightly more work. I’ve noticed a cool date picker at http://ui.jquery.com, and a cool time picker at http://haineault.com/media/jquery/ui-timepickr/page/. Why not use them both?

Here’s the plain, simple view, no jQuery used:

[code:c#]

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<TwitterMatic.Shared.Domain.TimedTweet>" %>

<asp:Content ID="title" ContentPlaceHolderID="TitleContent" runat="server">
    Schedule a tweet for <%=User.Identity.Name%>
</asp:Content>

<asp:Content ID="content" ContentPlaceHolderID="MainContent" runat="server">
    <h3>Schedule a tweet for <%=User.Identity.Name%></h3>

    <% if (!ViewData.ModelState.IsValid) { %>
        <%= Html.ValidationSummary("Could not schedule tweet. Please correct the errors and try again.") %>
    <% } %>

    <% using (Html.BeginForm()) {%>
        <%=Html.Hidden("UtcOffset", 0)%>

        <fieldset>
            <legend>Schedule a tweet</legend>
            <p>
                <label for="Status">Message:</label>
                <%= Html.TextArea("Status") %>
                <%= Html.ValidationMessage("Status", "*") %>
                <span id="status-chars-left">140</span>
            </p>
            <p>
                <label for="SendOn">Send on:</label>
                <%= Html.TextBox("SendOnDate", ViewData["SendOnDate"]) %>
                <%= Html.TextBox("SendOnTime", ViewData["SendOnTime"]) %>
            </p>
            <p>
                <button type="submit" value="Schedule">
                    Schedule tweet!
                </button>
            </p>
        </fieldset>

    <% } %>

    <p style="clear: both;">
        <%= Html.ActionLink("Back to list of scheduled tweets", "Index", "Tweet", null, new { @class = "more" })%>
    </p>
    <p style="clear: both;">&nbsp;</p>
</asp:Content>

[/code]

Nothing fancy in there: just a boring data-entry form. Now let’s spice that one up: we’ll add the datepicker and timepicker:

[code:c#]

<script type="text/javascript">
    $(function() {
        $("#SendOnDate").datepicker({ minDate: 0, showAnim: 'slideDown', dateFormat: 'mm/dd/yy' });
        $("#SendOnTime").timepickr({ convention: 12, rangeMin: ['00', '05', '10', '15', '20', '25', '30', '35', '40', '45', '50', '55'] });
    });
</script>

[/code]

We’re telling jQuery to make a datepicker of the DOM element with id #SendOnDate, and to make a timepickr of the element #SendOnTime. Now let’s add some more useful things:

[code:c#]

<script type="text/javascript">
    $(function() {
        calcCharsLeft();
        $("#Status").keyup(function() {
            calcCharsLeft();
        });
    });

    var calcCharsLeft = function() {
        var charsLeft = (140 - $("#Status").val().length);

        $("#status-chars-left").html(charsLeft);
        if (charsLeft < 0) {
            $("#status-chars-left").css('color', 'red');
            $("#status-chars-left").css('font-weight', 'bold');
        } else {
            $("#status-chars-left").css('color', 'white');
            $("#status-chars-left").css('font-weight', 'normal');
        }
    }
</script>

[/code]

jQuery will now do some more things when the page has loaded: we’re telling the browser to call calcCharsLeft every time a key is pressed in the message text area. This way, we can add a fancy character counter next to the text box, which receives different colors when certain amount of text is entered.

Validation: using DataAnnotations

In the action methods listed earlier in this post, you may have noticed that we are not doing a lot of validation checks. Except for the “Time in the future” check, we’re actually not doing any validation at all!

The reason for not having any validation calls in my controller’s action method, is that I’m using a different model binder than the default one: the ASP.NET MVC team’s DataAnnotationsModelBinder. This model binder makes use of the System.ComponentModel.DataAnnotations namespace to perform validation at the moment of binding data to the model. This concept was used for ASP.NET Dynamic Data, recently picked up by the RIA services team and now also available for ASP.NET MVC.

Basically, what we have to do, is decorating our TimedTweet class’ properties with some DataAnnotations:

[code:c#]

public class TimedTweet : TableStorageEntity, IComparable
{
    public string Token { get; set; }
    public string TokenSecret { get; set; }

    [Required(ErrorMessage = "Twitter screen name is required.")]
    public string ScreenName { get; set; }

    [Required(ErrorMessage = "Message is required.")]
    [StringLength(140, ErrorMessage = "Message length must not exceed 140 characters.")]
    public string Status { get; set; }

    [Required(ErrorMessage = "A scheduled time is required.")]
    [CustomValidation(typeof(TimedTweetValidation), "ValidateFutureDate", ErrorMessage = "The scheduled time should be in the future.")]
    public DateTime SendOn { get; set; }
    public DateTime SentOn { get; set; }
    public string SendStatus { get; set; }
    public int RetriesLeft { get; set; }

    public bool Archived { get; set; }

    // ...
}

[/code]

See how easy this is? Add a [Required] attribute to make a property required. Add a [StringLength] attribute to make sure a certain length is not crossed, … The DataAnnotationsModelBinder will use these hints as a guide to perform validation on your model.

Conclusion

We now know how to work with ASP.NET MVC’s future DataAnnotations validation and have implemented this in TwitterMatic.

In the next part of this series, we’ll have a look at the worker role for TwitterMatic.

kick it on DotNetKicks.com

How we built TwitterMatic.net - Part 4: Authentication and membership

TwitterMatic - Schedule your Twitter updates“Knight Maarten The Brave Coffeedrinker just returned from his quest to a barn in the clouds, when he discovered that he forgot to lock the door to his workplace. He immediately asked the digital village’s smith.to create a lock and provide him a key. Our knight returned to his workplace and concluded that using the smith’s lock would be OK, but having the great god of social networking, Twitter, as a guardian, seemed like a better idea. “O, Auth!”, he said. And the god provided him with a set of prayers, an API, which our knight could use.”

This post is part of a series on how we built TwitterMatic.net. Other parts:

kick it on DotNetKicks.com

Authentication and membership

Why reinvent the wheel when we already have so many wheel manufacturers? I’m really convinced that from now on, nobody should EVER provide subscribe/login/password retrieval/… functionality to his users again! Use OpenID, or Live ID, or Google Accounts, or JanRain’s RPX bundling all types of existing authentication mechanisms. Did you hear me? NEVER provide your own authentication mechanism again, unless you have a solid reason for it!

Since we’re building an application for Twitter, and Twitter provides OAuth API for delegating authentication, why not use OAuth? As a start, here’s the flow that has to be respected when working with OAuth.

OAuth request flow diagram

Now let’s build this in to our application…

Implementing OAuth in TwitterMatic

First of all: if you are developing something and it involves a third-party product or service, chances are there’s something useful for you on CodePlex. In TwitterMatic’s case, that useful tool is LINQ to Twitter, providing OAuth implementation as well as a full API to the Twitter REST services. Thank you, JoeMayo!

The only thing we still have to do in order for TwitterMatic OAuth authentication to work, is create an AccountController in the web role project. Let’s start with a Login action method:

[code:c#]

IOAuthTwitter oAuthTwitter = new OAuthTwitter();
oAuthTwitter.OAuthConsumerKey = configuration.ReadSetting("OAuthConsumerKey");
oAuthTwitter.OAuthConsumerSecret = configuration.ReadSetting("OAuthConsumerSecret");

if (string.IsNullOrEmpty(oauth_token)) {
    // Not authorized. Redirect to Twitter!
    string loginUrl = oAuthTwitter.AuthorizationLinkGet(
        configuration.ReadSetting("OAuthRequestTokenUrl"),
        configuration.ReadSetting("OAuthAuthorizeTokenUrl"),
        false,
        true
    );
    return Redirect(loginUrl);
}

[/code]

Our users will now be redirected to Twitter in order to authenticate, if the method receives an empty or invalid oauth-token. If we however do retrieve a valid token, we’ll use FormsAuthentication cookies to keep the user logged in on TwitterMatic as well. Note that we are also saving the authentication token as the user’s password, we’ll be needing this same token to post updates afterwards.

[code:c#]

// Should be authorized. Get the access token and secret.
string userId = "";
string screenName = "";

oAuthTwitter.AccessTokenGet(oauth_token, configuration.ReadSetting("OAuthAccessTokenUrl"),
    out screenName, out userId);
if (oAuthTwitter.OAuthTokenSecret.Length > 0)
{
    // Store the user in membership
    MembershipUser user = Membership.GetUser(screenName);
    if (user == null)
    {
        MembershipCreateStatus status = MembershipCreateStatus.Success;
        user = Membership.CreateUser(
            screenName,
            oAuthTwitter.OAuthToken + ";" + oAuthTwitter.OAuthTokenSecret,
            screenName, 
            "twitter", 
            "matic",
            true,
            out status);
    }

    // Change user's password
    user.ChangePassword(
        user.GetPassword("matic"),
        oAuthTwitter.OAuthToken + ";" + oAuthTwitter.OAuthTokenSecret
    );
    Membership.UpdateUser(user);

    // All is well!
    FormsAuthentication.SetAuthCookie(screenName, true);
    return RedirectToAction("Index", "Home");
}
else
{
    // Not OK...
    return RedirectToAction("Login");
}

[/code]

Here’s the full code to AccountController:

[code:c#]

[HandleError]
public class AccountController : Controller
{
    protected IConfigurationProvider configuration;

    public ActionResult Login(string oauth_token)
    {
        IOAuthTwitter oAuthTwitter = new OAuthTwitter();
        oAuthTwitter.OAuthConsumerKey = configuration.ReadSetting("OAuthConsumerKey");
        oAuthTwitter.OAuthConsumerSecret = configuration.ReadSetting("OAuthConsumerSecret");

        if (string.IsNullOrEmpty(oauth_token)) {
            // Not authorized. Redirect to Twitter!
            string loginUrl = oAuthTwitter.AuthorizationLinkGet(
                configuration.ReadSetting("OAuthRequestTokenUrl"),
                configuration.ReadSetting("OAuthAuthorizeTokenUrl"),
                false,
                true
            );
            return Redirect(loginUrl);
        } else {
            // Should be authorized. Get the access token and secret.
            string userId = "";
            string screenName = "";

            oAuthTwitter.AccessTokenGet(oauth_token, configuration.ReadSetting("OAuthAccessTokenUrl"),
                out screenName, out userId);
            if (oAuthTwitter.OAuthTokenSecret.Length > 0)
            {
                // Store the user in membership
                MembershipUser user = Membership.GetUser(screenName);
                if (user == null)
                {
                    MembershipCreateStatus status = MembershipCreateStatus.Success;
                    user = Membership.CreateUser(
                        screenName,
                        oAuthTwitter.OAuthToken + ";" + oAuthTwitter.OAuthTokenSecret,
                        screenName,
                        configuration.ReadSetting("OAuthConsumerKey"),
                        configuration.ReadSetting("OAuthConsumerSecret"),
                        true,
                        out status);
                }

                // Change user's password
                user.ChangePassword(
                    user.GetPassword(configuration.ReadSetting("OAuthConsumerSecret")),
                    oAuthTwitter.OAuthToken + ";" + oAuthTwitter.OAuthTokenSecret
                );
                Membership.UpdateUser(user);

                // All is well!
                FormsAuthentication.SetAuthCookie(screenName, true);
                return RedirectToAction("Index", "Home");
            }
            else
            {
                // Not OK...
                return RedirectToAction("Login");
            }
        }
    }

    public ActionResult Logout()
    {
        FormsAuthentication.SignOut();
        return RedirectToAction("Index", "Home");
    }
}

[/code]

Using ASP.NET provider model

The ASP.NET provider model provides abstractions for features like membership, roles, sessions, … Since we’ll be using membership to store authenticated users, we’ll need a provider that works with Windows Azure Table Storage. The Windows Azure SDK samples contain a project '”AspProviders”. Reference it, and add the following to your web.config:

[code:xml]

<?xml version="1.0"?>
<configuration>
  <!-- ... -->

  <appSettings>
    <add key="DefaultMembershipTableName" value="Membership" />
    <add key="DefaultRoleTableName" value="Roles" />
    <add key="DefaultSessionTableName" value="Sessions" />
    <add key="DefaultProviderApplicationName" value="TwitterMatic" />
    <add key="DefaultProfileContainerName" />
    <add key="DefaultSessionContainerName" />
  </appSettings>

  <connectionStrings />

  <system.web>
    <!-- ... -->

    <authentication mode="Forms">
      <forms loginUrl="~/Account/Login" />
    </authentication>

    <membership defaultProvider="TableStorageMembershipProvider">
      <providers>
        <clear/>
        <add name="TableStorageMembershipProvider"
             type="Microsoft.Samples.ServiceHosting.AspProviders.TableStorageMembershipProvider"
             description="Membership provider using table storage"
             applicationName="TwitterMatic"
             enablePasswordRetrieval="true"
             enablePasswordReset="true"
             requiresQuestionAndAnswer="true"
             minRequiredPasswordLength="1"
             minRequiredNonalphanumericCharacters="0"
             requiresUniqueEmail="false"
             passwordFormat="Clear" />
      </providers>
    </membership>

    <profile enabled="false" />

    <roleManager enabled="true" defaultProvider="TableStorageRoleProvider" cacheRolesInCookie="false">
      <providers>
        <clear/>
        <add name="TableStorageRoleProvider"
             type="Microsoft.Samples.ServiceHosting.AspProviders.TableStorageRoleProvider"
             description="Role provider using table storage"
             applicationName="TwitterMatic" />
      </providers>
    </roleManager>

    <sessionState mode="Custom" customProvider="TableStorageSessionStateProvider">
      <providers>
        <clear />
        <add name="TableStorageSessionStateProvider"
             type="Microsoft.Samples.ServiceHosting.AspProviders.TableStorageSessionStateProvider"
             applicationName="TwitterMatic" />
      </providers>
    </sessionState>

    <!-- ... -->
  </system.web>

  <!-- ... -->
</configuration>

[/code]

TwitterMatic should now be storing sessions (if we were to use them), membership and roles in the cloud, by just doing some configuration magic. I love ASP.NET for this!

Conclusion

We now know how to leverage third-party authentication (OAuth in our case) and have implemented this in TwitterMatic.

In the next part of this series, we’ll have a look at the ASP.NET MVC front end and how we can validate user input before storing it in our database.

kick it on DotNetKicks.com

How we built TwitterMatic.net - Part 3: Store data in the cloud

TwitterMatic - Schedule your Twitter updates “After setting up his workplace, knight Maarten The Brave Coffeedrinker thought of something else: if a farmer wants to keep a lot of hay, he needs a barn, right? Since the cloudy application would also need to keep things that can be used by the digital villagers, our knight needs a barn in the clouds. Looking at the azure sky, an idea popped into the knight’s head: why not use Windows Azure storage service? It’s a barn that’s always there, a barn that can catch fire and will still have its stored items located in a second barn (and a third). Knight Maarten The Brave Coffeedrinker jumped on his horse and went on a quest, a quest in the clouds.

This post is part of a series on how we built TwitterMatic.net. Other parts:

kick it on DotNetKicks.com

Store data in the cloud

Windows Azure offers 3 types of cloud storage: blobs, tables and queues. Blob Storage stores sets of binary data, organized in containers of your storage account. Table Storage offers structured storage in the form of tables. The Queue service stores an unlimited number of messages, each of which can be up to 8 KB in size.

Let’s look back at the TwitterMatic architecture:

“The worker role will monitor the table storage for scheduled Tweets. If it’s time to send them, the Tweet will be added to a queue. This queue is then processed by another thread in the worker role, which will publish the Tweet to Twitter. ”

This means we’ll be using two out of three storage types: Table Storage and Queue Storage. Problem: these services are offered as a RESTful service, somewhere in the cloud. Solution to that: use the StorageClient project located in the Windows Azure SDK’s samples directory!

The StorageClient project contains .NET API’s that work with the blob, table and queue storage services provided by Windows Azure. For Table Storage, StorageClient provides an extension on top of Astoria (ADO.NET Data Services Framework, but I still say Astoria because it’s easier to type and say…). This makes it easier for you as a developer to use existing knowledge, from LINQ to SQL or Entity Framework or Astoria, to develop Windows Azure applications.

Setting up StorageClient

Add a reference to the StorageClient project to your WebRole project. Next, add some settings to the ServiceConfiguration.cscfg file:

[code:xml]

<?xml version="1.0"?>
<ServiceConfiguration serviceName="TwitterMatic" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceConfiguration">
  <Role name="WebRole">
    <Instances count="1"/>
    <ConfigurationSettings>
      <Setting name="AccountName" value="twittermatic"/>
      <Setting name="AccountSharedKey" value=”..."/>
      <Setting name="BlobStorageEndpoint" value="http://blob.core.windows.net"/>
      <Setting name="QueueStorageEndpoint" value = "http://queue.core.windows.net"/>
      <Setting name="TableStorageEndpoint" value="http://table.core.windows.net"/>
      <Setting name="allowInsecureRemoteEndpoints" value="true"/>
    </ConfigurationSettings>
  </Role>
  <Role name="WorkerRole">
    <Instances count="1"/>
    <ConfigurationSettings>
      <Setting name="AccountName" value="twittermatic"/>
      <Setting name="AccountSharedKey" value=”..."/>
      <Setting name="BlobStorageEndpoint" value="http://blob.core.windows.net"/>
      <Setting name="QueueStorageEndpoint" value = "http://queue.core.windows.net"/>
      <Setting name="TableStorageEndpoint" value="http://table.core.windows.net"/>
      <Setting name="allowInsecureRemoteEndpoints" value="true"/>
    </ConfigurationSettings>
  </Role>
</ServiceConfiguration>

[/code]

This way, both the web and worker role know where to find their data (URI) and how to authenticate (account name and shared key).

Working with tables

We’ll only be using one domain class in our entire project: TimedTweet. This class represents a scheduled Twitter update, containing information required to schedule the update. Here’s a list of properties:

  • Token: A token used to authenticate against Twitter
  • TokenSecret: A second token used to authenticate against Twitter
  • ScreenName: Twitter screen name of the user.
  • Status: The message to publish on Twitter.
  • SendOn: Time to send the message.
  • SentOn: Time the message was sent.
  • SendStatus: A status message (pending, in progress, published, …)
  • RetriesLeft: How many retries left before giving up on the Twitter update.
  • Archived: Yes/no if the message is archived.

Here’s the code:

[code:c#]

public class TimedTweet : TableStorageEntity, IComparable
{
    public string Token { get; set; }
    public string TokenSecret { get; set; }
    public string ScreenName { get; set; }
    public string Status { get; set; }
    public DateTime SendOn { get; set; }
    public DateTime SentOn { get; set; }
    public string SendStatus { get; set; }
    public int RetriesLeft { get; set; }
    public bool Archived { get; set; }

    public TimedTweet()
        : base()
    {
        SendOn = DateTime.Now.ToUniversalTime();
        Timestamp = DateTime.Now;
        RowKey = Guid.NewGuid().ToString();
        SendStatus = "Scheduled";
        RetriesLeft = 3;
    }

    public TimedTweet(string partitionKey, string rowKey)
        : base(partitionKey, rowKey)
    {
        SendOn = DateTime.Now.ToUniversalTime();
        SendStatus = "Scheduled";
        RetriesLeft = 3;
    }

    public int CompareTo(object obj)
    {
        TimedTweet target = obj as TimedTweet;
        if (target != null) {
            return this.SendOn.CompareTo(target.SendOn);
        }
        return 0;
    }
}

[/code]

Note that our TimedTweet is inheriting TableStorageEntity. This class provides some base functionality for Windows Azure Table Storage.

We’ll also need to work with this class against table Storage. For that, we can use TableStorage and TableStorageDataServiceContext class, like this:

[code:c#]

public List<TimedTweet> RetrieveAllForUser(string screenName) {
    StorageAccountInfo info = StorageAccountInfo.GetDefaultTableStorageAccountFromConfiguration(true);
    TableStorage storage = TableStorage.Create(info);

    storage.TryCreateTable("TimedTweet");

    TableStorageDataServiceContext svc = storage.GetDataServiceContext();
    svc.IgnoreMissingProperties = true;

    List<TimedTweet> result = svc.CreateQuery<TimedTweet>("TimedTweet").Where(t => t.ScreenName ==     screenName).ToList();
    foreach (var item in result)
    {
        svc.Detach(item);
    }
    return result;
}

[/code]

Using this, we can build a repository class based on ITimedtweetRepository and implemented against Table Storage:

[code:c#]

public interface ITimedTweetRepository
{
    void Delete(string screenName, TimedTweet tweet);
    void Archive(string screenName, TimedTweet tweet);
    void Insert(string screenName, TimedTweet tweet);
    void Update(TimedTweet tweet);
    List<TimedTweet> RetrieveAll(string screenName);
    List<TimedTweet> RetrieveDue(DateTime dueDate);
    TimedTweet RetrieveById(string screenName, string id);
}

[/code]

Working with queues

Queues are slightly easier to work with: you can enqueue messages and dequeue messages, and that’s about it. Here’s an example snippet:

[code:c#]

public void EnqueueMessage(string message) {
    StorageAccountInfo info = StorageAccountInfo.GetDefaultTableStorageAccountFromConfiguration(true);

    QueueStorage queueStorage = QueueStorage.Create(info);
    MessageQueue updateQueue = queueStorage.GetQueue("updatequeue");
    if (!updateQueue.DoesQueueExist())
        updateQueue.CreateQueue();

    updateQueue.PutMessage(new Message(message));
}

Conclusion

[/code]

We now know how to use StorageClient and have a location in the cloud to store our data.

In the next part of this series, we’ll have a look at how we can leverage Twitter’s OAuth authentication mechanism in our own TwiterMatic application and make use of some other utilities packed in the Windows Azure SDK.

kick it on DotNetKicks.com

How we built TwitterMatic.net - Part 2: Creating an Azure project

TwitterMatic - Schedule your Twitter updates “Knight Maarten The Brave Coffeedrinker was about to start working on his TwitterMatic application, named after the great god of social networking, Twitter. Before he could start working, he first needed the right tools. He downloaded the Windows Azure SDK, a set of tools recommended by the smith (or was it the carpenter?) of the digital village. Our knight’s work shack was soon ready to start working. The table on which the application would be crafted, was still empty. Time for action, the knight thought. And he started working.”

This post is part of a series on how we built TwitterMatic.net. Other parts:

kick it on DotNetKicks.com

Creating an Azure project

Here we go. After installing the Windows Azure SDK, start a new project: File > New > Project... Next, add a “Web And Worker Cloud Service”, located under Visual C# (or VB...) > Cloud Service > Web And Worker Cloud Service.

The project now contains three projects:

TwitterMatic Visual Studio Solution

The web role project should be able to run ASP.NET, but not yet ASP.NET MVC. Add /Views, /Controllers, Global.asax.cs, references to System.Web.Mvc, System.Web.Routing, … to make the web role an ASP.NET MVC project. This should work out fine, except for the tooling in Visual Studio. To enable ASP.NET MVC tooling, open the TwitterMatic_WebRole.csproj file using Notepad and add the following: (copy-paste: {603c0e0b-db56-11dc-be95-000d561079b0})

Enable ASP.NET MVC tooling in Windows Azure project

Visual Studio will prompt to reload the project, allow this by clicking the "Reload" button.

(Note: we could have also created a web role from an ASP.NET MVC project, check my previous series on Azure to see how to do this.)

Sightseeing

True, I ran over the previous stuff a bit fast, as it is just plumbing ASP.NET MVC in a Windows Azure Web Role. I have written about this in more detail before, check my previous series on Azure. There are some other things I would like to show you though.

The startup project in a Windows Azure solution contains some strange stuff:

TwitterMatic startup project

This project contains 3 types of things: the roles in the application, a configuration file and a file describing the required configuration entries. The “Roles” folder contains a reference to the projects that perform the web role on one side, the worker role on the other side. ServiceConfiguration.cscfg contains all configuration entries for the web and worker roles, such as the number of instances (yes, you can add more servers simply by putting a higher integer in there!) and other application settings such as storage account info.

ServiceDefinition.csdef is basically a copy of the other config file, but without the values. If you are wondering why: if someone screws with ServiceConfiguration.cscfg when deploying the application to Windows Azure, the deployment interface will know that there are settings missing or wrong. Perfect if someone else will be doing deployment to production!

Conclusion

We now have a full Windows Azure solution, with ASP.NET MVC enabled! Time to grab a prefabricated CSS theme, add a logo, … Drop-in ASP.NET MVC themes can always be found at http://www.asp.net/mvc/gallery. The theme we used should be there as well (thanks for that, theme author!).

In the next part of this series, we’ll have a look at where and how we can store our data.

kick it on DotNetKicks.com

How we built TwitterMatic.net - Part 1: Introduction

TwitterMatic “Once upon a time, Microsoft started a Windows Azure developing contest named new CloudApp();. While it first was only available for US candidates, the contest was opened for international submissions too. Knight Maarten The Brave Coffeedrinker and his fellow knightsmen at RealDolmen decided to submit a small sample application that could be hosted in an unknown environment, known by the digital villagers as “the cloud”. The application was called TwitterMatic, named after the great god of social networking, Twitter. It would allow digital villagers to tell the latest stories, even when they were asleep or busy working.”

There, a nice fairy tale :-) It should describe the subject of a blog post series that I am starting around the techncal background of TwitterMatic, our contest entry for the new CloudApp(); contest. Now don't forget to vote for us between 10 July and 20 July!

Some usage scenario’s for TwitterMatic:

  • Inform your followers about interesting links at certain times of day
  • Stay present on Twitter during your vacation
  • Maintain presence in your activity stream, even when you are not available
  • Never forget a fellow Twitterer's birthday: schedule it!
  • Trick your boss: of course you are Tweeting you're leaving the office at 8 PM!

Perfect excuses to build our application for the clouds. Now for something more interesting: the technical side!

If you are impatient and immediately want the source code for TwitterMatic, check http://twittermatic.codeplex.com.

kick it on DotNetKicks.com

TwitterMatic architectural overview

Since we’re building a demo application, we thought: why not make use of as much features as possible which Windows Azure has to offer? We’re talking web role, worker role, table storage and queue storage here!

  • The web role will be an application built in ASP.NET MVC, allowing you to schedule new Tweets and view archived scheduled Tweets.
  • The worker role will monitor the table storage for scheduled Tweets. If it’s time to send them, the Tweet will be added to a queue. This queue is then processed by another thread in the worker role, which will publish the Tweet to Twitter.
  • We’ll be using OAuth, delegating authentication for TwitterMatic to Twitter itself. This makes it easier for us: no need to store credentials, no need to maintain a user database, …
  • The web role will perform validation of the domain using data annotations. More on this in one of the next parts.

For people who like images, here’s an architecture image:

TwitterMatic Architecture

What’s next?

RealDolmen Windows Azure The next parts of this series around Windows Azure will be focused on the following topics:

Stay tuned during the coming weeks! And don’t forget to start scheduling Tweets using TwitterMatic.

kick it on DotNetKicks.com

Announcing: Azure User Group Belgium

After the summer, I'll be joining Kurt Claeys and Yves Goeleven and for the start of the Azure User Group Belgium (or AZUG.BE).  AZUG.BE is a Belgian user group with focus on development and architecture of the Microsoft Azure Services Platform. Azure is a cloud hosted development platform for internet oriented applications aimed at high scalability and based on .NET technology.

azuglogo

Our goal is to share knowledge and experiences with the .NET community in development and architecture in the Azure Services Platform and the .NET Services technology. As new programming skills and a new architectural approach are needed we are inviting you to become member of this user group to prepare you for building this new style of applications.

We’ll have regular public meetings (free of charge) providing demos, training, presentations and discussions on the technology part of Azure. First public meeting is planned in september 09. E-mail us at newsletter@azug.be with the word subscribe as subject. We’ll inform you on what, when and where.

Interested in participating as boardmember ? Shoot a mail at board@azug.be.

Hope to see you!

Application-wide action filters in ASP.NET MVC

Ever had a team of developers using your ASP.NET MVC framework? Chances are you have implemented some action filters (i.e. for logging) which should be applied on all controllers in the application. Two ways to do this: kindly ask your developers to add a [Logging] attribute to the controllers they write, or kindly ask to inherit from SomeCustomControllerWithActionsInPlace.

If you have been in this situation, monday mornings, afternoons, tuesdays and other weekdays are in fact days where some developers will forget to do one of the above. This means no logging! Or any other action filters that are executed due to a developer that has not been fed with enough coffee… Wouldn’t it be nice to have a central repository where you can register application-wide action filters? That’s exactly what we are going to do in this blog post.

Note: you can in fact use a dependency injection strategy for this as well, see Jeremy Skinner’s blog.

Download the example code: MvcGlobalActionFilter.zip (24.38 kb)

kick it on DotNetKicks.com

The idea

Well, all things have to start with an idea, otherwise there’s nothing much left to do. What we’ll be doing in our solution to global action filters is the following:

  1. Create a IGlobalFilter interface which global action filters have to implement. You can discuss about this, but I think it’s darn handy to add some convenience methods like ShouldBeInvoked() where you can abstract away some checks before the filter is actually invoked.
  2. Create some IGlobalActionFilter, IGlobalResultFilter, IGlobalAuthorizationFilter, IGlobalExceptionFilter interfaces, just for convenience to the developer that is creating the global filters. You’ll see the use of this later on.
  3. Create a GlobalFilterActionInvoker, a piece of logic that is set on each controller so the controller knows how to call its own action methods. We’ll use this one to inject our lists of global filters.
  4. Create a GlobalFilterControllerFactory. I’m not happy with this, but we need it to set the GlobalFilterActionInvoker instance on each controller when it is created.

IGlobalFilter, IGlobalActionFilter, …

Not going to spend too much time on these. Actually, these interfaces are just descriptors for our implementation so it knows what type of filter is specified and if it should be invoked. Here’s a bunch of code. No comments.

[code:c#]

public interface IGlobalFilter
{
    bool ShouldBeInvoked(ControllerContext controllerContext);
}

public interface IGlobalAuthorizationFilter : IGlobalFilter, IAuthorizationFilter

public interface IGlobalActionFilter : IGlobalFilter, IActionFilter { }

public interface IGlobalResultFilter : IGlobalFilter, IResultFilter { }

public interface IGlobalExceptionFilter : IGlobalFilter, IExceptionFilter { }

[/code]

And yes, I did suppress some Static Code Analysis rules for this :-)

GlobalFilterActionInvoker

The GlobalFilterActionInvoker will take care of registering the global filters and making sure each filter is actually invoked on every controller and action method in our ASP.NET MVC application. Here’s a start for our class:

[code:c#]

public class GlobalFilterActionInvoker : ControllerActionInvoker
{
    protected FilterInfo globalFilters;

    public GlobalFilterActionInvoker()
    {
        globalFilters = new FilterInfo();
    }

    public GlobalFilterActionInvoker(FilterInfo filters)
    {
        globalFilters = filters;
    }

    public GlobalFilterActionInvoker(List<IGlobalFilter> filters)
        : this(new FilterInfo())
    {
        foreach (var filter in filters)
            RegisterGlobalFilter(filter);
    }

    public FilterInfo Filters
    {
        get { return globalFilters; }
    }

    // - more code -

}

[/code]

We’re providing some utility constructors that take a list of global filters and add it to the internal FilterInfo instance (which is an ASP.NET MVC class we can leverage in here). RegisterGlobalFilter() will do the magic of adding filters to the right collection in the FilterInfo instance.

[code:c#]

public void RegisterGlobalFilter(IGlobalFilter filter)
{
    if (filter is IGlobalAuthorizationFilter)
        globalFilters.AuthorizationFilters.Add((IGlobalAuthorizationFilter)filter);

    if (filter is IGlobalActionFilter)
        globalFilters.ActionFilters.Add((IGlobalActionFilter)filter);

    if (filter is IGlobalResultFilter)
        globalFilters.ResultFilters.Add((IGlobalResultFilter)filter);

    if (filter is IGlobalExceptionFilter)
        globalFilters.ExceptionFilters.Add((IGlobalExceptionFilter)filter);
}

[/code]

One override left in our implementation: ControllerActionInvoker, the class we are inheriting from, provides a method named GetFilters(), which is used to get the filters for a specific controller context. Ideal one to override:

[code:c#]

protected override FilterInfo GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
{
    FilterInfo definedFilters = base.GetFilters(controllerContext, actionDescriptor);

    foreach (var filter in Filters.AuthorizationFilters)
    {
        IGlobalFilter globalFilter = filter as IGlobalFilter;
        if (globalFilter == null ||
            (globalFilter != null && globalFilter.ShouldBeInvoked(controllerContext)))
        {
            definedFilters.AuthorizationFilters.Add(filter);
        }
    }

    // - same for action filters -

    // - same for result filters -

    // - same for exception filters -

    return definedFilters;
}

[/code]

Basically, we are querying our IGlobalFilter if it should be invoked for the given controller context. If so, we add it to the FilterInfo object that is required by the ControllerActionInvoker base class. Piece of cake!

GlobalFilterControllerFactory

I’m not happy having to create this one, but we need it to set the GlobalFilterActionInvoker instance on each controller that is created. Otherwise, there is no way to specify our global filters on a controller or action method… Here’s the class:

[code:c#]

public class GlobalFilterControllerFactory : DefaultControllerFactory
{
    protected GlobalFilterActionInvoker actionInvoker;

    public GlobalFilterControllerFactory(GlobalFilterActionInvoker invoker)
    {
        actionInvoker = invoker;
    }

    public override IController CreateController(System.Web.Routing.RequestContext requestContext, string controllerName)
    {
        IController controller = base.CreateController(requestContext, controllerName);
        Controller controllerInstance = controller as Controller;
        if (controllerInstance != null)
        {
            controllerInstance.ActionInvoker = actionInvoker;
        }
        return controller;
    }
}

[/code]

What we do here is let the DefaultControllerFactory create a controller. Next, we simply set the controller’s ActionInvoker property to our GlobalFilterActionInvoker .

Plumbing it all together!

To plumb things together, add some code in your Global.asax.cs class, under Application_Start:

[code:c#]

protected void Application_Start()
{
    RegisterRoutes(RouteTable.Routes);

    ControllerBuilder.Current.SetControllerFactory(
        new GlobalFilterControllerFactory(
            new GlobalFilterActionInvoker(
                new List<IGlobalFilter>
                {
                    new SampleGlobalTitleFilter()
                }
            )
        )
    );
}

[/code]

We are now setting the controller factory for our application to GlobalFilterControllerFactory, handing it a GlobalFilterActionInvoker which specifies one global action filter: SampleGlobalTitleFilter.

Sidenote: SampleGlobalTitleFilter

As a sidenote, I created a sample result filter named SampleGlobalTitleFilter, which is defined as a global filter that always appends a string (“ – Sample Application”) to the page title. Here’s the code for that one:

[code:c#]

public class SampleGlobalTitleFilter : IGlobalResultFilter
{
    public bool ShouldBeInvoked(System.Web.Mvc.ControllerContext controllerContext)
    {
        return true;
    }

    public void OnResultExecuted(System.Web.Mvc.ResultExecutedContext filterContext)
    {
        return;
    }

    public void OnResultExecuting(System.Web.Mvc.ResultExecutingContext filterContext)
    {
        if (filterContext.Controller.ViewData["PageTitle"] == null)
            filterContext.Controller.ViewData["PageTitle"] = "";

        string pageTitle = filterContext.Controller.ViewData["PageTitle"].ToString();

        if (!string.IsNullOrEmpty(pageTitle))
            pageTitle += " - ";

        pageTitle += "Sample Application";

        filterContext.Controller.ViewData["PageTitle"] = pageTitle;
    }
}

[/code]

Conclusion

Download the sample code: MvcGlobalActionFilter.zip (24.38 kb)

There is no need for my developers to specify SampleGlobalTitleFilter on each controller they write. There is no need for my developers to use the ControllerWithTitleFilter base class. People can come in and even develop software without drinking 2 liters of coffee! Really, development should not be hard for your developers. Make sure all application-wide infrastructure is there and our people are ready to go. And I’m really loving ASP.NET MVC’s extensibility on that part!

kick it on DotNetKicks.com

Revised: ASP.NET MVC and the Managed Extensibility Framework (MEF)

A while ago, I did a blog post on combining ASP.NET MVC and MEF (Managed Extensibility Framework), making it possible to “plug” controllers and views into your application as a module. I received a lot of positive feedback as well as a hard question from Dan Swatik who was experiencing a Server Error with this approach… Here’s a better approach to ASP.NET MVC and MEF.

kick it on DotNetKicks.com

The Exception

Server Error

The stack trace was being quite verbose on this one:

InvalidOperationException

The view at '~/Plugins/Views/Demo/Index.aspx' must derive from ViewPage, ViewPage<TViewData>, ViewUserControl, or ViewUserControl<TViewData>.

at System.Web.Mvc.WebFormView.Render(ViewContext viewContext, TextWriter writer) at System.Web.Mvc.ViewResultBase.ExecuteResult(ControllerContext context) at System.Web.Mvc.ControllerActionInvoker.InvokeActionResult(ControllerContext controllerContext, ActionResult actionResult) at System.Web.Mvc.ControllerActionInvoker.<>c__DisplayClass11.<InvokeActionResultWithFilters>b__e() at System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilter(IResultFilter filter, ResultExecutingContext preContext, Func`1 continuation) at System.Web.Mvc.ControllerActionInvoker.<>c__DisplayClass11.<>c__DisplayClass13.<InvokeActionResultWithFilters>b__10() at System.Web.Mvc.ControllerActionInvoker.InvokeActionResultWithFilters(ControllerContext controllerContext, IList`1 filters, ActionResult actionResult) at System.Web.Mvc.ControllerActionInvoker.InvokeAction(ControllerContext controllerContext, String actionName) at System.Web.Mvc.Controller.ExecuteCore() at System.Web.Mvc.ControllerBase.Execute(RequestContext requestContext) at System.Web.Mvc.ControllerBase.System.Web.Mvc.IController.Execute(RequestContext requestContext) at System.Web.Mvc.MvcHandler.ProcessRequest(HttpContextBase httpContext) at System.Web.Mvc.MvcHandler.ProcessRequest(HttpContext httpContext) at System.Web.Mvc.MvcHandler.System.Web.IHttpHandler.ProcessRequest(HttpContext httpContext) at System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)

Our exception seemed to be thrown ONLY when the following conditions were met:

  • The View was NOT located in ~/Views but in ~/Plugins/Views (or other path)
  • The View created in our MEF plugin was strong-typed

Problem one… Forgot to register ViewTypeParserFilter…

Allright, go calling me stupid… Our ~/Plugins/Views folder was not containing the following Web.config file:

[code:c#]

<?xml version="1.0"?>
<configuration>
  <system.web>
    <httpHandlers>
      <add path="*" verb="*"
          type="System.Web.HttpNotFoundHandler"/>
    </httpHandlers>

    <!--
        Enabling request validation in view pages would cause validation to occur
        after the input has already been processed by the controller. By default
        MVC performs request validation before a controller processes the input.
        To change this behavior apply the ValidateInputAttribute to a
        controller or action.
    -->
    <pages
        validateRequest="false"
        pageParserFilterType="System.Web.Mvc.ViewTypeParserFilter, System.Web.Mvc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"
        pageBaseType="System.Web.Mvc.ViewPage, System.Web.Mvc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"
        userControlBaseType="System.Web.Mvc.ViewUserControl, System.Web.Mvc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
      <controls>
        <add assembly="System.Web.Mvc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" namespace="System.Web.Mvc" tagPrefix="mvc" />
      </controls>
    </pages>
  </system.web>

  <system.webServer>
    <validation validateIntegratedModeConfiguration="false"/>
    <handlers>
      <remove name="BlockViewHandler"/>
      <add name="BlockViewHandler" path="*" verb="*" preCondition="integratedMode" type="System.Web.HttpNotFoundHandler"/>
    </handlers>
  </system.webServer>
</configuration>

[/code]

Now why would you need this one anyway? Well: first of all, you do not want your views to expose their source code. Therefore, we add the HttpNotFoundHandler for this folder. Next, we do not want request validation to happen again (because this is already done when invoking the controller). Next: we want the MvcViewTypeParserFilter to be used for enabling strong-typed views (more on this by Phil Haack).

Problem two: MEF’s approach to plugins and ASP.NET’s approach to rendering views…

When compiling a view, ASP.NET dynamically compiles the markup into a temporary assembly, after which it is rendered. This compilation process knows only the assemblies loaded by your web application’s AppDomain. Unfortunately, assemblies loaded by MEF are not available for this compilation process… I went ahead and checked with Reflector if we could do something about this on ASP.NET side: nope. The main classes we need for this are internal :-( The MEF side could be easily tweaked since its source code is available on CodePlex, but… it’s still subject to change and will be included in .NET 4.0 as a framework component, which would limit my customizations a bit for the future.

Now let’s describe this problem as one, simple sentence: we need the MEF plugin assembly loaded in our current AppDomain, available for all other components in the web application.

The solution to this: I want a MEF DirectoryCatalog to monitor my plugins folder and load/unload the assemblies in there dynamically. Loading should be no problem, but unloading… The assemblies will always be locked by my web server’s process! So let’s go for another approach: monitor the plugins folder, copy the new/modified assemblies to the web application’s /bin folder and instruct MEF to load its exports from there. The solution: WebServerDirectoryCatalog. Here’s the code:

[code:c#]

public sealed class WebServerDirectoryCatalog : ComposablePartCatalog
{
    private FileSystemWatcher fileSystemWatcher;
    private DirectoryCatalog directoryCatalog;
    private string path;
    private string extension;

    public WebServerDirectoryCatalog(string path, string extension, string modulePattern)
    {
        Initialize(path, extension, modulePattern);
    }

    private void Initialize(string path, string extension, string modulePattern)
    {
        this.path = path;
        this.extension = extension;

        fileSystemWatcher = new FileSystemWatcher(path, modulePattern);
        fileSystemWatcher.Changed += new FileSystemEventHandler(fileSystemWatcher_Changed);
        fileSystemWatcher.Created += new FileSystemEventHandler(fileSystemWatcher_Created);
        fileSystemWatcher.Deleted += new FileSystemEventHandler(fileSystemWatcher_Deleted);
        fileSystemWatcher.Renamed += new RenamedEventHandler(fileSystemWatcher_Renamed);
        fileSystemWatcher.IncludeSubdirectories = false;
        fileSystemWatcher.EnableRaisingEvents = true;

        Refresh();
    }

    void fileSystemWatcher_Renamed(object sender, RenamedEventArgs e)
    {
        RemoveFromBin(e.OldName);
        Refresh();
    }

    void fileSystemWatcher_Deleted(object sender, FileSystemEventArgs e)
    {
        RemoveFromBin(e.Name);
        Refresh();
    }

    void fileSystemWatcher_Created(object sender, FileSystemEventArgs e)
    {
        Refresh();
    }

    void fileSystemWatcher_Changed(object sender, FileSystemEventArgs e)
    {
        Refresh();
    }

    private void Refresh()
    {
        // Determine /bin path
        string binPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "bin");

        // Copy files to /bin
        foreach (string file in Directory.GetFiles(path, extension, SearchOption.TopDirectoryOnly))
        {
            try
            {
                File.Copy(file, Path.Combine(binPath, Path.GetFileName(file)), true);
            }
            catch
            {
                // Not that big deal... Blog readers will probably kill me for this bit of code :-)
            }
        }

        // Create new directory catalog
        directoryCatalog = new DirectoryCatalog(binPath, extension);
    }

    public override IQueryable<ComposablePartDefinition> Parts
    {
        get { return directoryCatalog.Parts; }
    }

    private void RemoveFromBin(string name)
    {
        string binPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "bin");
        File.Delete(Path.Combine(binPath, name));
    }
}

[/code]

Download the example code

First of all: this was tricky, and the solution to it is also a bit tricky. Use at your own risk!

You can download the example code here: RevisedMvcMefDemo.zip (1.03 mb)

kick it on DotNetKicks.com

ASP.NET MVC TDD using Visual Studio 2010

Phil Haack announced yesterday that the tooling support for ASP.NET MVC is available for Visual Studio 2010. Troy Goode already blogged about the designer snippets (which are really really cool, just like other parts of the roadmap for ASP.NET MVC 2.0). I’ll give the new TDD workflow introduced in VS2010 a take.

kick it on DotNetKicks.com

Creating a new controller, the TDD way

First of all, I’ll create a new ASP.NET MVC application in VS2010. After installing the project template (and the designer snippets if you are cool), this is easy in VS2010:

Step 1

Proceed and make sure to create a unit test project as well.

Next, in your unit test project, add a new unit test class and name it DemoControllerTests.cs.

Step 2Go ahead and start typing the following test:

Step 3Now when you type CTRL-. (or right click the DemoController unknown class), you can pick “Generate other…”:

Step 4A new window will appear,  where you can select the project where you want the new DemoController class to be created. Make sure to enter the MvcApplication project here (and not your test project).

Step 5

Great, that class has been generated. But how about the constructor accepting List<string>? Press CTRL-. and proceed with the suggested action.

Step 6

Continue typing your test and let VS2010 also implement the Index() action method.

Step 7You can now finish the test code:

Step 8The cool thing is: we did not have to go out of our DemoControllerTests.cs editor while writing this test class, while VS2010 took care of stubbing my DemoController in the background:Step 9Run your tests and see it fail. That’s the TDD approach: first make it fail, and then implement what’s needed:

Step 10

If you run your tests  now, you’ll see the test pass.

Conclusion

I like this new TDD approach and ASP.NET MVC! It’s not ReSharper yet, but I think its a fine step that the Visual Studio team has taken.

kick it on DotNetKicks.com