(X) Hide this
    • Login
    • Join
      • Generate New Image
        By clicking 'Register' you accept the terms of use .

Part 7: Developing for a multitude of clients: strategies for code reuse and keeping costs in check.

(1 votes)
Kevin Dockx
>
Kevin Dockx
Joined Nov 15, 2010
Articles:   19
Comments:   8
More Articles
1 comments   /   posted on Jun 12, 2012
Tags:   code-reuse , kevin-dockx
Categories:   Line-of-Business
Tweet

In the previous articles in this article series, we've looked into different ways of getting our code split up in modules, so they can be loaded on demand, and easily reused across different types of clients - thus cutting costs and saving on development time.

We created a Silverlight web client & OOB client, and in the last part, we started thinking about our Windows Phone client. We ended up with some thoughts about what we should and shouldn't reuse - the conclusion was that we could reuse our existing XAML & C# skills, our domain services, and that we would be able to share code through the use of a Portable Class Library.

In this article, it's time to delve further into this: we exposed our domain services through SOAP endpoints, and now we'll learn how to consume them. We'll also look into that Portable Class Library.

You can download the source code for this article here (you can log in to the demo application with pete.campbell@madmen.com / abc@123).

 

Consuming domain services from Windows Phone.

Let's start with the Windows Phone client. We'll add the necessary references to the Windows Phone application by selecting "Add Service Reference…" and inputting the correct URI's: http://localhost/SalesDashboard.ServiceHost/EmployeeOpportunityService.svc/soap for the EmployeeOpportunity domain service, and http://localhost/SalesDashboard.ServiceHost/SalesAuthenticationService.svc/soap for the SalesAuthentication domain service. Visual Studio will generate the necessary proxy classes for us, including NotifyPropertyChanged notifications. We end up with 2 service references & corresponding proxies, which we can now use from our application.

The first thing we need to do is log in, which can be done with the following code:

private void Login()
{
    // try to log in with the provided credentials
    SalesAuthenticationServicesoapClient authenticationClientProxy = 
        new SalesAuthenticationServicesoapClient();
    authenticationClientProxy.LoginCompleted += (sLogin, aLogin) =>
    {
        if (aLogin.Error == null)
        {
            if (aLogin.Result.RootResults != null && aLogin.Result.RootResults.Count() > 0)
            {
                LocalStateContainer.AuthenticatedEmployee = aLogin.Result.RootResults.First();
                LocalStateContainer.CookieContainer = authenticationClientProxy.CookieContainer;
                var root = App.Current.RootVisual as PhoneApplicationFrame;
                root.Navigate(new Uri("/Views/MainView.xaml", UriKind.Relative));
                Messenger.Default.Send<LoadOpportunitiesMessage>(new LoadOpportunitiesMessage() 
                { EmployeeId = LocalStateContainer.AuthenticatedEmployee.Id });
            }
        }
    };
    authenticationClientProxy.LoginAsync(UserName, Password, true, "");
}

After this, we can fetch our data. Note that, if a domain service requires you to be authenticated, you need to pass in the CookieContainer value you got after logging in to each service call. We can do that by setting the proxy clients' CookieContainer to that value:

private void LoadOpportunities(LoadOpportunitiesMessage msg)
{
    var client = new EmployeeOpportunityDomainServicesoapClient();
    client.CookieContainer = LocalStateContainer.CookieContainer;
    client.GetOpportunitiesForUserCompleted += (s, a) =>
    {
        if (a.Error == null)
        { 
            Opportunities = a.Result.RootResults;
        }
    };
    client.GetOpportunitiesForUserAsync(msg.EmployeeId);
}

When we try to test this… we'll get an error message: "Unable to set the CookieContainer. Please make sure the binding contains an HttpCookieContainerBindingElement."

Why is this? Well, by adding a service reference, Visual Studio creates a ServiceReferences.ClientConfig file, but by default, the binding that is create is a regular BasicHttpBinding. We need to change this to ensure the HttpCookieContainer is enabled. We can do this by opening the configuration file, and changing the binding to our EmployeeOpportunityDomainService as such:

<binding name="BasicHttpBinding_EmployeeOpportunityDomainServicesoap"
enableHttpCookieContainer="true"
maxBufferSize="2147483647" maxReceivedMessageSize="2147483647">

Please note that this alone is not sufficient to secure your services, nor to pass and check credentials - it's just the starting point. Detailing the security aspects (transport security certificates & identity) of a Windows Phone application in combination with WCF RIA Services is out of scope for this article series, but if you're interested in this, you might want to have a look at my blog: in the recent past, I delivered a few sessions on this at various conferences, for which both the source code and slidedecks can be downloaded (Techdays session, WiPhug session) - the demo application for those sessions also includes code detailing how to submit data to a WCF RIA Service from a Windows Phone application.

If we build & run our application again and log in, we've got our list of Opportunities, straight from the domain service. However, there seems to be a problem when binding it to our View:

image

… as you might notice, there seems to be no value for Sector, Status, … What's happening?

 

Working with child entities.

The problem we're noticing in the above screenshot is due to the fact that only the "simple properties" from our entities are generated by default. While, in the Silverlight application, child entities that were sent over the wire (or were already available in the DomainContext instance) were accessible through, for example, Opportunity.Sector, this Sector property is now missing: we're only getting a SectorId. How can we solve this?

If we look at the generated proxy code, we see that the proxies classes for our entities are implemented as partial:

public partial class Opportunity : object, System.ComponentModel.INotifyPropertyChanged

This enables us to add code to that class in a different file. We can add a new property to another partial implementation of our Opportunity class, and this property will represent the Sector.

On to the next part of the problem: where does this Sector-property come from? As mentioned in the previous article, the result you're getting from a call to a domain service query method are actually divided into two properties: RootResults and IncludedResults. The IncludedResults include possible child entities (it's an ObservableCollection<object>, so it can actually contain different types of entities - you'll have to check for this). This means that, if the query method that returns the list of Opportunities is coded in such a way that it also returns the corresponding Sector entity, we can simply fetch that Sector entity from the IncludedResults list, and assign it to the Sector property we implemented in our partial class.

Easy enough :-)

But… what if the query method didn't return those child entities (remember: we're reusing an existing implementation, and we're trying to change as little as possible to the service layer)? Well, the first part remains the same: extend the partial Opportunity class so it contains a Sector property. You could now go and fetch that entity from another query method for each Opportunity, but that would create a lot of overhead, and way too many requests to our service layer: after all, the list of sectors is quite small, and lots of opportunities share the same sector. So instead, we can opt for loading the list of sectors when we start the application, and storing them in a container. The Sector property would then get the corresponding sector from that list, in its property getter, as such:

/// <summary>
/// The Sector property
/// </summary>
public SalesDashboard.PhoneApplication.EmployeeOpportunityRef.Sector Sector
{
    get
    {
        return 
            LocalStateContainer.Sectors == null ? 
            null : LocalStateContainer.Sectors.FirstOrDefault(s => s.Id == this.SectorId);
    }
}

Either way, our opportunity list now includes the correct sector identification:

image

The same logic can be applied to add the Customer property of an Opportunity to the screen. After this, our application includes all the information we need to provide to the end user while he's on the road.

 

Sharing code: the Portable Class Library.

In a lot of projects, you have some common code you want to use across different clients: this might be code to execute some common business logic or algorithms, or some often-used functions. Typically, you'd put these in a class library project, which is then referenced by each client that needs that common code.

However, references are typically only allowed if they're built against the same CLR, and at the moment we already have 3 different CLR's we're working with: the full .NET 4.0 CLR, the Silverlight CLR, and the Silverlight for Windows Phone CLR. For our initial two clients (Silverlight & Silverlight OOB), we could leverage some of WCF RIA Services' shared code features, but this is getting harder as we progress to the Windows Phone client, and will become even more annoying when we're going to add a Windows 8 Metro client to the equation later on in this article series.

One often-used way to achieve code sharing without having to copy code around is by using the "Add as link" option. It works like this:

  • Create 1 class library project, typically built against the CLR that has the "least" features - in our case, that would be a Windows Phone class library project.
  • Create extra class library projects for each CLR you need the common code to be built against. For example, we'd end up with Common.WP7, Common.SL and Common.Server class library projects.
  • In those class library projects, add the files containing the common code "as link".
  • The class library projects themselves are then referenced by their respective client projects, allowing those clients to access the common code.

While this technique works, it has 2 major disadvantages:

  • You end up with clutter: extra projects in your solution, simply to be able to reference them.
  • It's easy to make mistakes: if you happen to add code to the common code files which doesn't result in a successful build against each CLR, your projects won't run anymore - even though the code successfully builds against one specific CLR version.

The Portable Class Library template solves this. On MSDN, we can find the following description:

"The Portable Class Library project enables you to write and build managed assemblies that work on more than one .NET Framework platform. You can create classes that contain code you wish to share across many projects, such as shared business logic, and then reference those classes from different types of projects.

Using the Portable Class Library project, you can build portable assemblies that work without modification on the .NET Framework, Silverlight, Windows Phone 7, or Xbox 360 platforms. Without the Portable Class Library project, you must target a single platform and then manually rework the class library for other platforms. The Portable Class Library project supports a subset of assemblies from these platforms, and provides a Visual Studio template that makes it possible to build assemblies that run without modification on these platforms."

You can add the Portable Class Tools from this link.

So: it allows you to add 1 class library project to your solution, in the properties of which you can tell which CLR's should be supported. The code you add to that class libary project will have to adhere to the lowest common denominator for the combination of selected CLR's (ensuring your code will build against each CLR version you selected). Next to that, Visual Studio allows you to add a reference to this class library from each project which builds against the CLR versions you selected in the class library's project properties, leaving us with only 1 common project.

image

For reference, I've added a Portable Class Library project to the solution, SalesDashboard.Common, which I referenced without issue in 3 projects built against a different CLR. Doing something like this in the past would have required the "add as link" trick mentioned above - thanks to the Portable Class Library templates, we no longer have to resort to such tricks, making it easier to share code, maintain the code base, and thus: reuse the common library & cut costs.

 

Conclusion

In this article, we’ve learned how to consume existing domain services from a new type of client: a Windows Phone application.  We’ve also learned how we can reuse common code using the Portable Class Library project template.

Stay tuned for the next part! :-)

 

About the author

Kevin Dockx lives in Belgium and works at RealDolmen, one of Belgium's biggest ICT companies, where he is a technical specialist/project leader on .NET applications, mainly XAML-based, and a solution manager for Rich Applications (Windows 8 Metro, Silverlight, Windows Phone 7 Series, WPF, Surface, HTML5). His main focus lies on all things XAML, but he still keeps an eye on the new developments concerning other products from the Microsoft .NET (Web) Stack. As a XAML enthusiast, he's a regular speaker on various national and international events, like Microsoft Techdays in Belgium, Portugal & Finland, NDC2011, Community Day, ... Next to that, he also authored two Silverlight books at Packt Publishing, of which the most recent one is the Silverlight 5 Data and Services Cookbook, together with Gill Cleeren. His blog, which contains various tidbits on XAML, .NET, and the occasional rambling, can be found at http://blog.kevindockx.com/, and you can contact him on Twitter via @KevinDockx.


Subscribe

Comments

  • craig61a

    Re: Part 7: Developing for a multitude of clients: strategies for code reuse and keeping costs in check.


    posted by craig61a on Aug 17, 2012 22:38
    Great series of articles, very helpful!

Add Comment

Login to comment:
  *      *       

From this series