Silverlight deployment: deploying to multiple environments

Today I got an interesting question, namely they were using services in their silverlight application, but each time they needed to deploy they unzipped the .xap file to change the ServiceReferences.ClientConfig configuration to use their production server. of course this meant re-zipping back to .xap, and possible mistakes during deployment. So on several occasions a QA .xap was deployed in production.

So how can you avoid this? Well, I think the best way is to move the changing configuration to outside the .xap file. For example we can deploy a simple file to the web server where the silverlight application is deployed and add the server address in it:

<servers>

  <server type="movieServer"

          uri="http://PRODUCTION_SERVER/MovieService.svc"

  />

</servers>

 

For each deployment environment you would keep a separate file, making sure that future deployments cannot overwrite it by accident (for example by setting ACL’s on the file).

Next, when the silverlight application starts we download this file from the server using the WebClient class:

Dim WithEvents wc As New WebClient

 

Private Sub GetTheServers()

  wc.DownloadStringAsync(New Uri("servers.xml", UriKind.Relative))

End Sub

 

Once we receive the DownloadStringCompleted event we parse the server’s address out of the received xml:

Dim movieServerAddress As String

Private Sub wc_DownloadStringCompleted(ByVal sender As Object,

             ByVal e As System.Net.DownloadStringCompletedEventArgs)

             Handles wc.DownloadStringCompleted

  Dim doc = XDocument.Parse(e.Result)

  Dim movieServerQuery = From server In doc...<server> _

                         Where server.@type = "movieServer" _

                         Select server.@uri

 

  movieServerAddress = movieServerQuery.Single()

End Sub

 

When we need to call the service (for example a WCF service) we simply use the stored movieServerAddress:

Private Sub LoadTheMovies(ByVal sender As System.Object,

                          ByVal e As System.Windows.RoutedEventArgs)

  proxy = New MovieServiceReference.MovieServiceClient( _

            New BasicHttpBinding(), 
            New EndpointAddress(movieServerAddress))

  proxy.GetListOfMoviesAsync()

End Sub

 

You could avoid downloading the servers.xml file multiple times by storing it in Isolated storage. So the first time isolated storage wouldn’t have this information yet, so you would call GetTheServers. Then you could parse its contents and store the information in isolated storage. Next time you would find it in isolated storage, saving the round-trip to download it…

Using T4 to automatically generate your entities

*** This is a repost of a previous post because of moving to a new blog engine in which some formatting was lost ***

Today I discovered something very interesting, namely how to automatically generate my entity classes (classes that also implement the INotifyPropertyChanged and IDataErrorInfo interfaces) using T4.

T4 is a text generation language built into Visual Studio 2005 and later, normally intended for DSL code generation. But we can also use it for generating any code we would like. I for one, I would like automatic properties to automatically implement INotifyPropertyChanged if the interface is on the class, but of course, life doesn’t work that way *sigh*. The real problem is that for each property you have, part of the implementation needs to raise an event using the property name. When you change the name of the property you also have to change this string, which is error prone…

Instead we can generate code using T4, and to be honest, it isn’t that difficult to use. First of all, go to tangible to download their free T4 intelli-sense tool.

Next add to your project a new text file, naming it Entity.tt (the extension is important!). Now add this content:

<#@ template language="C#" #>
<#@ output extension="cs" #>
<#@ import namespace="System.Collections" #>
<#@ import namespace="System.Collections.Generic" #>
// ---------------------------------
// U2U Sample, use at your own risk!
//        http://www.u2u.be
// ---------------------------------

using System;
using System.ComponentModel;
using System.Collections.Generic;

namespace <#= this.Namespace #> 
{  
  public partial class <#= this.ClassName #> 
    : INotifyPropertyChanged
    , IDataErrorInfo  
  {
    #region Private fields
    <# for (int idx = 0; idx < this.properties.GetLength(0); idx++) { #>    
    private <#= this.properties[idx,0] #> _<#= this.properties[idx,1].ToLower() #>;<# }#>   
    
    #endregion
    
    #region Properties
    <# for (int idx = 0; idx < this.properties.GetLength(0); idx++) { #>    
    public <#= this.properties[idx,0] #> <#= this.properties[idx,1] #>
    {        
      get
      {            
        return _<#= this.properties[idx,1].ToLower() #>;        
      }        
      set        
      {            
        _<#= this.properties[idx,1].ToLower() #> = value;
        OnPropertyChanged("<#= this.properties[idx,1]#>");        
      }   
    }
    <#   }   #>      
    #endregion
    
    #region INotifyPropertyChanged Members   
    
    public event PropertyChangedEventHandler PropertyChanged;
    
    protected virtual void OnPropertyChanged(string propertyName)   
    {       
      if (PropertyChanged != null)           
        PropertyChanged(this, new PropertyChangedEventArgs(propertyName));   
    }   
    
    #endregion
    
    #region IDataErrorInfo Members
    
    private string _error = null;
    
    public string Error {
      get
      { 
        return _error; 
      }
      set 
      {
        _error = value;
      }
    }
    
    private Dictionary<string,string> _columnErrors =
      new Dictionary<string,string>();
      
    public string this[string columnName]
    {
      get
      {
        if( _columnErrors != null && _columnErrors.ContainsKey(columnName))
          return _columnErrors[columnName];
        else
          return null;
      }
      set
      {
        if( value != null ) // Insert error
        {
          if( _columnErrors == null )
          {
            _columnErrors = new Dictionary<string,string>();
          }
          _columnErrors[columnName] = value;
        }
        else // Clear error
        {
          _columnErrors.Remove(columnName);
          if (_columnErrors.Count == 0)
          {
            _columnErrors = null;
          }
        }
        // Notify validation that something has changed
        OnPropertyChanged( columnName );
      }
    }
    #endregion
  }
}

<#+    
  string Namespace = "Demo";    
  string ClassName = "DemoClass";    
  string[,] properties = {        
    {"int", "Property1"},        
    {"string", "Property2"}
  };               
#>

Building your project will generate a little DemoClass with these two properties. But that is of course not what you want; so add another file Category.tt:

<#    
  this.Namespace = "U2U.Samples";    
  this.ClassName = "Category";    
  this.properties = new string[,]
  {        
    {"int"   , "CategoryID"}
  , {"string", "CategoryName"}      
  , {"string", "Description"}      
  , {"byte[]", "Picture"}
  }; 
#>

<#@ include file="Entity.tt" #>

Building will now automatically generate a Category.cs file with code like this:

// ---------------------------------
// U2U Sample, use at your own risk!
//        http://www.u2u.be
// ---------------------------------

using System;
using System.ComponentModel;
using System.Collections.Generic;

namespace U2U.Samples
{
  public partial class Category
    : INotifyPropertyChanged
    , IDataErrorInfo
  {
    #region Private fields

    private int _categoryid;
    private string _categoryname;
    private string _description;
    private byte[] _picture;

    #endregion

    #region Properties

    public int CategoryID
    {
      get
      {
        return _categoryid;
      }
      set
      {
        _categoryid = value;
        OnPropertyChanged("CategoryID");
      }
    }

    public string CategoryName
    {
      get
      {
        return _categoryname;
      }
      set
      {
        _categoryname = value;
        OnPropertyChanged("CategoryName");
      }
    }

    public string Description
    {
      get
      {
        return _description;
      }
      set
      {
        _description = value;
        OnPropertyChanged("Description");
      }
    }

    public byte[] Picture
    {
      get
      {
        return _picture;
      }
      set
      {
        _picture = value;
        OnPropertyChanged("Picture");
      }
    }

    #endregion

    #region INotifyPropertyChanged Members

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged(string propertyName)
    {
      if (PropertyChanged != null)
        PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }

    #endregion

    #region IDataErrorInfo Members

    private string _error = null;

    public string Error
    {
      get
      {
        return _error;
      }
      set
      {
        _error = value;
      }
    }

    private Dictionary<string, string> _columnErrors =
      new Dictionary<string, string>();

    public string this[string columnName]
    {
      get
      {
        if (_columnErrors != null && _columnErrors.ContainsKey(columnName))
          return _columnErrors[columnName];
        else
          return null;
      }
      set
      {
        if (value != null) // Insert error
        {
          if (_columnErrors == null)
          {
            _columnErrors = new Dictionary<string, string>();
          }
          _columnErrors[columnName] = value;
        }
        else // Clear error
        {
          _columnErrors.Remove(columnName);
          if (_columnErrors.Count == 0)
          {
            _columnErrors = null;
          }
        }
        // Notify validation that something has changed
        OnPropertyChanged(columnName);
      }
    }
    #endregion
  }
}

So to change/add/remove a property, we edit the Category.tt file and all the rest is done automatically.

Now for some feedback, what is your way of implementing IDataErrorInfo?

Serializing Cyclic Graphs of objects with WCF

*** This is a repost of a previous post because of moving to a new blog engine in which some formatting was lost ***

Abstract

In this blog post I will be discussing how the serialize cyclic graphs of objects using WCF, including data contracts and POCO’s ( Plain Old CLR Objects).

Introduction

WCF allows you to serialize any number of objects over the network or to disk using the DataContractSerializer class. However, some issues can arise when you have cycles in your object graph, and especially when you want some shared objects to stay shared!

Serializing cyclic objects

Let’s look at this with a couple of examples. First I want some datacontracts with possible cycles:

  [DataContract(Namespace=Namespaces.U2U, Name="address", IsReference=false)]
  public class Address
  {
    [DataMember(Name="street", Order=1)]
    public string Street { get; set; }
    [DataMember(Name="city", Order=2)]
    public string City { get; set; }
  }

  [DataContract(Namespace = Namespaces.U2U, Name = "person", IsReference = false)]
  public class Person
  {
    [DataMember(Name="name", Order=1)]
    public string Name { get; set; }
    [DataMember(Name="age", Order=2)]
    public int Age { get; set; }
    [DataMember(Name="partner",Order=3)]
    public Person Partner { get; set; }
    [DataMember(Name = "address", Order = 4)]
    public Address Address { get; set; }
  }

 

I’ll create a person object with a reference to another person. Since they are partners, they will point to each other.

image

DataContractSerializer ser = new DataContractSerializer(typeof(Person));
string path = Path.Combine( Environment.CurrentDirectory, "cartoon.xml" );
using (FileStream ms = File.Open(path, FileMode.Create, FileAccess.Write))
{
  Address home = new Address { Street = "ToonLane", City = "ToonTown" };
  Person tom = new Person { Name = "Tom", Age = 4, Address = home };
  Person jerry = new Person { Name = "Jerry", Age = 2, Address = home };
  tom.Partner = jerry;
  jerry.Partner = tom;
  ser.WriteObject(ms, tom);
}

So what happens when you try this? You’ll get an exception:

image

[NOTE: By the way, this is much better then in .NET 1.0 with the XmlSerializer, it would serialize the first object, then follow the link to the second, then back the to first, then back to the second, until your disk ran out of space :) Now XmlSerializer will also throw an exception if you try to serialize cycles.]

Using IsReference

To make the exception go away, you add IsReference=true to the Person’s DataContract:

The result now looks like this:

<person z:Id="i1" 
xmlns="urn://www.u2u.be/samples/wcf/2009"
xmlns:i=http://www.w3.org/2001/XMLSchema-instance
xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/"> <name>Tom</name> <age>4</age> <partner z:Id="i2"> <name>Jerry</name> <age>2</age> <partner z:Ref="i1"/> <address> <street>ToonLane</street> <city>ToonTown</city> </address> </partner> <address> <street>ToonLane</street> <city>ToonTown</city> </address> </person>

Look how Jerry refers back to Tom using the z:Ref! Now you can easily send objects with cycles. But wait! Both point to the same address, but now Tom and Jerry both have an address, although identical.

image

Again we can change this by changing the IsReference to true in the Address DataContract. Now the result looks like this:

<person z:Id="i1" xmlns="urn://www.u2u.be/samples/wcf/2009" xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/">
  <name>Tom</name>
  <age>4</age>
  <partner z:Id="i2">
    <name>Jerry</name>
    <age>2</age>
    <partner z:Ref="i1"/>
    <address z:Id="i3">
      <street>ToonLane</street>
      <city>ToonTown</city>
    </address>
  </partner>
  <address z:Ref="i3"/>
</person>

Please note that now both Tom and Jerry use the same address instance, not clones.

Serializing cyclic POCO’s with WCF

“And what about POCO’s? (Plain Old Clr Objects) I hear you say. Well in that case you cannot attach the DataContract attribute of course, so you‘ll need to use another variant of the DataContractSerializer constructor:

new DataContractSerializer(typeof(PocoPerson), null, int.MaxValue, false
                          , /* preserveObjectReferences */ true, null, null);

So, using this constructor the previous example looks like this:

  public class PocoPerson
  {
    public string Name { get; set; }
    public int Age { get; set; }
    public PocoPerson Partner { get; set; }
    public PocoAddress Address { get; set; }
  }

  public class PocoAddress
  {
    public string Street { get; set; }
    public string City { get; set; }
  }
  DataContractSerializer ser = 
    new DataContractSerializer(typeof(PocoPerson), null, int.MaxValue, false
                              , /* preserveObjectReferences */ true, null, null);
  string path = Path.Combine(Environment.CurrentDirectory, "cartoon2.xml");
  using (FileStream ms = File.Open(path, FileMode.Create, FileAccess.Write))
  {
    PocoAddress home = new PocoAddress { Street = "ToonLane", City = "ToonTown" };
    PocoPerson tom = new PocoPerson { Name = "Tom", Age = 4, Address = home };
    PocoPerson jerry = new PocoPerson { Name = "Jerry", Age = 2, Address = home };
    tom.Partner = jerry;
    jerry.Partner = tom;
    ser.WriteObject(ms, tom);
}

Running this will result in following Xml:

<PocoPerson z:Id="1" 
            xmlns="http://schemas.datacontract.org/2004/07/ReferenceSerialization" 
            xmlns:i="http://www.w3.org/2001/XMLSchema-instance" 
            xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/">
  <Address z:Id="2">
    <City z:Id="3">ToonTown</City>
    <Street z:Id="4">ToonLane</Street>
  </Address>
  <Age>4</Age>
  <Name z:Id="5">Tom</Name>
  <Partner z:Id="6">
    <Address z:Ref="2" i:nil="true"/>
    <Age>2</Age>
    <Name z:Id="7">Jerry</Name>
    <Partner z:Ref="1" i:nil="true"/>
  </Partner>
</PocoPerson>

Using a custom behavior

Of course it is not that easy to use this contructor inside a real service, because you’re not in charge of creating the serializer. But hey, WCF is very extensible right? So to plug the hole, you need to build your own Behavior, specifically a DataContractSerializerOperationBehavior.

public class CyclicSerializationBehavior : DataContractSerializerOperationBehavior
{
  public CyclicSerializationBehavior(OperationDescription od)
    : base(od)
  { }

  private const bool preserveObjectReferences = true;

  public override XmlObjectSerializer CreateSerializer(
Type type, XmlDictionaryString name, XmlDictionaryString ns, IList<Type> knownTypes) { return new DataContractSerializer(type, name, ns, knownTypes,
this.MaxItemsInObjectGraph, this.IgnoreExtensionDataObject,
preserveObjectReferences, this.DataContractSurrogate); } public override XmlObjectSerializer CreateSerializer(
Type type, string name, string ns, IList<Type> knownTypes) { return new DataContractSerializer(type, name, ns, knownTypes,
this.MaxItemsInObjectGraph, this.IgnoreExtensionDataObject,
preserveObjectReferences, this.DataContractSurrogate); } }

To get this behavior installed you need to write some code to add it the the operation’s behavior, or even better is to use an IContractBehavior:

  [AttributeUsage(AttributeTargets.All)]
  public class CyclicSerializationAttribute : Attribute, IContractBehavior
  {
    public void AddBindingParameters(ContractDescription contractDescription, 
ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) { } public void ApplyClientBehavior(ContractDescription contractDescription,
ServiceEndpoint endpoint, ClientRuntime clientRuntime) { ReplaceDataContractSerializerOperationBehavior(contractDescription); } public void ApplyDispatchBehavior(ContractDescription contractDescription,
ServiceEndpoint endpoint, DispatchRuntime dispatchRuntime) { ReplaceDataContractSerializerOperationBehavior(contractDescription); } private void ReplaceDataContractSerializerOperationBehavior(ContractDescription contractDescription) { foreach (OperationDescription operation in contractDescription.Operations) { DataContractSerializerOperationBehavior beh =
operation.Behaviors.Find<DataContractSerializerOperationBehavior>(); if (beh != null) { operation.Behaviors.Remove(beh); operation.Behaviors.Add(new CyclicSerializationBehavior(operation)); } } } public void Validate(ContractDescription contractDescription, ServiceEndpoint endpoint) { } }

I made this class into an attribute so you can apply it to your service contract or service class, and when WCF loads it it replaces the default DataContractSerialerOperationBehavior. At first I simply added it to the collection of behaviors, but then I got all kinds of strange things :)

So, to use this now you apply the attribute to the service contract:

  [ServiceContract]
  [CyclicSerializationAttribute]
  public interface ICyclicSerializationService
  {
    [OperationContract]
    PocoPerson GetPocoPerson();
  }

Don’t forget, you should add this to both the server en client side.

Using T4 to automatically generate your entities

Today I discovered something very interesting, namely how to automatically generate my entity classes (classes that also implement the INotifyPropertyChanged and IDataErrorInfo interfaces) using T4.

T4 is a text generation language built into Visual Studio 2005 and later, normally intended for DSL code generation. But we can also use it for generating any code we would like. I for one, I would like automatic properties to automatically implement INotifyPropertyChanged if the interface is on the class, but of course, life doesn’t work that way *sigh*. The real problem is that for each property you have, part of the implementation needs to raise an event using the property name. When you change the name of the property you also have to change this string, which is error prone…

Instead we can generate code using T4, and to be honest, it isn’t that difficult to use. First of all, go to tangible to download their free T4 intelli-sense tool.

Next add to your project a new text file, naming it Entity.tt (the extension is important!). Now add this content:

<#@ template language="C#" #> <#@ output extension="cs" #> <#@ import namespace="System.Collections" #> <#@ import namespace="System.Collections.Generic" #> // --------------------------------- // U2U Sample, use at your own risk! //        http://www.u2u.be // ---------------------------------  using System; using System.ComponentModel; using System.Collections.Generic;  namespace <#= this.Namespace #>  {     public partial class <#= this.ClassName #>      : INotifyPropertyChanged     , IDataErrorInfo     {     #region Private fields     <# for (int idx = 0; idx < this.properties.GetLength(0); idx++) { #>         private <#= this.properties[idx,0] #> _<#= this.properties[idx,1].ToLower() #>;<# }#>             #endregion          #region Properties     <# for (int idx = 0; idx < this.properties.GetLength(0); idx++) { #>         public <#= this.properties[idx,0] #> <#= this.properties[idx,1] #>     {               get       {                     return _<#= this.properties[idx,1].ToLower() #>;               }               set               {                     _<#= this.properties[idx,1].ToLower() #> = value;         OnPropertyChanged("<#= this.properties[idx,1]#>");               }        }     <#   }   #>           #endregion          #region INotifyPropertyChanged Members             public event PropertyChangedEventHandler PropertyChanged;          protected virtual void OnPropertyChanged(string propertyName)        {              if (PropertyChanged != null)                    PropertyChanged(this, new PropertyChangedEventArgs(propertyName));        }             #endregion          #region IDataErrorInfo Members          private string _error = null;          public string Error {       get       {          return _error;        }       set        {         _error = value;       }     }          private Dictionary<string,string> _columnErrors =       new Dictionary<string,string>();            public string this[string columnName]     {       get       {         if( _columnErrors != null && _columnErrors.ContainsKey(columnName))           return _columnErrors[columnName];         else           return null;       }       set       {         if( value != null ) // Insert error         {           if( _columnErrors == null )           {             _columnErrors = new Dictionary<string,string>();           }           _columnErrors[columnName] = value;         }         else // Clear error         {           _columnErrors.Remove(columnName);           if (_columnErrors.Count == 0)           {             _columnErrors = null;           }         }         // Notify validation that something has changed         OnPropertyChanged( columnName );       }     }     #endregion   } }  <#+       string Namespace = "Demo";       string ClassName = "DemoClass";       string[,] properties = {             {"int", "Property1"},             {"string", "Property2"}   };                #>

Building your project will generate a little DemoClass with these two properties. But that is of course not what you want; so add another file Category.tt:

<#       this.Namespace = "U2U.Samples";       this.ClassName = "Category";       this.properties = new string[,]   {             {"int"   , "CategoryID"}   , {"string", "CategoryName"}         , {"string", "Description"}         , {"byte[]", "Picture"}   };  #>  <#@ include file="Entity.tt" #>  

Building will now automatically generate a Category.cs file with code like this:

// --------------------------------- // U2U Sample, use at your own risk! //        http://www.u2u.be // ---------------------------------  using System; using System.ComponentModel; using System.Collections.Generic;  namespace U2U.Samples {   public partial class Category     : INotifyPropertyChanged     , IDataErrorInfo   {     #region Private fields      private int _categoryid;     private string _categoryname;     private string _description;     private byte[] _picture;      #endregion      #region Properties      public int CategoryID     {       get       {         return _categoryid;       }       set       {         _categoryid = value;         OnPropertyChanged("CategoryID");       }     }      public string CategoryName     {       get       {         return _categoryname;       }       set       {         _categoryname = value;         OnPropertyChanged("CategoryName");       }     }      public string Description     {       get       {         return _description;       }       set       {         _description = value;         OnPropertyChanged("Description");       }     }      public byte[] Picture     {       get       {         return _picture;       }       set       {         _picture = value;         OnPropertyChanged("Picture");       }     }      #endregion      #region INotifyPropertyChanged Members      public event PropertyChangedEventHandler PropertyChanged;      protected virtual void OnPropertyChanged(string propertyName)     {       if (PropertyChanged != null)         PropertyChanged(this, new PropertyChangedEventArgs(propertyName));     }      #endregion      #region IDataErrorInfo Members      private string _error = null;      public string Error     {       get       {         return _error;       }       set       {         _error = value;       }     }      private Dictionary<string, string> _columnErrors =       new Dictionary<string, string>();      public string this[string columnName]     {       get       {         if (_columnErrors != null && _columnErrors.ContainsKey(columnName))           return _columnErrors[columnName];         else           return null;       }       set       {         if (value != null) // Insert error         {           if (_columnErrors == null)           {             _columnErrors = new Dictionary<string, string>();           }           _columnErrors[columnName] = value;         }         else // Clear error         {           _columnErrors.Remove(columnName);           if (_columnErrors.Count == 0)           {             _columnErrors = null;           }         }         // Notify validation that something has changed         OnPropertyChanged(columnName);       }     }     #endregion   } }  So to change/add/remove a property, we edit the Category.tt file and all the rest is done automatically.  Now for some feedback, what is your way of implementing IDataErrorInfo?