Browse by Tags

All Tags » rest services   (RSS)

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

One of the most common examples to help learn a language or framework is an RSS Reader. This is an ideal mini-project because it includes networking, parsing XML, and binding to data elements such as lists. I wanted to provide an example that shows some more interesting solutions that are possible using C# in Silverlight. This is the first part in a series. By the end of this post, we'll have a working reader. What I'll then do is add some more detailed error handling, provide unit tests, tackle skinning and visual states, and hopefully end up with a fairly advanced client.

Today, let's focus on getting a client where we can enter a feed and get some listings. A perfect way to test this will be a Twitter feed. If you would like to read my recent tweets as RSS, for example, you can simply navigate to http://twitter.com/statuses/user_timeline/46210370.rss. By viewing the source, you can see the basic structure of an RSS feed. It is fairly straightforward.

Download the source for this project

Domain Entities

The first step is to define some domain entities. These are the "models" for our application, and represent the actual data we are working with. With an RSS feed, it is fairly simple. We'll work our way backward from the item up to the channel. Our item to start with looks like this:

 
public class Item
{
    public string Title { get; set; }

    public string Description { get; set; }

    public DateTime PublishDate { get; set; }

    public string Guid { get; set; }

    public Uri Link { get; set; }
}

A channel is simply some header information with a collection of items. For starters, we'll make it look like this:

public class Channel
{
    public Channel()
    {
        Items = new ObservableCollection<Item>();
    }

    public string Title { get; set; }

    public Uri Link { get; set; }

    public string Description { get; set; }

    public ObservableCollection<Item> Items { get; set; }
}

Note that I build my entities based on the actual content, not how the content is delivered. In other words, even though the RSS stream may encode a link or a date as text, I want my domain entity to be able to work with the property as it was intended. Therefore, there are no "hacks" to force something into a Uri that doesn't belong: we use that type for the links. Similarly, the date time is handled as a date, and we can worry about how to display it when the time is right. The point is this keeps the data we're dealing with abstracted from the way we receive it, so if down the road we are reading a database or connecting to a different type of service, we only need to change the way we adapt from the service into our domain.

De-serialization

One of the challenges we'll face with RSS feeds is taking the feed data and turning it into our domain objects (de-serializing the data). There are a number of different approaches, but I wanted to present one that I think makes it extremely easy to expand and extend entities that are mapped to XML documents. It involves using Silverlight's powerful LINQ to XML features.

The concept is relatively simple: XML data is mostly strings in elements and attributes. For RSS feeds, the data is entirely in elements. Therefore, it should be relatively straightforward to parse an XML element into an entity. We'll know the target property and the type of that property, so all we need is the element and the conversion.

To tackle the element, I decided to create a custom attribute called RssElement that allows me to tag a property with the name of the element the property's value is contained within. Here is the attribute:

[AttributeUsage(AttributeTargets.Property,AllowMultiple = false)]
public class RssElementAttribute : System.Attribute
{
    public readonly string ElementName; 

    public RssElementAttribute(string elementName)
    {
        ElementName = elementName;
    }
}

Now I can circle back to my entities and tag them with the source elements. First, the item:

public class Item
{
    [RssElement("title")]
    public string Title { get; set; }

    [RssElement("description")]
    public string Description { get; set; }

    [RssElement("pubDate")]
    public DateTime PublishDate { get; set; }

    [RssElement("guid")]
    public string Guid { get; set; }

    [RssElement("link")]
    public Uri Link { get; set; }
}

And then the channel:

public class Channel
{
    public Channel()
    {
        Items = new ObservableCollection();
    }

    [RssElement("title")]
    public string Title { get; set; }

    [RssElement("link")]
    public Uri Link { get; set; }

    [RssElement("description")]
    public string Description { get; set; }

    public ObservableCollection<Item> Items { get; set; }
}

Now that we have a way of tagging the properties, we can create a fluent extension to move from an XElement (what LINQ gives us) to an actual type.

I created a static class called LinqExtensions to extend my LINQ classes. Again, working backward, let's consider what happens to the value of an element in XML when we move it to our entity. We'll need to take that value (which is a string) and convert it based on the target type. It has a consistent method signature: receive a string, return a type. Therefore, I decided to implement a dictionary to map the conversion process to the target types. Working with the types I know I'll need, my dictionary looks like this:

public static class LinqExtensions
{
    private static readonly Dictionary<Type, Func<string, object>> _propertyMap;

    static LinqExtensions()
    {
        _propertyMap = new Dictionary<Type, Func<string, object>>
                           {
                               {typeof (string), s => s},
                               {typeof (Uri), s => new Uri(s)},
                               {typeof (DateTime), s => DateTime.Parse(s)}
                           };
    }

This is very simple when you see where I'm going. Everything is mapped to a function that takes a string and returns an object. A string is a pass-through (s => s). A Uri converts the string to a Uri (s => new Uri(s)) and a date time attempts to parse the date (s => DateTime.Parse(s)). By using the type as the key to the dictionary, I can do this:

...
DateTime date = _propertyMap[typeof(DateTime)]("1/1/2010") as DateTime; 
...

Of course, part of what we can do down the road is add some better error handling and contract checking. Next, let's use this conversion. I am taking in an element, then using the property information that I have to set the value onto an instance of the target class. Given a class instance T, an element, and the information about a property, I can inspect the custom attribute to find the element name, then use the conversion for the type of the property to set the value. My method looks like this:

private static void _ProcessProperty<T>(T instance, PropertyInfo property, XContainer element)
{
    var attribute =
        (RssElementAttribute) (property.GetCustomAttributes(true)
                                  .Where(a => a is RssElementAttribute))
                                  .SingleOrDefault();

    XElement targetElement = element.Element(attribute.ElementName);

    if (targetElement != null && _propertyMap.ContainsKey(property.PropertyType))
    {
        property.SetValue(instance, _propertyMap[property.PropertyType](targetElement.Value), null);
    }
}

As you can see, we grab the element, find the value in the XML fragment we're passed, then use the property's SetValue method to set the value based on the conversion we find in the property map.

How do we get the property info? Take a look at this fluent extension. It takes an element and is typed to a target class. It will create a new instance of the class and iterate the properties to set the values. By using it as a fluent extension, I can write this in code, which I think makes it very readable:

...
var widget = xmlElement.ParseAs<Widget>(); 
...

The "parse as" method looks like this:

public static T ParseAs<T>(this XElement element) where T : new()
{
    var retVal = new T();

    ((from p in typeof (T).GetProperties()
      where (from a in p.GetCustomAttributes(true) where a is RssElementAttribute select a).Count() > 0
      select p).AsEnumerable()).ForEach(p => _ProcessProperty(retVal, p, element));

    return retVal;
}

I basically grab all properties with the custom attribute, then pass the class, element, and property to the method we developed earlier to set the value. Finally, I return the new instance of the class. The ForEach is available with the toolkit, but you can roll your own enumerator extension like this:

public static class IteratorExtensions
{
    public static void ForEach<T>(this IEnumerable<T> list, Action<T> action)
    {
        foreach(var item in list)
        {
            action(item);
        }
    }
}

To me, it saves code and makes it easier to read what's going on if you have to perform a simple action and use the ForEach extension to do it.

While we're having fun with our fluent extensions, we're almost ready to take an RSS feed and turn it into a group of objects. We need a way to get the text we receive from the call into an XDocument, so I made this extension:

public static XDocument AsXDocument(this string xml)
{
    return XDocument.Parse(xml);
}

We also need to take the XDocument and iterate the elements to build channels and items. Because we already built our ParseAs method and have tagged the entities for channels and elements, this becomes as simple as:

public static List<Channel> AsRssChannelList(this XDocument rssDoc)
{
    var retVal = new List<Channel>();

    if (rssDoc.Root != null)
    {
        foreach(var c in rssDoc.Root.Elements("channel"))
        {
            var channel = c.ParseAs<Channel>();
            c.Elements("item").ForEach(item => channel.Items.Add(item.ParseAs<Item>()));
            retVal.Add(channel);                    
        }
    }

    return retVal;
}

Again, I believe the fluent extensions are making our life easier because one logical layer builds on the next. We already have our entities tagged and our parsing worked out, so now we simply iterate the channels and parse those as channels, then iterate the items and parse those as items. Could it be an easier? Now, if I have the text of an RSS feed in a variable called rssFeed, the only thing I need to do in order to turn it into a list of channels and items is this:

...
var channelList = rssFeed.AsXDocument().AsRssChannelList(); 
...

We've done most of the heavy lifting, so let's tackle actually going out and fetching the feed. If you've worked with Silverlight and tried to fetch any type of information from another website, you'll be familiar with the strict cross-domain policies that Silverlight has. You can read the Microsoft article on Network Security Access Restrictions in Silverlight to get the full scoop.

In a nutshell, it says most likely we won't be able to reach out from our Silverlight client to touch the RSS feeder and bring back the information. How do you like that twist? Fortunately, there is a workaround.

Using a Handler to Bypass Cross Domain Concerns

Silverlight is allowed to pull information from the same server that it is hosted from. One simple way to bypass the issue with going out directly from the client to the reader is to create a proxy. We don't have the same restrictions on the server with going across domains, so we can have Silverlight "ask" the server to pull the feed for us, and deliver it back.

This, however, has its own issues. I can make a handler that takes a Uri and then returns the data, but this would expose a potential security risk in my web application. It would open the door for malicious attackers to hit other websites and make it look like it is coming from my server! It's sort of like the old "call-forwarding" techniques that old school phreakers would use to hide their illicit telecommunications activities.

I am going to use the handler approach, but make it a little more difficult to abuse. In a production system, I'd implement a full brown encryption scheme. The issue with the Silverlight side is that it gets downloaded to the browser, so a would-be hacker can easily pull apart the code and dissect what's going on. You'll want a fairly strong algorithm to circumvent foul play. For this blog example, I'm going to implement a very light security piece that will thwart any casual foul play by preventing someone from guessing how to pull a feed without knowing the algorithm. OK, so it's not very secret because I'm letting you in on the deal.

For the sake of illustration, I'm simply going to have the Silverlight client generate a guid, then generate a hash on the GUID using SHA1. Again, for a more secured solution, I'd have a SALT and keywords, etc, but this at least shows a way to secure the communication and should get you thinking about security concerns when you start exposing services for Silverlight to consume.

In order for this to work, both the client and the server need to implement the same algorithm. I am a big fan of DRY (Don't Repeat Yourself) so I don't want to duplicate the algorithm in both places. Instead, I'll build this simple piece on the Silverlight side:

public static class SecurityToken
{        
    public static string GenerateToken(string seed)
    {
        Encoder enc = Encoding.Unicode.GetEncoder();

        var unicodeText = new byte[seed.Length * 2];
        enc.GetBytes(seed.ToCharArray(), 0, seed.Length, unicodeText, 0, true);

        System.Security.Cryptography.SHA1 sha = new System.Security.Cryptography.SHA1Managed();

        byte[] result = sha.ComputeHash(unicodeText);

        var sb = new StringBuilder();

        for (int i = 0; i 

It's a simple method that takes a string and returns the hash as digits. On the web server side, I go into my project and use "add existing item."

Then, I navigate to the security class in my Silverlight project, highlight it, then choose "add as link."

This allows me to share the code between the two sides. I can make a change once and ensure both are always in sync. I call this "poor man's WCF RIA." OK, bad joke. Let's move along.

In the ClientBin folder, I add a handler. I do it here because it makes it easy for Silverlight to reference the handler using a relative Uri. Otherwise, I'll have to hack around with the application root and parse strings and do lots of work I just don't care to do. This way, I can reference it as:

...
var handlerUri = new Uri("RssHandler.ashx"); 
...

And be done with it. Silverlight will pass the guid, the key, and the desired Uri. My handler will generate the key from the guid, make sure it matches what was passed, and then proxy the feed. The code looks like this:

[WebService(Namespace = "http://jeremylikness.com/rssReader")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class RssHandler : IHttpHandler
{
    public void ProcessRequest(HttpContext context)
    {
        context.Response.ContentType = "text/xml";

        if (context.Request.QueryString["key"].Equals(SecurityToken.GenerateToken(context.Request.QueryString["guid"])))
        {
            using (var client = new WebClient())
            {
                context.Response.Write(client.DownloadString(context.Request.QueryString["rssuri"]));
            }
        }
    }

    public bool IsReusable
    {
        get
        {
            return false;
        }
    }
}

Now we need to the build the service on Silverlight.

The Reader Service

First, let's start with the interface for the service. What we want is to pass in the URL of the RSS feed, and receive a list of channels with corresponding items that were fetched from the feed. That ends up looking like this:

public interface IRssReader
{
    void GetFeed(Uri feedUri, Action<List<Channel>> result);
}

Notice I'm making life easy for classes that use the service. They won't have to worry about the asynchronous nature of the service calls or how the result comes back, etc. All that is needed is to pass a method that will accept the list of channels when it becomes available.

As I mentioned earlier, we already did most of the heavy lifting with parsing out the feed, so wiring in the service is very straightforward. The implementation looks like this:

public class RssReader : IRssReader 
{
    private const string PROXY = "RssHandler.ashx?guid={0}&key={1}&rssuri={2}";

    public void GetFeed(Uri feedUri, Action<List<Channel>> result)
    {

        var client = new WebClient();

        Guid guid = Guid.NewGuid();
       
        client.DownloadStringCompleted += ClientDownloadStringCompleted;
        client.DownloadStringAsync(new Uri(
                                       string.Format(PROXY,
                                                     guid,                                                         
                                                     SecurityToken.GenerateToken(guid.ToString()),
                                                     feedUri), UriKind.Relative), result);
    }

    static void ClientDownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
    {
        var client = sender as WebClient;
        
        if (client != null)
        {
            client.DownloadStringCompleted -= ClientDownloadStringCompleted;
        }

        if (e.Error != null)
        {
            throw e.Error; 
        }

        var result = e.UserState as Action<List<Channel>>; 

        if (result != null)
        {
            result(e.Result.AsXDocument().AsRssChannelList()); 
        }
    }
}

Walking through the steps, we first get the Uri and the method to call when we're done. We wire up a web client and pass it the URL of our handler. This has parameters for the guid and our key, as well as the Uri we want to fetch. We also pass the method to call when we're done. The asynchronous calls all provide a generic object to pass state, so when the call returns we know which version of the call we're dealing with (nice of them, hey?)

When the call is completed, we unhook the event to avoid any dangling references. If there was an error, we throw it. (Yeah, part of our refactoring in future posts will be dealing with all of the errors I'm either throwing or not catching throughout the example). We take the state object and turn it back into an action, then pass the received value as the list of channels using our fluent extensions.

So now we can get it and load it into our models. We need a view model to host all of this!

The View Model

For the first pass on this, I'm going to wire everything myself. No fancy PRISM or MEF extensions, we can refactor those later. Let's get to showing something on the screen.

Our view model should be able to accept the URL of a feed, let the user click "load" and possibly "refresh" on the feed, then show it to us. We want to give them some feedback if the feed isn't a valid Uri, and we probably should keep the load button disabled until there is a valid feed. I ended up creating this for phase one:

public class RssViewModel : INotifyPropertyChanged
{
    private readonly IRssReader _reader; 

    public RssViewModel(IRssReader reader)
    {
        _reader = reader;
        Channels = new ObservableCollection<Channel>();
    }

    private Uri _feedUri;

    private string _uri; 

    public string FeedUri
    {
        get { return _uri; }
        set
        {
            // this will throw an error if invalid, for validation
            _feedUri = new Uri(value);
            _uri = value;
            RaisePropertyChanged("FeedUri");
            RaisePropertyChanged("CanRefresh");
        }
    }

    public bool CanRefresh { get { return _feedUri != null; }}

    public void RefreshCommand()
    {
        if (CanRefresh)
        {
            Channels.Clear();
            _reader.GetFeed(_feedUri, LoadChannels);
        }
    }

    public void LoadChannels(List<Channel> channelList)
    {
        channelList.ForEach(channel=>Channels.Add(channel));
    }

    public ObservableCollection<Channel> Channels { get; set; }

    protected void RaisePropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

The view model implements INotifyPropertyChanged to support binding. In lieu of using a converter for the Uri, I decided to deal with it as a string, then cast it to a Uri. If the cast fails, it throws an error, so we can use the Silverlight validation engine to display the error. Only if the text was successfully cast to a valid Uri will we enable the refresh button. When it's clicked, we call the service and clear the list of channels, then populate the list on the return call.

A Basic Page

Now we can throw together some XAML to show it. In this first iteration I'm not about getting stylish or fancy, so I tossed together something fast. I'm using grids in lieu of stack panels so they auto-size well, and a custom ItemsControl that allows vertical scroll bars. We simply add the scroll viewer around the items presenter using a style, like this:

<Style 
    TargetType="ItemsControl" 
    BasedOn="{StaticResource ItemsControlStyle}"
    x:Name="ItemsControlOuterStyle">
    <Setter Property="ItemsControl.Template">
        <Setter.Value>
            <ControlTemplate>
                <ScrollViewer x:Name="ScrollViewer" Padding="{TemplateBinding Padding}">
                    <ItemsPresenter />
                </ScrollViewer>
            </ControlTemplate>
        </Setter.Value>
    </Setter>            
</Style>

I know the high priests of MVVM are going to try and burn me for blasphemy with this next move, but we're still in the proof of concept, OK? So far we have no testing, IoC, nor beautiful command extensions. Yes, you are about to see a plain old code-behind event:

<TextBlock
    Grid.Column="0"
    Margin="5"
    Text="RSS Feed URL:"/>
<TextBox 
    Grid.Column="1"
    Width="200"
    Margin="5"
    Text="{Binding Path=FeedUri,Mode=TwoWay,NotifyOnValidationError=True,ValidatesOnExceptions=True}"/>
<Button
    Grid.Column="2"
    Margin="5"
    IsEnabled="{Binding Path=CanRefresh}"
    Content="Refresh"
    Click="Button_Click"/>

Here you can enter the feed. When you tab out, it will validate it and show an error if it is invalid. If it passes, the button will be enabled.

This next chunk of XAML shows the items. We'll do more refactoring as well. I will introduce the concept of formatters than will parse things like hyperlinks and Twitter hash terms to provide more interactive functionality, but for now let's just get it out there:

<ItemsControl
    Style="{StaticResource ItemsControlOuterStyle}"
    Grid.Row="1" 
    ItemsSource="{Binding Channels}">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Grid 
                Margin="5"
                HorizontalAlignment="Stretch"
                VerticalAlignment="Top">
                <Grid.RowDefinitions>
                    <RowDefinition/>
                    <RowDefinition/>
                    <RowDefinition/>
                </Grid.RowDefinitions>
                    <HyperlinkButton 
                        Grid.Row="0"
                        Margin="5"
                        FontWeight="Bold"
                        Content="{Binding Title}"
                        NavigateUri="{Binding Link}"/>
                    <TextBlock 
                        Grid.Row="1"
                        Margin="5"
                        TextWrapping="Wrap"
                        Text="{Binding Description}"/>
                <ItemsControl 
                    Grid.Row="2"
                    Style="{StaticResource ItemsControlStyle}"
                    ItemsSource="{Binding Items}">
                    <ItemsControl.ItemTemplate>
                        <DataTemplate>
                            <Grid HorizontalAlignment="Stretch"
                                  VerticalAlignment="Top"
                                  Margin="5">
                                <Grid.RowDefinitions>
                                    <RowDefinition/>
                                    <RowDefinition/>
                                </Grid.RowDefinitions>
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="Auto"/>
                                    <ColumnDefinition Width="Auto"/>
                                </Grid.ColumnDefinitions>
                                <TextBlock 
                                    Margin="5"
                                    Grid.Row="0"
                                    Grid.Column="0"
                                    Text="{Binding PublishDate}"/>
                                <HyperlinkButton
                                    Margin="5"
                                    Grid.Row="0"
                                    Grid.Column="1" 
                                    Content="{Binding Title}"
                                    NavigateUri="{Binding Link}"/>
                                <TextBlock 
                                    Grid.Row="1"
                                    Grid.Column="0"
                                    Grid.ColumnSpan="2"
                                    TextWrapping="Wrap"
                                    Text="{Binding Description}"/>
                            </Grid>
                        </DataTemplate>
                    </ItemsControl.ItemTemplate>
                </ItemsControl>
            </Grid>                   
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

Whew! Now we just need to put in a little bit of code behind. We'll enrage the dependency injection purists by creating hard coded instances and further stoke the ire of MVVM evangelists by wiring in a code behind for the button click:

public partial class MainPage
{
    public MainPage()
    {
        InitializeComponent();
        LayoutRoot.DataContext = new RssViewModel(new RssReader());
    }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        ((RssViewModel)LayoutRoot.DataContext).RefreshCommand();
    }
}

Upon publishing and hitting the service, I was able to pull up my Twitter feed:

That's it. Now we have a proof of concept, and it worked. We have a lot more to do ... making it expandable to handle different types of feeds including HTML, styling it, perhaps looking at using multiple feeds, tests, and the lot (yes, I know, I know, the tests should have come earlier). I hope you enjoyed this first look into my process of creating a basic reader and we'll continue to evolve the project with time!

Download the source for this project

Jeremy Likness

Silverlight has myriad ways to connect to other systems and retrieve information for your applications. It is a common question people ask ("How do I get my data from the database to Silverlight?") and each method has its own pros and cons. The purpose of this project, "Silverlight Communicator," is to provide a simple, easy reference project that demonstrates three sample methods for communication in Silverlight 3. While these certainly aren't all of the ways Silverlight can obtain data, it does summarize more commonly used methods.

Download Source Now (44 Kb)

Silverlight Communicator

The main page is straightforward and uses the MVVM (Model-View-ViewModel) pattern to bind to the selections (no code-behind). I kept it simple and sweet: just a grid with two listboxes, which are bound to the view model that is instantiated as a static resource.

<UserControl x:Class="SilverlightCommunication.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
    xmlns:local="clr-namespace:SilverlightCommunication"
    mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480">
    <UserControl.Resources>
        <local:ViewModel x:Key="ViewModel"/> 
    </UserControl.Resources>
        <Grid x:Name="LayoutRoot" DataContext="{StaticResource ViewModel}">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="1*"/>
            <ColumnDefinition Width="1*"/>
        </Grid.ColumnDefinitions>
        <ListBox Grid.Column="0" ItemsSource="{Binding CommunicationTypes}" DisplayMemberPath="Name"
                 SelectedItem="{Binding Path=Communication,Mode=TwoWay}"/>
        <ListBox Grid.Column="1" ItemsSource="{Binding Months}" DisplayMemberPath="Name"/>
  </Grid>
</UserControl>

The view model exposes a collection of CommunicationType. This could probably be an enum but for the contrived example, it is loaded up and wired in the view model constructor and maps to the options for choices of the communication type. The view model also exposes a collection of Month, which is a class that contains just one property: Name (you guessed it: the name of the month). Again, a contrived example but it made more sense to bind to a class than a simple string.

If you notice in the code behind, I wired in SelectedItem="{Binding Path=Communication,Mode=TwoWay}". This will data bind the selected item of the list box to the property called Communication on the view model. This allows me to intercept the setter and work some magic. The "magic" looks like this:

set
{
    _communicationType = value;
    IMonthService service = ServiceFactory.GetService(value);
    if (service != null)
    {
        service.MonthsLoaded += new EventHandler<MonthArgs>(service_MonthsLoaded);
        service.RequestMonths();
    }
}

As you can see, when a communication type is selected, the view model goes to a factory and retrieves a service that honors the IMonthService contract. It then wires into a loaded event (where it will insert the months into the Months property) and requests the month list. Because Months is an ObservableCollection, it will fire an event to notify the UI that it has changed and the result will be a month list populated in the list box on the right side.

The month service contract is simple, but this is where we will implement three different ways to retrieve our list of months:

namespace SilverlightCommunication
{
    public interface IMonthService
    {
        void RequestMonths();

        event EventHandler<MonthArgs> MonthsLoaded;
    }
}

Before we jump into the specific methods, here's a peek at the factory. Again, the focus here is the communication methods, not a full-blown enterprise application. You'll see a lack of exception handling and other "good coding practices" here so we can focus on the meat of the project. Based on the type, the factory just returns the appropriate instance.

I also just want to call this out: I know you won't be spending all day on the application, so I don't mind newing up the instances. In a production application, these would probably use the singleton pattern to provide a single instance, or perhaps be configured in an inversion of control container with a lifetime manager.

public static IMonthService GetService(CommunicationType type)
{
    if (type.ID.Equals(1))
    {
        return new WCFMonthServiceImpl();
    }
    else if (type.ID.Equals(2))
    {
        return new RESTMonthService();
    }
    else if (type.ID.Equals(3))
    {
        return new CallbackMonthService();
    }
    else return null; 
}

Method One: WCF Service

WCF is one of the more popular methods. It is also fairly straightforward with Silverlight. On the server side, we define a service that simply returns a list of strings for each month. The contract looks like this:

[ServiceContract]
public interface IMonthService
{
    [OperationContract]
    List<string> GetMonths();
}

And the implementation is simple:

public List<string> GetMonths()
{
    return new List<string> { "Enero", "Febrero", "Marzo", "Abril", "Mayo", "Junio", "Julio", "Agosto", "Septiembre", "Octubre", "Noviembre", 
        "Diciembre" };
}

The months will return in Spanish to distinguish the results from the other modes of communication.

The first key to making the service useable by Silverlight is to make sure it has a simple binding. The Web.config most likely generated it as a wsHttpBinding, what we want is a basicHttpBinding. A quick change leaves a configuration that looks like this:

<system.serviceModel>
  <behaviors>
   <serviceBehaviors>
    <behavior name="SilverlightCommunication.Web.MonthServiceBehavior">
     <serviceMetadata httpGetEnabled="true" />
     <serviceDebug includeExceptionDetailInFaults="false" />
    </behavior>
   </serviceBehaviors>
  </behaviors>
  <services>
   <service behaviorConfiguration="SilverlightCommunication.Web.MonthServiceBehavior"
    name="SilverlightCommunication.Web.MonthService">
    <endpoint address="" binding="basicHttpBinding" contract="SilverlightCommunication.Web.IMonthService">
     <identity>
      <dns value="localhost" />
     </identity>
    </endpoint>
    <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
   </service>
  </services>
 </system.serviceModel>

In production, we'd probably secure the service and use an HTTPS endpoint, and define a specific fault contract to manage errors.

At this point you should be able to publish the service and browse to it locally.

If your computer complains that there is a "page not found" at the service endpoint (on my machine, at http://localhost/MonthService.svc), the services are most likely not registered.

To register them, follow these steps:

  1. Open a command line as administrator
  2. Browse to the Windows Communication Foundation folder, on most systems it will be C:\windows\microsoft.NET\v3.0\Windows Communicatoin Foundation
  3. Run "ServiceModelReg -i"

Now you should be registered and able to browse to your service.

On the Silverlight side, we add a service reference. In this project, I added it as WCFMonthService. This generates the ServiceReferences.ClientConfig file. You will most likely have to modify this file to point to the location of your service, unless you are hosting at the localhost root as I did. To see how you can manipulate your service bindings to automatically adjust to the install location, read my post Abstracting WCF Service Calls in Silverlight 3 (and read the caveat about HTTPS as well.)

Now the implementation of WCFMonthServiceImpl is straightforward. In the request, we create a client to connect to the service, register to the loaded event, and fire it off:

public void RequestMonths()
{
    WCFMonthService.MonthServiceClient client = new SilverlightCommunication.WCFMonthService.MonthServiceClient();
    client.GetMonthsCompleted += new EventHandler<SilverlightCommunication.WCFMonthService.GetMonthsCompletedEventArgs>(client_GetMonthsCompleted);
    client.GetMonthsAsync();
}

And when the service comes back, we load our args and fire the loaded event:

void client_GetMonthsCompleted(object sender, SilverlightCommunication.WCFMonthService.GetMonthsCompletedEventArgs e)
{
    WCFMonthService.MonthServiceClient client = sender as WCFMonthService.MonthServiceClient;
    client.GetMonthsCompleted -= client_GetMonthsCompleted;
    if (MonthsLoaded != null)
    {
        MonthArgs args = new MonthArgs();
        foreach (string month in e.Result)
        {
            args.Months.Add(new Month { Name = month }); 
        }
        MonthsLoaded(this, args); 
    }
}

(If you're bothered by the way I call the loaded event, read my note at the end of the callback method).

Traditional SOAP/WCF calls definitely have the most overhead of the methods we'll cover (although the ability to define the service as binary certainly helps). Your best friend when troubleshooting services is a proxy or sniffer like Fiddler. This will tell you if the plugin is requesting a clientaccesspolicy.xml (I've included one as an example, and if you host this in the root, it should satisfy the request) but also allow you to inspect the traffic.

For this method, our post contains a formatted SOAP request:

POST /MonthService.svc HTTP/1.1
Host: localhost
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.4) Gecko/20091016 Firefox/3.5.4
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Content-Length: 135
Content-Type: text/xml; charset=utf-8
SOAPAction: "http://tempuri.org/IMonthService/GetMonths"

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
   <s:Body><GetMonths xmlns="http://tempuri.org/" />
   </s:Body></s:Envelope>

And the response is in an XML envelope:

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
   <s:Body>
      <GetMonthsResponse xmlns="http://tempuri.org/">
         <GetMonthsResult xmlns:a="http://schemas.microsoft.com/2003/10/Serialization/Arrays" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
        <a:string>Enero</a:string>
        <a:string>Febrero</a:string>
        <a:string>Marzo</a:string>
        <a:string>Abril</a:string>
        <a:string>Mayo</a:string>
        <a:string>Junio</a:string>
        <a:string>Julio</a:string>
        <a:string>Agosto</a:string>
        <a:string>Septiembre</a:string>
        <a:string>Octubre</a:string>
        <a:string>Noviembre</a:string>
        <a:string>Diciembre</a:string>
</GetMonthsResult></GetMonthsResponse></s:Body></s:Envelope>

Method Two: RESTful Service or Straight Web Stack

REST is a different type of service (Representational State Transfer). It uses the existing web (HTTP) protocol for communication, allowing "verbs" (you may be familiar with POST and GET, and REST adds more) and makes the assumption that a URL represents a certain "state." A URL might contain information about a product as /PRODUCT/1 and the expectation is that this URL always returns that product object in its current state. REST provides more flexibility than SOAP for the formatting of the response (it might be a string, a JSON object, a simple XML document, etc).

Many popular APIs like Twitter use the REST form of communication. ASP.NET provides support for these types of calls via the WebGet attribute. To simplify this example, however, I simply wired a simple handler that returns a list of months (in Italian, to distinguish them from the other methods). The handler code behind is very simple:

private const string MONTHS = "Gennaio,Febbraio,Marzo,Aprile,Maggio,Giugno,Luglio,Agosto,Settembre,Ottobre,Novembre,Dicembre";

public void ProcessRequest(HttpContext context)
{
    context.Response.ContentType = "text/plain";
    context.Response.Write(MONTHS);
}

As you can see, it literally spits out a list of months.

On the Silverlight side, we move from the concept of a WCF client to the more generic WebClient. This allows us to download strings or other content from the web, as well as upload content. It even includes support for credentials. In my case, I implemented the GetMonths with a simple request to download the string from the handler. Also, note I have hard-coded the URL. You may want to change this if your application is not hosted at the root.

public void RequestMonths()
{
    WebClient client = new WebClient();
    client.DownloadStringCompleted += new DownloadStringCompletedEventHandler(client_DownloadStringCompleted);
    client.DownloadStringAsync(new Uri("http://localhost/MonthHandler.ashx", UriKind.Absolute));
}

Once the string has been loaded, it's very easy to parse out and load (note that again, I'm not diving into error handling here, although one of the benefits of using the WebClient is vastly superior/simpler error handling over the WCF client).

void client_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
    WebClient client = sender as WebClient;
    client.DownloadStringCompleted -= client_DownloadStringCompleted;
    string[] months = e.Result.Split(',');
    MonthArgs args = new MonthArgs();
    foreach (string month in months)
    {
        args.Months.Add(new Month { Name = month });
    }
    if (MonthsLoaded != null)
    {
        MonthsLoaded(this, args);
    }
}

As you can see, it becomes simple string manipulation. We could just as easily retrieve an XML document and parse it using LINQ if we wanted to.

The post for this is simple, as the entire request is coded into the URL:

GET /MonthHandler.ashx HTTP/1.1
Host: localhost
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.4) Gecko/20091016 Firefox/3.5.4
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive

The response? Event simpler ...

Gennaio,Febbraio,Marzo,Aprile,Maggio,Giugno,Luglio,Agosto,Settembre,Ottobre,Novembre,Dicembre

Method Three: Callback (via DOM Interaction)

This is probably the least known and used methods. Because Silverlight can interact with the host browser, it can also interact with traditional ASP.NET callbacks, postbacks, JQuery, and the AJAX framework. This method is useful when you have an existing application and you are integrating new Silverlight components. In one line of business application I was building, we used the existing callbacks to iterate fast proof of concepts and then slowly rebuilt the calls as services where we needed.

The first step is to implement ICallbackEventHandler, which Ido in the default page. When the callback is raised, Iignore any parameters and simply return a list of months in English. The important element of the code behind here is the Page.ClientScript.GetCallbackEventReference. This sets up the call so I know exactly how to execute the callback. A little known fact is that if you do not call this at least once (even if you discard the output), ASP.NET will not wire up a listener for the callback! It must be called in order to set that up.

In the page, there are two JavaScript functions to assist with the callback. They look like this:

// on the return from the callback, finds the control and injects the months
function callbackHandler(result,context) {
    var slControl = document.getElementById('silverlightControl');
    slControl.Content.SilverlightApp.LoadMonths(result); 
}

// just takes the callback reference and triggers the callback
function doCallback() {
    <%=CALLBACKREF%>;
}

The first method is the callback method that is referenced in the code-behind (Default.aspx.cs). It is called with the result (our months list) and the context (null). Note the call to get the Silverlight control itself (this is not the host DIV element, but the actual OBJECT tag). If you use the ASP.NET Control, you will grab this by the client id of the control. Note the syntax of "Content.SilverlightApp.LoadMonths(result)." We'll get to that in a minute.

The doCallback function is just a wrapper for the method we generated in the code behind. If you view the source, you'll see it generates like this:

function doCallback() {
   WebForm_DoCallback('__Page',null,callbackHandler,null,null,false);
}

Now let's jump into the Silverlight application and look at the CallbackMonthService implementation. The constructor wires up the class to be accessible in BLOCKED SCRIPT

public CallbackMonthService()
{
    HtmlPage.RegisterScriptableObject("SilverlightApp", this);
}

This is where the "SilverlightApp" in the JavaScript above came from.

When the RequestMonths method is called, it triggers the callback like this:

public void RequestMonths()
{
   HtmlPage.Window.Invoke("doCallback");
}

When the callback is completed, the JavaScript method will call back to the class and pass in the month list. To facilitate this, I provided a method labeled with the ScriptableMember attribute to make it callable from JavaScript. The method splits the string, loads the arguments, and then fires the loaded event.

[ScriptableMember]
public void LoadMonths(string monthList)
{
    string[] list = monthList.Split(',');

    MonthArgs args = new MonthArgs();

    foreach (string month in list)
    {
        args.Months.Add(new Month { Name = month });
    }

    if (MonthsLoaded != null)
    {
        MonthsLoaded(this, args); 
    }
}

For those of you new to programming, a good homework assignment would be to investigate the potential bug above. I want to call it out so I'm not accused of spreading bad coding habits, but doing it this way make help it stick. The way I raise the MonthsLoaded event is not the best practice, especially considering the multi-cast nature of the underlying delegate. If that doesn't make sense, do some research and it will help you to understand events more (and why I am always unregistering my events once they are fired).

The event was registered to by our view model, which will bind to the observable collection and show the months on the grid.

In the end, the request for this method is similar to a REST call, with a few parameters that the ASP.NET framework uses to track and route the request and response:

POST /default.aspx HTTP/1.1
Host: localhost
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.4) Gecko/20091016 Firefox/3.5.4
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded; charset=utf-8
Referer: http://localhost/
Content-Length: 141
Pragma: no-cache
Cache-Control: no-cache

__EVENTTARGET=&__EVENTARGUMENT=&__VIEWSTATE=%2FwEPDwULLTExOTc0MjQyMjVkZOSAEQGSb7mMBhudDS%2FpDmv5GtNs&__CALLBACKID=__Page&__CALLBACKPARAM=null

But the response is just as simple:

0|January,February,March,April,May,June,July,August,September,October,November,December

(The 0 and pipe format help route multiple responses)

Conclusion

There you have it: a simple demonstration of three methods for getting data into your Silverlight application. I haven't touched upon local communication, sockets, or other more advanced methods, but this should provide a simple shell/skeleton for further exploration. In the future we'll talk more about securing those services and doing some more advanced things with messages on the client and server.

Download the Code (44 Kb)

Jeremy Likness