DCSIMG
Problem Deserializing Classes From Assemblies Loaded at Runtime Implicitly – TFS Build Custom UI Type Editor - Oshry Horn

Oshry Horn

Problem Deserializing Classes From Assemblies Loaded at Runtime Implicitly – TFS Build Custom UI Type Editor

Lately I’ve created a Custom UI Type Editor for the “Process” Tab in the TFS Build Definition Interface, and came across a problem.
Since my object was a bit complex combined with the reason shown here, I decided to use serialization to “Deep Copy” the object.

For an example I will use the Credential editor, made originally by “Ewald Hofman”. This is the modified code:

Credential objet:

   1: [Serializable]
   2: public class Credential
   3: {
   4:     public string Domain { get; set; }
   5:     public string UserName { get; set; }
   6:     public string Password { get; set; }
   7:  
   8:     public override string ToString()
   9:     {
  10:         if (string.IsNullOrEmpty(UserName))
  11:         {
  12:             return "No Credential";
  13:         }
  14:         else
  15:         {
  16:             return Domain + @"\" + UserName;
  17:         }
  18:     }
  19:  
  20:     public Credential DeepClone()
  21:     {
  22:         using (var ms = new MemoryStream())
  23:         {
  24:             var formatter = new BinaryFormatter();
  25:             formatter.Serialize(ms, this);
  26:             ms.Position = 0;
  27:  
  28:             formatter.AssemblyFormat = FormatterAssemblyStyle.Simple;
  29:  
  30:             return (Credential)formatter.Deserialize(ms);
  31:         }
  32:     }
  33: }

Credential editor:

   1: public class CredentialEditor : UITypeEditor
   2: {
   3:     
   4:     public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
   5:     {
   6:         if (provider != null)
   7:         {
   8:             IWindowsFormsEditorService editorService = (IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService));
   9:  
  10:             if (editorService != null)
  11:             {
  12:                 Credential credential = value as Credential;
  13:                 var editableCredential = credential.DeepClone();
  14:                 using (CredentialDialog dialog = new CredentialDialog(editableCredential))
  15:                 {
  16:                     dialog.Domain = credential.Domain;
  17:                     dialog.UserName = credential.UserName;
  18:                     dialog.Password = credential.Password;
  19:  
  20:                     if (editorService.ShowDialog(dialog) == DialogResult.OK)
  21:                     {
  22:                         return editableCredential;
  23:                     }
  24:                 }
  25:             }
  26:  
  27:         }
  28:  
  29:         return value;
  30:         
  31:     }
  32:  
  33:     public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
  34:     {
  35:         return UITypeEditorEditStyle.Modal;
  36:     }
  37: }

Happily I ran it for the first time and it (of course) crashed immediately with the following exception:

{"Unable to find assembly 'BuildTasks, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'."}

image

It terns out that due to the fact that for the deserialization  process your objects’ assembly will be loaded at runtime automatically but because it is not strongly typed and in the GAC, it is unable to relate between your objet and your objects’ assembly.

Luckily this is very easy to solve!

1) Add a Serialization Binder class.

   1: public class Binder : SerializationBinder
   2: {
   3:     public override Type BindToType(string assemblyName, string typeName)
   4:     {
   5:         return Type.GetType(String.Format("{0}, {1}", typeName, assemblyName));
   6:     }
   7: }

2) Attach the new Serialization Binder class to the BinaryFormatter instance in the DeepCopy method.

   1: public Credential DeepClone()
   2: {
   3:     using (var ms = new MemoryStream())
   4:     {
   5:         var formatter = new BinaryFormatter();
   6:         formatter.Serialize(ms, this);
   7:         ms.Position = 0;
   8:  
   9:         formatter.AssemblyFormat = FormatterAssemblyStyle.Simple;
  10:         formatter.Binder = new Binder();
  11:         formatter.Binder.BindToType(this.GetType().Assembly.FullName, this.GetType().Name);
  12:  
  13:         return (Credential)formatter.Deserialize(ms);
  14:     }
  15: }

3) Run :)

This will bind your class with it’s misguided runtime loaded assembly!

Comments

Oshry Horn said:

(* – Asterix mark on the parent window) When I built my own custom UI type editor it was fairly easy

# March 1, 2011 4:24 PM

Padda said:

Hi,

I'm having a problem with the example above and with Ewald's example. I've added the credentials custom type. When I enter detail when in the Edit Build Definition screen, it's fine. When queuing a new build, I enter different information in the credentials dialog, but they get ignored and the ones entered in the Edit Build Definition are kept. Are my changes being ignored, not saved, or overridden by the default settings from the Edit Build Definition? If so, how can I over come this as it's proving to be difficult to fix.

Thanks

# April 27, 2011 4:21 PM
Leave a Comment

(required) 

(required) 

(optional)

(required) 


Enter the numbers above: