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'."}

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!