May 18, 2008

Integrated social networking in ASP.NET

This riff is on the concept of baking social networking structures into our tools so that we don't have to re-invent the wheel every time we build an application that wants to do things like tagging, commenting, and communicating with a mash-up API.

Similar to ASP.NET Role, Profile and Membership providers, we should be able to opt in to these features, perhaps in a separate application-agnostic social data store, and be done with it. Here's an example of what I mean.

Using tagging as a simple example, we're talking about a many-tags-to-many-entities relationship where we can effectively tag anything. Our business validation probably includes ensuring that the tag is case insensitive and displayed uniformly in lowercase, that we either create a new tag or retrieve an old one when challenged with a new tag request, and that we trim any leading and trailing whitespace to preserve data integrity. The ORM model could look something like this:

Tagging 
All we're doing here is associating any tag to any entity that is uniquely identifiable. We have a TagType just in case we need to distinguish tags in the future, but that isn't necessary. Let's come up with a way to make use of generic tagging in a separate web application.

using Dimebrain.Biz.Social.Attributes;
using Dimebrain.Biz.Social.Entities;
 
namespace Dimebrain.Demo.Data.Entities
{
    [Taggable]
    public partial class Customer : ISocialEntity
    {
        
    }
}

In the code listing above, we're off in our own specific web application referencing a few namespaces that include an attribute and an interface. The 'Taggable' attribute serves to provide the class with some extension methods at runtime that let us tag customers. To do that, we can use a custom base class within the helper assembly to parse the attributes and provide details to the extension methods:

using System;
using System.Collections.Generic;
using Dimebrain.Biz.Social.Attributes;
using Dimebrain.Data;
using Dimebrain.Extensions;
 
namespace Dimebrain.Biz.Social.Entities
{
    public class SocialEntity : EntityBase, ISocialEntity
    {
        public virtual Guid Id { get; set; }
        public virtual bool IsTaggable { get; private set; }
        public virtual bool IsCommentable { get; private set; }
 
        private ISocialEntity _entity;
 
        public virtual ISocialEntity Entity
        {
            set { _entity = value; }
        }
 
        public SocialEntity()
        {
            //
        }
 
        public SocialEntity(ISocialEntity entity)
        {
            _entity = entity;
            ParseAttributes();
        }
 
        private void ParseAttributes()
        {
            var T = _entity.GetType();
            var attributes = new List<AttributeBase>();
 
            T.GetCustomAttributes(true).ForEach(c => attributes.Add(c as AttributeBase));
            attributes.ForEach(a => IsTaggable |= a is TaggableAttribute,
                               a => IsCommentable |= a is CommentableAttribute);
        }
    }
}

All that's required now is to add a Taggable attribute to a class, implement ISocialEntity (which requires a GUID-based identifier), and we can tag the entity in our social data store using the Tag extension method.

An important consideration for setting a system like this up is just in how you want to reference the social data in the application you're attempting to socialize. For this to work well, we don't want to get in the way of the developer trying to use its, so it's absolutely necessary to avoid asking them to use a base class for their entities. While in this example we are still enforcing a GUID-based identifier in order to associate our tag with an entity, we need to do that with an interface, not a base class, and get out of the way. We can definitely take this concept further (and we will), but for now this is a workable approach. We haven't spent any time dealing with persistence ignorance (in fact we're doing the opposite by pushing a GUID on the user) or inversion of control, nevermind LINQ to SQL's deep entity namespace dependencies (in other words we haven't created sufficient wrappers so our custom application needs to reference the data layer of our helper components just to get access to the extension methods, but hopefully you'll see this as the start of something rather than the end).

using System;
using Dimebrain.Biz.Social.Entities;
 
namespace Dimebrain.Biz.Extensions
{
    public static class Extensions
    {
        public static void Tag(this ISocialEntity entity, string tag)
        {
            var instance = new SocialEntity(entity) { Id = entity.Id };
 
            if(instance.IsTaggable)
            {
                Facade.AddTagToEntity(Facade.CreateOrFetchTag(tag).Id, entity.Id);
            }
            else
            {
                throw new MethodAccessException("Cannot tag an entity that is not declared as taggable.");
            }            
        }
 
        public static void Comment(this ISocialEntity entity, string comment)
        {
            // Persist to the repository
        }
    }
}

In the extension method implementation we'll take the ISocialEntity interface from the user and, since we really only have one entity to map to a concrete class, we can just do that explicitly, creating the concrete instance in the method to wrap the interface; at constructor time, the entity instance is parsed for its attributes. If it passes the 'taggable' test, we open up our internal business layer to perform the tagging task; if it doesn't, we'll let the developer know that they've missed an important declaration in their object model.

From here, there's not much else to it. The tagging story mentioned earlier is realized in the business layer, and we can now decorate our POCO objects with an interface and an attribute to provide social tagging "out of the box", and call a simple extension method to do the heavy lifting.

using System;
using System.Data.Linq;
using System.Linq;
using Dimebrain.Data;
using Dimebrain.Data.Entities;
 
namespace Dimebrain.Biz
{
    public class Facade
    {
        public static Tag CreateOrFetchTag(string tag)
        {
            var db = Database.Context;
            var text = tag.Trim().ToLower();
            var fetch = db.Tags.Where(t => t.Name == text).SingleOrDefault();
 
            return fetch ?? CreateTag(text);
        }
 
        private static Tag CreateTag(string tag)
        {
            var db = Database.Context;
            var text = tag.Trim().ToLower();
            var entity = new Tag{Name = text};
 
            db.Tags.InsertOnSubmit(entity);
            db.SubmitChanges(ConflictMode.FailOnFirstConflict);
 
            return entity;
        }
 
        public static bool AddTagToEntity(Guid tagId, Guid entityId)
        {
            var db = Database.Context;
 
            var map = db.EntityTags.Where(et => et.EntityId == entityId && et.TagId == tagId).SingleOrDefault();
            if (map == null)
            {
                map = new EntityTag{EntityId = entityId, TagId = tagId};
 
                db.EntityTags.InsertOnSubmit(map);
                db.SubmitChanges(ConflictMode.FailOnFirstConflict);
 
                return true;
            }
            return false;
        }
 
        public static bool RemoveTagFromEntity(Guid tagId, Guid entityId)
        {
            var db = Database.Context;
 
            var map = db.EntityTags.Where(et => et.EntityId == entityId && et.TagId == tagId).SingleOrDefault();
            if (map == null)
            {
                return false;
            }
 
            db.EntityTags.DeleteOnSubmit(map);
            db.SubmitChanges(ConflictMode.FailOnFirstConflict);
 
            return true;   
        }
    }
}

Social networking features are a good candidate to suffer from repetition, but we have many options to contain all of them in reusable components.

May 15, 2008

Five takeaways from DevTeach Toronto 2008

  1. Languages are more important than they used to be
    An ASP.NET web developer might intellectually/emotionally resist the surge of new dynamic languages running on the CLR/DLR, but think nothing of switching between HTML, Javascript, SQL, CSS, and C#. Delivering the next generation of experiences requires paying a tax with a slew of new languages: Iron/Ruby, Iron/Python, F#, Boo, Binsor, and the language you write yourself.

  2. True separation of concerns demands that you surrender your ego
    Separating concerns is more than slicing code and inverting dependencies to increase testability and maintainability. It's also about recognizing that there are tools that exist that solve the same problems that your hand-rolled code can solve, but much better. Being open to a variety of tools that solve a variety of challenges makes you a better separator/AOP developer. We can practice "elimination of concerns" and lean on solutions that are already proven.

  3. Achieving tool symmetry through blind vendor bias is not a good thing
    Choosing a set of tools to drive a solution forward by evaluating how well they integrate with each other is a good practice, but your inner compass should dial around if, on paper, the obvious choice appears to be selecting each tool from the same vendor. Often a set of disparate tools with strong minds behind them will integrate as well, or better, than the obvious choice. As an example, in my previous post about .NET startups I recommended LINQ to SQL, which depends on SQL Server 2005. If you don't use SQL Server, and you don't want the overhead of EF for a lean web application, then you will have to look outside the Microsoft eco-system for a solution; and there are greyer scenarios where it's still helpful to look.

  4. There are some brilliant minds working with .NET
    Oren Eini. Ted Neward. James Kovacs. These developers will turn you on your head. If there's a reason to attend a conference it's to be around people like these, and to have the time away from daily development work to simply allow yourself the chance to use the stimulation to propel your own new ideas.

  5. We like to repeat ourselves
    There are plentiful solutions for unit testing, mocking, scaffolding, inversion of control, object-relational mapping, some from large vendors, some open source. Follow any flavour and you'll see the same challenges crop up and disappear as each team advances their knowledge and grows the space. On the one hand the variety is welcome, on the other there is always a technical and a marketing leader; when they are not one and the same, everybody suffers, from the developer using a less mature product to the company accepting risk.

Technorati Tags: ,

April 21, 2008

Five recommendations for starting a startup with ASP.NET

Update [04/22/2008]: Now includes a bonus recommendation!

There seems to be a well-deserved observation that very few web startups are making use of ASP.NET, choosing instead to leverage more open platforms like LAMP, and ROR. As a web developer who has launched a startup in ASP.NET, I have to admit that there is some truth to the difficulties presented in the discussions that exist online (here are three examples), and as an ASP.NET startup developer, I'll offer five recommendations for you if you plan on going this route, not based on simple personal preference (I disobeyed many of these to the detriment of my sanity, pocketbook, and project lifecycle) but out of my desire for you to succeed without needing to overcome the same obstacles that I did.

For a bit of context, my startup took ten months to complete from concept to completion, which, in this world of "Getting Real" and "Getting Things Done", is nothing short of a death march. A one man death march should be impossible, one of my colleagues pointed out to me (he also founded an ASP.NET web startup, but followed most of these pointers and is doing great). So, here are my recommendations for the would-be startup founder flying the blue monster flag:

1. Do use ASP.NET MVC (or at least learn the web like everybody else)

Don't deal with ViewState, don't deal with leaky abstractions of the web in a way that confounds you. You'll thank yourself for writing cleaner code that you can test, and for mastering the deceptively simple art of web development. "It's all just markup" is a mantra that will keep you on track when you feel overwhelmed with the new model, but it's a new model you want to learn. If you don't want to learn ASP.NET MVC, do yourself a favor and disable ViewState at the page level, and do what you can to avoid using it, whether that's baking your own MVC-like "markup + business objects" design, or embracing the client-centric development model with JavaScript against ASP.NET controls that don't require ViewState. Make friends with the Repeater.

2. Don't use large third-party control libraries (they don't buy you the time you think they do)

I decided to buy a very popular (and expensive) third party control library. My instinct was that, as a single developer working on a web startup by moonlight, I could leverage enterprise-class support and a robust API to make short work of some of the more complex UI tasks. I ended up spending more time trying to ramp up with the documentation and hunting down bugs or my own misuse of the framework than I would have writing my own controls. It may be tempting to believe the third party solution will solve all of your problems, but the key to a startup is to release early and get feedback as soon as possible. Stick with simple controls and markup that you can understand and stay clear of off-the-shelf products if your time is tight (and it is). Remember that many hands built that product, and attempting to master its nuances is no different, to me, than stealing code and trying to reverse-engineer it; both cut out your momentum and take you away from your own ideas. Nobody needs extra dependence. If you have to use a framework, stick with a free one like ExtJs, and use the money on graphic design instead.

3. Do use jQuery (it's small and does it all)

You simply can't afford to send 150kb to a user's browser to buy you basic Javascript UI. This little monster costs you 15kb and it can do everything you can imagine. jQuery in combination with (optimized and combined) ASP.NET AJAX web service proxies is all you need to make calls over the wire and do amazing things on the browser. Any other solution is unwieldy, bloated, and takes too much time, period.

4. Don't use the UpdatePanel (tough love, but you need it)

Speaking of unwieldy, don't let this siren succor you into a false sense of ability. The UpdatePanel might be useful for the very isolated case of posting back a large form that needed to be submitted in the first place, but under no circumstances does sending most of a web page to the server and then updating small parts of it on the client constitute a practical or desirable approach to building AJAX sites that aren't on a corporate intranet with a giant pipe underneath. If you're a startup you're likely building a public-facing application that's intended to be SaaS. If you want that service to be performant, you need to make small client changes either entirely on the client itself, or submitting just what you intend to change through a web service call. Take the time and learn how to do "real" ASP.NET AJAX; that is, build static page methods, WCF services, or script methods, and call those on the client-side via JavaScript. ASP.NET AJAX will take care of the JSON serialization for you so you're free to return custom objects in your service methods. Anything else is asking for trouble.

5. Do use a graphic designer (A startup has no excuse for poor design)

A "programmer's aesthetic" is often cited as a suitable excuse for a shoddy design, and if you study the well-established startups that exist in the wild, many of them leave a lot to be desired in this department. Don't confuse successful-and-ugly with a real decision made somewhere along the line to skimp on design. Those sites are successful for a variety of unrelated reasons, including timing, and if you are similar in any way to one of these existing ventures, you're going to need to differentiate in more ways than features. Do yourself a favor and at least invest a little money on your logo.

6. Do use an automated entity persistence solution (because you have better things to do)

John S. pointed out that I had missed one of the fundamental pain points when building a web application: entity persistence, or "object-relational mapping". This is the concept of leaning on a reliable, automatic way of moving business objects (tags, people, groups, photos, forums, posts, et al.) to the database and back without having to think about, or write, reams of code to handle it. I ended up using LINQ to SQL for my startup because I had a simple data model (many social networking applications do) that didn't need multiple table inheritance, or non-PK relationships; otherwise I would recommend an alternative. A lot of agile developers recommend SubSonic (and frankly it looks so darn cool), and you could try NHibernate as well, and there are many other options; the emphasis here is to use one you like, but do use one.

kick it on DotNetKicks.com

April 12, 2008

Resourceful ASP.NET MVC: Multiple approaches to optimizing CSS

I've written several posts based on the goal of achieving the best balance between performance and maintainability when serving static CSS resources. Now that I've covered the various ways CSS makes it way to the browser (through ASP.NET themes, linked externally in <link> tags, written inline in <style> tags, or injected, either through <link> or <style> tags by a third party at runtime), I want to provide a working example of all of this code.

To do that I decided first to make it an example in ASP.NET MVC, as it is a new topic for me and one I find very exciting, and second to synchronize the example code with another project, the Dimebrain Web Framework, which is where I intend to check in all blog source code moving forward. I like this way of sharing source better than linking to archived files in the post, simply because I can build on previous examples while the most current code is always readily available, and that I can use the blog as a way of moving a project forward rather than have it blast you with random bits of (hopefully) useful information.

I will present all of the topics previously covered in other posts as they apply to a working ASP.NET MVC example, built on top of the default ASP.NET MVC project template. When I use the term "C4", I don't mean explode, I mean "crunch, combine, cache, and compress", which is a common optimization strategy for script resources.

C4 
Note: The minifier we'll use is my C# port of CssMin, a part of Yahoo's YUI Compressor

Example Topics

I want to C4 my ASP.NET Themes dynamically

ASP.NET Themes, when used, are automatically inserted into the <head> of your pages by the ASP.NET runtime. I like using ASP.NET Themes and breaking out CSS files by component. This usually results in multiple CSS files, complete with comments, which is a large payload to deliver to the browser. The example application uses an ASP.NET Theme, and a ViewPageBase class that handles the PreRender event and swaps out all of the ASP.NET Theme links with a link to a CSS handler that does its magic. The original article explaining how this works is here.

ThemeCombined


I want to C4 any CSS files I have added to the page <head> section

If any external CSS in <link> tags are added to the head section, the ViewPageBase will automatically send these to the same CSS handler that is C4'ing our ASP.Themes. No additional effort is required! In the example application, a file named External.css is referenced in a link. In the final page, it is a handler query that delivers the resource.

InlineStylePreHandler
Before

InlineStylePostHandler After

I want to C4 styles that are directly written on my pages:

This one is a little trickier, if only that you would normally not desire to inline CSS directly in the <head> section, since separating markup from content is always a good design decision. To accomplish this we'll use ASP.NET MVC's ActionFilterAttribute, which is a handy way to get access to the request and response as the page is executing. In classic ASP.NET 2.0 we'd use an IHttpModule to attach a stream filter (which I wrote about here), but we only need to provide a custom filter attribute in ASP.NET MVC and apply a minification filter directly to the output stream:

namespace Dimebrain.Web.Mvc.ActionFilterAttributes
{
    public class InlineResourcesAttribute : ActionFilterAttribute
    {
        // Details elided...
 
        public InlineResourcesAttribute()
        {
            _type = ResourceType.CascasingStyleSheets;
            _options = ResourceOptions.ToMinifier;
        }
 
        public override void OnActionExecuting(FilterExecutingContext filterContext)
        {
            var response = filterContext.HttpContext.Response;
 
            // Details elided...
 
            response.Filter = new InlineStylesMinifyStream(response.Filter);
        }
    }
}


Now we can add the attribute to our base controller:

namespace Dimebrain.Demo.Mvc.Controllers
{
    [InlineResources]
    public class ControllerBase : Controller
    {
    }
}

This will cause the inline CSS to get minified before rendering on the page. We'll leave compressing and combining to the actual page its on, and combining doesn't apply in this case. Since this minification occurs on every request, it isn't recommended. You should place your inline CSS in an external file. This still may be a useful option for you if you use third party libraries that inject inline styles against your will.

InlineStylePreMinify

Before

InlineStylePostMinified

After (all three approaches)

Hopefully these methods of C4'ing your CSS resources will help you deliver the best possible web applications. Support for similar handling of JavaScript files will make its way into the codebase in the future. You can download the latest Dimebrain Web Framework code from CodePlex and use the MVC demo example to see these techniques in practice.

kick it on DotNetKicks.com

April 11, 2008

Minifying embedded CSS using stream filters

Update [04/12/2008]: I made an assumption that <style> tags, like <script> tags, can have src attributes. In actuality, they will, according to this draft of XHTML 2.0, but today we use <link> tags to reference external styles. Now, we can update our filter described below to pull out the <link> tags and replace their href resources with our handler, but it is better to just bundle this up with the ASP.NET Theme combiner. In other words, a small change to that code which you will in the next post will automatically send external styles to the handler.

As an extension to my previous posts on CSS minification and combining ASP.NET Theme styles at runtime, a further step we need to take to truly optimize our production styles is to apply the same process to CSS styles that do not live in your Theme folder. These arrive either through our own doing, by directly creating CSS styles in <style> tags on our page, or against our will, when a well-meaning third party library injects the styles for us.

We already have a handler in place; the same handler we built to combine our Theme styles can also be used to reference individual files. What's missing is a way to minify the styles that appear in <style> tags, and a way to utilize both of these solutions at runtime.

We can accomplish this using one of my favorite techniques: stream filtering. I always think of stream filters like camera filters: you can add multiple filters to the same raw output stream of html, and they all do their job in sequence, leaving you with the transformed output.

So this is our plan of attack: for every <style> tag that possesses a 'src' attribute, in other words its contents are obtained from an external source, we will re-route that location to our existing style handler. For every <style> tag that does not possess a 'src' attribute, its inline style content will be minified on the spot.

To accomplish this we're going to build a special kind of Stream filter base class that we can reuse for task that requires us to intercept a section of text, and replace some of its contents with new content. In this case we're looking in markup text for <style> tags with and without 'src' attributes, and either replacing the 'src' attribute value with a handler query to the same location (to minify, cache, and compress it for us), or we're replacing the contents of the <style> tag itself with its minified version (and leaving any compression tasks up to whatever method is handling the entire page, if any). So far so good?

namespace Dimebrain.Web.Filters
{
    public abstract class ReplaceFilterStream : Stream
    {
        private readonly Stream _stream;
 
        #region Stream Overrides
 
        protected ReplaceFilterStream(Stream stream)
        {
            _stream = stream;
        }
 
        public override bool CanRead
        {
            get { return _stream.CanRead; }
        }
 
        public override bool CanSeek
        {
            get { return _stream.CanSeek; }
        }
 
        public override bool CanWrite
        {
            get { return _stream.CanWrite; }
        }
 
        public override long Length
        {
            get { return _stream.Length; }
        }
 
        public override long Position
        {
            get { return _stream.Position; }
            set { _stream.Position = value; }
        }
 
        public override void Flush()
        {
            _stream.Flush();