<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:blogChannel="http://backend.userland.com/blogChannelModule" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:pingback="http://madskills.com/public/xml/rss/module/pingback/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" xmlns:creativeCommons="http://backend.userland.com/creativeCommonsRssModule" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0">
  <channel>
    <title>Reddnet Scribbles</title>
    <description>It makes me want to gouge my eyes out with a cheese grater!</description>
    <link>http://reddnet.net/</link>
    <docs>http://www.rssboard.org/rss-specification</docs>
    <generator>BlogEngine.NET 1.6.1.6</generator>
    <language>en-US</language>
    <blogChannel:blogRoll>http://reddnet.net/opml.axd</blogChannel:blogRoll>
    <blogChannel:blink>http://www.dotnetblogengine.net/syndication.axd</blogChannel:blink>
    <dc:creator>Stephen M. Redd</dc:creator>
    <dc:title>Reddnet Scribbles</dc:title>
    <geo:lat>34.703080</geo:lat>
    <geo:long>-82.250910</geo:long>
    <atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/Reddnet" /><feedburner:info uri="reddnet" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><geo:lat>34.709869</geo:lat><geo:long>-82.283853</geo:long><creativeCommons:license>http://creativecommons.org/licenses/by/3.0/</creativeCommons:license><item>
      <title>MEF and MVC - Limitations and workarounds for partial trust environments</title>
      <description>&lt;p&gt;A while back I &lt;a href="post/2010/06/22/Hooked-on-MEF-Using-MEF-in-ASPNET-MVC-and-the-Nerd-Dinner-MEF-sample-fix.aspx"&gt;wrote about&lt;/a&gt; using MEF in MVC environments with extensions provided by &lt;a href="http://hammett.castleproject.org/"&gt;Hammet&lt;/a&gt; for the &lt;a href="http://www.hanselman.com/blog/ExtendingNerdDinnerAddingMEFAndPluginsToASPNETMVC.aspx"&gt;Nerd Dinner MEF&lt;/a&gt; sample application. Those extensions deal with dynamic discovery of parts based on MVC conventions (instead of attributes), as well as per-request composition containers. The extensions work great, after a few modifications that I talked about in &lt;a href="post/2010/06/22/Hooked-on-MEF-Using-MEF-in-ASPNET-MVC-and-the-Nerd-Dinner-MEF-sample-fix.aspx"&gt;the last post&lt;/a&gt;... but in partial trust environments it blows up in your face!&lt;/p&gt;
&lt;p&gt;BOOM!&lt;/p&gt;
&lt;p&gt;I spent hours and hours digging through the code, reading about CAS, trust policies, transparent code and whole mess of other junk that I'd really rather not have rattling around my skull. Long story short -- MEF isn't friendly with partially trusted asp.net environments.&lt;/p&gt;
&lt;div style="margin-left:40px;"&gt;
&lt;p&gt;&lt;em&gt;Now, you could write your custom MEF code in a class library, flag the assembly with the APTCA attribute, sign-it, and install it to the GAC if you want. That will get around these limitations neatly, but if you are running in partial trust you probably don't have the luxury of installing things to the GAC either.&lt;/em&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;The first major limitation is that you cannot access the parts collection within catalogs or containers. If you try it, your get  an exception like this:&lt;/p&gt;
&lt;pre class="brush: text;"&gt;Attempt by method 'DynamicClass.lambda_method(System.Runtime.CompilerServices.Closure)' 
to access type 'System.ComponentModel.Composition.Hosting.ComposablePartCatalogCollection' failed.
  
  
&lt;/pre&gt;
&lt;p&gt;The easiest way to reproduce the problem is to simply add the trust element to web.config like this:&lt;/p&gt;
&lt;pre class="brush: html"&gt;&amp;lt;trust level="High"/&amp;gt;
  
  
&lt;/pre&gt;
&lt;p&gt;Then add this to application startup in global.asax:&lt;/p&gt;
&lt;pre class="brush: csharp;"&gt;	var cat = new DirectoryCatalog(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "bin"));
	var parts = cat.Parts.ToArray();
  
   
&lt;/pre&gt;
&lt;p&gt;In the Nerd Dinner MEF sample, this limitation effectively kills the mechanisms that slit up parts into per-request containers vs. application wide containers.&lt;/p&gt;
&lt;p&gt;If you've done any reading about MEF online, you've likely run across code for a &lt;a href="http://mef.codeplex.com/wikipage?title=Filtering%20Catalogs"&gt;FilteredCatalog&lt;/a&gt; class. This thing is so commonly cited on the net that it seems beyond retarded that it wasn't built-in with MEF. But these limitations from partial trust kills FilteredCatalog; which the Nerd Dinner MEF sample uses heavily.&lt;/p&gt;
&lt;p&gt;The other major area of limitation is that you cannot use &lt;a href="http://msdn.microsoft.com/en-us/library/system.componentmodel.composition.reflectionmodel.reflectionmodelservices.aspx"&gt;ReflectionModelServices&lt;/a&gt;, which is needed in order to dynamically create export/import definitions programmatically. This kills the Nerd Dinner MEF sample's auto-discovery of controllers.&lt;/p&gt;
&lt;p&gt;Despite these limitations, you can still use MEF in medium trust, but only if you are careful to keep it simple and straight forward.&lt;/p&gt;
&lt;p&gt;Honestly, I recommend that you just use &lt;a href="http://ninject.org/"&gt;Ninject&lt;/a&gt; or a similar IoC/DI framework until the next version of MEF or MVC (hopefully) fixes these issues.&lt;/p&gt;
&lt;p&gt;In my case though, I really wanted to be able to support medium trust environments and I'm too damned stubborn to give up on MEF that easy.&lt;/p&gt;
&lt;p&gt;I'm OK with having to use the MEF attributes to decorate my controllers, so losing auto-discovery isn't much of a problem. Hammet's extensions are brilliant, but the auto-discovery mechanism is a lot of VERY complicated experimental code.&lt;/p&gt;
&lt;p&gt;Now, the simplest thing you can do is just use a custom MVC ControllerFactory that instantiates a new MEF container on each request. That works well, and is trivially easy to implement:&lt;/p&gt;
&lt;pre class="brush: csharp;"&gt;public class MefControllerFactory : IControllerFactory
{
    public IController CreateController(RequestContext requestContext, string controllerName)
    {
        var catalog = new DirectoryCatalog(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "bin"));
        var requestContainer = new CompositionContainer(catalog);
        var controller = requestContainer.GetExportedValue(controllerName);

        if (controller == null){ throw new HttpException(404, "Not found");}
        
        return controller;
    }
}
&lt;/pre&gt;
&lt;p&gt;Sure, this is fine, but it sort of undermines a lot of the power of MEF. MEF's default behavior uses a singleton pattern to reuse parts that have already been instantiated, but this mechanism eliminates ALL reuse, by recombobulating the entire container on each request. It also has an appreciable performance impact since reflection has to go over and build up the entire catalog each time too.&lt;/p&gt;
&lt;p&gt;Another solution is to just create an application wide container, and just keep the controllers from being reused by setting the PartCreationPolicy attribute to NonShared. That's a better solution, and simple to achieve too. It looks something like this:&lt;/p&gt;
&lt;pre class="brush: csharp;"&gt;public static class ContainerManager
{
    private static CompositionContainer _container;
    public static CompositionContainer ApplicationContainer
    {
        get
        {
            if (_container == null)
            {
                var catalog = new DirectoryCatalog(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "bin"));
                _container = new CompositionContainer(catalog);
            }
            return _container;
        }
    }
}
&lt;/pre&gt;
&lt;p&gt;Then your controller just uses the application container from this static class. Very simple, and allows you to control reuse using MEF's standard attributes.&lt;/p&gt;
&lt;p&gt;I actually recommend the above approach,  but it bothered me to mark controllers as NonShared. It isn't that controller instances cannot be reused, it's just that in MVC they can't be reused across multiple requests.&lt;/p&gt;
&lt;p&gt;So I came up with a more ghetto solution that can sort-of mimic a FilteredCatalog even in medium trust. This allows for a pattern more similar to the Nerd Dinner MEF sample;  you can have application scoped containers, and smaller per-request containers for just for the controllers too.&lt;/p&gt;
&lt;p&gt;It looks a little something like this:&lt;/p&gt;
&lt;p&gt;First create a class derived from HttpApplication so you can boot-strap creating the MEF containers and catalogs on application startup:&lt;/p&gt;
&lt;pre class="brush: csharp;"&gt;public class MefHttpApplication : HttpApplication
{
    public static ComposablePartCatalog RootCatalog { get; private set; }
    public static CompositionContainer ApplicationContainer { get; private set; }
    public static ComposablePartCatalog ControllerCatalog { get; private set; }

    protected virtual void Application_Start()
    {
        if (RootCatalog == null){ RootCatalog = CreateRootCatalog(); }
        if (ApplicationContainer == null)
        {
            ApplicationContainer = new CompositionContainer(RootCatalog, false);
        }
        if (ControllerCatalog == null)
        {
            var controllerTypes = Assembly.GetExecutingAssembly().GetTypes().Where(t =&amp;gt; t.GetInterfaces().Any(i =&amp;gt; i == typeof(IController)));
            ControllerCatalog = new TypeCatalog(controllerTypes);
        }
        ControllerBuilder.Current.SetControllerFactory(new MefControllerFactory());
    }

    protected virtual void Application_End()
    {
        if (ApplicationContainer != null){ApplicationContainer.Dispose();}
    }

    protected virtual ComposablePartCatalog CreateRootCatalog()
    {
        return new DirectoryCatalog(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "bin"));
    }
}
&lt;/pre&gt;
&lt;p&gt;On startup we create a master catalog of every part definition, and an application scoped container from that master catalog. But we also create a catalog containing just the controller parts by using a bit of reflection to pull out just controllers  and shoving them into a TypeCatalog (which is built-in with MEF)... the poor man's filtered catalog!&lt;/p&gt;
&lt;p&gt;Now just doctor up Global.asax to inherit the MefHttpApplication class:&lt;/p&gt;
&lt;pre class="brush: csharp;"&gt;public class MvcApplication : MefHttpApplication
{
    public static void RegisterRoutes(RouteCollection routes)
    {
       //normal route stuff
    }

    protected override void Application_Start()
    {
        base.Application_Start();
        AreaRegistration.RegisterAllAreas();
        RegisterRoutes(RouteTable.Routes);
    }
}
&lt;/pre&gt;
&lt;p&gt;And finally, we need our ControllerFactory:&lt;/p&gt;
&lt;pre class="brush: csharp;"&gt;public class MefControllerFactory : IControllerFactory
{
    public IController CreateController(RequestContext requestContext, string controllerName)
    {
        var requestContainer = GetRequestControllerContainer(requestContext.HttpContext.Items);
        var controller = requestContainer.GetExportedValue(controllerName);

        if (controller == null){throw new HttpException(404, "Not found");}

        return controller;
    }

    public void ReleaseController(IController controller){/*nothing to do*/}

    public static CompositionContainer GetRequestControllerContainer(IDictionary contextItemsCollection)
    {
        var app = (MefHttpApplication)HttpContext.Current.ApplicationInstance;

        if (contextItemsCollection == null) throw new ArgumentNullException("dictionary");

        var container = (CompositionContainer)contextItemsCollection["MefRequestControllerContainer"];

        if (container == null)
        {
            container = new CompositionContainer(MefHttpApplication.ControllerCatalog, false, MefHttpApplication.ApplicationContainer);
            contextItemsCollection["MefRequestControllerContainer"] = container;
        }
        return container;
    }
}
&lt;/pre&gt;
&lt;p&gt;As you can see, the overall technique here is similar to that used in the Nerd Dinner MEF sample. We have a static method that we can call to build a per-request container. It stuffs the entire container into context in case its needed later. The key to the container itself is that it is built from our catalog of just controller types, and uses the application scoped MEF container as an export provider for any other parts the controllers might need to import.&lt;/p&gt;
&lt;p&gt;In the long-run, this is probably no better than just marking our controllers as NonShared and using an application wide container, but the general concept of this technique can be applied to other situations besides just dependency injection with controllers. While you can't truly filter catalogs and manipulate part in partial trust, you can still use reflection to create specialized catalogs and achieve similar results... for the simpler cases anyway.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/Reddnet?a=TYMgbxjZjTQ:JBWOZN_8Ozc:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/Reddnet?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/Reddnet?a=TYMgbxjZjTQ:JBWOZN_8Ozc:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/Reddnet?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/Reddnet?a=TYMgbxjZjTQ:JBWOZN_8Ozc:y6ACzXPbQao"&gt;&lt;img src="http://feeds.feedburner.com/~ff/Reddnet?i=TYMgbxjZjTQ:JBWOZN_8Ozc:y6ACzXPbQao" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/Reddnet?a=TYMgbxjZjTQ:JBWOZN_8Ozc:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/Reddnet?i=TYMgbxjZjTQ:JBWOZN_8Ozc:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/Reddnet/~4/TYMgbxjZjTQ" height="1" width="1"/&gt;</description>
      <link>http://feedproxy.google.com/~r/Reddnet/~3/TYMgbxjZjTQ/post.aspx</link>
      <author>Stephen M. Redd</author>
      <comments>http://reddnet.net/post/2010/07/08/MEF-and-MVC-Limitations-and-workarounds-for-partial-trust-environments.aspx#comment</comments>
      <guid isPermaLink="false">http://reddnet.net/post.aspx?id=72445ae8-822b-4eee-ad49-284653e8b7fb</guid>
      <pubDate>Thu, 08 Jul 2010 21:20:00 -0900</pubDate>
      <category>Code</category>
      <dc:publisher>Stephen M. Redd</dc:publisher>
      <pingback:server>http://reddnet.net/pingback.axd</pingback:server>
      <pingback:target>http://reddnet.net/post.aspx?id=72445ae8-822b-4eee-ad49-284653e8b7fb</pingback:target>
      <slash:comments>0</slash:comments>
      <trackback:ping>http://reddnet.net/trackback.axd?id=72445ae8-822b-4eee-ad49-284653e8b7fb</trackback:ping>
      <wfw:comment>http://reddnet.net/post/2010/07/08/MEF-and-MVC-Limitations-and-workarounds-for-partial-trust-environments.aspx#comment</wfw:comment>
      <wfw:commentRss>http://reddnet.net/syndication.axd?post=72445ae8-822b-4eee-ad49-284653e8b7fb</wfw:commentRss>
    <feedburner:origLink>http://reddnet.net/post.aspx?id=72445ae8-822b-4eee-ad49-284653e8b7fb</feedburner:origLink></item>
    <item>
      <title>Moving forward with TicketDesk 2.0... again</title>
      <description>&lt;p&gt;I've been hard at work trying to nail together a stable beta of &lt;a href="http://www.codeplex.com/TicketDesk"&gt;TicketDesk&lt;/a&gt; 2.0. I'd &lt;a href="http://reddnet.net/post/2009/08/03/TicketDesk-20-and-the-ASPNET-MVC-Framework-.aspx"&gt;started the re-write&lt;/a&gt; last year for MVC, but decided to shelve the project in late December '09 until the final RTM versions of the .net 4 and related new stuff matured and were officially released.&lt;/p&gt;
&lt;p&gt;Now that the 4.0 stack is out, and I've managed to come to grips with it, I've resumed active work on TicketDesk 2.0 again.&lt;/p&gt;
&lt;p&gt;Overall, the plan remains the same; put together the same features as 1.x, but using the new platforms. There will be much streamlining of the features in 2.0, but not a whole lot of totally new stuff.&lt;/p&gt;
&lt;p&gt;The core technology stack is shaping up quite well now. I'm using MVC 2 on the front-end, and an Entity Framework 4 generated model on the back-end. In between I'm using the Managed Extensibility Framework (MEF) for IoC and dependency injection (for now). The overall design is MVC with view models on the front-end, and a transaction script based domain service behind.&lt;/p&gt;
&lt;p&gt;Right now, my focus is on finalizing the general architecture and getting it stable enough for a beta release and check-in at codeplex, and I'm probably about a month away from that point (assuming I don't get side-tracked with the day job of course).&lt;/p&gt;
&lt;p&gt;Once the core of the 2.0 project is done, I plan to aggressivly move forward with new features in subsequent mini-releases.&lt;/p&gt;
&lt;p&gt;On the proposed feature list right now are the following (no particular order, not all will be approved and implemented):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Private Tickets: Tickets that are visible only to the owner and assigned user. Useful for using TicketDesk as a personal task manager along side the regular help-desk roles. This may also relate to ticket-tasks/sub-tickets (see below) where you can attach private tickets as children to a public parent ticket. &lt;/li&gt;
&lt;li&gt;Multiple Editor options &amp;ndash; system-wide or as a user preference 				 
&lt;ul&gt;
&lt;li&gt;Markdown Editor (with WMD or MarkItUp!) &lt;/li&gt;
&lt;li&gt;Rich HTML Editor (with CKEdit or TinyMCE) &lt;/li&gt;
&lt;li&gt;Plain text Editor (with an empty box) &lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Add back tag &amp;amp; category views to ticketcenter &lt;/li&gt;
&lt;li&gt;Dashboard (widgets for close rates, new ticket rates, activity levels, etc.) &lt;/li&gt;
&lt;li&gt;List view editor: Ability to create custom lists for the ticket center &lt;/li&gt;
&lt;li&gt;Workflows Enhancements: 				 
&lt;ul&gt;
&lt;li&gt;Ability to disable the "more info" flow (system-wide) &lt;/li&gt;
&lt;li&gt;Skip "resolved to closed" workflow (submitter doesn&amp;rsquo;t have to sign-off, resolve auto-closes ticket) &lt;/li&gt;
&lt;li&gt;Shelve/Park ticket workflow (park ticket in limbo until later date) &lt;/li&gt;
&lt;li&gt;"no reopen" option. Prevent user from re-opening the same ticket after it is closed; can be set as a system-wide policy "never-allow re-open", or as a per-ticket option for helpdesk in resolve ticket dialog. &lt;/li&gt;
&lt;li&gt;Option for re-open to automatically re-assign to previous assigned user (system wide setting) &lt;/li&gt;
&lt;li&gt;Optional auto-assign for new tickets (either round-robin, or to single named person) &lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Multi-User tickets: Ability to have more than one staff or submitter on a ticket. 				 
&lt;ul&gt;
&lt;li&gt;Ticket would have a "currently responsible" staff member. This will allow IT to pass a ticket to another staff member without losing track of the ticket while it is being worked by someone else. &lt;/li&gt;
&lt;li&gt;Multiple ticket owners: optional primary owner (if not, any owner can manage the resolve/close workflow equally). &lt;/li&gt;
&lt;li&gt;Ability for any user to "follow" a ticket without being an assigned or owning user. &lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Merge Tickets (close as duplicate with automatic owner/subscriber hookups to the open ticket) &lt;/li&gt;
&lt;li&gt;Ticket Tasks: This is a sub-ticket concept where tickets can be split into multiple sub-tickets. The sub-tickets have to resolve before the parent ticket can resolve. Major state changes for sub-tickets would also log to parent's activity log. &lt;/li&gt;
&lt;li&gt;Admin Views/Reports: simple overview dashboard and some basic reporting features (non-performance tracking only) &lt;/li&gt;
&lt;li&gt;Suggest-a-ticket: when creating new tickets, perform tag and title analysis on recent and open tickets to suggest potential existing tickets to look at before submitting a new ticket (reduce duplicate problem reports) &lt;/li&gt;
&lt;li&gt;Multiple projects: Ability to have multiple projects within the system. Each "project" acts as a separate instance of ticketdesk with their own settings and tickets. &lt;/li&gt;
&lt;li&gt;Support for partial trust deployment environments (making it friendly to 3rd party hosting providers). This may require some additional arcitecting or have to wait on the ASP.NET MVC 3 framework (MEF, which I'm using for IoC, does not appear to be friendly in restricted trust right now; Hammett is currently working with MVC team on MEF related features for MVC 3) &lt;/li&gt;
&lt;li&gt;Email templates: control how notifications are formatted &lt;/li&gt;
&lt;li&gt;Mobile Device Support 				 
&lt;ul&gt;
&lt;li&gt;Limited display views &lt;/li&gt;
&lt;li&gt;Javascript free functionality &lt;/li&gt;
&lt;li&gt;Optional geo-location support &lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Actionable notifications &amp;amp; Escalations: 				 
&lt;ul&gt;
&lt;li&gt;Email submitters reminders for resolved but unclosed tickets after x time &lt;/li&gt;
&lt;li&gt;Email assigned users when ticket is untouched for x time &lt;/li&gt;
&lt;/ul&gt;
Email managers when ticket reaches x age (different ages depending on ticket&amp;rsquo;s status) &lt;/li&gt;
&lt;li&gt;External User Features: 				 
&lt;ul&gt;
&lt;li&gt;Support for Anonymous Users (by email address) &lt;/li&gt;
&lt;li&gt;POP 3 email Ticket Submission &lt;/li&gt;
&lt;li&gt;System wide-ability to prevent submitters from viewing other user&amp;rsquo;s tickets (also disables multi-owner and ticket "following" features for submitters). &lt;/li&gt;
&lt;li&gt;Modified email-notifications behavior (notify submitter of changes, send new ticket note to submitter) &lt;/li&gt;
&lt;li&gt;Customer Identity 						 
&lt;ul&gt;
&lt;li&gt;Ability to create organizations &amp;amp; assign users/staff to it &lt;/li&gt;
&lt;li&gt;Require organization identity for externally submitted tickets &lt;/li&gt;
&lt;li&gt;Customer specific ticket lists &amp;amp; views (may be related to multi-project tickets) &lt;/li&gt;
&lt;li&gt;Ability to enable submitter cross-viewing/following for submitters within same customer org (authenticated or verified users only) &lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Teams and Tiered Support Levels 				 
&lt;ul&gt;
&lt;li&gt;Limited support for users in standard help desk org teams or support tiers. &lt;/li&gt;
&lt;li&gt;Ticket&amp;rsquo;s awareness of which team/tier it is in &lt;/li&gt;
&lt;li&gt;Per-team/tier unassigned queues &amp;amp; ticketcenter views &lt;/li&gt;
&lt;li&gt;Optional ability to attach submitters to different default teams/tiers &lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you have any feature ideas that aren't on this list, please feel free to drop a comment below. Once the 2.0 branch is checked in on codeplex these will be tracked in issue tracker there&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/Reddnet?a=koK1Qictjrw:5lnBsJ8Sn-o:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/Reddnet?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/Reddnet?a=koK1Qictjrw:5lnBsJ8Sn-o:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/Reddnet?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/Reddnet?a=koK1Qictjrw:5lnBsJ8Sn-o:y6ACzXPbQao"&gt;&lt;img src="http://feeds.feedburner.com/~ff/Reddnet?i=koK1Qictjrw:5lnBsJ8Sn-o:y6ACzXPbQao" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/Reddnet?a=koK1Qictjrw:5lnBsJ8Sn-o:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/Reddnet?i=koK1Qictjrw:5lnBsJ8Sn-o:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/Reddnet/~4/koK1Qictjrw" height="1" width="1"/&gt;</description>
      <link>http://feedproxy.google.com/~r/Reddnet/~3/koK1Qictjrw/post.aspx</link>
      <author>Stephen M. Redd</author>
      <comments>http://reddnet.net/post/2010/07/05/Moving-forward-with-TicketDesk-20-again.aspx#comment</comments>
      <guid isPermaLink="false">http://reddnet.net/post.aspx?id=3fb2658d-5c2a-4bc2-a580-54bc85c9d9d6</guid>
      <pubDate>Mon, 05 Jul 2010 07:21:00 -0900</pubDate>
      <category>Code</category>
      <category>TicketDesk</category>
      <dc:publisher>Stephen M. Redd</dc:publisher>
      <pingback:server>http://reddnet.net/pingback.axd</pingback:server>
      <pingback:target>http://reddnet.net/post.aspx?id=3fb2658d-5c2a-4bc2-a580-54bc85c9d9d6</pingback:target>
      <slash:comments>0</slash:comments>
      <trackback:ping>http://reddnet.net/trackback.axd?id=3fb2658d-5c2a-4bc2-a580-54bc85c9d9d6</trackback:ping>
      <wfw:comment>http://reddnet.net/post/2010/07/05/Moving-forward-with-TicketDesk-20-again.aspx#comment</wfw:comment>
      <wfw:commentRss>http://reddnet.net/syndication.axd?post=3fb2658d-5c2a-4bc2-a580-54bc85c9d9d6</wfw:commentRss>
    <feedburner:origLink>http://reddnet.net/post.aspx?id=3fb2658d-5c2a-4bc2-a580-54bc85c9d9d6</feedburner:origLink></item>
    <item>
      <title>ASP.NET MVC 2: validation and binding issues with rich-text input, DataAnnotations, view-models, and partial model updates</title>
      <description>&lt;p&gt;Here I'm going to tackle a series of validation related annoyances with MVC 2 that tend to come up rather frequently.&lt;/p&gt;
&lt;p&gt;This will include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Input Validation: Potentially Dangerous Request even when using [ValidateInput(false)] attribute. &lt;/li&gt;
&lt;li&gt;DataAnnotations validation warnings with partial model updates. &lt;/li&gt;
&lt;li&gt;Dealing with View-Model binding AND DataAnnotations validation warnings with partial model updates together. &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;OK... so, you are writing a page on the asp.net MVC 2 framework that creates a record in the DB for you. You have a view. Nice!&lt;/p&gt;
&lt;p&gt;You have a controller. Also nice!&lt;/p&gt;
&lt;p&gt;The view needs select lists and stuff too, so you have a view-model. Sure thing!&lt;/p&gt;
&lt;p&gt;Your controller relies on a model class to handle business logic like auto-populating values on the entity and similar. You bet!&lt;/p&gt;
&lt;p&gt;And the entity itself is an EF 4 generated class. Nothing special there!&lt;/p&gt;
&lt;p&gt;And you created a meta-data buddy class for the entity so you can flag fields with attributes and get MVC to automate your validation. Absolutely!&lt;/p&gt;
&lt;p&gt;So you type in your values into the page, hit submit, and the page blows up!&lt;/p&gt;
&lt;p&gt;Fuck!&lt;/p&gt;
&lt;p&gt;Now, since you were using a view-model, your controller looks something like this:&lt;/p&gt;
&lt;pre class="brush: csharp;"&gt;[Authorize]
public virtual ActionResult Create()
{
    Ticket ticket = new Ticket();
    var model = new TicketCreateViewModel(ticket);
    return View(model);
}

[Authorize]
[HttpPost]
[ValidateInput(false)]
public virtual ActionResult Create(FormCollection collection)
{
    try
    {
        Ticket ticket = new Ticket();
        UpdateModel(ticket, collection);
        if(TicketService.CreateTicket(ticket))
        {
            RedirectToAction("Success");
        }
        else
        {
            return View(new TicketCreateViewModel(ticket));
        }
    }
    catch { return View(new TicketCreateViewModel(ticket)); }
}
&lt;/pre&gt;
&lt;p&gt;The first error you are likely to come across will be a potentially dangerous request error. This will happen if your view tries to submit a field that contains HTML characters, like with a rich text editor.&lt;/p&gt;
&lt;p&gt;Now, you used to be able to work around this by just flagging the action method with [ValidateInput(false)] and all was well; but not with MVC 2.0 you don't!&lt;/p&gt;
&lt;p&gt;In .NET 4.0, the MS dev team decided to overhaul (badly) the input validation mechanism. They wanted to have the same mechanism work for asp.net, web services, and just about any other kind of request too. So they now have a new input validation system that operates so high-up in the request pipeline that it dies long before it actually gets to your controller to see if you've overridden the default behavior. More info in &lt;a href="http://www.asp.net/learn/whitepapers/aspnet4/breaking-changes#0.1__Toc256770147"&gt;this whitepaper&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Basically, to work around this you need to tell ASP.NET to use the old .NET 2.0 validation mechanism instead of the fancy new one. You do this in web.config by adding this to the system.web section:&lt;/p&gt;
&lt;pre class="brush: xml;"&gt;&amp;lt;httpRuntime requestValidationMode="2.0"/&amp;gt;
  
  
&lt;/pre&gt;
&lt;p&gt;Basically, this change is so retarded, that any application with a rich text editor ANYWHERE has to turn off the new security feature for the entire application. The good news is that the old mechanism still protects asp.net pages, but you are on your own again for web services and other request types.&lt;/p&gt;
&lt;p&gt;Good job guys!&lt;/p&gt;
&lt;p&gt;Now, once you've fixed that up the next problem you'll likely run across is that the UpdateModel method has trouble populating the data into the right properties. This is because you are using a view-model class, but when the form is posted back you are trying to use UpdateModel on just an entity.&lt;/p&gt;
&lt;p&gt;Generally this is pretty easy. You just tell the UpdateModel method the "prefix" for the values in the form that should match the entity you are updating. In my example here, the property in the view-model that had the ticket fields was called "NewTicket"... we we just alter the controller to supply the prefix "NewTicket" and the UpdateModel method can figure out what properties in the form data should go get shoved into our entity.&lt;/p&gt;
&lt;pre class="brush: csharp; highlight: [9]"&gt;[Authorize]
[HttpPost]
[ValidateInput(false)]
public virtual ActionResult Create(FormCollection collection)
{
    try
    {
        Ticket ticket = new Ticket();
        UpdateModel(ticket, "NewTicket", collection);
        if(TicketService.CreateTicket(ticket))
        {
            RedirectToAction("Success");
        }
        else
        {
            return View(new TicketCreateViewModel(ticket));
        }
    }
    catch { return View(new TicketCreateViewModel(ticket)); }
}
&lt;/pre&gt;
&lt;p&gt;This is also how the standard example apps for MVC typically do things. And it works fantastic as long as you are posting back ALL of the properties from the view, or at least all the ones that have validations via DataAnnotations attached to them.&lt;/p&gt;
&lt;p&gt;But when you have a [Required] attribute from DataAnnotations on entity properties that are NOT being passed back from the view, then you run into the third major problem... the UpdateModel will blow up with validation errors for the fields that your view didn't post to the controller.&lt;/p&gt;
&lt;p&gt;This is also due to a late-breaking, and very unwise, change in behavior that the asp.net team made right before MVC 2.0 was released.&lt;/p&gt;
&lt;p&gt;The MVC validation stuff in 1.0 only validated properties that were posted up; a mechanism called input based validation. The problem with it is that a hacker could, in theory, get around triggering validation errors by hacking out required fields from the HTTP post. So, they decided to switch to "model based validation" where the validation would check the validity of ALL properties in the model even if they weren't included in the post.&lt;/p&gt;
&lt;p&gt;Again... this is an ok idea, but the asp.net team should not have implemented such a breaking-change without giving you some way to easily override the behavior too... after-all, doing a partial model update from a form is NOT exactly an edge case scenario. It is damned common!&lt;/p&gt;
&lt;p&gt;Now... Steve Sanderson (smart guy!) posted about &lt;a href="http://blog.stevensanderson.com/2010/02/19/partial-validation-in-aspnet-mvc-2/#comment-37723"&gt; a really slick action filter attribute&lt;/a&gt; way to handle partial model updates. In short, his mechanism allows you to flag an action method with an attribute, and an action filter will automatically loop in after the model binding is done and remove any model state validation errors for fields that weren't included in the post.&lt;/p&gt;
&lt;p&gt;Now... suppose you were doing this instead of what we were doing above:&lt;/p&gt;
&lt;pre class="brush: csharp;"&gt;[Authorize]
[HttpPost]
[ValidateInput(false)]
public virtual ActionResult Create(Ticket newTicket)
{
	// stuff
}
&lt;/pre&gt;
&lt;p&gt;With Steven's simple attribute, you could get the desired partial model update to work, without barfing on the data annotations by just flagging the action with one more attribute; like this:&lt;/p&gt;
&lt;pre class="brush: csharp;highlight: [4]"&gt;[Authorize]
[HttpPost]
[ValidateInput(false)]
[ValidateOnlyIncomingValuesAttribute]
public virtual ActionResult Create(Ticket newTicket)
{
	// stuff
}
&lt;/pre&gt;
&lt;p&gt;Fantastic!&lt;/p&gt;
&lt;p&gt;Except that it only works if you are using a model binder, but we weren't. Remember, in our case we're using a view-model. We don't want to bind to the view-model on the postback... we just want to bind to an instance of the entity we are trying to create.&lt;/p&gt;
&lt;p&gt;The UpdateModel method is NOT a model binder though largely it does the same thing as one. So the action filter attribute mechanism Steven describes doesn't work when using the UpdateModel method.&lt;/p&gt;
&lt;p&gt;sigh...&lt;/p&gt;
&lt;p&gt;OK, now I could just put similar code to what Steven's attribute was using in the controller itself. All his attribute does is loop through the model state and remove errors for fields that weren't in the posted form collection. But honestly, that's an ugly beast and I HATE having my controllers marshaling values around like that.&lt;/p&gt;
&lt;p&gt;Now... this example is actually simplistic, and the easiest way to work around this is to use the default binder and bind to the model automatically. This can be done simply with the Bind attribute, which allows you to supply a "prefix" to use for the binding. It looks something like this:&lt;/p&gt;
&lt;pre class="brush: csharp; highlight: [7]"&gt;[Authorize]
[HttpPost]
[ValidateInput(false)]
[ValidateOnlyIncomingValuesAttribute]
public virtual ActionResult Create
(
	[Bind(Prefix = "NewTicket")] Ticket ticket
)
{
	if(TicketService.CreateTicket(ticket))
	{
		RedirectToAction("Success");
	}
	else
	{
    	return View(new TicketCreateViewModel(ticket));
	}
}
&lt;/pre&gt;
&lt;p&gt;This works fantastic for simpler cases like this one. We just supply the prefix to use and the binder takes care of it.&lt;/p&gt;
&lt;p&gt;But, for reasons I don't want to delve into here, my code was a tad more complex and this technique didn't quite work out for me...&lt;/p&gt;
&lt;p&gt;So... what I did to work around this when the Bind attribute wasn't enough, was to create a custom model binder that could do the same thing we're doing with UpdateModel. Anyway, once we have a custom binder we can then modify the controller action to use the custom binder AND Steven's attribute both.&lt;/p&gt;
&lt;p&gt;So... here was my custom model binder:&lt;/p&gt;
&lt;pre class="brush: csharp;"&gt;public class NewTicketModelBinder : DefaultModelBinder
{
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        bindingContext.ModelName = "NewTicket";
        return base.BindModel(controllerContext, bindingContext);
    }
}
&lt;/pre&gt;
&lt;p&gt;Simple enough. All it does is supply the "prefix" to the binding context to achieve pretty much the same results as our UpdateModel method used to. Otherwise it uses the DefaultModelBinder's behavior as-is. Again though, this example is omitting some details that just aren't necessary to describe here, but my custom binder has a few other overloads besides what's shown here.&lt;/p&gt;
&lt;p&gt;Now the controller looks like this:&lt;/p&gt;
&lt;pre class="brush: csharp; highlight: [7]"&gt;[Authorize]
[HttpPost]
[ValidateInput(false)]
[ValidateOnlyIncomingValuesAttribute]
public virtual ActionResult Create
(
	[ModelBinder(typeof(NewTicketModelBinder))] Ticket ticket
)
{
	if(TicketService.CreateTicket(ticket))
	{
		RedirectToAction("Success");
	}
	else
	{
    	return View(new TicketCreateViewModel(ticket));
	}
}
&lt;/pre&gt;
&lt;p&gt;All I had to do here was declare what binder to use and specify Steven's [ValidateOnlyIncomingValuesAttribute]. I was also able to clean up the controller a bit, because now I don't need the try/catch for binding failures either.&lt;/p&gt;
&lt;p&gt;Now... one thing I still didn't like was that the custom model binder here is specific to each view model. There isn't a clean way to supply the "prefix" to the binder without some ugly reflection or such. But I didn't like the idea that every time I needed to bind this way I'd have to make a new custom model binder.&lt;/p&gt;
&lt;p&gt;So I settled on a "convention" for this and created a single custom model binder that could be used with any view-model, as long as it followed the convention:&lt;/p&gt;
&lt;pre class="brush: csharp; highlight: [1,5]"&gt;public class ViewModelBinder : DefaultModelBinder
{
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        bindingContext.ModelName = bindingContext.ModelType.Name;
        return base.BindModel(controllerContext, bindingContext);
    }
} 
&lt;/pre&gt;
&lt;p&gt;Here, the binder requires that the property on the view-model be named the same as the entity's type. So if we want to expose a Ticket entity, we need to name the property in the view-model "Ticket" too. &amp;nbsp;Previously in my view-model I was using the property named "NewTicket", so I just change that to use property name "Ticket" instead. This way the ViewModelBinder can always find the right prefix name to supply for the default binder.&lt;/p&gt;
&lt;p&gt;This still seems like an awful lot of work for a common scenario. I sure hope the MVC team gets all this worked out in the next release of the MVC framework to support this kind of situation more elegantly.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/Reddnet?a=52E-by1iaoM:u01S4995S_I:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/Reddnet?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/Reddnet?a=52E-by1iaoM:u01S4995S_I:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/Reddnet?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/Reddnet?a=52E-by1iaoM:u01S4995S_I:y6ACzXPbQao"&gt;&lt;img src="http://feeds.feedburner.com/~ff/Reddnet?i=52E-by1iaoM:u01S4995S_I:y6ACzXPbQao" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/Reddnet?a=52E-by1iaoM:u01S4995S_I:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/Reddnet?i=52E-by1iaoM:u01S4995S_I:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/Reddnet/~4/52E-by1iaoM" height="1" width="1"/&gt;</description>
      <link>http://feedproxy.google.com/~r/Reddnet/~3/52E-by1iaoM/post.aspx</link>
      <author>Stephen M. Redd</author>
      <comments>http://reddnet.net/post/2010/07/03/ASPNET-MVC-2-validation-and-binding-issues-with-rich-text-input-DataAnnotations-view-models-and-partial-model-updates.aspx#comment</comments>
      <guid isPermaLink="false">http://reddnet.net/post.aspx?id=1e7c6224-b31b-4b05-b193-6a3e86b43aa6</guid>
      <pubDate>Sat, 03 Jul 2010 06:21:00 -0900</pubDate>
      <category>Code</category>
      <dc:publisher>Stephen M. Redd</dc:publisher>
      <pingback:server>http://reddnet.net/pingback.axd</pingback:server>
      <pingback:target>http://reddnet.net/post.aspx?id=1e7c6224-b31b-4b05-b193-6a3e86b43aa6</pingback:target>
      <slash:comments>0</slash:comments>
      <trackback:ping>http://reddnet.net/trackback.axd?id=1e7c6224-b31b-4b05-b193-6a3e86b43aa6</trackback:ping>
      <wfw:comment>http://reddnet.net/post/2010/07/03/ASPNET-MVC-2-validation-and-binding-issues-with-rich-text-input-DataAnnotations-view-models-and-partial-model-updates.aspx#comment</wfw:comment>
      <wfw:commentRss>http://reddnet.net/syndication.axd?post=1e7c6224-b31b-4b05-b193-6a3e86b43aa6</wfw:commentRss>
    <feedburner:origLink>http://reddnet.net/post.aspx?id=1e7c6224-b31b-4b05-b193-6a3e86b43aa6</feedburner:origLink></item>
    <item>
      <title>A WMD markdown editor variant that works</title>
      <description>&lt;p&gt;I'm a big fan of markdown. If you've used &lt;a href="stackoverflow.com"&gt;Stack Overflow&lt;/a&gt;, then you are probably very familiar with markdown via their excellent variation of the WMD markdown editor. Markdown is popular in its way. A number of different web based content systems out there have adopted it, and there are plug-ins for a wide range of desktop utilities and development tools too.&lt;/p&gt;
&lt;p&gt;But if you are just wanting a javascript based WYSIWYG style editor for use in your own web applications, you are kinda screwed; there just aren't a lot of markdown editors out there. I know of only two working markdown editors of any merit that aren't tied to some specific application; &lt;a href="http://markitup.jaysalvat.com/home/"&gt;MarkItUp! by Jay Salvat&lt;/a&gt; and &lt;a href="http://wmd-editor.com/"&gt;WMD by John Fraser&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;WMD is fantastic, but John Fraser vanished from the net shortly after making WMD's initial code open source. His initial release was an obfuscated form, which makes it hard to deal with. He had plans to go ahead with a more developer friendly and advanced version of his editor, but it never happened. I have no idea what might have happened to John, but I suspect it was not good (active programmers don't typically disappear off the net entirely for years at a time without a trace). I do hope John is well, but the internet is a much poorer place without him I can tell you that!&lt;/p&gt;
&lt;p&gt;MarkItUp! is also a fine editor in its own way, but it was not designed with Markdown as the primary target and it isn't exactly a WYSIWYG editor. Technically, MarkItUp! is just a markup editor with some macros on the toolbar. It does have support for the markdown syntax, and it does a decent job as a markup editor even with markdown's syntax. But using it to author content in markdown isn't very approachable for a public facing application. The editor is just a tad too "bare-metal".&lt;/p&gt;
&lt;p&gt;WMD on the other hand is smoother and more viable.  It is also more of a markup editor than a real WYSIWYG, but it does has some really subtle, but important features that help make authoring markdown content enjoyable. But John's baseline version has several pretty major problems, and since the source is obfuscated, fixing it up and customizing it is next to impossible.&lt;/p&gt;
&lt;p&gt;Fortunately Dana Robinson and some others at Stack Overflow managed to de-obfuscate the original WMD source code, and published their their own &lt;a href="http://github.com/derobins/wmd"&gt;Stack Overflow specific version&lt;/a&gt; over at github. Now! That was some fine work, and I really do appreciate Dana's contribution. But the Stack Overflow version stripped out a lot of the original WMD's configuration features, and has several Stack Overflow specific tweaks besides. The end result of the SO version is that you can't really control it well. For example, I've found it nearly impossible to instantiate the SO version of WMD via JavaScript in response to a user action (like showing the editor in a pop-up for example). The SO version also doesn't deal well with multiple instances on the same page.&lt;/p&gt;
&lt;p&gt;There are quite a few branches from the SO version floating around on github. Fortunately there IS one branch that seems to have worked out most of the major problems and has actually advanced WMD quite a ways beyond the initial SO version. Anand Chitipothu maintains the &lt;a href="http://github.com/openlibrary/wmd"&gt;openlibrary branch of WMD&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Basically, this version is a WMD in a JQuery wrapper. Anand has added back much of the configuration stuff that the SO branch broke, tweaked a few of the behaviors (in pleasant ways), fixed up the multi-editor support, and most importantly made the editor JavaScript/JQuery instantiable.&lt;/p&gt;
&lt;p&gt;Overall, this is an excellent variant that can be used simply in just about any web application. It even has decent documentation too!&lt;/p&gt;
&lt;p&gt;I'm using &lt;a href="http://github.com/openlibrary/wmd/tree/v2.0"&gt;openlibrary / wmd Master branch tagged as 2.0&lt;/a&gt;, but there are a couple of minor catches I've found in this variant (as it was on July 1st 2010 anyway):&lt;/p&gt;
&lt;p&gt;First, there is a random stray undeclared variable that blows up in chrome (I didn't check other browsers for this one).&lt;/p&gt;
&lt;p&gt;The line with the problem is:&lt;/p&gt;
&lt;pre class="brush: csharp;"&gt;WMDEditor.Checks = Checks;&lt;/pre&gt;
&lt;p&gt;Commenting out this line fixes the problem simply enough.&lt;/p&gt;
&lt;p&gt;The other problem is much more complex. Basically, when putting this together, Anand made some fundamental changes to how the code is arranged, and some of the stuff in an IE specific branch hasn't been property updated with the new object model yet (the author probably isn't testing in IE 8 yet). Fixing this is rather annoying to describe, but the simplest way to do it is:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt; do a search/replace for "wmd.ieCachedRange" and replace with "WMDEditor.ieCachedRange"&lt;/li&gt;
&lt;li&gt;do a search/replace for "wmd.ieRetardedClick" and replace with "WMDEditor.ieRetardedClick"&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I have to say that I'm not fully up-to-speed on the innards of WMD, so my fix may not be optimal here... but it seems to work OK in my limited usages so far.&lt;/p&gt;
&lt;p&gt;For your convenience I've made my own variation of jquery.wmd.js and available for download. All of the lines I altered end with "//SMR". I am not including the minified version, but you should make your own minified for use in production environments.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://reddnet.net/file.axd?file=2010%2f7%2fopenlibrary-wmd-2.0-modified.zip"&gt;openlibrary-wmd-2.0-modified.zip (43.40 kb)&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/Reddnet?a=o1d959B4FVY:UKm1El3IJOM:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/Reddnet?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/Reddnet?a=o1d959B4FVY:UKm1El3IJOM:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/Reddnet?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/Reddnet?a=o1d959B4FVY:UKm1El3IJOM:y6ACzXPbQao"&gt;&lt;img src="http://feeds.feedburner.com/~ff/Reddnet?i=o1d959B4FVY:UKm1El3IJOM:y6ACzXPbQao" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/Reddnet?a=o1d959B4FVY:UKm1El3IJOM:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/Reddnet?i=o1d959B4FVY:UKm1El3IJOM:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/Reddnet/~4/o1d959B4FVY" height="1" width="1"/&gt;</description>
      <link>http://feedproxy.google.com/~r/Reddnet/~3/o1d959B4FVY/post.aspx</link>
      <author>Stephen M. Redd</author>
      <comments>http://reddnet.net/post/2010/07/02/A-WMD-markdown-editor-variant-that-works.aspx#comment</comments>
      <guid isPermaLink="false">http://reddnet.net/post.aspx?id=25d81ce7-05b1-4331-84d3-48c29e76b322</guid>
      <pubDate>Fri, 02 Jul 2010 15:46:00 -0900</pubDate>
      <category>Code</category>
      <dc:publisher>Stephen M. Redd</dc:publisher>
      <pingback:server>http://reddnet.net/pingback.axd</pingback:server>
      <pingback:target>http://reddnet.net/post.aspx?id=25d81ce7-05b1-4331-84d3-48c29e76b322</pingback:target>
      <slash:comments>0</slash:comments>
      <trackback:ping>http://reddnet.net/trackback.axd?id=25d81ce7-05b1-4331-84d3-48c29e76b322</trackback:ping>
      <wfw:comment>http://reddnet.net/post/2010/07/02/A-WMD-markdown-editor-variant-that-works.aspx#comment</wfw:comment>
      <wfw:commentRss>http://reddnet.net/syndication.axd?post=25d81ce7-05b1-4331-84d3-48c29e76b322</wfw:commentRss>
    <feedburner:origLink>http://reddnet.net/post.aspx?id=25d81ce7-05b1-4331-84d3-48c29e76b322</feedburner:origLink></item>
    <item>
      <title>Using T4 templates to generate DataAnnotations buddy classes for EF 4 entity models</title>
      <description>&lt;p&gt;One of the more frustrating things for me lately has been leveraging the ASP.NET MVC 2 validation support using DataAnnotations with a generated Entity Framework 4 model.&lt;/p&gt;
&lt;p&gt;Wow! That's was a mouthful!&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Anyway, it's great that the MVC Framework 2.0 can leverage DataAnnotations for both validation, as well as stuff like generating label text and such. Fantastic! But, for reasons that remain retarded, Microsoft chose not to include DataAnnotations attributes with generated EF 4 entity models. You can generate the models, and the models have attributes that mirror many of the ones DataAnnotations uses, but the attributes EF 4 generates are useless for use with MVC 2.0's validation features.&lt;/p&gt;
&lt;p&gt;DataAnnotations does have a rather interesting mechanism that you can use to add the annotations to your generated model though. You can't add the attributes directly to the generated code of course, they'd just get blown away next time the code gen ran. And you can't add the annotations to a custom partial class that extends the entities because the partial classes cannot each define two properties with the same name.&lt;/p&gt;
&lt;p&gt;Instead, what you are supposed to do is this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Create a custom "meta" class (sometimes called a buddy class) that mimics the entity's public properties&lt;/li&gt;
&lt;li&gt;Annotate the public properties in the custom meta class using attributes from Data Annotations&lt;/li&gt;
&lt;li&gt;Create a partial class that extends the&amp;nbsp;generated&amp;nbsp;entity you are annotating&lt;/li&gt;
&lt;li&gt;Flag the custom partial entity class with an attribute called MetadataTypeAttribute to link the entity class to the meta class where the annotations live&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Assuming you have a generated EF 4 entity named "Ticket", this will look something like this:&lt;/p&gt;
&lt;pre class="brush: csharp;"&gt;[MetadataType(typeof(TicketMetadata))]
public partial class Ticket
{
    //whatever extensions I might want to the entity

    /// 
    /// Class that mimics the entity so you have a place to put data annotations
    /// 
    internal sealed class TicketMetadata
    {
        [DisplayName("Ticket Id")]
        [Required]
        public int? TicketID { get; set; }
    }
}
&lt;/pre&gt;
&lt;p&gt;So, we have a way to add the annotations to our generated model, but you have to manually code this up... which is a BIG pain in the ass honestly, especially for larger entity models.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;This is a job for T4, but I have no interest in modifying the core T4 templates that generate the actual EF 4 entity model.&amp;nbsp;Fortunately&amp;nbsp;&lt;a href="http://weblogs.asp.net/rajbk/default.aspx"&gt;Raj Kaimal&lt;/a&gt; was kind enough to blog &lt;a href="http://weblogs.asp.net/rajbk/archive/2010/05/04/a-basic-t4-template-for-generating-model-metadata-in-asp-net-mvc2.aspx"&gt;his custom T4 template&lt;/a&gt; for generating metadata buddy classes.&lt;/p&gt;
&lt;p&gt;This is a pretty slick template that auto generates meta classes by just looking at an EF 4 generated edmx file. The template is super&amp;nbsp;simplistic. It&amp;nbsp;doesn't account for complex types, and there isn't a built-in mechanism where you can custom override or control how the attributes are generated. For example, the T4 generates the display name by just word-splitting at the capital letters in a Pascal cased property name. So if you want a different display name, there isn't a way to override the generated display name. The template does generate the meta classes as partials, so you can implement your own extension to catch any of the complex types that the template cant automatically generate attributes for.&lt;/p&gt;
&lt;p&gt;But because this template is so super-simple, it is a great place to start if you wanted to create your own template that can account for more complex needs specific to your own application.&lt;/p&gt;
&lt;p&gt;In my case, I found the template useful for generating the buddy classes initially, but then I just removed the T4 template and customized the code it had generated directly. That way I could make my customizations without worrying about the T4 over-writing my changes later. The T4 saved me a boat-load of time though by just doing that initial code generation, and the code it produced was about 95% sufficient for the final product's data annotation needs. &amp;nbsp;&lt;/p&gt;
&lt;p&gt;The annoyance of using data annotations with EF models comes up so often for me that I'm seriously considering writing a Visual Studio Extension, or my own complex T4 template to deal with the problem. Sadly, I doubt I'll ever find the time to actually write the code though.&amp;nbsp;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/Reddnet?a=R6L5kLj_KVE:yE6Qgdl3Qfk:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/Reddnet?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/Reddnet?a=R6L5kLj_KVE:yE6Qgdl3Qfk:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/Reddnet?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/Reddnet?a=R6L5kLj_KVE:yE6Qgdl3Qfk:y6ACzXPbQao"&gt;&lt;img src="http://feeds.feedburner.com/~ff/Reddnet?i=R6L5kLj_KVE:yE6Qgdl3Qfk:y6ACzXPbQao" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/Reddnet?a=R6L5kLj_KVE:yE6Qgdl3Qfk:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/Reddnet?i=R6L5kLj_KVE:yE6Qgdl3Qfk:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/Reddnet/~4/R6L5kLj_KVE" height="1" width="1"/&gt;</description>
      <link>http://feedproxy.google.com/~r/Reddnet/~3/R6L5kLj_KVE/post.aspx</link>
      <author>Stephen M. Redd</author>
      <comments>http://reddnet.net/post/2010/07/01/Automate-MVC-20-EF-4-Generated-Models-with-T4-Templates-for-DataAnnotations.aspx#comment</comments>
      <guid isPermaLink="false">http://reddnet.net/post.aspx?id=132671a1-6211-42c9-b951-c233aae3a8b3</guid>
      <pubDate>Thu, 01 Jul 2010 06:40:00 -0900</pubDate>
      <category>Code</category>
      <dc:publisher>Stephen M. Redd</dc:publisher>
      <pingback:server>http://reddnet.net/pingback.axd</pingback:server>
      <pingback:target>http://reddnet.net/post.aspx?id=132671a1-6211-42c9-b951-c233aae3a8b3</pingback:target>
      <slash:comments>0</slash:comments>
      <trackback:ping>http://reddnet.net/trackback.axd?id=132671a1-6211-42c9-b951-c233aae3a8b3</trackback:ping>
      <wfw:comment>http://reddnet.net/post/2010/07/01/Automate-MVC-20-EF-4-Generated-Models-with-T4-Templates-for-DataAnnotations.aspx#comment</wfw:comment>
      <wfw:commentRss>http://reddnet.net/syndication.axd?post=132671a1-6211-42c9-b951-c233aae3a8b3</wfw:commentRss>
    <feedburner:origLink>http://reddnet.net/post.aspx?id=132671a1-6211-42c9-b951-c233aae3a8b3</feedburner:origLink></item>
    <item>
      <title>Hooked on MEF - Using MEF in ASP.NET MVC, and the Nerd Dinner MEF sample fix</title>
      <description>&lt;p&gt;I'm about to embark on a major project in Silverlight 4. In advance of that project, I've been exploring some of the newer technologies such as &lt;a href="http://www.silverlight.net/getstarted/riaservices/"&gt;WCF RIA Services&lt;/a&gt; and the &lt;a href="http://mef.codeplex.com/"&gt;Microsoft Managed Extensibility Framework (MEF)&lt;/a&gt;; both of which shipped in .NET 4.0. While my usage for MEF in Silverlight is more about plug-in and modular architectures, I also have a good bit of interest in MEF as a basic IoC mechanism too.&lt;/p&gt;
&lt;p&gt;To get a handle on MEF, I decided to plug it into my TicketDesk 2.0 project, which is being written on the ASP.NET MVC platform. TicketDesk 2.0 uses a class library for all the business and entity framework bits. I generally avoid the IoC design pattern though, preferring instead to just use overloaded constructors; one that takes dependencies for unit testing, and the other that supplies default dependencies to be used by the application at runtime. This technique is common, and is sometimes called a poor-man's IoC. Insults aside, it is simple, easy to code, and works. Traditional IoC implementations on the other-hand tend to add complexity that doesn't do much to advance the application's core functionality.&lt;/p&gt;
&lt;p&gt;But MEF is interesting because it offers functionality that can improve how the application actually works, and it has the by-product of being a decent, and simple, IoC container too.&lt;/p&gt;
&lt;p&gt;Anyway, once I started using MEF for IoC in TicketDesk, I ran into a slight problem. See, MEF was designed with persistent applications like Silverlight in mind. But in an MVC web environment you have multiple user requests coming in, and many of the objects cannot easily be shared across multiple request threads. So using MEF in this environment requires that you deal with the fact that some objects have to be instantiated on a per-request basis, while others might be scoped to the entire application.&lt;/p&gt;
&lt;p&gt;Fortunately, there is this genius person named &lt;a href="http://hammett.castleproject.org/"&gt;Hamilton Verissimo de Oliveira&lt;/a&gt; (Hammett). Hammet is apparently one of the core devs on the MEF project, and he's done a good bit of writing about MEF in MVC environments. According to his blog, he's even working with the MVC team to get MEF officially supported in the ASP.NET MVC 3 platform.&lt;/p&gt;
&lt;p&gt;His most recent sample code is a modified MEF enabled version of the Nerd-Dinner MVC sample application. &lt;a href="http://www.hanselman.com/blog/ExtendingNerdDinnerAddingMEFAndPluginsToASPNETMVC.aspx"&gt;Scott Hanselman blogged about the MEF version of Nerd Dinner&lt;/a&gt; and hosts the &lt;a href="http://cid-cd06a7367371152c.office.live.com/self.aspx/Public/NerdDinner-MEF.zip"&gt;downloadable version of the code&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The code in the MEF version of Nerd Dinner is basically a beta of an extended version of MEF for use in MVC scenarios. It includes two class libraries that extend MEF and MVC. These extensions do two things:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Provide lazy MEF compositions on a per-request or per-application basis as appropriate. The per-request composition allows your application to handle compositions only for objects you need to service a request at runtime, while the per-application composition can be used for shared application scoped needs. &lt;/li&gt;
&lt;li&gt;Provide convention driven MEF design pattern. MEF is normally attribute driven, where you explicitly declare exports and imports by decorating your code with MEF attributes. But MVC applications are convention driven, so this feature set allows MEF to auto-discover composable parts based on similar conventions... for example, the nerd dinner application treats controllers as composable exports without you having to decorate them with the MEF attributes. &lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Overall, the code is a mostly complete MVC compatible set of MEF extensions. But the nerd dinner sample only really uses MEF for an IoC design pattern... the models are declared as MEF exports and the extensions handle supplying the appropriate dependencies to the controllers at runtime. But nothing about the way this same is setup should, in theory, prevent you from using MEF in other ways such as a plug-in extensibility mechanism (which is MEF's strong suit anyway).&lt;/p&gt;
&lt;p&gt;But... once I got to using the extensions in TicketDesk, I ran into a couple of unusual problems.&lt;/p&gt;
&lt;p&gt;First, code in my referenced class library wasn't being passed to my controllers. If I moved the code into the MVC app itself though, it worked like a charm. Second, code in my MVC application that was marked as exports using attributes weren't being passed into constructors for objects in my class library. The nerd dinner example contains the models and controllers both directly in the MVC application assembly itself, so these issues didn't occur there. But once you split your code into two assemblies, things didn't work too smooth. Well... this is just sample software.&lt;/p&gt;
&lt;p&gt;So... to track down and fix these problems.&lt;/p&gt;
&lt;p&gt;One of the more confusing bits about the nerd-dinner code sample is how the MEF catalogs are built when the app starts up. In global.asax.cs the sample code looks like this:&lt;/p&gt;
&lt;pre class="brush: csharp;"&gt;protected override ComposablePartCatalog CreateRootCatalog()
{
    var agg = new AggregateCatalog();

    foreach (Assembly assembly in BuildManager.GetReferencedAssemblies())
    {
        agg.Catalogs.Add(new AssemblyDiscoveryCatalog(assembly));
        agg.Catalogs.Add(new AssemblyCatalog(assembly));
    }

    return agg;
}
&lt;/pre&gt;
&lt;p&gt;Basically this just loops through all the assemblies in the application and builds an Aggregate catalog of all the MEF composable parts from each assembly. What's odd though is that, for each assembly, it builds two separate catalogs and adds both to the aggregate catalog. One is a standard AssemblyCatalog, and the other is a custom type of catalog called an AssemblyDiscoveryCatalog which is part of the extended MEF code shipped with the Nerd Dinner example.&lt;/p&gt;
&lt;p&gt;Now, the aggregate catalog is supposed to merge multiple catalogs, eliminating duplicates and all that... but why build two separate catalogs from each assembly in the first place? Shouldn't AssemblyDiscoveryCatalog alone contain all the parts from any one assembly?&lt;/p&gt;
&lt;p&gt;Now... I have been unable to understand exactly what it is that causes the problems I was seeing. When I examine the catalogs in the debugger, and the way they are used, the aggregate catalog appears to contain all the composable parts it should. And when the controller factory is invoked, it finds the right controller, and uses a container that has all the right parts in it.&lt;/p&gt;
&lt;p&gt;What I did find that was that by removing the duplicate catalog being created in global.asax.cs did fix the problem where my controllers weren't being given the imports when those parts were coming from the referenced class library.&lt;/p&gt;
&lt;p&gt;I chose to remove the AssemblyCatalog from the aggregate, leaving just the AssemblyDiscoveryCatalog being added for each reference assembly.&lt;/p&gt;
&lt;pre class="brush: csharp;"&gt;protected override ComposablePartCatalog CreateRootCatalog()
{
    var agg = new AggregateCatalog();

    foreach (Assembly assembly in BuildManager.GetReferencedAssemblies())
    {
        agg.Catalogs.Add(new AssemblyDiscoveryCatalog(assembly));
        //agg.Catalogs.Add(new AssemblyCatalog(assembly)); 
    }

    return agg;
}
&lt;/pre&gt;
&lt;p&gt;This worked fine, but then I ran into the second problem... if my class lib needed imports from the MVC application, they weren't getting them... basically the opposite problem.&lt;/p&gt;
&lt;p&gt;Digging deeper into the second problem, I was able to find a cause. The AssemblyDiscoveryCatalog type has a minor bug:&lt;/p&gt;
&lt;pre class="brush: csharp;"&gt;private IEnumerable InspectAssembliesAndBuildPartDefinitions()
{
    var parts = new List();

    foreach(var assembly in this.Assemblies)
    {
        var attributes = assembly.GetCustomAttributes(typeof(DiscoveryAttribute), true);

        if (attributes.Length == 0)
        {
             parts.AddRange(new AssemblyCatalog(assembly).Parts);
        }
        else
        {
            foreach (DiscoveryAttribute discoveryAtt in attributes)
            {
                var discovery = discoveryAtt.DiscoveryMethod.New();
                var discoveredParts = discovery.BuildPartDefinitions(assembly.GetTypes());

                parts.AddRange(discoveredParts);
            }
        }
    }

    return parts;
}
&lt;/pre&gt;
&lt;p&gt;First this code checks the assembly for a "DiscoveryAttribute". This attribute marks the entire assembly as one that contains classes for which the modified MEF system should "infer" composable parts based on conventions rather than by looking for the explicit MEF attributes. In the nerd dinner example, this attribute is declared at the top of the Conventions.cs class.&lt;/p&gt;
&lt;p&gt;Notice how the code immediately afterwards works though. If the assembly isn't marked with the DiscoveryAttribute, it uses the standard MEF mechanisms to add all the parts to the catalog; those mechanisms do so by looking for the explicit MEF attributes in the code.&lt;/p&gt;
&lt;p&gt;But when the assembly is marked with the DiscoveryAttribute, then the code does something quite different; it goes through the assembly looking for dynamically discoverable parts based on any defined conventions. It then adds those dynamically discovered parts to the catalog.&lt;/p&gt;
&lt;p&gt;And that's the bug! The branch handling DiscoveryAttribute assemblies doesn't have any code that looks for traditional MEF parts declared by attributes!&lt;/p&gt;
&lt;p&gt;So... all I had to do was modify this code so it adds both discoverable and inferred attributes both:&lt;/p&gt;
&lt;pre class="brush: csharp;"&gt;private IEnumerable InspectAssembliesAndBuildPartDefinitions()
{
    var parts = new List();

    foreach (var assembly in this.Assemblies)
    {
        var attributes = assembly.GetCustomAttributes(typeof(DiscoveryAttribute), true);

        parts.AddRange(new AssemblyCatalog(assembly).Parts); // add the standard MEF locatable parts
        if (attributes.Length &amp;gt; 0)
        {
            //add any convention inferred parts 
            foreach (DiscoveryAttribute discoveryAtt in attributes)
            {
                var discovery = discoveryAtt.DiscoveryMethod.New();
                var discoveredParts = discovery.BuildPartDefinitions(assembly.GetTypes());

                parts.AddRange(discoveredParts);
            }
        }
    }

    return parts;
}
&lt;/pre&gt;
&lt;p&gt;Now we have a single kind of catalog that can locate both inferred and explicitly declared MEF parts in any assembly. And after this change, my MVC controllers are getting imports from the class lib, and the class lib is getting imports from the MVC app.&lt;/p&gt;
&lt;p&gt;Everyone is happy...&lt;/p&gt;
&lt;p&gt;Except that it still bugs me why having the two kinds of catalog added to the aggregate catalog keeps the controllers from getting imports from my class library. I suspect strongly though that this may be some kind of bug in the core MEF implementation, perhaps with how Lazy composition actually works.&lt;/p&gt;
&lt;p&gt;But either way... fixing the bug in AssemblyDiscoveryCatalog allows you to handle everything in one kind of catalog and works around both problems.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/Reddnet?a=aKeO7zS3SHs:M4FXHTqklTA:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/Reddnet?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/Reddnet?a=aKeO7zS3SHs:M4FXHTqklTA:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/Reddnet?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/Reddnet?a=aKeO7zS3SHs:M4FXHTqklTA:y6ACzXPbQao"&gt;&lt;img src="http://feeds.feedburner.com/~ff/Reddnet?i=aKeO7zS3SHs:M4FXHTqklTA:y6ACzXPbQao" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/Reddnet?a=aKeO7zS3SHs:M4FXHTqklTA:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/Reddnet?i=aKeO7zS3SHs:M4FXHTqklTA:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/Reddnet/~4/aKeO7zS3SHs" height="1" width="1"/&gt;</description>
      <link>http://feedproxy.google.com/~r/Reddnet/~3/aKeO7zS3SHs/post.aspx</link>
      <author>Stephen M. Redd</author>
      <comments>http://reddnet.net/post/2010/06/22/Hooked-on-MEF-Using-MEF-in-ASPNET-MVC-and-the-Nerd-Dinner-MEF-sample-fix.aspx#comment</comments>
      <guid isPermaLink="false">http://reddnet.net/post.aspx?id=df0c6ef9-7d50-4484-820e-a31df4c3a0f6</guid>
      <pubDate>Tue, 22 Jun 2010 22:35:00 -0900</pubDate>
      <category>Code</category>
      <dc:publisher>Stephen M. Redd</dc:publisher>
      <pingback:server>http://reddnet.net/pingback.axd</pingback:server>
      <pingback:target>http://reddnet.net/post.aspx?id=df0c6ef9-7d50-4484-820e-a31df4c3a0f6</pingback:target>
      <slash:comments>0</slash:comments>
      <trackback:ping>http://reddnet.net/trackback.axd?id=df0c6ef9-7d50-4484-820e-a31df4c3a0f6</trackback:ping>
      <wfw:comment>http://reddnet.net/post/2010/06/22/Hooked-on-MEF-Using-MEF-in-ASPNET-MVC-and-the-Nerd-Dinner-MEF-sample-fix.aspx#comment</wfw:comment>
      <wfw:commentRss>http://reddnet.net/syndication.axd?post=df0c6ef9-7d50-4484-820e-a31df4c3a0f6</wfw:commentRss>
    <feedburner:origLink>http://reddnet.net/post.aspx?id=df0c6ef9-7d50-4484-820e-a31df4c3a0f6</feedburner:origLink></item>
    <item>
      <title>Diaspora Follow-Up: How I'd Do Social Networking...</title>
      <description>&lt;p&gt;&lt;img width="158" height="160" vspace="3" hspace="3" border="0" align="right" alt="" src="/files/media/image/critic1.jpg" /&gt;So... after having spent so much time bashing the &lt;a href="http://www.joindiaspora.com/"&gt;Diaspora&lt;/a&gt; project &lt;a href="http://reddnet.net/code/diaspora-good-idea-but/"&gt;in my last post&lt;/a&gt;, I'd promised to provide some constructive input.&lt;/p&gt;
&lt;p&gt;Here it is!&lt;/p&gt;
&lt;p&gt;Rather than address Diaspora's design directly, what I'll do instead is outline how I'd go about starting a project like Diaspora myself, with the same general goals in mind.&lt;/p&gt;
&lt;h2&gt;Name:&lt;/h2&gt;
&lt;p style="margin-left: 40px; "&gt;Any good project has to have a name, and preferably one that regular Americans, as the lowest common denominator, might pronounce correctly. It also helps not to include random special characters for no apparent reason (other than to confuse search engines perhaps).&lt;/p&gt;
&lt;p style="margin-left: 40px; "&gt;Since I'm not feeling inspired today, I'll just steal the fictional name &amp;quot;Gink&amp;quot; (from the &lt;a href="http://www.collegehumor.com/video:1935630"&gt;college humor parody video&lt;/a&gt; on social networking --very funny stuff!). Gink would have been a pretty good name for a real social-network; easy to remember, catchy, fun, and unlikely to be mispronounced... it also makes a good verb (which is one of Google's marketing strengths too). As a word, &lt;a href="http://www.urbandictionary.com/define.php?term=gink"&gt;Gink does have some definitions already&lt;/a&gt;, but none seem so well entrenched yet that we couldn't usurp it for ourselves. &amp;nbsp;&lt;/p&gt;
&lt;h2&gt;Defining the Mission:&lt;/h2&gt;
&lt;p style="margin-left: 40px; "&gt;There are two ways to go about Gink.&lt;/p&gt;
&lt;p style="margin-left: 40px; "&gt;Creating fantastic social-networking software can be the mission. Here the software is your focus, and the network a by-product of success. In the wild, it would (hopefully) evolve organically and displace entrenched social-networks through just sheer awesomeness. This seems to be the angle the Diaspora is taking, and it is a viable one --Linux started out like this.&lt;/p&gt;
&lt;p style="margin-left: 40px; "&gt;Me though, I'd set a more explicit mission. My primary goal would be to create the social-network everyone wants to use; the one that empowers users. The software would exist only as a means to achieve that mission.&lt;/p&gt;
&lt;h2&gt;Security, Privacy and Control - Organization and Politics:&lt;/h2&gt;
&lt;p style="margin-left: 40px; "&gt;Giving the users a sense of ownership of their data and control over their privacy is the driving factor for Diaspora's public interest, and is central to its design. Even with Gink's slightly modified mission, this still remains the primary requirement.&lt;/p&gt;
&lt;p style="margin-left: 40px; "&gt;So let's start by solving this problem with Gink...&lt;/p&gt;
&lt;p style="margin-left: 40px; "&gt;As I mentioned in my last post, these requirements have been misunderstood by the Diaspora team. Users want control over how their data is used, and a sense of ownership. They want a binding promise that the system will not abuse their privacy. The part Diaspora misunderstands is that users have no interest in taking responsibility for the technical details themselves. They don't want to manage infrastructure, nor keep up with the data themselves.&lt;/p&gt;
&lt;p style="margin-left: 40px; "&gt;At heart, this is a just a trust issue. The requirement is political, and political requirements are best solved through political architectures, not technical ones.&lt;/p&gt;
&lt;p style="margin-left: 40px; "&gt;Since our team has the mission of providing a social-network, not just to develop software; the easiest way to handle this whole trust thing is by establishing a formal non-profit organization to oversee the network itself.&lt;/p&gt;
&lt;p style="margin-left: 40px; "&gt;It is this organization to which I'd give the name &amp;quot;Gink&amp;quot;.&lt;/p&gt;
&lt;p style="margin-left: 40px; "&gt;The org can make enough promises in its charter, sign-up policies, etc. to solve the trust issues. The promises would take lawyer time to word up, but the highlights would be:&lt;/p&gt;
&lt;ul style="margin-left: 40px; "&gt;
    &lt;li&gt;The system would not be a vehicle for advertisers; an ad-free network.&lt;/li&gt;
    &lt;li&gt;Users would have full control of their own personal data and how it used&lt;/li&gt;
    &lt;li&gt;Users would have the ability to examine all data the system collected about them (down to the logs) at any time.&lt;/li&gt;
    &lt;li&gt;The network would not censor user generated content.&lt;/li&gt;
    &lt;li&gt;The network would not be able to sell or disclose personal data (individually, or in aggregate) to 3rd parties.&lt;/li&gt;
    &lt;li&gt;Any non-personal data that may be shared would be public to all.&lt;/li&gt;
    &lt;li&gt;A prohibition against the transfer of ownership of the data --the network cannot be bought out by someone else who then changes the rules.&lt;/li&gt;
    &lt;li&gt;These basic promises would be unalterable after the fact; none of that &amp;quot;we reserve the right to screw you with 30 days written notice&amp;quot; trickery.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="margin-left: 40px; "&gt;There are probably several other promises to make too, but being a non-profit organization and having made our policy plain, unalterable and user-centric, we've pretty much solved the critical trust issues; and without any code at all.&lt;/p&gt;
&lt;p style="margin-left: 40px; "&gt;All the code has to do is avoid leaking data all over the place.&lt;/p&gt;
&lt;p style="margin-left: 40px; "&gt;The Gink organization would be chartered to oversee the network itself and steward the direction of the &amp;quot;official&amp;quot; branch of the software. It would also completely control the infrastructure of the network, though it need not necessarily own that infrastructure directly.&lt;/p&gt;
&lt;h2&gt;System Architecture:&lt;/h2&gt;
&lt;p style="margin-left: 40px; "&gt;In my previous post, I also went into detail about my misgivings on Diaspora's technical design. Having solved Gink's trust issues, a fully distributed system of user operated peer-server nodes is unnecessary. Such a design would not advance Gink's mission; which is just to provide a network of pure amazing!&lt;/p&gt;
&lt;p style="margin-left: 40px; "&gt;Still though, a system like this will be very large, and a distributed architecture of some type is absolutely essential. We control the infrastructure ourselves though, so we can manage a lot more architectural diversity on the back-end.&lt;/p&gt;
&lt;p style="margin-left: 40px; "&gt;I would not design a &amp;quot;do-everything&amp;quot; kind of distributed server, such as what Diaspora proposes. Instead, I'd break the system down into separate service roles; each having its own server design using whatever architecture best supports that particular role.&lt;/p&gt;
&lt;p style="margin-left: 40px; "&gt;Why should my aggregation services be limited by constraints imposed by the data storage architecture?&lt;/p&gt;
&lt;p style="margin-left: 40px; "&gt;I'm not going to go into specifics about the design here. We have not fleshed out the requirements enough to even begin thinking about more specific details yet.&lt;/p&gt;
&lt;h2&gt;Software Project:&lt;/h2&gt;
&lt;p style="margin-left: 40px; "&gt;While the Gink network is operated as a non-profit organization, there is no reason to develop the software through the same organization. There several good reasons not too though. As non-profit, employees of the org would be limited in any income potential... Not that the org can't pay them well, but there may be opportunities related to the software beyond just the Gink network itself. Separating the development from the non-profit would allow the development team to capitalize on other opportunities without unnecessary restrictions.&lt;/p&gt;
&lt;p style="margin-left: 40px; "&gt;So I'd develop the software though a different organization, which we'll call &amp;quot;Ginkworks&amp;quot; for lack of a better name. Ginkworks would produce open, royalty-free specs for the protocols, APIs, etc. of the Gink network. It would then develop an open source reference implementation of those specs.&lt;/p&gt;
&lt;p style="margin-left: 40px; "&gt;The version of the software in use on the Gink network itself doesn't have to be the same internally as the reference implementation. There are cases where the network, due to practical realities, might need different or extended internal implementations... as long as it conforms to the open specs, this should be allowed where needed.&lt;/p&gt;
&lt;p style="margin-left: 40px; "&gt;This is how Google runs projects like Wave, and Microsoft did much the same with the .NET CLR and C# languages. This strategy seems to work quite well, allowing each organization to use the best political architecture to meet their different requirements in much the same way as our system architecture does.&lt;/p&gt;
&lt;h2&gt;Driving User Adoption:&lt;/h2&gt;
&lt;p style="margin-left: 40px; "&gt;In order to be successful, we need to get users to switch to using our network as their primary social networking tool. This is the hardest part of the entire plan by far. It isn't enough to just enable users to do what they can do on other networks. You have to give them reasons beyond that, or they will just stay where they are comfortable.&lt;/p&gt;
&lt;p style="margin-left: 40px; "&gt;This is the make or break requirement, and will decide the priorities and focus for the rest of the project's design. The focus would be on user features, and all back-end considerations must align to these.&lt;/p&gt;
&lt;p style="margin-left: 40px; "&gt;I can identify four major areas of user feature enhancement that could generate the needed attractiveness for user switch-over. There are probably others, but these are the ones that stand out to me.&lt;/p&gt;
&lt;h3 style="margin-left: 40px; "&gt;Interoperability:&lt;/h3&gt;
&lt;p style="margin-left: 80px; "&gt;Here Diaspora has the right idea. To get users on the system, you have will have let them bring their established networks with them. They aren't going to give up existing communities that they've spent years building on other sites. So the system must aggregate the user's other online identities, and allow them to participate across multiple networks simultaneously.&lt;/p&gt;
&lt;p style="margin-left: 80px; "&gt;You can see the general shape of this already, especially in the smart-phone market, with social aggregator apps, but there is a lot more that needs to be done in this area.&lt;/p&gt;
&lt;p style="margin-left: 80px; "&gt;The interoperation needs to be dumb-ass simple; so intuitive, seamless and effortless that cross participation appears to just happen by pure-fucking-magic (for you non-programmer types, this is a real technical term).&lt;/p&gt;
&lt;p style="margin-left: 80px; "&gt;This interoperation needs to be bi-directional and highly cooperative, not just a read-only aggregation. For example, if users like flickr better than gink's own photo systems, let them use flickr through gink as if it were a native part of our system (as much as it is possible to do so, of course).&lt;/p&gt;
&lt;p style="margin-left: 80px; "&gt;This will probably be the hardest of the technical challenges to meet since interoperation depends on the APIs of other networks, which are beyond our control and highly variable in their capabilities.&lt;/p&gt;
&lt;h3 style="margin-left: 40px; "&gt;Mobile:&lt;/h3&gt;
&lt;p style="margin-left: 80px; "&gt;So far, the other social networks are providing mobile support as a secondary add-on service. Their mobile experience is limited to a sub-set of the network's features. But with social networks in particular, mobile devices are fast becoming the primary means by which people interact.&lt;/p&gt;
&lt;p style="margin-left: 80px; "&gt;I'd develop the entire system as if the primary clients were mobile devices, treating full desktop browsers as a secondary design consideration. There will still be a small set of features that just can't be adapted to mobile (yet) of course, but these should be limited to admin and management functions where possible.&lt;/p&gt;
&lt;p style="margin-left: 80px; "&gt;There are also some opportunities to provide features that leverage the unique strengths of mobile devices too. The most obvious of these are location services, and this is an area of opportunity that the competition seems to largely ignore (except Google).&lt;/p&gt;
&lt;h3 style="margin-left: 40px; "&gt;Neglected Social Networks:&lt;/h3&gt;
&lt;p style="margin-left: 80px; "&gt;One of the bigger failing of the major social networks is that they all fail to recognize older social network technologies, many of which are quite well established.&lt;/p&gt;
&lt;p style="margin-left: 80px; "&gt;Email is the most obvious one. Email is very much a form of social networking; and is the oldest such mainstream service still in existence. The existing social nets all treat email as a just an out-of-band notification channel for their own convenience. At best, they provide a poor-man's &amp;quot;message&amp;quot; feature, but otherwise treat actual email like it were a plague (which it probably is).&lt;/p&gt;
&lt;p style="margin-left: 80px; "&gt;But why duplicate, badly, email-like functionality in Gink when we can try to aggregate real email into our system? So I'd try to treat this as if it were just another social network users participate in.&lt;/p&gt;
&lt;p style="margin-left: 80px; "&gt;Online forums are another neglected social network that needs to be pulled in, along with users' blogs, if they have one.&lt;/p&gt;
&lt;p style="margin-left: 80px; "&gt;I might not put much effort into real-time chat networks though. I'd have to investigate this area more myself to be sure how to handle it.&lt;/p&gt;
&lt;h3 style="margin-left: 40px; "&gt;Non-Social Information Streams:&lt;/h3&gt;
&lt;p style="margin-left: 80px; "&gt;Social Networking, from the user's point of view, is largely just a personalized information stream. But on most networks, one of the most popular sets of 3rd party add-on features are ones that bring in read-only subscriptions and news. Since we're aggregating so much already, I'd want to subscription service aggregation to be a core, and first-class, feature set.&lt;/p&gt;
&lt;p style="margin-left: 40px; "&gt;These four broad swaths of user-centric feature sets are things that might generate enough user acceptances to get them to switch. Of course, you also have to do a bang-up job on the expected feature sets as well.&lt;/p&gt;
&lt;p style="margin-left: 40px; "&gt;So before I started on the deep technical nitty-gritty of the Gink back-end architecture, we'd flesh out as much about these user features as we can. Identify how these features might work, so we can ensure that we build a framework that directly addresses the technical needs of our feature set.&lt;/p&gt;
&lt;p&gt;And that's how I'd go about starting a social networking project.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/Reddnet?a=6f85nxpILpM:MRkOAOJQF8s:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/Reddnet?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/Reddnet?a=6f85nxpILpM:MRkOAOJQF8s:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/Reddnet?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/Reddnet?a=6f85nxpILpM:MRkOAOJQF8s:y6ACzXPbQao"&gt;&lt;img src="http://feeds.feedburner.com/~ff/Reddnet?i=6f85nxpILpM:MRkOAOJQF8s:y6ACzXPbQao" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/Reddnet?a=6f85nxpILpM:MRkOAOJQF8s:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/Reddnet?i=6f85nxpILpM:MRkOAOJQF8s:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/Reddnet/~4/6f85nxpILpM" height="1" width="1"/&gt;</description>
      <link>http://feedproxy.google.com/~r/Reddnet/~3/6f85nxpILpM/post.aspx</link>
      <author>Stephen M. Redd</author>
      <comments>http://reddnet.net/post/2010/05/26/Diaspora-Follow-Up-How-Id-Do-Social-Networking.aspx#comment</comments>
      <guid isPermaLink="false">http://reddnet.net/post.aspx?id=1f94a1b7-24f5-441b-971d-23fb7aee1a28</guid>
      <pubDate>Wed, 26 May 2010 10:12:00 -0900</pubDate>
      <category>Code</category>
      <dc:publisher>Stephen M. Redd</dc:publisher>
      <pingback:server>http://reddnet.net/pingback.axd</pingback:server>
      <pingback:target>http://reddnet.net/post.aspx?id=1f94a1b7-24f5-441b-971d-23fb7aee1a28</pingback:target>
      <slash:comments>2</slash:comments>
      <trackback:ping>http://reddnet.net/trackback.axd?id=1f94a1b7-24f5-441b-971d-23fb7aee1a28</trackback:ping>
      <wfw:comment>http://reddnet.net/post/2010/05/26/Diaspora-Follow-Up-How-Id-Do-Social-Networking.aspx#comment</wfw:comment>
      <wfw:commentRss>http://reddnet.net/syndication.axd?post=1f94a1b7-24f5-441b-971d-23fb7aee1a28</wfw:commentRss>
    <feedburner:origLink>http://reddnet.net/post.aspx?id=1f94a1b7-24f5-441b-971d-23fb7aee1a28</feedburner:origLink></item>
    <item>
      <title>Diaspora - Good Idea, But...</title>
      <description>&lt;p&gt;I've been watching the &lt;a href="http://www.joindiaspora.com/"&gt;Diaspora&lt;/a&gt; project carefully for little while now. If you aren't familiar with it, the simplest explanation is that it is an open-source version of facebook where users owns their own server.&lt;/p&gt;
&lt;p&gt;&lt;img vspace="3" hspace="3" border="0" align="right" alt="Diaspora" src="/files/media/image/diaspora_handouts_printout.png" /&gt;&lt;/p&gt;
&lt;p&gt;Diaspora's own explanation is not so concise, but here it is:&lt;/p&gt;
&lt;p style="margin-left: 40px; "&gt;Diaspora aims to be a distributed network, where totally separate computers connect to each other directly, will let us connect without surrendering our privacy. We call these computers &amp;lsquo;seeds&amp;rsquo;. A seed is owned by you, hosted by you, or on a rented server. Once it has been set up, the seed will aggregate all of your information: your facebook profile, tweets, anything. We are designing an easily extendable plugin framework for Diaspora, so that whenever newfangled content gets invented, it will be automagically integrated into every seed.&lt;/p&gt;
&lt;p style="margin-left: 40px; "&gt;[&lt;a href="http://www.joindiaspora.com/2010/04/21/a-little-more-about-the-project.html"&gt;read the rest here&lt;/a&gt;]:&amp;nbsp;&lt;/p&gt;
&lt;p&gt;I've been looking for something like this to come along for a while. There have been a few feeble attempts so far, but none have gained any traction in the wild yet... and I'm not expecting this one to either.&lt;/p&gt;
&lt;p&gt;Here's my take on it...&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;The Diaspora project has gotten a lot of media attention recently including a feature story in the New York Times. The reason it has gotten so much coverage is mostly just lucky timing. They happened to launch &lt;a href="http://www.kickstarter.com/projects/196017994/diaspora-the-personally-controlled-do-it-all-distr"&gt;a $10k request for donations&lt;/a&gt; on &lt;a href="http://kickstarter.com"&gt;kickstarter.com&lt;/a&gt; right as privacy and security concerns with Facebook became a mainstream press issue. Being lazy, the media just latched onto Diaspora as the underdog counter-story to Facebook, rather than do something rash like actual journalism.&lt;/p&gt;
&lt;p&gt;Diaspora, thanks to the coverage spike, has accumulated over $175,000; much more than the mere $10k they'd originally asked for. If the donations keep rolling in, they can just skip code and go straight to being famous, rich, and retired.&lt;/p&gt;
&lt;p&gt;I'm always pretty excited by these kinds of projects though, and I love the general idea of an open source social network. And, really, who doesn't like a good under-dog?&lt;/p&gt;
&lt;p&gt;But, despite my enthusiasm, I am not all that impressed by what I've seen and read about Diaspora so far. I don't have a good feeling about the technical approach, nor the fundamental concepts.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Fundamental Concepts:&lt;/strong&gt;&lt;/p&gt;
&lt;p style="margin-left: 40px; "&gt;The project's fundamentals are subtly flawed in at least two very important ways.&lt;/p&gt;
&lt;p style="margin-left: 40px; "&gt;The first is related to the design architecture; that Diaspora will be a distributed multi-node system.&lt;/p&gt;
&lt;p style="margin-left: 40px; "&gt;The idea of distributed systems itself is quite solid of course. Spread storage and processing out to as many machines as possible, each fully responsible for their own information domains. This is the principle upon which the web itself operates, so it is well proven.&lt;/p&gt;
&lt;p style="margin-left: 40px; "&gt;Diaspora's flaw is the assumption that every user will &amp;quot;own their own server&amp;quot;.&lt;/p&gt;
&lt;p style="margin-left: 40px; "&gt;This isn't 1994!&lt;/p&gt;
&lt;p style="margin-left: 40px; "&gt;Social networking users are regular people, and regular people do NOT want to manage a server node. It doesn't matter how easy you make node management!&lt;/p&gt;
&lt;p style="margin-left: 40px; "&gt;Users can barely manage copy and paste, and you want to put them in charge of a distributed information server?&lt;/p&gt;
&lt;p style="margin-left: 40px; "&gt;I think the team has completely misunderstood the user's requirements here. Users &amp;quot;&lt;em&gt;want control &lt;/em&gt;&lt;strong&gt;of&lt;/strong&gt; &lt;em&gt;their data&amp;quot;&lt;/em&gt;. They never said that they &amp;quot;&lt;em&gt;want&lt;/em&gt; &lt;strong&gt;to&lt;/strong&gt; &lt;em&gt;control their data&amp;quot;&lt;/em&gt;. This isn't just semantics; it is the critical distinction between what they want, and what Diaspora is proposing to build.&lt;/p&gt;
&lt;p style="margin-left: 40px; "&gt;For several years, the trend among users, even the technical ones, has been away from user owned software. Today's users don't want to install or configure anything themselves. They use pure internet apps almost exclusively. The same is true with user data. They don't want to have to deal with keeping track of it, or backing it up, or moving it around. In short, they don't want the responsibility for all that technical crap. They just want to use the software, have the data, and have someone else to blame when things break or data gets lost.&lt;/p&gt;
&lt;p style="margin-left: 40px; "&gt;You can see this user trend clearly in the rise of gmail, youtube, flickr, google apps, etc. Wherever possible, people are steadily switching to web supplied software, and are moving data off of their own vulnerable machines into the cloud. All the users have been asking for is a sense of control over what the cloud does with the data once it gets there.&lt;/p&gt;
&lt;p style="margin-left: 40px; "&gt;The second conceptual flaw is more subtle. It is the simple lack of focus on end-user features. Their explanations of the user features are, at best, vague; when they bother to mention them at all.&lt;/p&gt;
&lt;p style="margin-left: 40px; "&gt;Their focus seems confined to the technical back-end details. Even worse, they use the term &amp;quot;feature&amp;quot; to label the various plumbing aspects of the system. These are not features, these are mechanics. Mechanics are just the means to the ends; it is in the ends that the features hide.&lt;/p&gt;
&lt;p style="margin-left: 40px; "&gt;My concern here is really that the team just doesn't seem to understand their true goal well enough to build towards it reliably. They seem focused too narrowly on &amp;quot;how&amp;quot;, but not enough on &amp;quot;what&amp;quot;.&lt;/p&gt;
&lt;p style="margin-left: 40px; "&gt;The plan appears to be to design and build a framework first, and deal with user features later. They say so on their blog in some detail actually, but frameworks are not applications.&lt;/p&gt;
&lt;p style="margin-left: 40px; "&gt;I like the iterative and pragmatic approach they describe, but they are already building a framework and still they don't provide coherent explanations of actual user facing features. They only describe them in abstract vagaries; and this is what concerns me.&lt;/p&gt;
&lt;p style="margin-left: 40px; "&gt;This kind of thinking is tragically common in application design, and it rarely turns out well. If you don't clearly define the user features you are aiming for, in quite some detail, then you cannot know if you are building the *right* framework; one that will usefully support the user features you'll layer on top later. The framework is likely to be too generic to be very useful during the real feature development stages. When developing front-end code, nothing is harder than having to code against an abstract framework's limitations and constraints, but not getting much help with the very concrete requirements you are faced with.&lt;/p&gt;
&lt;p style="margin-left: 40px; "&gt;There is nothing wrong with building a great framework of course, but the team is getting donations based on the promise of a better social-networking application; not just a social networking framework.&lt;/p&gt;
&lt;p style="margin-left: 40px; "&gt;Perhaps the fault just lies in the public's misconception about what Diaspora is proposing to build.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Technical Approach:&lt;/strong&gt;&lt;/p&gt;
&lt;p style="margin-left: 40px; "&gt;Distributed systems are difficult, but distributed systems with a variable number of member nodes, coming and going at will, are many orders of magnitude more so.&lt;/p&gt;
&lt;p style="margin-left: 40px; "&gt;Then there are the problems of distributed document and NoSQL database systems, which are still rather new and immature technologies. Even the best of these still have serious limitations.&lt;/p&gt;
&lt;p style="margin-left: 40px; "&gt;Performance is a significant concern in such a system. Nodes have to be able to locate and query other nodes for data very quickly, and a social network will need to hit a large number of peer-nodes for even the simplest user features.&lt;/p&gt;
&lt;p style="margin-left: 40px; "&gt;And then there is the problem of fault-tolerance in such a highly distributed system. When a member node is down, slow, or unreachable, the system has to gracefully degrade, without error, and without significant disruption in the user's experience.&lt;/p&gt;
&lt;p style="margin-left: 40px; "&gt;Any of these areas present the kind of technical challenge that even companies the size of Google struggle with, but few systems combine so many such elements as Diaspora would. Their back-end architecture is up against some of the nastiest technical challenges the field has to offer, plus competing with established players whose centralized systems do not suffer the same.&lt;/p&gt;
&lt;p style="margin-left: 40px; "&gt;To me, the worst part is that, after having overcome the design problems, the system itself will have gained almost nothing that would add any value for the users. I can't think of anything this design allows Diaspora's users to do, that a centralized social network doesn't also provide. Even the promise of giving users control over their data is not in any way unique to this design. Facebook can give users the same control, and with little technical effort (though, it would have a severe impact to their business model).&lt;/p&gt;
&lt;p style="margin-left: 40px; "&gt;Despite my misgivings about the architecture, I still think Diaspora should try this approach. Just because I shat all over it, doesn't mean it's wrong to give it a try. If nothing else, they might advance the applied sciences of distributed system design. Even if Diaspora doesn't succeed with a distributed social-network, someone eventually will.&lt;/p&gt;
&lt;p style="margin-left: 40px; "&gt;Whether real users will care or not is an entirely different matter. With the team putting so much effort into the complex back-end architecture, they will likely not have the resources left to do a bang-up job on the front-end features; at least not quickly enough to still be relevant.&lt;/p&gt;
&lt;p style="margin-left: 40px; "&gt;The front-end features have to be amazing, or the system will not have much user interest.  They are up against well-established competition that has had a lot of time to evolve and design their UIs. Creating a compelling experience to compete with them will be quite a daunting, and non-trivial, technical challenge all by itself.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Next...&lt;/strong&gt;&lt;/p&gt;
&lt;p style="margin-left: 40px; "&gt;It isn't fair to spend this much text bashing someone else's project without at least doing them the courtesy of providing constructive suggestions or alternative ideas. But, I'm going to save the constructive ideas for &lt;a href="http://reddnet.net/code/diaspora-follow-up-how-i-d-do-social-networking/"&gt;another post&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/Reddnet?a=Ul0mX0O0B68:E4FOX4s7cxw:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/Reddnet?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/Reddnet?a=Ul0mX0O0B68:E4FOX4s7cxw:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/Reddnet?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/Reddnet?a=Ul0mX0O0B68:E4FOX4s7cxw:y6ACzXPbQao"&gt;&lt;img src="http://feeds.feedburner.com/~ff/Reddnet?i=Ul0mX0O0B68:E4FOX4s7cxw:y6ACzXPbQao" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/Reddnet?a=Ul0mX0O0B68:E4FOX4s7cxw:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/Reddnet?i=Ul0mX0O0B68:E4FOX4s7cxw:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/Reddnet/~4/Ul0mX0O0B68" height="1" width="1"/&gt;</description>
      <link>http://feedproxy.google.com/~r/Reddnet/~3/Ul0mX0O0B68/post.aspx</link>
      <author>Stephen M. Redd</author>
      <comments>http://reddnet.net/post/2010/05/22/Diaspora-Good-Idea-But.aspx#comment</comments>
      <guid isPermaLink="false">http://reddnet.net/post.aspx?id=0968335e-cdc2-4984-a788-4816a4f69f1a</guid>
      <pubDate>Sat, 22 May 2010 13:21:00 -0900</pubDate>
      <category>Code</category>
      <dc:publisher>Stephen M. Redd</dc:publisher>
      <pingback:server>http://reddnet.net/pingback.axd</pingback:server>
      <pingback:target>http://reddnet.net/post.aspx?id=0968335e-cdc2-4984-a788-4816a4f69f1a</pingback:target>
      <slash:comments>4</slash:comments>
      <trackback:ping>http://reddnet.net/trackback.axd?id=0968335e-cdc2-4984-a788-4816a4f69f1a</trackback:ping>
      <wfw:comment>http://reddnet.net/post/2010/05/22/Diaspora-Good-Idea-But.aspx#comment</wfw:comment>
      <wfw:commentRss>http://reddnet.net/syndication.axd?post=0968335e-cdc2-4984-a788-4816a4f69f1a</wfw:commentRss>
    <feedburner:origLink>http://reddnet.net/post.aspx?id=0968335e-cdc2-4984-a788-4816a4f69f1a</feedburner:origLink></item>
    <item>
      <title>In the South: I am Me, Just Don't Ask Y</title>
      <description>&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;Southerners have never been all that fond of the letter &amp;quot;i&amp;quot;. It's just too pointy and ornery.&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;So deep is this prejudice that when spoken by a southerner, the word &amp;quot;I&amp;quot; will have two distinct syllables neither of which, when spelled phonetically, will actually contain the letter &amp;quot;i&amp;quot;.&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;Northerners have always insisted that the only correct way to refer to oneself in the subject of a sentence is with the word &amp;quot;I&amp;quot;.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;This pisses southerners off, and&amp;nbsp;would probably have caused a second civil war had it not been for the fact that southerners could avoid pronouncing the &amp;quot;i&amp;quot; in &amp;quot;I&amp;quot;. Most of the time they could work around it completely by re-arranging sentences to accommodate the much preferable &amp;quot;me&amp;quot; instead.&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;In case you were wondering, when spoken aloud by a southerner, the word &amp;quot;me&amp;quot; also has two syllables.&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;There are no southern mono-syllables, except perhaps with the word &amp;quot;terrace&amp;quot;.&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;Mississippi, whose name was overrun with refugee &amp;quot;i&amp;quot;s after the civil war, is particularly touchy about the letter &amp;quot;i&amp;quot;.&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;An unfortunate, and unwise, northerner visiting Mississippi by mistake, once remarked that if you listened close there seems to be a subtle &amp;quot;i&amp;quot; sound somewhere in the southern pronunciation of the word &amp;quot;me&amp;quot;.&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;Upon hearing this, the &amp;quot;m&amp;quot; turned bright red. The &amp;quot;h&amp;quot; said nothing (it never does). And &amp;quot;y&amp;quot; looked somewhat guilty and stared at her feet until a whole shit-load of &amp;quot;e&amp;quot;s cleared their throats.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;The northerner was later found dead; shot three times in the back with a double barrel shotgun.&amp;nbsp;The corner ruled it a suicide, and everyone agrees that this is the only logical explanation for the death.&lt;/div&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;There is no point to this story.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/Reddnet?a=QphYdDVgyUA:psJinh2i6Tk:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/Reddnet?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/Reddnet?a=QphYdDVgyUA:psJinh2i6Tk:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/Reddnet?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/Reddnet?a=QphYdDVgyUA:psJinh2i6Tk:y6ACzXPbQao"&gt;&lt;img src="http://feeds.feedburner.com/~ff/Reddnet?i=QphYdDVgyUA:psJinh2i6Tk:y6ACzXPbQao" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/Reddnet?a=QphYdDVgyUA:psJinh2i6Tk:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/Reddnet?i=QphYdDVgyUA:psJinh2i6Tk:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/Reddnet/~4/QphYdDVgyUA" height="1" width="1"/&gt;</description>
      <link>http://feedproxy.google.com/~r/Reddnet/~3/QphYdDVgyUA/post.aspx</link>
      <author>Stephen M. Redd</author>
      <comments>http://reddnet.net/post/2010/05/13/In-the-South-I-am-Me-Just-Dont-Ask-Y.aspx#comment</comments>
      <guid isPermaLink="false">http://reddnet.net/post.aspx?id=2cd9eeab-3434-411b-b74a-3d982dec81e3</guid>
      <pubDate>Thu, 13 May 2010 11:48:00 -0900</pubDate>
      <category>Rants &amp; Stupidity</category>
      <dc:publisher>Stephen M. Redd</dc:publisher>
      <pingback:server>http://reddnet.net/pingback.axd</pingback:server>
      <pingback:target>http://reddnet.net/post.aspx?id=2cd9eeab-3434-411b-b74a-3d982dec81e3</pingback:target>
      <slash:comments>0</slash:comments>
      <trackback:ping>http://reddnet.net/trackback.axd?id=2cd9eeab-3434-411b-b74a-3d982dec81e3</trackback:ping>
      <wfw:comment>http://reddnet.net/post/2010/05/13/In-the-South-I-am-Me-Just-Dont-Ask-Y.aspx#comment</wfw:comment>
      <wfw:commentRss>http://reddnet.net/syndication.axd?post=2cd9eeab-3434-411b-b74a-3d982dec81e3</wfw:commentRss>
    <feedburner:origLink>http://reddnet.net/post.aspx?id=2cd9eeab-3434-411b-b74a-3d982dec81e3</feedburner:origLink></item>
    <item>
      <title>How Apple Will Lose the Mobile Computing Race</title>
      <description>&lt;p&gt;&lt;img alt="bad apple" width="242" height="257" vspace="3" hspace="3" border="0" align="right" src="/files/media/image/bad-apple-skull.jpg" /&gt;I've heard several people say that &amp;quot;Apple is the new Microsoft&amp;quot;. These people are wrong; dead wrong.&lt;/p&gt;
&lt;p&gt;Apple is the new Apple. They've not changed in the last 20 years, not even a little bit.&lt;/p&gt;
&lt;p&gt;Google is the new Microsoft, and Apple is going to lose the mobile device market to Google the exact same way it lost the desktop market to Microsoft 20 years ago.&lt;/p&gt;
&lt;p&gt;How is this going to happen? First you have to know the history.&lt;/p&gt;
&lt;p&gt;Let me tell you a little story...&lt;/p&gt;
&lt;p style="margin-left: 40px; "&gt;Back in the 80's there were two companies; Apple and Microsoft. Suddenly Apple comes out with a magic new product way ahead of the competition. Enter the Macintosh. The Mac was guaranteed to change personal computing forever, and everyone knew it before the Mac even hit the shelves. There was media hype and gobs of nerd excitement well in advance of the Mac's debut. And the Mac delivered spectacularly on all of its promises.&lt;/p&gt;
&lt;p style="margin-left: 40px; "&gt;But Apple insisted on near complete control of the entire platform; hardware and soft. They were terrified that someone else might come along and spoil the pure awesome.&lt;/p&gt;
&lt;p style="margin-left: 40px; "&gt;Apple used deliberately unusual hardware, and controlled it with an iron fist. Only Apple and a few licensees were allowed to make Mac hardware. The 3rd party accessory market was as restricted as Apple could make it too.&lt;/p&gt;
&lt;p style="margin-left: 40px; "&gt;Programmers could make applications for the Mac of course, but Apple maintained tight control over how applications used the hardware and interacted with the OS. Developers played in the sandbox, but it was Apple and select partners that created the majority of useful Mac software.&lt;/p&gt;
&lt;p style="margin-left: 40px; "&gt;Apple was also territorial about who could sell their precious. Macs were distributed mostly through select retail partners. Anyone else that wanted to sell a Mac paid full retail price --which made it very hard to compete with Apple's preferred retailers.&lt;/p&gt;
&lt;p style="margin-left: 40px; "&gt;Because Apple had tight control end-to-end, the Mac was a very stable and reliable computer. Most of the software was also high quality, which made the Mac a joy to use.&lt;/p&gt;
&lt;p style="margin-left: 40px; "&gt;On the other side, you had Microsoft. They weren't really a hardware company though. They concentrated on making a decent operating system and left the hardware to other companies that specialized in that sort of thing.&lt;/p&gt;
&lt;p style="margin-left: 40px; "&gt;There was a lot of compromise and cooperation between Microsoft and the hardware makers though, of which there were many. They created a large market with a wide range of different systems, each with different capabilities, quality, and prices. Microsoft just did what they could to make sure that Windows worked reasonably well with whatever crazy-ass hardware the other guys came up with.&lt;/p&gt;
&lt;p style="margin-left: 40px; "&gt;Microsoft made a decent enough OS, but what they did really well was make programming languages, compilers, and development tools --all that stuff you need in order to make software for the regular folks. Microsoft made it easy to develop applications for Windows, and they stayed out of the way as much as possible. They didn't put up a fight when developers released software to compete with Microsoft's own, and they didn't stop developers from extending the operating system itself in new and unusual directions.&lt;/p&gt;
&lt;p style="margin-left: 40px; "&gt;Microsoft was later than Apple in coming out with a good GUI based OS. Windows wasn't too pretty nor much fun compared to the Mac; especially at first. But PCs were cheaper, and came in a variety that fit different people's needs and budgets. The most important thing was that a Windows PC could do everything that a Mac could, though not always with the elegance of the Mac. People often preferred a Mac, but they could (and did) settle for PCs instead.&lt;/p&gt;
&lt;p style="margin-left: 40px; "&gt;In the end, what killed Apple the first time around was Apple's own paranoia. All by itself, Apple couldn't evolve the Mac hardware nor the OS fast enough to keep up with a young and rapidly changing personal computer market.&lt;/p&gt;
&lt;p style="margin-left: 40px; "&gt;All Microsoft had to do was try and keep up while other companies raced forward with new ideas.&lt;/p&gt;
&lt;p&gt;Now jump forward 20 years.&lt;/p&gt;
&lt;p style="margin-left: 40px; "&gt;There are two companies; Apple and Google. Suddenly Apple comes out with a magic new product way ahead of the competition. Enter the iPhone. The iPhone was guaranteed to change mobile computing forever, and everyone knew it before the iPhone even hit the shelves. There was media hype and gobs of nerd excitement well in advance of the iPhone's debut; and the iPhone delivered spectacularly on all of its promises.&lt;/p&gt;
&lt;p style="margin-left: 40px; "&gt;But Apple insists on near complete control of the entire platform; hardware and soft. They are terrified that someone else might come along and spoil the pure awesome.&lt;/p&gt;
&lt;p style="margin-left: 40px; "&gt;Apple uses deliberately unusual hardware, and they control it with an iron fist. Only apple is allowed to make iPhones, and the 3rd party accessory market is as restricted as Apple can make it.&lt;/p&gt;
&lt;p style="margin-left: 40px; "&gt;Programmers can (and do) make applications for the iPhone of course, but Apple maintains near complete control over how those applications use the hardware and interact with the OS. Developers are allowed to play in the sandbox only if they use the specific tools that apple permits. Apple and a few select partners make the majority of the useful iPhone apps. Most developers that make apps to competes with Apple's own are denied access to the app store; which is the only way users can obtain apps for the iPhone. Apple will even deny an app if they just don't like what it does.&lt;/p&gt;
&lt;p style="margin-left: 40px; "&gt;Apple is territorial about who sells their precious. The iPhone is (currently) only distributed through one network in the U.S., and apple exerts enormous control over how that network does business. AT&amp;amp;T had to wait to roll out 3G services until there was an iPhone that also supported it. There were other mobile phones sold by AT&amp;amp;T that had &amp;nbsp;3G support as much as a full year before, and AT&amp;amp;T had deployed the network hardware long before Apple's 3G iPhone came out.&lt;/p&gt;
&lt;p style="margin-left: 40px; "&gt;Because Apple is in such tight control, the iPhone is a very stable and reliable smartphone. Most of the software is also of high quality, which makes the iPhone a joy to use.&lt;/p&gt;
&lt;p style="margin-left: 40px; "&gt;On the other side, you have Google. They aren't really a hardware company though. They make a good mobile operating system, but have left most of the hardware in the hands of companies that specialized in that sort of thing.&lt;/p&gt;
&lt;p style="margin-left: 40px; "&gt;There is a lot of compromise and cooperation between Google and the many hardware makers. They have created large market with a wide range of different Android phones with different capabilities, quality, and price. Google just does what it can to make sure the Android OS works reasonably well with whatever crazy-ass hardware the other guys come up with.&lt;/p&gt;
&lt;p style="margin-left: 40px; "&gt;Google makes a good mobile OS, but what they do really well is provide cloud services --services developers can use to make useful mobile software for the regular folks. Google makes it easy to build applications for Android, and supply much of the cloud services free of charge. They stay out of the way as much as possible, but give developers a way to distribute software through a centralized app store, with very few restrictions. Apps can also be obtained through independent channels too. Google doesn't put up a fight when developers release software to compete with Google's own products, and they don't stop developers from extending the Android OS in new and unusual directions.&lt;/p&gt;
&lt;p style="margin-left: 40px; "&gt;Google was later than Apple with a smartphone. Android isn't quite as pretty compared to the iPhone; though it is rapidly getting there. Android phones are cheaper, available for nearly any network, and come in a wide variety of forms to fit different people's needs and budgets. And the important thing is that an Android Phone can do anything that an iPhone can; sometimes even with the same elegance as the iPhone. People often prefer the iPhone, but they can (and do) settle for Android phones instead.&lt;/p&gt;
&lt;p style="margin-left: 40px; "&gt;In the end, what will kill Apple the second time around is Apple's own paranoia. All by itself, Apple cannot evolve the iPhone fast enough to keep up a young and rapidly changing mobile computing market. Their treatment of developers is pushing them to other platforms fast.&lt;/p&gt;
&lt;p style="margin-left: 40px; "&gt;All Google has to do is try and keep up while other hardware and software companies race forward with new ideas.&lt;/p&gt;
&lt;p style="margin-left: 40px; "&gt;This same exact pattern of behavior is repeated with the new iPad.&lt;/p&gt;
&lt;p&gt;The situations are so similar it is almost insane that Apple can't see their own doom coming.&lt;/p&gt;
&lt;p&gt;There are some major differences between now and 20 years ago, but none of them bode well for Apple.&lt;/p&gt;
&lt;p&gt;Google is in a much more competitive position than Microsoft was 20 years ago, and Android is a much better product to pit against the iPhone than Windows was against the Mac.&lt;/p&gt;
&lt;p&gt;Perhaps the biggest difference between now and then though is that Apple wasn't crapping all over their software developers back then. Apple did limit developers in some ways, but never in the arrogant and insulting way they do now... and especially not through any legal or contract trickery like the iPhone developer agreement.&lt;/p&gt;
&lt;p&gt;Apple is also much more blatantly motivated by pure monetary greed. Twenty years ago, it felt like the Mac more about a vision of excellence, making a better tomorrow, being different, and enabling new and wonderful possibilities. The iPhone today though is clearly just a cash cow. Apple's every legal and marketing move is so transparently profit motivated that it doesn't even fool kindergarteners.&lt;/p&gt;
&lt;p&gt;There is no greater vision behind the iPhone like there was the Mac; behind the iPhone it's just dollar signs, abused developers, and shackled customers.&amp;nbsp;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/Reddnet?a=XJ_m0P42sf4:AHJvWwVuhyA:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/Reddnet?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/Reddnet?a=XJ_m0P42sf4:AHJvWwVuhyA:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/Reddnet?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/Reddnet?a=XJ_m0P42sf4:AHJvWwVuhyA:y6ACzXPbQao"&gt;&lt;img src="http://feeds.feedburner.com/~ff/Reddnet?i=XJ_m0P42sf4:AHJvWwVuhyA:y6ACzXPbQao" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/Reddnet?a=XJ_m0P42sf4:AHJvWwVuhyA:-BTjWOF_DHI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/Reddnet?i=XJ_m0P42sf4:AHJvWwVuhyA:-BTjWOF_DHI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/Reddnet/~4/XJ_m0P42sf4" height="1" width="1"/&gt;</description>
      <link>http://feedproxy.google.com/~r/Reddnet/~3/XJ_m0P42sf4/post.aspx</link>
      <author>Stephen M. Redd</author>
      <comments>http://reddnet.net/post/2010/05/09/How-Apple-Will-Lose-the-Mobile-Computing-Race.aspx#comment</comments>
      <guid isPermaLink="false">http://reddnet.net/post.aspx?id=845dbe6f-4d69-4580-a54d-6d0a91e28f61</guid>
      <pubDate>Sun, 09 May 2010 00:41:00 -0900</pubDate>
      <category>Rants &amp; Stupidity</category>
      <dc:publisher>Stephen M. Redd</dc:publisher>
      <pingback:server>http://reddnet.net/pingback.axd</pingback:server>
      <pingback:target>http://reddnet.net/post.aspx?id=845dbe6f-4d69-4580-a54d-6d0a91e28f61</pingback:target>
      <slash:comments>3</slash:comments>
      <trackback:ping>http://reddnet.net/trackback.axd?id=845dbe6f-4d69-4580-a54d-6d0a91e28f61</trackback:ping>
      <wfw:comment>http://reddnet.net/post/2010/05/09/How-Apple-Will-Lose-the-Mobile-Computing-Race.aspx#comment</wfw:comment>
      <wfw:commentRss>http://reddnet.net/syndication.axd?post=845dbe6f-4d69-4580-a54d-6d0a91e28f61</wfw:commentRss>
    <feedburner:origLink>http://reddnet.net/post.aspx?id=845dbe6f-4d69-4580-a54d-6d0a91e28f61</feedburner:origLink></item>
  </channel>
</rss>
