Wednesday, May 25, 2011

Entity Framework 4.1 Code First, Silverlight, and Shared Models with REST+JSON

It's a debate that has existed for decades. The database architects insist that data is golden and the most efficient way to build software is to generate it from the database schema, while the object-oriented code junkies swear that a nice "domain model" is the way to go and that persistence is a side effect of making those objects stick around longer. Of course, if you've worked on enterprise projects long enough you'll find the answer is often somewhere in the middle: some projects benefit from a database-centric approach, others from a model-centric approach, but many require a outward in approach. Design the schemas for the database to do what it does best with relationships, design the domain model to encapsulate the data and behavior of the class, and translate one to the other (hence the ORM, or object-relational mapper).

For a very light example, consider a conference event that has speakers and sessions. Speakers may "own" several sessions they speak at, while sessions may "have" several speakers when the session is co-hosted.

The Database

In SQL this relationship would be expressed through foreign keys, often with a "link" table to facilitate the many-to-many relationship, like this:

You can easily generate this database with the following T-SQL statements:

CREATE TABLE [dbo].[tblSession] (
    [Id]          INT           IDENTITY (1, 1) NOT NULL,
    [SessionName] NVARCHAR (50) NOT NULL
);
GO
ALTER TABLE [dbo].[tblSession]
    ADD CONSTRAINT [PK_tblSession] PRIMARY KEY CLUSTERED ([Id] ASC) WITH (ALLOW_PAGE_LOCKS = ON, ALLOW_ROW_LOCKS = ON, PAD_INDEX = OFF, IGNORE_DUP_KEY = OFF, STATISTICS_NORECOMPUTE = OFF);
GO
CREATE TABLE [dbo].[tblSessionSpeaker] (
    [Id]        INT IDENTITY (1, 1) NOT NULL,
    [SessionId] INT NOT NULL,
    [SpeakerId] INT NOT NULL
);
GO
ALTER TABLE [dbo].[tblSessionSpeaker]
    ADD CONSTRAINT [PK_tblSessionSpeaker] PRIMARY KEY CLUSTERED ([Id] ASC) WITH (ALLOW_PAGE_LOCKS = ON, ALLOW_ROW_LOCKS = ON, PAD_INDEX = OFF, IGNORE_DUP_KEY = OFF, STATISTICS_NORECOMPUTE = OFF);
GO
CREATE TABLE [dbo].[tblSpeaker] (
    [Id]   INT           IDENTITY (1, 1) NOT NULL,
    [Name] NVARCHAR (50) NOT NULL
);
GO
ALTER TABLE [dbo].[tblSpeaker]
    ADD CONSTRAINT [PK_tblSpeaker] PRIMARY KEY CLUSTERED ([Id] ASC) WITH (ALLOW_PAGE_LOCKS = ON, ALLOW_ROW_LOCKS = ON, PAD_INDEX = OFF, IGNORE_DUP_KEY = OFF, STATISTICS_NORECOMPUTE = OFF);
GO
ALTER TABLE [dbo].[tblSessionSpeaker] WITH NOCHECK
    ADD CONSTRAINT [FK_tblSessionSpeaker_tblSession] FOREIGN KEY ([SessionId]) REFERENCES [dbo].[tblSession] ([Id]) ON DELETE NO ACTION ON UPDATE NO ACTION;
GO
ALTER TABLE [dbo].[tblSessionSpeaker] WITH NOCHECK
    ADD CONSTRAINT [FK_tblSessionSpeaker_tblSpeaker] FOREIGN KEY ([SpeakerId]) REFERENCES [dbo].[tblSpeaker] ([Id]) ON DELETE NO ACTION ON UPDATE NO ACTION;

The Application

However, moving over the application side, the "link table" just doesn't make sense. Relationships can be expressed as navigation properties, so that speakers contain sessions and vice versa. The model there looks more like this:

These classes are expressed using the code below. Note that the fact this example will use an object relational mapper (ORM) has already interfered with our design a bit, because for it to provide peristence ignorance the collections had to be tagged virtual:

public abstract class BaseIdentityModelCore
{
    public virtual int Id { get; set; }
}

public abstract class BaseIdentityModel<T> : BaseIdentityModelCore where T: BaseIdentityModel<T>  
{        
    public override bool Equals(object obj)
    {
        return obj is T && ((T) obj).Id.Equals(Id);
    }

    public override int GetHashCode()
    {
        return Id;
    }
}

public class Session : BaseIdentityModel<Session>
{
    public string Name { get; set; }

    public virtual List<Speaker> Speakers { get; set; }

    public void AddSpeaker(Speaker speaker)
    {
        if (Speakers == null)
        {
            Speakers = new List<Speaker>();
        }

        if (speaker.Sessions == null || !(from s in speaker.Sessions where s.Name.Equals(Name,StringComparison.InvariantCultureIgnoreCase) select 1).Any())
        {
            if (speaker.Sessions == null)
            {
                speaker.Sessions = new List<Session>();
            }
            speaker.Sessions.Add(this);
        }
            
        Speakers.Add(speaker);
    }
}

public class Speaker : BaseIdentityModel<Speaker>
{
    public string Name { get; set; }

    public virtual List<Session> Sessions { get; set; }

    public void AddSession(Session session)
    {
        if (Sessions == null)
        {
            Sessions = new List<Session>();
        }

        if (session.Speakers == null || !(from s in session.Speakers where s.Name.Equals(Name, StringComparison.InvariantCultureIgnoreCase) select 1).Any())
        {
            if (session.Speakers == null)
            {
                session.Speakers = new List<Speaker>();
            }
            session.Speakers.Add(this);
        }
            
        Sessions.Add(session);
    }
}

It's important to note that these models don't just contain data. They also contain some business logic or behavior because the methods are exposed to add children and handle the reverse navigation (i.e. if I add a speaker to a session, the session should also get added to the speaker).

The models were created in a Silverlight 3 class project. This allows the project to be shared directly in the core framework as well. Without using linked files or other exotic means, we have an assembly that is truly shared between the Silverlight client and the .NET desktop or server framework. Note also that "JounceDataGuidance" is a strong term ... this isn't meant to be total "guidance" but more an experiment and reference for one way to glue data between the client and the server.

A Business Contract

Next is a simple contract, also defined in a Silverlight 3 project so it can be shared in multiple places, to interact with the models. The methods should be self-explanatory, but note the fact that this is built with asynchronous operations in mind so it can be shared between the client and the server:

public interface ISpeakerServices
{
    void AddSpeaker(Speaker speaker, Action<int> key);
    void AddSession(Session session, Action<int> key);
    void ListSpeakers(Action<IEnumerable<Speaker>> speakers);
    void ListSessionsForSpeaker(int speakerId, Action<IEnumerable<Session>> sessions);
    void AddSessionToSpeaker(int speakerId, int sessionId);
}

Data Access

Next, we'll define a simple data access layer. This will be an abstraction to the underlying mechanism which in this case happens to be Entity Framework 4.1. Some people may argue these abstractions aren't necessary because people seldom actually switch their data provider. I disagree for two reasons. I've been on projects where switching the data provider has happened (or we've eventually had to aggregate data from multiple sources). Unit testing of a business class should never require access and setup to a database implementation. You should be able to mock the data access contract instead.

There are two contracts. The first is for a unit of work. This is a pattern that similar to the concept of a transaction. It allows you to manipulate your objects in a discrete "unit of work" and then either cancel that unit of work or commit it all at once. This prevents sending a bunch of smaller, atomic operations to the database and allows the data access layer to coordinate the changes all at once.

public interface IUnitOfWork
{
    void Commit();
    void Complete();
}

Commit will complete changes. Complete will close the connections but will not commit. It's not a "rollback" because we won't have actually interacted with the database, but it says, "complete this operation and flush any database goo without sending my changes over the wire."

The data access contract defines some common CRUD operations and assumes the models participating all use the identity structure defined in the base class:

public interface IDataAccess<T> where T: BaseIdentityModelCore
{        
    T Load(int key, bool includeChildren);
    void Save(T instance);
    void Delete(int key);
    IQueryable<T> Query(bool includeChildren);
}

Entity Framework 4.1 Code First

Now the database schema can be mapped to the domain model. The model project is referenced directly even though the data access project is on the server. While Visual Studio flags a warning due to the potential for incompatible types:



The project will work perfectly fine and compile without issue.

First, the framework is instructed how to map a speaker to the underlying database. The following configuration specifies the table and key, and also describes the many-to-many relationship between speakers and sessions with the linked table that connects the two:

public class SpeakerConfig : EntityTypeConfiguration<Speaker>
{
    public SpeakerConfig()
    {
        ToTable("tblSpeaker");
        HasKey(s => s.Id);
        Property(s => s.Id).IsRequired().HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
        HasMany(s => s.Sessions).WithMany(speaker => speaker.Speakers)
            .Map(config => config.MapLeftKey("SpeakerId").MapRightKey("SessionId").ToTable("tblSessionSpeaker"));
    }
}

If you've got a sharp eye for detail, you will have noted that the session table was defined in a "non-standard" way. The speaker simply has a "name" property, but the session uses a "sessionName" property. The following configuration handles that difference:

public class SessionConfig : EntityTypeConfiguration<Session>
{
    public SessionConfig()
    {
        ToTable("tblSession");
        HasKey(s => s.Id);
        Property(s => s.Id).IsRequired().HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
        Property(s => s.Name).HasColumnName("SessionName");
        HasMany(s => s.Speakers).WithMany(speaker => speaker.Sessions)
            .Map(config => config.MapLeftKey("SessionId").MapRightKey("SpeakerId").ToTable("tblSessionSpeaker"));
    }
}

Now that the mappings have been defined, a context for accessing the data can be created. The class defines the two collections to map as well as the configurations to map them:

public class JounceEventContext : DbContext 
{
    public DbSet<Speaker> Speakers { get; set; }
    public DbSet<Session> Sessions { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Configurations.Add(new SpeakerConfig());
        modelBuilder.Configurations.Add(new SessionConfig());
        base.OnModelCreating(modelBuilder);
    }
}

Notice all of this was done without any generated model. The next step is to implement the basic data access layer. The base class performs the common operations, and allows the derived classes to supply what the target collection is. There are two offered: a set (shallow) and a query (deep) for including children in the object graph or not.

public abstract class DataAccess<T> : IDataAccess<T> where T : BaseIdentityModelCore
{        
    protected abstract DbSet<T> GetDbSet();

    protected abstract DbQuery<T> GetDbQuery();

    public JounceEventContext Context { get; private set; }

    protected DataAccess(UnitOfWork workUnit)
    {
        Context = workUnit.Context as JounceEventContext;
    }

    public virtual IQueryable<T> Query(bool includeChildren)
    {
        return includeChildren ? GetDbQuery() : GetDbSet().AsQueryable();
    }

    private void _SetState<TBaseIdentity>(TBaseIdentity model) where TBaseIdentity : BaseIdentityModelCore
    {
        Context.Entry(model).State = model.Id > 0 ? EntityState.Modified : EntityState.Added;
    }

    public virtual T Load(int key, bool includeChildren)
    {
        return (from instance in Query(includeChildren) where instance.Id.Equals(key) select instance).FirstOrDefault();
    }

    public virtual void Save(T instance)
    {
        _SetState(instance);            
    }

    public virtual void Delete(int key)
    {
        if (GetDbSet().Where(t => t.Id.Equals(key)).Any())
        {
            return;
        }
        GetDbSet().Remove(Load(key,false));            
    }      
}

Because the objects are ignorant of persistence, we use the "set state" to communicate to the underlying context whether something needs to be inserted or updated. The fact that the navigation collections are virtual allows the Entity Framework to generate proxies and track the relationships through the link table automatically for us. You'll notice the base class requires a unit of work to be passed in, the implementation for Entity Framework looks like this:

public class UnitOfWork : IUnitOfWork 
{
    public DbContext Context { get; private set; }

    public UnitOfWork(DbContext context)
    {
        Context = context;
    }

    public void Commit()
    {
        Context.SaveChanges();
        Context.Dispose();
        Context = null;
    }

    public void Complete()
    {
        Context.Dispose();
        Context = null;
    }
}

Finally, implementing the data access is easy. If you wanted to extend the base contract with specific queries, you could, but in this case I'm simply informing the base class of the correct sets and queries to use:

public class SessionDataAccess : DataAccess<Session>
{
    public SessionDataAccess(UnitOfWork work) : base(work)
    {
            
    }

    protected override DbSet<Session> GetDbSet()
    {
        return Context.Sessions;
    }

    protected override DbQuery<Session> GetDbQuery()
    {
        return Context.Sessions.Include("Speakers");
    }
}

Speakers looks very similar so no need to post it. Notice that while this layer implements the Entity Framework and the code-first feature, the contracts for the unit of work and data access contracts are sufficiently generic enough to allow for any type of implementation. You could easily use a generate model, plug in nHibernate or use ADO and still be able to honor the contracts.

Business Layer

Now we can implement the business contract. On the server we'll reference the same contract the client will use. I'll use a simple factory to avoid having to reference the implementations directly:

public class Factory
{
    public static IUnitOfWork GetUnitOfWork()
    {
        return new UnitOfWork(new JounceEventContext());
    }

    public static IDataAccess<T> GetDataAccess<T>(IUnitOfWork work) where T: BaseIdentityModelCore
    {
        if (typeof(T).Equals(typeof(Speaker)))
        {
            return (IDataAccess<T>) new SpeakerDataAccess((UnitOfWork)work);
        }

        if (typeof(T).Equals(typeof(Session)))
        {
            return (IDataAccess<T>) new SessionDataAccess((UnitOfWork)work);
        }

        throw new TypeLoadException(string.Format("Invalid type: {0}", typeof(T).FullName));
    }
}

Then the contract can be implemented:

public class SpeakerServices : ISpeakerServices
{
    public void AddSpeaker(Speaker speaker, Action<int> key)
    {
        var unitOfWork = Factory.GetUnitOfWork();
        var dataAccess = Factory.GetDataAccess<Speaker>(unitOfWork);
        dataAccess.Save(speaker);
        unitOfWork.Commit();
        key(speaker.Id);
    }

    public void AddSession(Session session, Action<int> key)
    {
        var unitOfWork = Factory.GetUnitOfWork();
        var dataAccess = Factory.GetDataAccess<Session>(unitOfWork);
        dataAccess.Save(session);
        unitOfWork.Commit();
        key(session.Id);
    }

    public void ListSpeakers(Action<IEnumerable<Speaker>> speakers)
    {
        var unitOfWork = Factory.GetUnitOfWork();
        var dataAccess = Factory.GetDataAccess<Speaker>(unitOfWork);
        var query =
            new List<Speaker>(from speaker in dataAccess.Query(false) orderby speaker.Name select speaker);
        speakers(query);
        unitOfWork.Complete();
    }

    public void ListSessionsForSpeaker(int speakerId, Action<IEnumerable<Session>> sessions)
    {
        var unitOfWork = Factory.GetUnitOfWork();
        var dataAccess = Factory.GetDataAccess<Speaker>(unitOfWork);
        var speaker = dataAccess.Load(speakerId, true);
        var sess = speaker == null ? Enumerable.Empty<Session>() : new List<Session>(speaker.Sessions);

        var retVal = new List<Session>();
            
        // flatten for return
        foreach (var session in sess)
        {
            var sessionReturned = new Session {Id = session.Id, Name = session.Name, Speakers = new List<Speaker>()};
            foreach(var speak in session.Speakers)
            {
                sessionReturned.Speakers.Add(new Speaker {Id = speak.Id, Name = speak.Name});
            }
            retVal.Add(sessionReturned);
        }
        sessions(retVal);
        unitOfWork.Complete();
    }

    public void AddSessionToSpeaker(int speakerId, int sessionId)
    {
        var unitOfWork = Factory.GetUnitOfWork();
        var speakerAccess = Factory.GetDataAccess<Speaker>(unitOfWork);
        var sessionAccess = Factory.GetDataAccess<Session>(unitOfWork);

        var speaker = speakerAccess.Load(speakerId, true);
        var session = sessionAccess.Load(sessionId, true);

        if (session.Speakers.Contains(speaker))
        {
            unitOfWork.Complete();
            return;
        }

        session.AddSpeaker(speaker);
        sessionAccess.Save(session);

        unitOfWork.Commit();
    }
}

Notice that at this level, the objects are being interacted with directly. To add a session for a speaker, the data access layer is queried to get the latest classes, but then the behavior on the class itself is used to add a speaker to a session.

The Service Layer

Having the contract implemented is fine but we need to be able to access it from Silverlight. In this example, we'll expose the functions as REST services with JSON:

[ServiceContract(Namespace = "http://jounce.codeplex.com/services")]
[SilverlightFaultBehavior]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class SpeakerServiceEndpoint
{
    public ISpeakerServices Services = new SpeakerServices();

    [WebGet(UriTemplate = "", ResponseFormat = WebMessageFormat.Json)]
    public List<Speaker> GetSpeakers()
    {
        var speakers = new List<Speaker>();
        Services.ListSpeakers(speakers.AddRange);

        return speakers.Select(speaker => new Speaker {Id = speaker.Id, Name = speaker.Name, Sessions = new List<Session>()}).ToList();
    }

    [WebGet(UriTemplate = @"?speakerId={speakerId}", ResponseFormat = WebMessageFormat.Json)]
    public List<Session> GetSessionsForSpeaker(int speakerId)
    {
        var retVal = new List<Session>();
        Services.ListSessionsForSpeaker(speakerId, retVal.AddRange);
        return retVal;
    }

    [OperationContract]
    [WebInvoke(UriTemplate=@"AddSpeaker", BodyStyle = WebMessageBodyStyle.Bare, RequestFormat = WebMessageFormat.Json, 
        ResponseFormat = WebMessageFormat.Json)]
    public int AddSpeaker(Speaker speaker)
    {
        var retVal = 0;
        Services.AddSpeaker(speaker, key => retVal = key);
        return retVal; 
    }

    [OperationContract]
    [WebInvoke(UriTemplate = @"AddSession", BodyStyle = WebMessageBodyStyle.Bare, RequestFormat = WebMessageFormat.Json,
        ResponseFormat = WebMessageFormat.Json)]
    public int AddSession(Session session)
    {
        var retVal = 0;
        Services.AddSession(session, key => retVal = key);
        return retVal;
    }

    [OperationContract]
    [WebInvoke(UriTemplate = @"AddSessionToSpeaker?speakerId={speakerId}&sessionId={sessionId}", BodyStyle = WebMessageBodyStyle.Bare, RequestFormat = WebMessageFormat.Json,
        ResponseFormat = WebMessageFormat.Json)]
    public void AddSessionToSpeaker(int speakerId, int sessionId)
    {
        Services.AddSessionToSpeaker(speakerId, sessionId);
    }
}

Notice these services are basically just façades to the business layer. There is some definition around how the services are accessed and attributes to flag the format as JSON. The service is in a standalone class (there is not a file with the .SVC extension. The host application is MVC. To register the service we can simply add a route to the MvcApplication class. The line to add the route is:

routes.Add(new ServiceRoute("Speakers", new WebServiceHostFactory(), typeof(SpeakerServiceEndpoint)));

This establishes it with the prefix /Speakers and injects the service into the application. Now we can actually test the service end points with fiddler. A post to http://localhost/Speakers/ yields the following response (for a database that is already populated):

[{"Id":351,"Name":"Bob German","Sessions":[]},{"Id":347,"Name":"John Papa","Sessions":[]},{"Id":348,"Name":"Mike Taulty","Sessions":[]},{"Id":350,"Name":"Paul Stubbs","Sessions":[]},{"Id":349,"Name":"Ward Bell","Sessions":[]}]

Note these are actual names and sessions from the MIX 11 event. Because serialization cannot handle children that reference their parents, the sessions are passed as empty and a subsequent call grabs the sessions for a speaker: http://localhost/Speakers/?speakerId=351 (and REST purists will likely gasp and beg me NOT to use querystrings) which yields:

[{"Id":335,"Name":"Silverlight for SharePoint","Speakers":[{"Id":351,"Name":"Bob German","Sessions":null},{"Id":350,"Name":"Paul Stubbs","Sessions":null}]}]

Again, note the sessions are flattened to avoid recursion.

The Silverlight Business Layer

Silverlight can now access the exact same model project and business contract we used on the server side. There is no need to map to intermediary objects, juggle and jump through hoops. Yes, you can even share behavior without using WCF RIA!

First, we'll make a few helper classes to handle the REST calls. REST is fairly easy with Silverlight and doesn't require adding service references. Instead, we can use the WebClient.

public class WebClientHelper<T> where T : class
{
    private Action<IEnumerable<T>> _callback;
    private Action<int> _key;

    public WebClientHelper(Uri uri, Action<int> key, T instance)
    {
        _key = key;
        var wc = new WebClient();

        if (key != null)
        {
            wc.UploadStringCompleted += _WcUploadStringCompleted;
        }

        wc.Headers["Content-Type"] = "application/json";
        var serializer = new DataContractJsonSerializer(typeof (T));
        using (var memStream = new MemoryStream())
        {
            serializer.WriteObject(memStream, instance);
            memStream.Seek(0, SeekOrigin.Begin);
            using (var reader = new StreamReader(memStream))
            {
                wc.UploadStringAsync(uri, reader.ReadToEnd());
            }
        }
    }

    private void _WcUploadStringCompleted(object sender, UploadStringCompletedEventArgs e)
    {
        ((WebClient) sender).UploadStringCompleted -= _WcUploadStringCompleted;
        var serializer = new DataContractJsonSerializer(typeof (int));
        _key((int) serializer.ReadObject(new MemoryStream(Encoding.Unicode.GetBytes(e.Result))));
        _key = null;
    }

    public WebClientHelper(Uri uri, Action<IEnumerable<T>> result)
    {
        _callback = result;
        var wc = new WebClient();
        wc.DownloadStringCompleted += _WcDownloadStringCompleted;
        wc.DownloadStringAsync(uri);
    }

    private void _WcDownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
    {
        ((WebClient) sender).DownloadStringCompleted -= _WcDownloadStringCompleted;
        var serializer = new DataContractJsonSerializer(typeof (List<T>));
        var results = (List<T>) serializer.ReadObject(new MemoryStream(Encoding.Unicode.GetBytes(e.Result)));
        _callback(results);
        _callback = null;
    }
}

Notice this helper handles two patterns (I know, I'm not completely separating my concerns ... but isn't the project getting big enough already?) The first is a POST where the payload is serialized in the post and a key is retrieved. The second is a GET that returns a list.

With that in place to manage our calls and turning objects to JSON and back, we can implement the same business contract we implemented on the server side:

[Export(typeof (ISpeakerServices))]
public class SpeakerServices : ISpeakerServices
{
    private const string ADD_SPEAKER_POST = "/Speakers/AddSpeaker";
    private const string ADD_SESSION_POST = "/Speakers/AddSession";
    private const string ADD_SESSION_TO_SPEAKER_POST = @"/Speakers/AddSessionToSpeaker?speakerId={0}&sessionId={1}";
    private const string LIST_SESSIONS_FOR_SPEAKER_GET = "/Speakers/?speakerId={0}";
    private const string LIST_SPEAKERS_GET = "/Speakers/";

    public void AddSpeaker(Speaker speaker, Action<int> key)
    {
        new WebClientHelper<Speaker>(new Uri(ADD_SPEAKER_POST, UriKind.Relative), key, speaker);
    }

    public void AddSession(Session session, Action<int> key)
    {
        new WebClientHelper<Session>(new Uri(ADD_SESSION_POST, UriKind.Relative), key, session);
    }

    public void AddSessionToSpeaker(int speakerId, int sessionId)
    {
        new WebClientHelper<Session>(
            new Uri(string.Format(ADD_SESSION_TO_SPEAKER_POST, speakerId, sessionId), UriKind.Relative), null,
            null);
    }

    public void ListSessionsForSpeaker(int speakerId, Action<IEnumerable<Session>> sessions)
    {
        new WebClientHelper(
            new Uri(string.Format(LIST_SESSIONS_FOR_SPEAKER_GET, speakerId), UriKind.Relative), sessions);
    }

    public void ListSpeakers(Action<IEnumerable<Speaker>> speakers)
    {
        var retVal = new List<Speaker>();
        new WebClientHelper<Speaker>(new Uri(LIST_SPEAKERS_GET, UriKind.Relative),
                                        results =>
                {
                    int[] count = {results.Count()};

                    if (count[0] == 0)
                    {
                        speakers(Enumerable.Empty<Speaker>());
                        return;
                    }

                    foreach (var speaker in results)
                    {
                        retVal.Add(speaker);
                        var speaker1 = speaker;
                        ListSessionsForSpeaker(speaker1.Id,
                                            s =>
                                                {
                                                    foreach (var session in s)
                                                    {
                                                        speaker1.AddSession(session);
                                                    }

                                                    count[0]--;
                                                    if (count[0] == 0)
                                                    {
                                                        speakers(retVal);
                                                    }
                                                });
                    }
        }
}

Notice how easy it is to just pass through to the helper for the various functions becaus they are mapped faithfully to the service layer! Now there is some hackery going on in the final list, but it is there to illustrate an important point. The speakers are returned without sessions, so an inner loop requests the sessions for each speaker. When the sessions are returned, the method on the class is used to add them so we are using the exact same behavior for the model regardless of whether we are on the server or the client - it is truly shared between both. In this case it will make sure the session is added to the speaker and then the speaker is added back to the session. A count is kept to make sure we get sessions for all speakers before the list is returned to the caller.

Now we can create a simple view model that takes advantage of the business tier and wires up the speakers and sessions:

[ExportAsViewModel(typeof(MainViewModel))]
public partial class MainViewModel : BaseViewModel, IMainViewModel
{
    [Import]
    public ISpeakerServices SpeakerServices { get; set; }

    [Import]
    public GenerateData DataGenerater { get; set; }
        
    public MainViewModel()
    {
        Speakers = new ObservableCollection<Speaker>();
        DesignerData();
    }

    public ICollection<Speaker> Speakers { get; private set; }   
             
    protected override void InitializeVm()
    {
        SpeakerServices.ListSpeakers(_GetSpeakers);
    }

    private void _GetSpeakers(IEnumerable<Speaker> speakers)
    {
        if (!speakers.Any())
        {
            WorkflowController.Begin(DataGenerater.GenerateServerDataWorkflow(SpeakerServices, 
                ()=>SpeakerServices.ListSpeakers(_GetSpeakers)));
            return;
        }
            
        foreach(var speaker in speakers)
        {
            Speakers.Add(speaker);
        }           
    }        
}

Generating Data

What about the data generation? I didn't have time to build a full CRUD example but I wanted to show inserts and updates from the client, so I decided to generate the data from the client. The first time you run the application, it will request the list of speakers and receive and empty list. It will then generate the speakers and sessions, ask the server to update them and then request the speakers list a second time. This time it will faithfully show a list of speakers and sessions (with a sub-list of speakers to show the navigation properties).

Because our data generation needs to happen sequentially, I'll take advantage of the IWorkflow interface in Jounce. I created two implementations, one to add a speaker and one to add a session. Here is what the session action looks like. It uses the business contract to add a session and then calls invoked, and the Jounce controller ensures it completes (without blocking the UI thread) before the next step is run.

public class AddSessionAction : IWorkflow
{
    private readonly ISpeakerServices _service;
    private readonly Session _session;

    public AddSessionAction(ISpeakerServices service, Session session)
    {
        _service = service;
        _session = session;
    }

    public void Invoke()
    {
        _service.AddSession(_session, key =>
        {
            _session.Id = key;
            Invoked();
        });
    }

    public Action Invoked { get; set; }
}

The workflow engine allows us to wire several sequential calls even though they are asynchronous, and essentially build the sessions and speakers we want:

public IEnumerable<IWorkflow> GenerateServerDataWorkflow(ISpeakerServices services, Action completed)
{
    var bootcamp = new Session { Name = "Silverlight Boot Camp" };
    var thumbs = new Session { Name = "All Thumbs" };
    var sharepoint = new Session { Name = "Silverlight for SharePoint" };
    var experiences = new Session { Name = "Great Experiences for SharePoint" };

    var john = new Speaker { Name = "John Papa" };
    var mike = new Speaker { Name = "Mike Taulty" };
    var ward = new Speaker { Name = "Ward Bell" };
    var paul = new Speaker { Name = "Paul Stubbs" };
    var bob = new Speaker { Name = "Bob German" };

    yield return new AddSpeakerAction(services, john);
    yield return new AddSessionAction(services, bootcamp);

    services.AddSessionToSpeaker(john.Id, bootcamp.Id);

    yield return new AddSpeakerAction(services, mike);

    services.AddSessionToSpeaker(mike.Id, bootcamp.Id);

    yield return new AddSpeakerAction(services, ward);
    yield return new AddSessionAction(services, thumbs);

    services.AddSessionToSpeaker(ward.Id, thumbs.Id);

    yield return new AddSpeakerAction(services, paul);
    yield return new AddSessionAction(services, sharepoint);
    yield return new AddSessionAction(services, experiences);

    services.AddSessionToSpeaker(paul.Id, sharepoint.Id);
    services.AddSessionToSpeaker(paul.Id, experiences.Id);

    yield return new AddSpeakerAction(services, bob);

    services.AddSessionToSpeaker(bob.Id, sharepoint.Id);

    completed();
}   

It should be very clear and natural what we are doing. Create a speaker. Create a session. Now add the session to the speaker ... and because the service layer shares the models, the behavior of the recursive links between sessions and speakers is preserved.

What does the SQL look like? We can run the profiler and see:

-- insert the speaker
exec sp_executesql N'insert [dbo].[tblSpeaker]([Name])
values (@0)
select [Id]
from [dbo].[tblSpeaker]
where @@ROWCOUNT > 0 and [Id] = scope_identity()',N'@0 nvarchar(max) ',@0=N'John Papa'

-- insert the session
exec sp_executesql N'insert [dbo].[tblSession]([SessionName])
values (@0)
select [Id]
from [dbo].[tblSession]
where @@ROWCOUNT > 0 and [Id] = scope_identity()',N'@0 nvarchar(max) ',@0=N'Silverlight Boot Camp'

-- select the session 
exec sp_executesql N'SELECT 
[Project2].[Id] AS [Id], 
[Project2].[Name] AS [Name], 
[Project2].[C1] AS [C1], 
[Project2].[Id1] AS [Id1], 
[Project2].[SessionName] AS [SessionName]
FROM ( SELECT 
 [Limit1].[Id] AS [Id], 
 [Limit1].[Name] AS [Name], 
 [Join1].[Id] AS [Id1], 
 [Join1].[SessionName] AS [SessionName], 
 CASE WHEN ([Join1].[SpeakerId] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C1]
 FROM   (SELECT TOP (1) 
  [Extent1].[Id] AS [Id], 
  [Extent1].[Name] AS [Name]
  FROM [dbo].[tblSpeaker] AS [Extent1]
  WHERE [Extent1].[Id] = @p__linq__0 ) AS [Limit1]
 LEFT OUTER JOIN  (SELECT [Extent2].[SpeakerId] AS [SpeakerId], [Extent3].[Id] AS [Id], [Extent3].[SessionName] AS [SessionName]
  FROM  [dbo].[tblSessionSpeaker] AS [Extent2]
  INNER JOIN [dbo].[tblSession] AS [Extent3] ON [Extent3].[Id] = [Extent2].[SessionId] ) AS [Join1] ON [Limit1].[Id] = [Join1].[SpeakerId]
)  AS [Project2]
ORDER BY [Project2].[Id] ASC, [Project2].[C1] ASC',N'@p__linq__0 int',@p__linq__0=352

-- insert the session/speaker link
exec sp_executesql N'insert [dbo].[tblSessionSpeaker]([SpeakerId], [SessionId])
values (@0, @1)
',N'@0 int,@1 int',@0=352,@1=337

As you can see, very straightforward (and readable) statements, including the correct inserts for our linked table based on the mapping we declared in the configuration.

This is not much different than the process to wire the design-time data, despite the fact that the runtime process is serializing to JSON, sending over the wire using a REST service and then updating a backend database. Here is the design-time data:

public partial class MainViewModel
{
    [Conditional("DEBUG")]
    public void DesignerData()
    {
        if (!InDesigner)
        {
            return;
        }

        foreach(var speaker in _GenerateData())
        {
            Speakers.Add(speaker);
        }
    }

    private IEnumerable<Speaker> _GenerateData()
    {
        var john = new Speaker { Name = "John Papa" };
        var mike = new Speaker { Name = "Mike Taulty" };
        var ward = new Speaker { Name = "Ward Bell" };
        var paul = new Speaker { Name = "Paul Stubbs" };
        var bob = new Speaker { Name = "Bob German" };

        var bootcamp = new Session { Name = "Silverlight Boot Camp" };
        bootcamp.AddSpeaker(john);
        bootcamp.AddSpeaker(mike);
        var thumbs = new Session { Name = "All Thumbs" };
        thumbs.AddSpeaker(ward);
        var sharepoint = new Session { Name = "Silverlight for SharePoint" };
        sharepoint.AddSpeaker(paul);
        sharepoint.AddSpeaker(bob);
        var experiences = new Session { Name = "Great Experiences for SharePoint" };
        experiences.AddSpeaker(paul);
        return new[] { bob, john, mike, paul, ward };
    }

}

Now some XAML to bind it together:

<Grid x:Name="LayoutRoot" Background="White" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
        d:DataContext="{d:DesignInstance vm:MainViewModel, IsDesignTimeCreatable=True}">
    <ListBox ItemsSource="{Binding Speakers}">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <Grid HorizontalAlignment="Stretch">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="200"/>
                        <ColumnDefinition Width="*"/>
                    </Grid.ColumnDefinitions>
                    <TextBlock Text="{Binding Name}" FontSize="16" FontWeight="Bold"/>
                    <ListBox ItemsSource="{Binding Sessions}" Grid.Column="1" Margin="20 0 0 0">
                        <ListBox.ItemTemplate>
                            <DataTemplate>
                                <StackPanel HorizontalAlignment="Stretch" Orientation="Vertical">
                                    <TextBlock Text="{Binding Name}" FontWeight="Bold"/>
                                    <ItemsControl ItemsSource="{Binding Speakers}" DisplayMemberPath="Name"/>
                                </StackPanel>
                            </DataTemplate>
                        </ListBox.ItemTemplate>
                    </ListBox>
                </Grid>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
</Grid>

And the final result of an end-to-end project that performs CRUD operations using Entity Framework 4.1 code-first, REST services with JSON, and shares models, behavior, and contracts between the client and the server:

Grab the full project with database schema and tests here.

Jeremy Likness

Tuesday, May 17, 2011

Using ICustomTypeProvider in Silverlight 5 to Display JSON Data

By now you've likely heard the buzz surrounding the release of the Silverlight 5 beta and some of the great new features. Developers have been waiting some time for a dynamic type that can participate in data-binding for Silverlight. Silverlight 5 introduces this ability, but not in the form of Expando objects or the dynamic keyword as most expected. Instead, it comes in the form of ICustomTypeProvider.

The custom type provider interface declares a single method:

Type GetCustomType();

That's it! Sounds easy, but there is actually quite a bit of work that goes into providing a custom type. The actual "work" behind that method could be something like the dynamic types that I described in the linked post. In this post, however, I'll focus on the helper method that Microsoft employee Alexandra Rusina was kind enough to provide in her post, binding to dynamic properties with ICustomTypeProvider.

The key to ICustomTypeProvider is reflection. In anticipation of focusing more on the reflection engine for dynamic types, Silverlight 5 provides overrides for many of the base reflection classes. Alexandra's CustomTypeHelper class takes advantage of this to help construct types on the fly and provide for setting dynamic property values. I'll let you read her post to understand the details, and then return here for an example of a practical application of the concept.

For our example we're going to display a sortable grid that derives from JSON data. The example will assume the JSON data is an array of a simple (only one level deep) object graph. Here is a JSON file with information about the planets:

[
   { 
 "Name" : "Mercury",
 "Mass" : 0.05,
 "Diameter" : 4876
   },
   { 
 "Name" : "Venus",
 "Mass" : 0.81,
 "Diameter" : 12107
   },
   { 
 "Name" : "Earth",
 "Mass" : 1.0,
 "Diameter" : 12755
   },
   { 
 "Name" : "Mars",
 "Mass" : 0.1,
 "Diameter" : 6794
   },
   { 
 "Name" : "Jupiter",
 "Mass" : 317,
 "Diameter" : 142983
   },
   { 
 "Name" : "Saturn",
 "Mass" : 95,
 "Diameter" : 120536
   },
   { 
 "Name" : "Uranus",
 "Mass" : 14.6,
 "Diameter" : 51117
   },
   { 
 "Name" : "Neptune",
 "Mass" : 17,
 "Diameter" : 4876
   },
   { 
 "Name" : "Pluto",
 "Mass" : 0.0002,
 "Diameter" : 2390
   }
]

To start with, create a basic class that encapsulates the custom type. It will have a unique identifier and implement Equals and GetHashCode based on the unique identifier. It will also expose some helper methods that pass through to the CustomTypeHelper class in order to dynamically wire the type:

public class CustomType : ICustomTypeProvider 
{
    public CustomType()
    {
        Id = Guid.NewGuid();
    }

    [Display(AutoGenerateField = false)]
    public Guid Id { get; set; }        

    readonly CustomTypeHelper<CustomType> _helper = new CustomTypeHelper<CustomType>();

    public static void AddProperty(String name)
    {
        CustomTypeHelper<CustomType>.AddProperty(name);
    }

    public static void AddProperty(String name, Type propertyType)
    {
        CustomTypeHelper<CustomType>.AddProperty(name, propertyType);
    }

    public static void AddProperty(String name, Type propertyType, List<attribute> attributes)
    {
        CustomTypeHelper<CustomType>.AddProperty(name, propertyType, attributes);
    }


    public void SetPropertyValue(string propertyName, object value)
    {
        _helper.SetPropertyValue(propertyName, value);
    }

    public object GetPropertyValue(string propertyName)
    {
        return _helper.GetPropertyValue(propertyName);
    }

    public PropertyInfo[] GetProperties()
    {
        return _helper.GetProperties();
    }

    public Type GetCustomType()
    {
        return _helper.GetCustomType();
    }

    public override bool Equals(object obj)
    {
        return obj is CustomType && ((CustomType) obj).Id.Equals(Id);
    }

    public override int GetHashCode()
    {
        return Id.GetHashCode();
    }
}

Notice it internally stores an instance of the helper for the type and passes through to it to implement the ICustomTypeProvider interface. As much as I loathe data annotations in clean data models, here I'm using one to suppress the inclusion of the Id in the grid because I will let it auto-generate columns to show the dynamic type.

Next is a helper method to wire in the type. The constructor is given the "template" of a single JsonObject (I'll be using System.Json for this example). The class will parse the values try to infer the type (this is a very simple algorithm, obviously a more complex one is needed for more complex data types). It will then wire up a new property on the custom type and provide a function for conversion to the target type. Another method given a JsonValue and a delegate to the property setter will parse the value out and set the property.

public class JsonHelper<T> where T: ICustomTypeProvider
{
    private readonly IEnumerable<string> _keys = Enumerable.Empty<string>();
    private readonly Dictionary<string,Func<object,object>> _converters = new Dictionary<string, Func<object, object>>();

    public JsonHelper(IDictionary<string,JsonValue> template)
    {
        _keys = (from k in template.Keys select k).ToArray();

        foreach (var key in template.Keys)
        {
            int integerTest;
            double doubleTest;
            var value = template[key].ToString();
            if (int.TryParse(value, out integerTest))
            {
                CustomTypeHelper<T>.AddProperty(key, typeof(int));
                _converters.Add(key, obj => int.Parse(obj.ToString()));
            }
            else if (double.TryParse(value, out doubleTest))
            {
                CustomTypeHelper<T>.AddProperty(key, typeof(double));
                _converters.Add(key, obj => double.Parse(obj.ToString()));
            }
            else
            {
                CustomTypeHelper<T>.AddProperty(key, typeof(string));                    
                _converters.Add(key, obj =>
                                            {
                                                // strip quotes
                                                var str = obj.ToString().Substring(1);
                                                return str.Substring(0, str.Length - 1);
                                            });
            }
        }
    }

    public void MapJsonObject(Action<string,object> setValue, JsonValue item)
    {
        foreach (var key in _keys)
        {
            setValue(key, _converters[key](item[key]));
        }
    }
}

Now that we have the helpers in place to parse the JSON, it is possible to expose a collection and parse the JSON file:

public List<CustomType> Items { get; private set; }

public JsonViewModel()
{
    Items = new List<CustomType>();

    using (
        var stream =
            typeof (JsonViewModel).Assembly.GetManifestResourceStream(
                typeof (JsonViewModel).FullName.Replace
                ("ViewModels.JsonViewModel", 
                 "SampleData.Data.json")))
    {
        var jsonArray = JsonValue.Load(stream) as JsonArray;

        if (jsonArray == null) return;

        var template = jsonArray[0] as JsonObject;

        if (template == null) return;

        var jsonHelper = new JsonHelper<CustomType>(template);

        foreach (var item in jsonArray)
        {
            var customType = new CustomType();

            jsonHelper.MapJsonObject(customType.SetPropertyValue, item);                            
            Items.Add(customType);
        }
    }
}

The stream is an embedded resource and it simply infers from the location of the view model where the location of the sample data will be - you could easily use a constant or other means of locating the embedded resource. It then grabs the first entry, creates the helper class with the template, then iterates the items and loads the type.

Now the exposed list can be databound to a grid:

<Controls:DataGrid HorizontalAlignment="Center" VerticalAlignment="Center" AutoGenerateColumns="True" ItemsSource="{Binding Items}" Grid.Row="1"/>

And when we run the example, we get exactly what we want - the JSON data in nice, formatted columns that can be sorted, grouped, or otherwise manipulated as first class types:

Where's the Source?

I'll be giving a talk in Knoxville at CodeStock 2011 on Friday, June 3rd about Silverlight 5 for Line of Business Applications. If you're able to join me, please do - as of this post there were less than 100 tickets remaining to the event. Otherwise, keep your eyes on this blog. As part of that talk I've created a large application based on the Jounce framework that demonstrates most of the new Silverlight 5 features that are available as of the beta. I'll post the code and slides after. As a bonus, if you do attend, check out my colleague Rik Robinson's Introduction to Jounce for Silverlight.

Jeremy Likness

Thursday, May 5, 2011

A Fluent Backwards Design

Fluent interfaces make code easier to read, understand, and use. They can also provide a layer of separation and decoupling to make it easier to reuse components in a straightforward fashion. In this post I will walk through the scenario of creating a fluent interface with a "backwards design." This means I'll start with the end in mind: what I want my interface to look like, and build the API to support that.

The scenario is a common one that illustrates the point nicely: a feed reader. Regardless of using a fluent interface, you are likely to be following the MVVM model and practice SOLID principles. (If not, please read the linked article for a jumpstart).

The individual feed item can be modeled simply like this:

public class FeedItem
{
    public string Id { get; set; }

    public DateTime Posted { get; set; }

    public string Title { get; set; }

    public string Description { get; set; }
}

Knowing the definition of the feed item now makes it possible to create a view model to host it:

public partial class ViewModel
{
    public ViewModel()
    {
        FeedItems = new ObservableCollection<FeedItem>();

        DesignData();

        if (DesignerProperties.IsInDesignTool) return;
    }

    public ObservableCollection<FeedItem> FeedItems { get; private set; }
}

The design data can then populate the sample feeds:

public partial class ViewModel
{        
    [Conditional("DEBUG")]
    protected void DesignData()
    {
        if (!DesignerProperties.IsInDesignTool)
        {
            return;
        }

        for (var x = 0; x < 20; x++)
        {
            var feedItem = new FeedItem();
            feedItem.Id = Guid.NewGuid().ToString();
            feedItem.Posted = DateTime.Now.AddDays(-20 + x);
            feedItem.Title = "Blog Post Entry";
            feedItem.Description =
                "This is a sample blog post entry for the purpose of design-time data on the design surface.";
            FeedItems.Add(feedItem);
        }
    }
}

This simply wires up a short list for data-binding. With some simple XAML, the entire structure is complete:

<Grid x:Name="LayoutRoot" Background="White">
    <Grid.DataContext>
        <FluentBackwardsDesign:ViewModel/>
    </Grid.DataContext>
    <ListBox ItemsSource="{Binding FeedItems}">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto"/>
                        <RowDefinition Height="Auto"/>
                    </Grid.RowDefinitions>
                    <StackPanel Orientation="Horizontal">
                        <TextBlock FontWeight="Bold" Text="{Binding Posted}"/>
                        <TextBlock Text="{Binding Title}" FontStyle="Italic" HorizontalAlignment="Right" Margin="20 0 0 0"/>                            
                    </StackPanel>
                    <TextBlock TextWrapping="Wrap" Grid.Row="1" Text="{Binding Description}"/>
                </Grid>                    
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
</Grid>

This project is set as a Silverlight OOB application with elevated trust to avoid any cross-domain issues with loading the feed. To load the feed requires several steps:

  1. Initiate the request to fetch the feed using the WebClient
  2. Receive the response
  3. Stuff the response into a string
  4. Pass the string into an XmlReader
  5. Pass the XmlReader into the SyndicationFeed
  6. Iterate the feeds and populate the feed items

The non-fluent way to perform these steps may look something like this:

protected void OldWay()
{
    var wc = new WebClient();
    wc.DownloadStringCompleted += _WcDownloadStringCompleted;
    wc.DownloadStringAsync(_targetUri);
}

void _WcDownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
    ((WebClient) sender).DownloadStringCompleted -= _WcDownloadStringCompleted;

    using (var stringReader = new StringReader(e.Result))
    {
        using (var reader = XmlReader.Create(stringReader))
        {
            var feed = SyndicationFeed.Load(reader);
            foreach(var feedItem in feed.Items.Select(
                item => new FeedItem
                            {
                                Id = item.Id,
                                Posted = item.PublishDate.Date,
                                Title = item.Title.Text,
                                Description = item.Summary.Text
                            }))
            {
                FeedItems.Add(feedItem);
            }
        }
    }
}

This code adds a lot to the view model. It violates the Single Responsibiity Principle by requiring the view model to understand how to fetch the data, how to handle the return, how to parse it and then feed it in. The various steps are also not reusable or unit testable as they depend on the live feed and connection.

Good practices would dictate moving away from this model to decoupled classes with separated responsibilities. Why not go ahead and make this fluent?

Ideally, the pattern of opening some data (whether a feed or other format), parsing it and populating a list will be easy and fluent. Starting with the end in mind, what if the code looked like this?

ServiceLocator
    .GetHelper<FeedItem>()
    .For(FeedItems)
    .From(_targetUri)
    .ParseWith(new FeedParser())
    .Go();

This would be all of the code necessary to make the view model work: get a helper for the type of list, indicate what collection should be populated, provide the target URI, inject the strategy to map the format to a known entity, and kick it all off.

The model for fluent extensions is to provide a method on the class (whether a local method or an extension method) that ultimately returns itself. This allows for chaining of multiple method calls because each call returns the class to make it available for the next. Knowing this, the contract for the helper can be defined like this:

public interface IWebClientHelper<T>
{
    IWebClientHelper<T> For(ICollection<T> target);

    IWebClientHelper<T> From(Uri uri);
        
    IWebClientHelper<T> ParseWith(IParser<T> parse);
        
    void Go();
}

The parser contract can also be inferred, because it would take in the string resulting from the download and return the collection.

public interface IParser<T> 
{
    IEnumerable<T> Parse(string src);
}

Now it's possible to implement the parser. The implementation is nice because it can be unit tested by supplying a predefined string and comparing against the expected output:

public class FeedParser : IParser<FeedItem>
{
    public IEnumerable<FeedItem> Parse(string src)
    {
        using (var stringReader = new StringReader(src))
        {
            using (var reader = XmlReader.Create(stringReader))
            {
                var feed = SyndicationFeed.Load(reader);
                return feed.Items.Select(
                    item => new FeedItem
                                {
                                    Id = item.Id,
                                    Posted = item.PublishDate.Date,
                                    Title = item.Title.Text,
                                    Description = item.Summary.Text
                                });
            }
        }
    }
}

The implementation of the web helper can store the stateful information about the call, then generically implement the call:

public class WebClientHelper<T> : IWebClientHelper<T>
{
    private Uri _uri;

    private ICollection<T> _collection;

    private IParser<T> _parser;        

    public IWebClientHelper<T> For(ICollection<T> target)
    {
        _collection = target;
        return this;
    }

    public IWebClientHelper<T> From(Uri uri)
    {
        _uri = uri;
        return this;
    }

    public IWebClientHelper<T> ParseWith(IParser<T> parse)
    {
        _parser = parse;
        return this; 
    }

    public void Go()
    {
        if (_uri == null)
        {
            throw new ArgumentException("Uri must be set.");
        }

        if (_parser == null)
        {
            throw new ArgumentException("No parser was specified.");
        }

        if (_collection == null)
        {
            throw new ArgumentException("No collection was specified.");
        }

        var wc = new WebClient();
        wc.DownloadStringCompleted += _WcDownloadStringCompleted;
        wc.DownloadStringAsync(_uri);            
    }

    private void _WcDownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
    {
        ((WebClient) sender).DownloadStringCompleted -= _WcDownloadStringCompleted;

        foreach (var item in _parser.Parse(e.Result))
        {
            _collection.Add(item);
        }
    }
}

This could be abstracted even further by providing a strategy for the web client download so that process can be mocked as well.

Finally, instead of dealing directly with the helper, a typical implementation would provide some sort of Dependency Injection/Inversion of Control to fire this off. This example simulates that using a simple service locator:

public static class ServiceLocator
{
    public static IWebClientHelper<T> GetHelper<T>()
    {
        return new WebClientHelper<T>();
    }
}

Anything that depends on the fluent object can now be easily mocked as well, so the fluent interface is perfectly testable. And that's it ... working with the end in mind, this exercise simplified the view model and provided a reusable, fluent interface for grabbing information from remote websites and parsing it into typed collections.

Download the source here.

Jeremy Likness