Browse by Tags

All Tags » silverlight   (RSS)

I created a quick video to help you get started with using Jounce. The video starts with a blank slate, steps through installation of Jounce and the creation of a sample view model for a simple contact record that includes validation.

You can access the video directly at http://vimeo.com/jeremylikness/jounce-getting-started, or view it below:

Jounce: Getting Started from Jeremy Likness on Vimeo.

Jeremy Likness

While writing Chapter 14 of my book, Designing Silverlight Business Applications: Best Practices for Using Silverlight Effectively in the Enterprise (Microsoft .NET Development Series) I focused on an area that is quite common with line of business applications: extremely large data sets. In the example I generated almost 1,000,000 contact rows to illustrate how they would be managed by the Silverlight client. Like many software problems, there are many solutions; here is an excerpt of the three I demonstrated in the book.

All of these solutions use the Entity Framework for data access. How that data access is projected to the client is illustrated bythree different patterns: OData (the straight services, not the checkbox on the WCF RIA Services tab), WCF RIA Services, and using the MVVM pattern. To simplify the examples I'm only focused on reads here. Writes do add a layer of complexity and change tracking, but I argue that the problem to solve there is not how to manage a large data set because anything the user actually interacts with is going to be a smaller order of magnitude.

RESTful OData

It's extremely easy to expose an OData end point from a .NET web application. You can simply add a new WCF Data Service and then define what it has access to. In this example I have a Contact table that looks like this:

After generating the data model, the Entity Framework provides this in my ContactModel space:

The underlying context that was generated is called ContactEntities so for my OData service I can simply point to the underlying context and specify which collections are available and what access rights the client should have:

public class ContactOData : DataService<ContactsEntities>
{
    public static void InitializeService(DataServiceConfiguration config)
    {            
        config.SetEntitySetAccessRule("Contacts", EntitySetRights.AllRead);
        config.SetEntitySetPageSize("Contacts", 20);
        config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V2;
    }
}

Note I've specified a page size of 20 records so that the service doesn't return all 1,000,000 records at once. When I hit the service endpoint, I get this:

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<service 
   xml:base="http://localhost:59389/ContactOData.svc/" 
   xmlns:atom="http://www.w3.org/2005/Atom" 
   xmlns:app="http://www.w3.org/2007/app" 
   xmlns="http://www.w3.org/2007/app">
  <workspace>
    <atom:title>Default</atom:title>
    <collection href="Contacts">
      <atom:title>Contacts</atom:title>
    </collection>
  </workspace>
</service>

This is all a client needs to begin navigating entities as the location of the first collection, Contacts, is clearly specified. We can now navigate to the contacts and receive something like this:

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<feed 
   xml:base="http://localhost:59389/ContactOData.svc/" 
   xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices" 
   xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" 
   xmlns="http://www.w3.org/2005/Atom">
  <title type="text">Contacts</title>
  <id>http://localhost:59389/ContactOData.svc/Contacts</id>
  <updated>2011-11-20T16:24:05Z</updated>
  <link rel="self" title="Contacts" href="Contacts" />
  <entry>
    <id>http://localhost:59389/ContactOData.svc/Contacts(1)</id>
    <title type="text"></title>
    <updated>2011-11-20T16:24:05Z</updated>
    <author>
      <name />
    </author>
    <link rel="edit" title="Contact" href="Contacts(1)" />
    <category term="ContactsModel.Contact" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />
    <content type="application/xml">
      <m:properties>
        <d:Id m:type="Edm.Int32">1</d:Id>
        <d:LastName>Alford</d:LastName>
        <d:FirstName>Lucius</d:FirstName>
        <d:Address>Ap #363-9702 Sit Rd.</d:Address>
        <d:City>Jordan Valley</d:City>
        <d:State>CO</d:State>
        <d:Email>mi.eleifend.egestas@mauriserateget.com</d:Email>
      </m:properties>
    </content>
  </entry>
  ...
  <link rel="next" href="http://localhost:59389/ContactOData.svc/Contacts?$skiptoken=20" />
</feed>

Notice that it follows the standard Atom format. Every entry has a clear location including where to go to post updates ("edit"). The full data set includes 20 entries and then specifies a link to grab the next page.

Adding this to the Silverlight client is easy. You can add a service reference, discover it in the current solution and the client will be wired for you automatically. Using a DataGrid that auto-generates the columns, I can write the following code behind:

public partial class ODataClient
{
    private readonly DataServiceCollection<Contact> _contacts;
    private readonly ContactsEntities _context;
    private DataServiceQueryContinuation<Contact> _nextPage;

    public ODataClient()
    {
        InitializeComponent(); 
        _context = new ContactsEntities(new Uri("../ContactOData.svc", UriKind.Relative));
        _contacts = new DataServiceCollection<Contact>();
        _contacts.LoadCompleted += ContactsLoadCompleted;
        var query = _context.Contacts.IncludeTotalCount();
        _contacts.LoadAsync(query);
    }

    void ContactsLoadCompleted(object sender, LoadCompletedEventArgs e)
    {
        _nextPage = _contacts.Continuation;

        if (_nextPage == null)
        {
            NextPage.IsEnabled = false;
        }

        TotalCount.Text = e.QueryOperationResponse.TotalCount.ToString();
        Count.Text = _contacts.Count.ToString();
        dgContacts.ItemsSource = _contacts;
        dgContacts.UpdateLayout();            
    }

    void Button_Click(object sender, RoutedEventArgs e)
    {
        _contacts.LoadNextPartialSetAsync();
    }
}

The fields hold references to the service. It is instantiated with a relative path to the end point. The query is extended to include a total count of records so it can be used to calculate page sizes, and the link to the next page is retrieved and stored for the continuation. When the user clicks the button to load the next block of records, the continuation is called to fetch the next page. The grid and button look like this:

The example is a quick-and-dirty way to parse the OData stream but can be extended to include a proper paging control (instead of simply expanding the grid) as well as edit and update functionality. Now I'll show you how to do the same thing using WCF RIA. The obvious advantage with WCF RIA is that the code projection removes most of the manual steps you need to take.

WCF RIA Services

The domain service for WCF RIA simply maps operations like queries to the corresponding LINQ-to-Entities commands. This example is read-only so the full implementation looks like this:

[EnableClientAccess]
public class ContactService : LinqToEntitiesDomainService<ContactsEntities>
{       
    public IQueryable<Contact> GetContacts()
    {
        return ObjectContext.Contacts.OrderBy(c => c.Id);
    }
}

The service derives from the context for the contact database and the query simply orders the items to allow paging (the result set must be deterministic for paging to work). That's it on the server side. A metadata class is also generated that you can use to apply data annotations to specify column names, validations, etc.

The client automatically has the WCF RIA classes "projected" which is a fancy way of saying the code is generated for the client. In fact, WCF RIA handles so much plubming that the WCF RIA client doesn't have to have a single line of code-behind. Instead, you can drop in a domain data source:

<riaControls:DomainDataSource 
   AutoLoad="true" 
   d:DesignData="{d:DesignInstance Web:Contact, CreateList=true}"                             
   Height="0" 
   Name="contactDataSource" 
   QueryName="GetContacts" 
   Width="0"
   LoadSize="60" 
   PageSize="20">
    <riaControls:DomainDataSource.DomainContext>
        <Web:ContactContext/>
    </riaControls:DomainDataSource.DomainContext>
</riaControls:DomainDataSource> 

By convention, ContactService is renamed to ContactContext on the client. The domain data service uses context as its data source. It is given some design-time data to generate the grid properly, the query to get the list of contacts is specified along with how many records to pre-fetch and how many to show on a page. A DataGrid simply binds to this as the data source along with a DataPager:

<sdk:DataGrid AutoGenerateColumns="True" 
   ItemsSource="{Binding ElementName=contactDataSource, Path=Data}"/>
<sdk:DataPager 
   Source="{Binding ElementName=contactDataSource, Path=Data}"/>

And that is it! It will handle computing total records, managing pages, and generating the grid. The result looks like this:

That provides what I would call the ultimate rapid development experience. It can literally take just five minutes to create the Entity Framework model, map the domain service, then drop the domain data source and grid controls on the client to have a fully functional application. Of course, as a developer you may want more control over how the application works and perhaps need to make sure this fits within your existing patterns. The most popular pattern for Silverlight development is Model-View-ViewModel (MVVM) so here is a quick view model to make it work:

MVVM

Instead of relying on WCF RIA you can abstract the data access layer using a pattern such as repository. This isn't a full implementation but the simple interface looks like this:

public interface IRepository
{
    void ProcessPage(int page, int pageSize, 
       Action<IEnumerable<Contact>> callback);
    int GetTotalPages(int pageSize);
}

Now you can use a mock to test access to the repository and even swap layers if or when it is necessary. Here is an implementation that works directly with WCF RIA:

public class Repository : IRepository
{
    private int _totalCount;
    private readonly ContactContext _contactContext = new ContactContext();

    public Repository()
    {
        var query = (
                        from c
                            in _contactContext.GetContactsQuery()
                        select c).Take(1);
        query.IncludeTotalCount = true;
        _contactContext.Load(
            query,
            callback => { _totalCount = callback.TotalEntityCount; }, null);
    }

    public void ProcessPage(int page, int pageSize, 
        Action<IEnumerable<Contact>> callback)
    {
        var take = pageSize;
        var skip = pageSize*(page - 1);
        var query = (from c in _contactContext.GetContactsQuery()
                        select c).Skip(skip).Take(take);
        query.IncludeTotalCount = true;
        _contactContext.Load(
            query,
            cb =>
                {
                    _totalCount = cb.TotalEntityCount;
                    callback(cb.Entities);
                }, null);
    }

    public int GetTotalPages(int pageSize)
    {
        return (_totalCount/pageSize) + 1;
    }
}

Note when the repository is created, it queries for a single item just to grab the full count for computing the page size. This is updated each subsequent call for a page. The call to grab the page computes how many records to skip and take for a page and then executes the query.

A simple view model can be constructed that uses the repository:

public class ViewModel : INotifyPropertyChanged
{
    private List<Contact> _contacts = new List<Contact>();
    private readonly IRepository _repository;

    public int CurrentPage { get; set; }

    public int TotalPages { get; set; }

    public IActionCommand NextPage { get; set; }

    public IActionCommand PreviousPage { get; set; }

    public IEnumerable<Contact> Contacts
    {
        get
        {
            if (_contacts.Count == 0)
            {
                Refresh();
            }

            return _contacts;
        }

        set { _contacts = new List<Contact>(value); }
    }
}

It exposes current page, total pages, commands to paginate and a list of the current contacts. The constructor sets up the initial conditions and sets a dummy page and page size in the design view:

public ViewModel()
{
    Contacts = new List<Contact>();
    NextPage = new ActionCommand<object>(obj => GoToNextPage(),
                                            obj => CurrentPage < TotalPages);
    PreviousPage = new ActionCommand<object>(obj => GoToPreviousPage(),
                                            obj => CurrentPage > 1);

    if (!DesignerProperties.IsInDesignTool)
    {
        CurrentPage = 1;
        _repository = new Repository();                
    }
    else
    {
        CurrentPage = 2;
        TotalPages = 10;
    }
}

The Refresh method fetches the current page:

private void Refresh()
{
    _repository.ProcessPage(
        CurrentPage,
        20,
        cb =>
            {
                TotalPages = _repository.GetTotalPages(20);
                _contacts = new List<Contact>(cb);
                RaiseChanges();
            });
}       

The commands simply change the current page and call refresh. For example, the command to advance by one page:

private void GoToNextPage()
{
    CurrentPage++;
    Refresh();
}

Now the view model can be bound to a grid. In this example, the current page and page count are used to construct a very simple paging control. All of the information you need to make a full-blown control is available. The MVVM-based view looks like this:

The ease with which it is possible to navigate a large data set from the client is one of the reasons I believe Silverlight is still a strong player in the Line of Business application space. More details and the full source code for this example will be available when my book publishes. As of this writing on November 20, 2011 it is discounted over 40% on Amazon when you pre-order a copy from here. Thanks!

Jeremy Likness

Once again speculation and worry has developers around the world biting their fingers and lamenting the end of a new era. It almost seems developers want to stick a fork in their Silverlight development because so many are latching onto the hype wagon, calling .NET "legacy" and refusing to see the bigger picture. Microsoft announces a new run-time called "Metro" that is not based on .NET, and suddenly .NET is dead — even though anyone can download the bits for the new operating system, install it, and see for themselves that .NET is alive and well. There happen to be two modes for the application, one in a new "Metro" environment and one in a more "desktop" environment. Microsoft's own Visual Studio is NOT a Metro app so I don't buy the line that this is just a staged coup d'etat. Microsoft announces that there are going to be two version of Internet Explorer: a "Metro" version with no plug-ins, and a desktop version that supports them. I downloaded Silverlight, installed it, and even loaded our Silverlight tablet Rooms to Go Application. It installed fine, appeared on the start menu for Windows 8 and launched without an issue ... but the headlines are reading "Microsoft abandons plug-ins."

First, let me say that I believe Silverlight is alive and well. I'm not saying that because I'm writing a book. People who know me understand I don't get emotionally invested in projects and try to force something to happen when it's not meant to be. I'm not basing this on mere speculation. "What if" doesn't buy me anything. Don't get wrong: I know "what if" is real because it tumbles stocks and cancels contracts, so it does have an impact, but instead of adding to the hysteria I prefer to look at the facts. I know that Windows 8 is not going to be the de facto operating system over night. I know that even when it does, I will be able to build and deploy Silverlight applications to it. I know customers will still have applications to support. I also know I work on a lot of Silverlight applications that simply won't work with the new "Metro" interface and require a more immersive UI and model, and that the guidelines for Metro will probably force those to be written in a traditional stack anyway. I know that a new version of Silverlight is coming out at the end of the year if Microsoft makes their drop target, and I know companies have already begun developing applications that take advantage of its features. But just in case the world is really ending and .NET, Silverlight, and all of those other technologies are truly legacy as of today, what will really happen? I asked my followers on Twitter just what it is that would be missing, and here's what I found out:

Multi-platform and OSX Support

Let's be real here. The only "real" multi-platform support Silverlight has right now is between the Mac OSX and Windows. There is a fledgling presence on Linux but that's hasn't impacted any deals that I'm aware of. The cross-browser story is better because between the two platforms, there is solid compatibility across FireFox, Chrome, Internet Explorer, and Safari. I believe this is a very valid point, as I've done many projects where the customer was most interested in the fact that Silverlight could easily target both platforms with one maintained code base. Of course, the original dream was for this reach to be extended but those hopes were largely dashed by the surge in popularity of the iPad that refused to run the plug-in. While this is a very important aspect of Silverlight, I don't buy that Windows 8 impacts this at all. Windows 8 is an operating system that will run new applications and boasts a new version of a web browser. It also runs the traditional browser and we've confirmed it fully supports Silverlight. So what is missing? The changes to Windows 8 don't remove the ability to drop Silverlight applications on to Mac or existing Windows machines, let alone the next version. It seems the only thing that could negatively impact this point would be lack of features (i.e. I'm missing something that I was expecting to come in a future version) or simply self-fulfilling prophecy, i.e. developers complain so much about it going away that people believe them and pull the plug.

Deployment and web-based Xaml Delivery

This is another sweet spot for Silverlight. The deployment strategy is insanely simple, and many large corporations embrace the easy of installation. This is a double-edged sword however, because for every shop that enjoys the fact that users can navigate to a web page and download the latest version of their application, there is another shop with complicated security policies that must make significant changes to their infrastructure in order to allow the plugin and make Silverlight applications available to employees. This really gets back to the traditional decisions used to drive technology and I don't see how Silverlight is really the primary ingredient. If you want to reach Macs and Windows machines, you will still have that option. If you need a different type of reach or want to take advantage of new innovations, the decision is the same it has always been: either build specific to the platforms you are targeting, with the trade-off that you must now have several teams and versions of the product but it provides a seamless, native experience, or compromise by using a technology like HTML5. That same trade-off existed with the previous version of HTML, it's just that Silverlight development was superior in many cases. I still see cases where HTML5 development is too painful compared to Silverlight, but what it were actually easier to build and deploy HTML5 apps with the same featureset as the Silverlight version? Why on earth would you even want to stick with Silverlight in that scenario?

Community

When I was younger and programmed the Commodore 64, there was a great Commodore community in the St. Petersburg, Florida area. We'd meet up, show each other how to crack open the bread box and solder the little red RadioShack reset button, then swap assembly snippets and chat about the latest VIC chip register hack. When the Commodore began to wane in popularity, people moved onto other platforms and built community around those. Ironically, there is still a strong, growing Commodore 64 community. I'm pretty sure community is more about the individuals in the community than the technology that drives it. Silverlight has a great community. If Silverlight goes away, that community will evolve. (OK, so there are always one or two who can't move on and are still stuck on Information Society breaking up and refuse to program on anything other than an Amiga).

Income

This is like community. First, let me say I know the financial impact of decisions is very real. Speculation about technology causes contracts to get canceled and people to lose jobs. However, if you believe your income is based on the success of Silverlight you are making a very, very big mistake. I remember when I received my fitness and sports nutrition certifications so I could coach people to lose weight. Many of the other "graduates" felt they should research what the pay ranges for trainers were, start at the lowest rate and work their way up. I felt like I should get paid what I was worth, not what the average income was, and had some success working with people so I started at the high end of the range and built a practice around that. My income wasn't based on the latest diet fad or exercise scam, it was based on results. When people lost weight and kept it off, they told others, and my practice grew. It was the value of my service that determined my rates, not industry averages or trends. Software development is the same way. Skilled developers provide value, the customer perceives that value, and they continue to deliver the best possible solutions based on the right technology for the job. If that technology changes, they change with it. Of course, if you want to focus all of your energy on complaining about how horrible it is that Microsoft shifts their strategies and let everyone know how much time and money you invested in learning dead technologies, you're welcome to. Complain away. The people I know who are successful in this industry move on. I'm not saying it's a good thing when you find a contract canceled because of a shift in technology, I'm saying you can either focus on the one that got away or figure out how to make sure it doesn't happen with the next one. Your choice.

P/Invoke

Really? "I don't want Silverlight to go away because it makes sense to run an abstraction layer dependent on a browser so I can go through another abstraction layer to talk back to native code." I just don't get it — is p/Invoke a vaid reason to worry about Silverlight's future? There's no equivalent on Mac OSX so you aren't doing it for cross platform. I'm not sure why you're doing it, really. I like programming in Silverlight: it's fast and it's easy, and p/Invoke opens up options. With the new development platform I can skip the p/Invoke, call the WinRT layer directly and save myself the overhead of importing DLLs and creating obscure structures or learning about how to marshal. To me this one just says, "If you're going heavy p/Invoke maybe it makes sense to look at Visual Studio 2011 and native applications instead."

C# Web App

I don't agree with this one at all. If you are bulding a web site, I think going with the web technologies is key - if I want an application, I'll build it. If I want a rich web site, I'll build it — but different approaches and different tools. Rich web applications still have nuances like page navigation, browser back buttons, and compability that make them different than applications. When I write Silverlight, I'm not writing a web application. I'm writing an application that is easily delivered over the web. That's why I don't use the navigation framework, because I don't think customers are used to navigating desktop applications using URLs. So if it's a web site I want to build and I want to use C#, why not MVC instead? If it's an application, let me use C# by all means ... and as I mentioned above, I may just lose the ease of deploying it over the web. Oh, by the way, did you notice that MVC 4 preview was released? Weird: it's not written in Metro, doesn't create Metro applications, yet Microsoft is still developing it and I don't hear people crying that MVC is dead.

Line of Business Applications

One key thing I see most people missing is that Windows 8 is two separate, distinct systems. Just launch the Visual Studio 2011 to see what I mean. That is not a Metro application. I like Metro, it works great on my phone and think it is the right way to build tablet-based applications, but there are just some things like development environments that don't and won't make sense. If you build for Metro you MUST follow the rules of the sand box, and you are limited. If you jump outside of that, it's back to the traditional stack. So I don't see this shifting gears. Personally the reason why I like Silverlight for line of business applications is because of Xaml and C#. I don't see that changing. I've converted a few applications to Windows 8 as well as walked through the examples and the gap is not huge. It's there, but not huge.

Bottom Line

I see myself having a choice. If I want to develop line of business applications that run on Mac OSX and are delivered over the web, I can develop Silverlight 5 applications. Regardless of whether someone labels that "legacy" it will be supported through 2020 (when the HTML5 spec will finally get ratified) and it will run on my target environments. If I'm building a rich, interactive line of business application, I'm going to want it in that environment. If I'm looking for a user-friendly, touch-first tablet interface, Metro doesn't change things for me. Today without Windows 8 my options are HTML5 or writing native iOS, Java, and Silverlight for Windows. With Windows 8, those options simply shift to HTML or native iOs, Java, and XAML/C#/WinRT. For many years to come, most of my customers will still be on Windows 7 and even Windows XP so guess what: Silverlight and WPF will still be our best options.

Silverlight development will be alive and strong for at least a decade even if no new version is in the pipeline, and longer if there is one. Even if it is the last version, though, as I'm writing my book I see that 90% of the topics covered are going to be the relevant to developing in the new Windows 8 runtime. Here's something that won't come as a surprise to seasoned developers: even if it does go away, there is no way it will ever happen overnight. As long as there is speculation, it's still around. When there is no more speculation, i.e. we know for a fact there is no vNext, there will be plenty of time to transition.

What's my point? My point is this: there are always people who are going to shout that the sky is falling and the world is coming to an end and air all of their woes to the world. The reality is we all have the same hours in the day and we can do with them as we choose. While I've been accused of being a blind, passionate follower of Silverlight for my optimism and enthusaism, I think they've got it wrong. I'm just optimistic and enthusiastic in spite of Silverlight. If it goes away I will adapt and continue to provide value where I can because at the end of the day I'm a developer and Silverlight is a tool in my arsenal. To me, moaning, whining, and complaining is a waste of time. If something's broke, it isn't going to fix itself. I'd like to see more posts on solutions and options moving forward rather than "Hey, I've got a flat, so let's sit on the side of the road, point at it and talk about all of the things that went wrong with my day."

That's my opinion, and not necessarily the opinion of my employer, and I'm sticking to it. What are your thoughts? Please sound off with comments below.

Jeremy Likness

Tomorrow is the start of the major event that has been hyped as heralding the most significant changes to Windows in the past 15 years (the last was when Windows 95 was announced). There has been a lot of speculation around what will happen tomorrow. Some people claim it will be the death of Silverlight and even managed code, while others hope to see news about the next version of the Windows Phone operating system. While I am not able to attend the event, I will be watching the keynotes closely, collaborating with my associates in the field who are at the conference, and blogging my insights over the next few days.

I've been asked by many customers, fellow developers and Twitter followers to share my thoughts on the Build Conference and, as a Silverlight MVP, what I think about the future of Silverlight. I obviously have a stake in its future as I'm about 2/3 of the way through writing a book about building enterprise applications using Silverlight. So what are my thoughts?

Personally, I think there has been far too much read into the focus on HTML5 and JavaScript. I may be proven wrong, but based on what I've seen (and this is all public knowledge as I have no "insider information" about Windows 8) I believe the following will happen:

  • Microsoft will focus on Windows 8 as the platform for multiple devices and specifically address the tablet/slate competition space. To be convincing they'll need to address speed and responsiveness, but likely they'll focus on:
    • The richness of the OS - it's not just a big phone, but the full Windows enchilada
    • Security and policies that are lacking in iPad
    • The new HTML5+JavaScript option for development to draw in the developers who traditionally don't work with the Microsoft stack
    • Some lip service to writing it once and running it everywhere whether it's the desktop, laptop, or slate
    • The touch-first features of the new OS
  • Microsoft will emphasize that managed code and Silverlight are not dead and will be fully supported on Windows 8. I don't see the death of Silverlight any time soon (again, these are my predictions ... it will be interesting to see what really happens)
  • They almost have to address the community of developers concerned about managed code and emphasize that it is not going away. I'm guessing they'll talk about how managed code will work with Windows 8 and perhaps reveal some details about Xaml and C# either as Silverlight or something new for developing apps that target Windows 8
  • Microsoft will also address the unmanaged coders and focus on how C++ is coming back to front and center on the new platform, and likely cite some performance examples of why native code is important

Those are my predictions, for what they're worth. I wanted to post these today so I could take a look back over the next few days to see if I was close or completely missed the mark. I'm looking forward to an exciting conference and sharing my thoughts with you as it unfolds!

Jeremy Likness

Yesterday I had the opportunity to present a demo at the Worldwide Partner Conference (WPC) in Los Angeles. The session was called "Profiting from the Consumerization of IT with Windows Devices and Windows 7 Enterprise." The focus was on how consumer-driven trends impact the enterprise and ways to work with, rather than against, that trend to create successes.

In recent months there were two events that were perceived as blows to the success of Silverlight at large. The first was the announcement that it would not be supported on the iPhone. While this caused a lot of concern and grief, it did not bother me because I knew that the phone is a different target and it would never make sense to run the same application "out of the box" on that space. Even when the Windows Phone 7 was announced along with the support of Silverlight, developers quickly realized that while there are many assets they can share between projects that target the desktop, slate, and the phone, there is still a substantial bit of development specifically targeted to the smaller form factor of the phone.

The bigger trend was when the iPad exploded into popularity. This resulted in a typical scenario: CEO purchases iPad, loves it, brings it into the office and asks, "Why can't all of our applications run on this thing?" and then IT scrambles to rewrite everything using HTML5. Of course, they quickly found that path wasn't easy, either, and although they could produce content for the iPad using HTML5, that application was not the same application they'd be using for other targets.

Our company recently completed a project for Rooms to Go. This what I was asked to demo on stage to present our solution and why it is so important in this space. The company sells furniture with packaged deals that allow you to literally "buy the room." The problem they were solving was a customer experience issue. Sales would engage with a customer on the floor, but if the customer wanted more information about a package, wanted to estimate delivery costs or even just share information, the salesperson had to break away, locate a kiosk, and log in there. It was a very disruptive process.

Rooms to Go wanted something they could carry with them and use on the floor to engage with the customer without having to break away. They wanted it to be easy and intuitive to minimize disruption with training and learning curves. They wanted something rugged so that if it happened to get dropped on the floor (or have something spilled on it) it could keep on running (can you imagine dropping an iPad on the floor?).

The device they went with is the Motion Computing CL900 and I'll let you visit the link for all of the statistics. Why did they go this route, as opposed to some of the other popular devices?

  • They needed something that had a security story. There is no security story around many of the other consumer devices. This runs Windows 7 and integrates with Active Directory, honors group policies and interfaces with all of their existing infrastructure.
  • They wanted to run their legacy applications. One distinct difference between Windows tablets and other consumer products is that they run the full Windows operating system and are not just a "phone on a slate" that doesn't make calls. This means it was easy for them to load their existing apps right on the tablet and still use them even if they weren't built with "touch first" in mind.
  • They wanted to leverage existing mindshare. By building the application in Silverlight they were able to stick with a development environment (Visual Studio) and a language (C#) they were familiar with. Our team worked shoulder to shoulder with theirs to deliver the product and ensure they understood the framework and owned the code. The application used my Jounce MEF and MVVM framework for Silverlight.

There are many more facets to the story that you can learn about in the case study and by watching the video (here is a direct link.)

If you are a Silverlight developer and are shaking in your boots over recent announcements, I wouldn't be. While the future is still not clear and we won't know much more until the BUILD conference still weeks away, keep in mind this scenario and the fact that companies want to have an engaging, interactive, .NET and Windows based experience they can deliver, and the demand for Silverlight line of business is only growing. It will only increase in my opinion with the release of version 5. With the number of companies that are still on Windows XP it would take 300,000 upgrades a day to convert them to Windows 7 before end of support ... and that means regardless of the Windows 8 story, there is still going to be a strong platform and support base for delivering Silverlight line of business applications.

The slate is the perfect example of a use case for Silverlight Line of Business. I'll be delivering a talk about developing for slate using Silverlight at reMIX South in the Atlanta area on August 6th. I'll cover how we were able to use existing frameworks and libraries like the LightTouch library to quickly develop a comprehensive solution that solved a real world problem. I hope to see you there. If there is nothing else you take away from this, hopefully I've demonstrated the real world demand and application of solutions for line of business applications written in Silverlight. Jeremy Likness

I've seen a lot of speculation around the future of Silverlight and how it compares to HTML5 lately. If you've followed my posts and tweets you'll find that I still believe Silverlight is strong in the line of business area and in fact my company Wintellect is still doing quite a bit with it.

The interest is so strong that I'll be traveling to Los Angeles in a week to present at the Worldwide Partners Conference (WPC). The session speaks to the business side of Silverlight but with a very powerful message. It is a breakout session titled, "Profiting from Consumerization of IT with Windows Devices and Windows 7 Enterprise" and is on Thursday July 12th from 4:30pm to 5:30pm PST.

While I can't reveal all of the details just yet, my portion of the session is based on a case study from a very successful project our company recently completed. The client wanted to enhance the ability of their salespeople to interact with customers by providing an interactive touch experience on a Windows slate device. We built that experience in Silverlight and used it to interface with their existing backend systems. Silverlight made it easy and fast for us to develop a rich, interactive application with seamless integration to existing and new services on their backend. We were able to share development with their team while building a highly modular solution, and because of the parallel development and design it was completed in a very short time frame.

I'll post more details as they become available so that you can learn more about the case study and how Silverlight combined with the slate provided the solution they were looking for. The target device was Windows because the customer had an existing Windows infrastructure and also felt the security around Windows-based devices was much stronger than the story with competing devices like the iPad.

If you're at the conference and are able to join me I look forward to seeing you there. Otherwise, stay tuned as more details are released. I love sharing case studies with Silverlight because at the end of the day I can tell you how valuable and useful I think it is, but its the customer and their perception that ultimately determines success.

Jeremy Likness

You have heard of an elevator pitch, right? It's that quick, 30-second pitch that starts with a hook, goes on to the how, what and why and closes with a strong call to action. It's the perfect way to quickly provide the value proposition for something you're trying to sell, condensed into an easy, fast solution that you can literally share while passing floors in an elevator.

Microsoft has been selling for decades, so I assume they have a little bit of experience crafting their pitches. It turns out that they've been pitching Silverlight to us for years. But how has the message changed, and what does it mean for Silverlight developers?

Let's take a quick look at the early days just before Silverlight got it's name. Back then you had to call it by the awkward "WPF/E" which seemed to imply that Windows Presentation Foundation would eventually be found on phones and hiding inside of Linux machines. According to Microsoft (see this link) the first pitch was:

December 2006

"WPF/E "(code name) provides designers and developers with a cross-platform solution for delivering richly interactive experiences for the Web and beyond. It is a key part of the next-generation Web platform from Microsoft, delivering visually stunning and interactive user experiences. It supports multiple operating systems (including Apple Macintosh OS X) and combines 2-D animation, video, and audio within a lightweight yet flexible browser plug-in (currently under 1 MB). The XML-based presentation (XAML) makes it easy to upgrade applications that are based on AJAX (Asynchronous JavaScript and XML) to take advantage of "WPF/E." This breaks rich Web-based applications out of the proprietary "black box" in which they exist today, by using XML (XAML) for presentation and AJAX for logic.

Did they really say what was at the time an ActiveX plug-in would break applications out of being ... propietary?

Then, Silverlight got a name ... and developers braced themselves for a wild ride.

June 2007 (all of the following quotes are archived from the Silverlight.Net site)

Microsoft® Silverlight™ is a cross-browser, cross-platform plug-in for delivering the next generation of .NET based media experiences and rich interactive applications for the Web. Silverlight offers a flexible programming model that supports AJAX, VB, C#, Python, and Ruby, and integrates with existing Web applications. Silverlight supports fast, cost-effective delivery of high-quality video to all major browsers running on the Mac OS or Windows.

This is where I believe the confusion began. CIOs trying to discern what this new thing was felt their eyes glaze over at the mention of so many languages and instead latched onto the comfortable terms of "media" and "high-quality video," then told everyone else, "Oh, it's another Flash plugin for playing movies."

July 2008

Microsoft Silverlight is a cross-browser, cross-platform, and cross-device plug-in for delivering the next generation of .NET based media experiences and rich interactive applications for the Web. By using Expression Studio and Visual Studio, designers and developers can collaborate more effectively using the skills they have today to light up the Web of tomorrow.

Thank goodness it could now run on other devices, not just platforms and browsers. XAML made it ... but ... unmanaged C++ hooks anyone? While designer and developers were mentioned earlier on, here we get the first taste of the designer/developer workflow ... made possible by XAML and Expression.

November 2008

Microsoft Silverlight powers rich application experiences and delivers high quality, interactive video across the Web and mobile devices through the most powerful runtime available on the Web.

Uh-oh, we should have seen the writing on the wall. What happened to cross-platform and cross-browser? Now we're just on the web and mobile devices? And how many mobile devices? And dangit, they keep saying "video" in their pitch making it real hard for me to sell it to line of business.

June 2009

Microsoft Silverlight is a free runtime that powers rich application experiences and delivers high quality, interactive video across multiple platforms and browsers, using the .NET framework.

Hey everyone! Look ... it's free! It's free! Not quite open source, but definitely ... free! Unfortunately, the PHP developers saw "using the .NET Framework" and ran the other way. Everyone else still called it a video player.

August 2009

Silverlight helps you create rich web applications that run on Mac OS, Windows, and Linux. Welcome to a new level of engaging, rich, safe, secure, and scalable cross-platform experience.

Now we're not just cross-platform, we've got a list of platforms. We also ditched the video, so I can finally sell line of business applications. Not only are Silverlight applications safe, but they are secure. Did we mention scalable? Enterprise, get ready ... yeah, we're talking to you.

February 2010 (to present)

Silverlight is a powerful development platform for creating engaging, interactive user experiences for Web, desktop, and mobile applications when online or offline.

And here we are today. Painfully obvious that we're no longer chasing the "cross-platform" experience. However, the key here and the reason why I love Silverlight (and the reason why customers continue to invest in it) is the fact that it does provide engaging, interactive experiences ... and the key here is both online and offline (and while it's not in the pitch, it just so happens that those experiences run really well on both Windows and Mac OSX machines).

So there you have it ... the history of the Silverlight elevator pitch.

Jeremy Likness

I was at the MIX 2011 event. There was an "Open Source Fest" before the event and I came to showcase the Sterling NoSQL Database project that I run (and code most of, although there have been numerous enhancements and patches now added by a growing team of fantastic supporters). I was happy to speak to several people who hadn't heard of the database before, not because it was unknown but because of the opportunity to introduce it to others.

There seems to be a common impression that Sterling is a database specifically designed to fill a hole on the Windows Phone 7 platform - namely, the lack of a database. That's actually not entirely true. The database was originally created to fill that hole for Silverlight in the web browser. It happened that the platform shared so much with the phone that it was an easy port (perhaps a few dozen lines of code to tweak, mostly to close the gap between the Silverlight 4 version and the Silverlight 3-ish that the phone presented at the time).

Once on the phone, it filled another gap I hadn't anticipated: the dreaded tombstone operation. People were struggling to find a consistent, easy way to serialize state and Sterling, able to handle nearly any type and depth of object graph and capable of serializing it without all of the ceremony of deriving from a custom class or splashing attributes through all of your code, was happy to oblige. It was definitely the phone that pushed its popularity.

Once people began downloading it, I started to receive a barage of questions. "What about on the server? I want something super lightweight that is object-based, not relational." "What about the local file system instead of isolated storage for out of browser applications?"

I began working on these changes. In the meantime, I wrote an article for MSDN about Sterling on the phone, was interviewed by Jesse Liberty about it and even conducted an MSDN geekSpeak about Sterling. And then it was announced: "Windows Phone 7.1 dubbed Mango will have SQL CE."

You'd almost think I'd invested everything I owned in Sterling by the way people tiptoed around me at the conference. "Jeremy, did you ... um ... hear?" "What are you going to do?"

I've been asked that enough that I wanted to write a quick little post to share what's going on. The momentum for Sterling has been picking up. If you check out the release notes for the 1.5 version, you'll see that most of the changes had nothing to do with the phone at all. In fact, 1.5 was all about reach: I brought Sterling to the server by popular demand, refactored the serialization model so the persistence model could be completely separate and based on "drivers," and then wrote drivers for memory, isolated storage, file system for elevated trust, isolated storage on the phone, and the local file system for the desktop or .Net 4.0 version. The serialization routine was optimized and extended to handle more cases than ever before and some refactoring removed some dependencies to make it even more flexible to work with your existing types (for example, you can now specify what you would like your "ignore this property" attribute to be, and proxy it to, say, an existing XmlIgnore attribute or something completely different).

The truth is, I'm excited that SQL CE has come to the phone. I never doubted it would, and I never had a goal to compete with it. That is years of brilliant developers using experience across a vast array of applications in the field to tweak and improve and create a compact, blazingly fast means of persistence on a small footprint. Not my niche. People may even be more surprised when they hear me say if it works for them, seems simple and easy and their applications use it without issue, then GO FOR IT. I'm not trying to "sell" you on Sterling (that would be rather tough for a free open source project, no?)

So what IS the story for Sterling on the phone in a Mango world? I think it's quite powerful, personally.

Is it a 2 million row story? No. 9 times out of 10 when you have that much data on your phone I think you're writing it the wrong way. What user could possibly process, need, or use that much data and absolutely HAVE to access it when offline? Most of the time a service model with a local cache holding the juicy tidbits should do just fine (and I bet you can guess what I think a good candidate for that cache will be). In the 1 out of 10 cases where it has to be that way, yes, please, find a better way to do it because I didn't write Sterling for that.

The story Sterling brings to the phone is this: extremely simple. If you already have classes that contain other classes and they reference (even with circular references) or are five levels deep and contain custom enumerations based on certain value types, great. Pass the top level object to Sterling and I'm confident it will handle it. If not, write a custom serializer for the tough parts and you're good to go.

The story is a model/behavior-based one. Sterling is about turning an object graph into bytes and back, with in-memory insights into your data to use fast queries and navigate where you need to go. You can share models between the client and server, take the bytes from one database and plop it somewhere else. The classes can have tons of behavior and Sterling won't mind - it navigates the data and worries about bringing that back. Imagine tombstoning with SQL CE and defining a set of tables with name/value pairs that you must convert into strings and back every time ... now imagine having a Tombstone<T> with a SetObject and GetObject<T> method to put any type of class there and save it ... and it works. That's the goal.

Finally, the story is about lightweight and flexible. Take a look at the binaries. Sterling is tiny - less than a few hundred kilobytes of data and lines of code in the hundreds, not tens of thousands. It has numerous extension points to allow you to plug in encryption, compression, create a custom driver that works with Azure or define your own triggers and database constraints. It's all there in a compact framework.

I'm not saying these things to brag and by no means is the system perfect. There's a lot of work to do but the community has really stood behind the implementations of Sterling to date and helped drive features. One benefit for Sterling I hadn't even imagined is the ability to create shared libraries because the API on the client, server, phone, or browser can be 100% consistent even if the underlying method of persistence is actually memory, isolated storage or the file system.

So I do think Sterling will continue to fill a gap that SQL CE doesn't - and I see some hybrid applications that use SQL CE to index references to objects and Sterling to actually serialize their complex graphs. The best of both worlds - there's no reason why the two can't work together.

What's on the road map for Sterling?

You can take a look and vote, add your own features and see the popularity of existing features here. Based on that and internal work, here's what some ideas are that the team is considering for the 2.0 release (and this will be influenced mostly by the votes on the features page, so make sure your voice is heard!)

  • Lazy loading of child objects - exploring a way to do this without using proxies or dynamic types
  • Read-only version - build a massive database, save it as a resource and then instead of saving to isolated storage, use a read-only driver to access it immediately (so no delay moving the resource into isolated storage, for example)
  • Azure drivers to use table storage
  • Internal optimizations to use a single file on disk rather than multiple files as the current version does
  • More hooks into the serialization engine to serialize types and get byte arrays without having to participate in saving
  • Dynamic table, key, and index definitions that will build these on the fly and persist them between sessions
  • Better concurrency for file-system based applications
  • Built-in synchronization (possibly through Microsoft's Synch Framework) for "sometimes connected" applications
  • Facades to expose Sterling as a cache engine
  • LINQPad support
  • MonoTouch support (same API, different platform)
  • Schema support - handling classes and types that change over time out of the box
  • ...and I haven't forgotten, I need to update the extensive 1.0 documentation to reflect the new 1.5

These are just a few items. Obviously I'm just one person with a lot of projects. The team is slowly growing as is the audience using it, so I appreciate both your ideas and your assistence if you decide you'd like to be part of the team and can help out. I think there is a rich future and right now is a great time as the team decides what 2.0 will look like.

Take care and check out the latest version - 1.5 - right here.

Jeremy Likness

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

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

Today I decided to work on some miscellaneous performance optimization tasks for my Sterling NoSQL Database that runs on both Silverlight and Windows Phone 7. The database will serialize any class to any level of depth without you having use to attributes or derive from other classes, and despite doing this and tracking both keys and relationships between classes, you can see it manages to serialize and deserialize almost as fast as native methods that don't provide any of the database features:

Note: click on any smaller-sized image in this post to see a full-sized version in case the text is too small for the sample screenshots.

Obviously the query column is lightning fast because Sterling uses in-memory indexes and keys. I decided to start focusing on some areas that can really help speed up the performance of the engine. While there are a lot of fancy profiling tools on the market, there really isn't much need to look beyond the tools built into Visual Studio 2010. To perform my analysis, I decided to run the profile against the unit tests for the in-memory version of the database. This will take isolated storage out of the picture and really help me focus on the issues that are part of the application logic itself and not artifacts of blocking file I/O.

The performance profiling wizard is easy to use. With my tests set as the start project, I simply launched the performance wizard:

I chose the CPU sampling. This will give me an idea of where most of the work is being performed. While it's not as precise as knowing time spent in functions, when a sample rate goes down you can be reasonably assured that the function is executing more quickly and less time is being spent there.

Next, I picked the test project.

Finally, I chose to launch profiling so I could get started right away and finished the process.

The profiler ran my application and I had it execute all of the tests. When done, I closed the browser window and the profiler automatically generated a report. The report showed CPU samples over time, which wasn't as useful to me as a section that reads "Functions doing the most individual work." There are where most of the time is spent. Some of the methods weren't a surprise because they involved reflection, but one was a list "contains" method which implies issues with looking up items in a collection.

Clicking on the offending function quickly showed me where the issues lie. It turns out that a lot of time is being spent in my index collections and key collections. This is expected as it is a primary function of the database, but this is certainly an area to focus on optimizing.

Clicking on this takes me directly to the source in question, with color-coded lines of source indicating time spent (number of samples):

Swapping my view to the functions view, I can bring the functions with the most samples to the top. A snapshot paints an interesting picture. Of course the contains is a side effect of having to parse the list, which uses the equals and hash codes. Sure enough, you can see Sterling is spending a lot of time evaluating these:

So diving into the code, the first thing I looked at was the Equals method. For the key, it basically said "what I'm being compared to must also be the same type of key, and then the value of the key I hold must also be the same."

public override bool Equals(object obj)
{
    return obj is TableKey<T, TKey> && ((TableKey<T, TKey>) obj).Key.Equals(Key);
}

Because these keys are mainly being compared via LINQ functions and other queries on the same table, I'm fairly confident there won't be an issue with the types not matching. So, I removed that condition from the keys and indexes and ran the performance wizard again to see if there were any significant gains.

This time the list stayed on the top, but the table index compare feature also bubbled up:

One nice feature of the profiler is that you can compare two sessions. I highlighted both sessions and chose the comparison feature:

The report actually showed more samples now for the index, still remaining one of the most problematic areas of code:

Obviously, my issue is with the comparison of keys. In Sterling you can define anything as your key, so Sterling has no way of necessarily optimizing the comparison. Or does it? One comparison guaranteed to be fast is integer comparison. All objects have a hash code. Instead of always comparing the key values, why not evaluate the hash code for the key, then compare that first to eliminate any mismatches? Then only if the hash codes are equal, have Sterling compare the actual keys themselves as well to be sure.

I made those tweaks, and the new equals function:

public override bool Equals(object obj)
{
    return obj.GetHashCode() == _hashCode && ((TableKey<T, TKey>) obj).Key.Equals(Key);
}

Notice the fast check for the hash code first, then evaluation down to the key only if the hash code check succeeds. What's nice about this approach is that Sterling probably won't be doing much unless something interesting like a match happens in the first place, so the extra check only takes place when that "interesting event" happens. All of the boring events now benefit from a faster comparison as they are quickly rejected.

That's the theory, anyway. What did the profiler say? I executed a final profiler run. This time there was definite improvement in the equality function and the list lookup:

Of course, the only caveat here is that the samples aren't statistically significant. The improvement is slight, but with a significant margin of error. Fortunately, I was able to run the profiler multiple times in the old and new, and consistently saw improves in the functionality, so there is some improvement and even a little counts as this is the area Sterling spends the most of its time. After evaluating this, a HashSet<T> may out perform the list, but also is not available on the Windows Phone 7 version, so I have some more tweaking and testing to do.

I also managed to find a few other nuggets, including converting the reflection cache to use dynamic methods and taking an expensive operation that was evaluating types and caching the lookup in a dictionary instead. After tweaking and checking these improvements in, next up will be looking at the isolated storage driver and potential bottle necks there.

As you can see, the built-in profiling is very powerful and a useful tool to help improve application performance and identify issues before they become serious.

Jeremy Likness

My first day of MIX was an exciting one. I expected the keynote would cover the non-Silverlight and Windows Phone 7 topics so contrary to the reports of "oh no" you'll invariably hear, it only served to stretch my own excitement as I look forward to today's keynote and what it will have to offer. I already reported on the keynote, so I thought I'd share a bit about sessions.

The first session I attended was HTML5 for Silverlight Developers. It was very eye-opening to me. The speaker had experience on both the Silverlight product teams for earlier versions and the phone, as well as more recent tooling for HTML5 so he spoke from a position of authority. It turns out there are far more similarities between the two than I expected. The comparison, however, also served to draw out the fact we'll hear time and time again: the lack of good tooling for HTML5.

The most interesting part for me was when he designed a full path in Expression Blend, then took the path expression and pasted it into SVG and demonstrated the same drawing in HTML5. That was very interesting, as well as some tools and plug-ins that would generate the code from existing products. He also provided a large chart comparing features between the two ... for example, SVG mapping to XAML, Timer vs. DispatcherTimer, etc. I enjoyed the session and learned quite a bit.

My next session was a Deep Dive MVVM with Laurent Bugnion. Laurent did a great job because there was a ton of information to pack into only a very short window of time. While there were some things I didn't necessarily agree 100% with on the approach, the point wasn't about a debate around "what's the pure or right way" but instead, "here is your tool box and these are the challenges people claim to face, so here's a way to solve them." In that sense I think the talk drove home that you cannot overcomplicate matters and sometimes it's OK to have a little code behind (yes, his demo had code behind!) He managed to hit most of the questions people tend to ask about MVVM with a solution within the framework.

The final session was Pragmatic JavaScript, jQuery, and AJAX with ASP.NET. It was again a very informative session. I learned new ways the Microsoft tools support jQuery and even help provide support for extensions, intellisense, and more. He also tackled some various challenges you might face such as long-polling and how to resolve it using the combination of jQuery and the ASP.NET tools. At the end of the meeting I felt like the open web (meaning tags and JavaScript) looks very fun again and I want to get more involved with it in addition to Silverlight.

As fun as that day was, I know this day will be even more exciting. Stay tuned on my twitter feed (@JeremyLikness) as I report from the keynote floor for what I expect to be some amazing announcements in the Silverlight and Windows Phone 7 space.

#msmix11sw

Jeremy Likness

I recently had the opportunity to work on a rather nagging issue with a customer related to how Silverlight scales UI elements. The customer was kind enough to approve me posting the solution. The problem is with scaling UI elements on a canvas. The scenario is simple (and easy to reproduce).

First, create a canvas exactly 1000 pixels wide. Then, place two blue rectangles in the canvas that are exactly 500 pixels wide. Offset the second rectangle by 500, like this:

<Canvas Width="1000" Height="800" >
        <Rectangle Fill="Blue" Width="500" Height="800"/>
        <Rectangle Fill="Blue" Width="500" Height="800" Canvas.Left="500"/>
</Canvas>

So far, so good. You'll get the illusion of a single, solid blue rectangle because the edges of the rectangles are flush.

Now, let's toss the rectangle inside a Viewbox. You can also simply apply a scale transform to the containing grid or canvas, but a view box makes this easier. Put the canvas inside a grid and add some controls to adjust the height dynamically.

<Grid x:Name="LayoutRoot" Background="White">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <Viewbox Width="{Binding ElementName=slider, Path=Value}" Stretch="Uniform" Grid.Row="1">
        <Canvas Width="1000" Height="800" >
            <Rectangle Fill="Blue" Width="500" Height="800"/>
            <Rectangle Fill="Blue" Width="500" Height="800" Canvas.Left="500"/>
        </Canvas>
    </Viewbox>        
    <StackPanel Grid.Row="0" Orientation="Horizontal" HorizontalAlignment="Center">
            <Slider x:Name="slider" Width="1000" Minimum="100" Maximum="1024" Value="2000"/>
    </StackPanel>
</Grid>

Launch the program, and slide the slider to scale the rectangles. You'll almost immediately catch the issue with anti-aliasing:

At certain zoom levels, the algorithm that computes the shape is obviously allowing the background to bleed through. Here's the same picture above, magnified several times:

The anti-aliasing happens by blending the background into the edge. You can confirm the background is bleeding through by adding a rectangle behind the two that is a different color. Here's an example with a red rectangle:

<Canvas Width="1000" Height="800" >
        <Rectangle Fill="Red" Width="1000" Height="800"/>
        <Rectangle Fill="Blue" Width="500" Height="800"/>
        <Rectangle Fill="Blue" Width="500" Height="800" Canvas.Left="500"/>
</Canvas>

... and the magnified result:

After discovering this and scouring the forums for an answer, it appears the problem has not been resolved. Overlapping the edges is unacceptable in cases where the borders contain intricate designs that break when they are shifted. Even with the solid rectangles and a full pixel overlap, the issue can still be seen at certain zoom resolutions. So, finally, I decided to take advantage of the defect and use the "background" to my advantage.

For the blue rectangles, this was simple. I created a new "hack panel" as a blue rectangle. To see the difference, I added a button with code behind to toggle the visibility. Here is the XAML with the blue correction panel. Notice it is only 5 pixels wide and positioned just right to overlap the edges of the panels on top of it:

<Canvas Width="1000" Height="800" >
                    <Rectangle x:Name="HackPanel" Fill="Blue" Width="5" Height="800" Visibility="Visible" Canvas.Left="499"/>
                    <Rectangle Fill="Blue" Width="500" Height="800"/>
                <Rectangle Fill="Blue" Width="500" Height="800" Canvas.Left="500"/>
            </Canvas>

The new toggle button simply toggles the visibility of the strip:

private void _ButtonClick(object sender, RoutedEventArgs e)
        {
            _visibility = !_visibility;
            HackPanel.Visibility = _visibility ? Visibility.Visible : Visibility.Collapsed;
        }

So the problem is solved with a solid color, but what about a more complex border? To solve this, I took my Jounce logo and hacked it in half. It's not a perfect job because I threw this together quickly, so you'll notice I shift the right side down by a pixel and it doesn't line up perfectly, but the point is the anti-aliasing effect. With the hacks turned off, you can see a very clear line appearing up and down the edge. But what to put behind it? Green will bleed on the white, and vice versa.

The solution? A hack grid. I dropped this onto the canvas:

<Grid x:Name="HackGrid" Loaded="HackGrid_Loaded" Width="5" Height="800" Visibility="Collapsed" Canvas.Left="499"/>

And in the loaded event, transform the image into it. What essentially happens is we get a 5 pixel strip behind the panels that is a shifted view of the left seam of the right picture (the grid width will clip the rest). Because the anti-alias bleeds through the background, the pixels will overlap just enough to blend the seams and reduce the impact of the effect. Here's the code that can easily be turned into a behavior:

private void HackGrid_Loaded(object sender, RoutedEventArgs e)
{
    var bitmap = new WriteableBitmap(RightImage, null);
    var imageClone = new Image
                            {
                                Width = bitmap.PixelWidth,
                                Height = bitmap.PixelHeight,
                                Source = bitmap,
                                Stretch = Stretch.None
                            };
    imageClone.SetValue(HorizontalAlignmentProperty, HorizontalAlignment.Left);
    imageClone.SetValue(VerticalAlignmentProperty, VerticalAlignment.Top);
    HackGrid.Children.Clear();
    HackGrid.Children.Add(imageClone);
}

And here is the chopped up picture before:

...and after applying the strip behind the two parts:


(The blue strip on the top is from the rectangles).

It's not a perfect solution, but it reduces the impact of the effect and hopefully will help others who run into the same issue. Here's the source code for you to play with it yourself: Side-by-Side Source.

Jeremy Likness

Codestock is an amazing event that I first attended last year in Knoxville, Tennessee. It's hosted at a great venue in a wonderful city and loaded with exciting sessions. I had just as much fun speaking at my session as I did attending the other sessions and learning about new technologies.

I've submitted two talks this year and if you are planning on coming and are interested in these topics, please cast your vote. Codestock, unlike other venues, gives priority voting to those who have registered. If you do register, you can vote for my sessions here:

The Sterling Database for Silverlight and Windows Phone 7: The release of Silverlight 4 and Windows Phone 7 have created an explosion of line of business applications written in Silverlight. The framework provides a powerful advantage with its ability to run offline and disconnected from the network. For browser applications this provides a unique data storage challenge. Windows Phone 7 applications must also cater to the “tombstoning” scenario and efficiently serialize and rehydrate data when the program is swapped to the background and back. Sterling is an open source object-oriented database that addresses these needs by storing data in isolated storage. Sterling works with existing classes/types and is extremely lightweight. Learn how to use Sterling from its creator, Jeremy Likness, when he walks you through various scenarios and features involving indexes, LINQ to object queries, foreign keys, triggers, encryption, and compression.

Silverlight 5 for Line of Business Applications: Silverlight 5 has been announced and will provide a variety of incredible features beneficial to line of business applications. Microsoft Silverlight MVP Jeremy Likness will cover these features and teach you how to produce high performance scalable applications using the latest Silverlight framework. Learn how Postscript printing, improved MVVM support with XAML and data-binding debugging, implicit data templates, style binding, WS-Trust support, 64-bit support and more enhance the current runtime and take Silverlight to the next level within the enterprise.

I appreciate your support and hope to see you in Knoxville!

Jeremy Likness

In early 2010 I was working on several Silverlight projects that were driven by the need to provide a working offline solution. Isolated storage was the obvious answer. Unfortunately, serializing 10,000 contacts came with some overhead and I realized quickly that I was following some similar patterns when it came to handling large sets of data: I would serialize the key fields (for example, names for contacts) in order to manage lists that I could filter in-memory, while saving the bulk of data for separate serialization events.

This inspired me to create Sterling as a way to ease the burden of persisting data. I looked at other options and didn't like having to change my classes just to save data (for example, make them derive from a database base class when I already was deriving them from my own helper classes). I also didn't like the inflexibility of options available. So, I set out to build something lightweight and non-intrusive that would give the user full control over serialization. The key would be to provide keys and indexes through lambda expressions to make it easy to access common data using LINQ to Object queries in memory while saving the "slow" part (loading from disk) for when it was needed.

I knew I would use Sterling in my own projects but I never imagined how popular it would become with other users. The timing for Sterling was perfect because when the Windows Phone 7 was released, the application developers were faced with a unique scenario called "tombstoning" that requires saving data when the application is swapped to the background. Sterling turns out to be a perfect fit for saving and restoring that data.

After asking some of my peers whether or not it would make sense to provide the solution as an open source community project, the responses were consistent and I realized this was definitely the way to go. I uploaded the bits and quickly went from a fledgling "0.1" release to several interim change sets that morphed into something I was comfortable calling "beta."

That's when the real power of the open source community began to transform the project. Numerous people were pulling down Sterling and using it in their applications, then providing me with valuable feedback for features and bug fixes. Some even sent me code-complete patches to implement various changes. We went from a small set of options for serializing data to a rich, interactive, highly adaptive and flexible solution that can handle a large variety of class configurations and relationships.

It's been a challenging 8 months of late nights fixing bugs, responding to feature requests, and writing a comprehensive guide to make sure the product is well documented, but I would be lying if I said I didn't enjoy every moment. I'm excited to see what the future versions will bring but for now am absolutely ecstatic to announce that I've released Sterling from beta to version 1.0. Enough people have shared the testimonial of their projects and helped me expand the battery of unit tests on both the phone and browser versions to finally make this milestone possible.

You can download the release at CodePlex. You can read the full user's guide at www.SterlingDatabase.com. Please take the time to rate your experience with the product so others know what to expect, and if you are interested in what others are doing or are willing to share your own story, visit our Real World Sterling Projects thread. Thanks everyone and I look forward to exciting features to come.

Jeremy Likness

More Posts Next page »