Archive

Archive for March, 2014

Template Solution For Deploying TFS Checkin Policies To Multiple Versions Of Visual Studio And Having Them Automatically Work From “TF.exe Checkin” Too

March 24th, 2014 No comments

Get the source code

Let’s get right to it by giving you the source code.  You can get it from the MSDN samples here.

 

Explanation of source code and adding new checkin policies

If you open the Visual Studio (VS) solution the first thing you will likely notice is that there are 5 projects.  CheckinPolicies.VS2012 simply references all of the files in CheckinPolicies.VS2013 as links (i.e. shortcut files); this is because we need to compile the CheckinPolicies.VS2012 project using TFS 2012 assemblies, and the CheckinPolicies.VS2013 project using TFS2013 assemblies, but want both projects to have all of the same checkin policies.  So the projects contain all of the same files; just a few of their references are different.  A copy of the references that are different between the two projects are stored in the project’s “Dependencies” folder; these are the Team Foundation assemblies that are specific to VS 2012 and 2013.  Having these assemblies stored in the solution allows us to still build the VS 2012 checkin policies, even if you (or a colleague) only has VS 2013 installed.

Update: To avoid having multiple CheckinPolicy.VS* projects, we could use the msbuild targets technique that P. Kelly shows on his blog. However, I believe we would still need multiple deployment projects, as described below, in order to have the checkin policies work outside of Visual Studio.

The other projects are CheckinPolicyDeployment.VS2012 and CheckinPolicyDeployment.VS2013 (both of which are VSPackage projects), and CheckinPolicyDeploymentShared.  The CheckinPolicyDeployment.VS2012/VS2013 projects will generate the VSIX files that are used to distribute the checkin policies, and CheckinPolicyDeploymentShared contains files/code that are common to both of the projects (the projects reference the files by linking to them).

Basically everything is ready to go.  Just start adding new checkin policy classes to the CheckinPolicy.VS2013 project, and then also add them to the CheckinPolicy.VS2012 project as a link.  You can add a file as a link in 2 different ways in the Solution Explorer:

  1. Right-click on the CheckinPolicies.VS2012 project and choose Add -> Existing Item…, and then navigate to the new class file that you added to the CheckinPolicy.VS2013 project.  Instead of clicking the Add button though, click the little down arrow on the side of the Add button and then choose Add As Link.
  2. Drag and drop the file from the CheckinPolicy.VS2013 project to the CheckinPolicy.VS2012 project, but while releasing the left mouse button to drop the file, hold down the Alt key; this will change the operation from adding a copy of the file to that project, to adding a shortcut file that links back to the original file.
    There is a DummyCheckinPolicy.cs file in the CheckinPolicies.VS2013 project that shows you an example of how to create a new checkin policy.  Basically you just need to create a new public, serializable class that extends the CheckinPolicyBase class.  The actual logic for your checkin policy to perform goes in the Evaluate() function. If there is a policy violation in the code that is trying to be checked in, just add a new PolicyFailure instance to the failures list with the message that you want the user to see.

      Building a new version of your checkin policies

      Once you are ready to deploy your policies, you will want to update the version number in the source.extension.vsixmanifest file in both the CheckinPolicyDeployment.VS2012 and CheckinPolicyDeployment.VS2013 projects.  Since these projects will both contain the same policies, I recommend giving them the same version number as well.  Once you have updated the version number, build the solution in Release mode.  From there you will find the new VSIX files at "CheckinPolicyDeployment.VS2012\bin\Release\TFS Checkin Policies VS2012.vsix" and "CheckinPolicyDeployment.VS2013\bin\Release\TFS Checkin Policies VS2013.vsix".  You can then distribute them to your team; I recommend setting up an internal VS Extension Gallery, but the poor-man’s solution is to just email the vsix file out to everyone on your team.

      Having the policies automatically work outside of Visual Studio

      This is already hooked up and working in the template solution, so nothing needs to be changed there, but I will explain how it works here.  A while back I blogged about how to get your Team Foundation Server (TFS) checkin polices to still work when checking code in from the command line via the “tf checkin” command; by default when installing your checkin policies via a VSIX package (the MS recommended approach) you can only get them to work in Visual Studio.  I hated that I would need to manually run the script I provided each time the checkin policies were updated, so I posted a question on Stack Overflow about how to run a script automatically after the VSIX package installs the extension.  So it turns out that you can’t do that, but what you can do is use a VSPackage instead, which still uses VSIX to deploy the extension, but then also allows us to hook into Visual Studio events to run our script when VS starts up or exits.

      Here is the VSPackage class code to hook up the events and call our UpdateCheckinPoliciesInRegistry() function:

      /// <summary>
      /// This is the class that implements the package exposed by this assembly.
      ///
      /// The minimum requirement for a class to be considered a valid package for Visual Studio
      /// is to implement the IVsPackage interface and register itself with the shell.
      /// This package uses the helper classes defined inside the Managed Package Framework (MPF)
      /// to do it: it derives from the Package class that provides the implementation of the 
      /// IVsPackage interface and uses the registration attributes defined in the framework to 
      /// register itself and its components with the shell.
      /// </summary>
      // This attribute tells the PkgDef creation utility (CreatePkgDef.exe) that this class is
      // a package.
      [PackageRegistration(UseManagedResourcesOnly = true)]
      // This attribute is used to register the information needed to show this package
      // in the Help/About dialog of Visual Studio.
      [InstalledProductRegistration("#110", "#112", "1.0", IconResourceID = 400)]
      // Auto Load our assembly even when no solution is open (by using the Microsoft.VisualStudio.VSConstants.UICONTEXT_NoSolution guid).
      [ProvideAutoLoad("ADFC4E64-0397-11D1-9F4E-00A0C911004F")]
      public abstract class CheckinPolicyDeploymentPackage : Package
      {
      	private EnvDTE.DTEEvents _dteEvents;
      
      	/// <summary>
      	/// Initialization of the package; this method is called right after the package is sited, so this is the place
      	/// where you can put all the initialization code that rely on services provided by VisualStudio.
      	/// </summary>
      	protected override void Initialize()
      	{
      		base.Initialize();
      
      		var dte = (DTE2)GetService(typeof(SDTE));
      		_dteEvents = dte.Events.DTEEvents;
      		_dteEvents.OnBeginShutdown += OnBeginShutdown;
      
      		UpdateCheckinPoliciesInRegistry();
      	}
      
      	private void OnBeginShutdown()
      	{
      		_dteEvents.OnBeginShutdown -= OnBeginShutdown;
      		_dteEvents = null;
      
      		UpdateCheckinPoliciesInRegistry();
      	}
      
      	private void UpdateCheckinPoliciesInRegistry()
      	{
      		var dte = (DTE2)GetService(typeof(SDTE));
      		string visualStudioVersionNumber = dte.Version;
      		string customCheckinPolicyEntryName = "CheckinPolicies";
      
      		// Create the paths to the registry keys that contains the values to inspect.
      		string desiredRegistryKeyPath = string.Format("HKEY_CURRENT_USER\\Software\\Microsoft\\VisualStudio\\{0}_Config\\TeamFoundation\\SourceControl\\Checkin Policies", visualStudioVersionNumber);
      		string currentRegistryKeyPath = string.Empty;
      		if (Environment.Is64BitOperatingSystem)
      			currentRegistryKeyPath = string.Format("HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\Microsoft\\VisualStudio\\{0}\\TeamFoundation\\SourceControl\\Checkin Policies", visualStudioVersionNumber);
      		else
      			currentRegistryKeyPath = string.Format("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\{0}\\TeamFoundation\\SourceControl\\Checkin Policies", visualStudioVersionNumber);
      
      		// Get the value that the registry should have, and the value that it currently has.
      		var desiredRegistryValue = Registry.GetValue(desiredRegistryKeyPath, customCheckinPolicyEntryName, null);
      		var currentRegistryValue = Registry.GetValue(currentRegistryKeyPath, customCheckinPolicyEntryName, null);
      
      		// If the registry value is already up to date, just exit without updating the registry.
      		if (desiredRegistryValue == null || desiredRegistryValue.Equals(currentRegistryValue))
      			return;
      
      		// Get the path to the PowerShell script to run.
      		string powerShellScriptFilePath = Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetAssembly(typeof(CheckinPolicyDeploymentPackage)).Location),
      			"FilesFromShared", "UpdateCheckinPolicyInRegistry.ps1");
      
      		// Start a new process to execute the batch file script, which calls the PowerShell script to do the actual work.
      		var process = new Process
      		{
      			StartInfo =
      			{
      				FileName = "PowerShell",
      				Arguments = string.Format("-NoProfile -ExecutionPolicy Bypass -File \"{0}\" -VisualStudioVersion \"{1}\" -CustomCheckinPolicyEntryName \"{2}\"", powerShellScriptFilePath, visualStudioVersionNumber, customCheckinPolicyEntryName),
      
      				// Hide the PowerShell window while we run the script.
      				CreateNoWindow = true,
      				UseShellExecute = false
      			}
      		};
      		process.Start();
      	}
      }
      

      All of the attributes on the class are put there by default, except for the “[ProvideAutoLoad("ADFC4E64-0397-11D1-9F4E-00A0C911004F")]” one; this attribute is the one that actually allows the Initialize() function to get called when Visual Studio starts.  You can see in the Initialize method that we hook up an event so that the UpdateCheckinPoliciesInRegistry() function gets called when VS is closed, and we also call that function from Initialize(), which is called when VS starts up.

      You might have noticed that this class is abstract.  This is because the VS 2012 and VS 2013 classed need to have a unique ID attribute, so the actual VSPackage class just inherits from this one.  Here is what the VS 2013 one looks like:

      [Guid(GuidList.guidCheckinPolicyDeployment_VS2013PkgString)]
      public sealed class CheckinPolicyDeployment_VS2013Package : CheckinPolicyDeploymentShared.CheckinPolicyDeploymentPackage
      { }
      

      The UpdateCheckinPoliciesInRegistry() function checks to see if the appropriate registry key has been updated to allow the checkin policies to run from the “tf checkin” command prompt command.  If they have, then it simply exits, otherwise it calls a PowerShell script to set the keys for us.  A PowerShell script is used because modifying the registry requires admin permissions, and we can easily run a new PowerShell process as admin (assuming the logged in user is an admin on their local machine, which is the case for everyone in our company).

      The one variable to note here is the customCheckinPolicyEntryName. This corresponds to the registry key name that I’ve specified in the RegistryKeyToAdd.pkgdef file, so if you change it be sure to change it in both places.  This is what the RegistryKeyToAdd.pkgdef file contains:

      // We use "\..\" in the value because the projects that include this file place it in a "FilesFromShared" folder, and we want it to look for the dll in the root directory.
      [$RootKey$\TeamFoundation\SourceControl\Checkin Policies]
      "CheckinPolicies"="$PackageFolder$\..\CheckinPolicies.dll"
      

      And here are the contents of the UpdateCheckinPolicyInRegistry.ps1 PowerShell file.  This is basically just a refactored version of the script I posted on my old blog post:

      # This script copies the required registry value so that the checkin policies will work when doing a TFS checkin from the command line.
      param
      (
      	[parameter(Mandatory=$true,HelpMessage="The version of Visual Studio to update in the registry (i.e. '11.0' for VS 2012, '12.0' for VS 2013)")]
      	[string]$VisualStudioVersion,
      
      	[parameter(HelpMessage="The name of the Custom Checkin Policy Entry in the Registry Key.")]
      	[string]$CustomCheckinPolicyEntryName = 'CheckinPolicies'
      )
      
      # Turn on Strict Mode to help catch syntax-related errors.
      # 	This must come after a script's/function's param section.
      # 	Forces a function to be the first non-comment code to appear in a PowerShell Module.
      Set-StrictMode -Version Latest
      
      $ScriptBlock = {
      	function UpdateCheckinPolicyInRegistry([parameter(Mandatory=$true)][string]$VisualStudioVersion, [string]$CustomCheckinPolicyEntryName)
      	{
      		$status = 'Updating registry to allow checkin policies to work outside of Visual Studio version ' + $VisualStudioVersion + '.'
      		Write-Output $status
      
      		# Get the Registry Key Entry that holds the path to the Custom Checkin Policy Assembly.
      		$HKCUKey = 'HKCU:\Software\Microsoft\VisualStudio\' + $VisualStudioVersion + '_Config\TeamFoundation\SourceControl\Checkin Policies'
      		$CustomCheckinPolicyRegistryEntry = Get-ItemProperty -Path $HKCUKey -Name $CustomCheckinPolicyEntryName
      		$CustomCheckinPolicyEntryValue = $CustomCheckinPolicyRegistryEntry.($CustomCheckinPolicyEntryName)
      
      		# Create a new Registry Key Entry for the iQ Checkin Policy Assembly so they will work from the command line (as well as from Visual Studio).
      		if ([Environment]::Is64BitOperatingSystem)
      		{ $HKLMKey = 'HKLM:\SOFTWARE\Wow6432Node\Microsoft\VisualStudio\' + $VisualStudioVersion + '\TeamFoundation\SourceControl\Checkin Policies' }
      		else
      		{ $HKLMKey = 'HKLM:\SOFTWARE\Microsoft\VisualStudio\' + $VisualStudioVersion + '\TeamFoundation\SourceControl\Checkin Policies' }
      		Set-ItemProperty -Path $HKLMKey -Name $CustomCheckinPolicyEntryName -Value $CustomCheckinPolicyEntryValue
      	}
      }
      
      # Run the script block as admin so it has permissions to modify the registry.
      Start-Process -FilePath PowerShell -Verb RunAs -ArgumentList "-NoProfile -ExecutionPolicy Bypass -Command &amp; {$ScriptBlock UpdateCheckinPolicyInRegistry -VisualStudioVersion ""$VisualStudioVersion"" -CustomCheckinPolicyEntryName ""$CustomCheckinPolicyEntryName""}"
      

      While I could have just used a much smaller PowerShell script that simply set a given registry key to a given value, I chose to have some code duplication between the C# code and this script so that this script can still be used as a stand-alone script if needed.

      The slight downside to using a VSPackage is that this script still won’t get called until the user closes or opens a new instance of Visual Studio, so the checkin policies won’t work immediately from the “tf checkin” command after updating the checkin policies extension, but this still beats having to remember to manually run the script.

       

      Conclusion

      So I’ve given you a template solution that you can use without any modification to start creating your VS 2012 and VS 2013 compatible checkin policies; Just add new class files to the CheckinPolicies.VS2013 project, and then add them to the CheckinPolicies.VS2012 project as well as links.  By using links it allows you to only have to modify checkin policy files once, and have the changes go to both the 2012 and 2013 VSIX packages.  Hopefully this template solution helps you to get your TFS checkin policies up and running faster.

      Happy Coding!

      Saving And Loading A C# Object’s Data To An Xml, Json, Or Binary File

      March 14th, 2014 15 comments

      I love creating tools, particularly ones for myself and other developers to use.  A common situation that I run into is needing to save the user’s settings to a file so that I can load them up the next time the tool is ran.  I find that the easiest way to accomplish this is to create a Settings class to hold all of the user’s settings, and then use serialization to save and load the class instance to/from a file.  I mention a Settings class here, but you can use this technique to save any object (or list of objects) to a file.

      There are tons of different formats that you may want to save your object instances as, but the big three are Binary, XML, and Json.  Each of these formats have their pros and cons, which I won’t go into.  Below I present functions that can be used to save and load any object instance to / from a file, as well as the different aspects to be aware of when using each method.

      The follow code (without examples of how to use it) is also available here, and can be used directly from my NuGet Package.

       

      Writing and Reading an object to / from a Binary file

      • Writes and reads ALL object properties and variables to / from the file (i.e. public, protected, internal, and private).
      • The data saved to the file is not human readable, and thus cannot be edited outside of your application.
      • Have to decorate class (and all classes that it contains) with a [Serializable] attribute.
      • Use the [NonSerialized] attribute to exclude a variable from being written to the file; there is no way to prevent an auto-property from being serialized besides making it use a backing variable and putting the [NonSerialized] attribute on that.
      /// <summary>
      /// Functions for performing common binary Serialization operations.
      /// <para>All properties and variables will be serialized.</para>
      /// <para>Object type (and all child types) must be decorated with the [Serializable] attribute.</para>
      /// <para>To prevent a variable from being serialized, decorate it with the [NonSerialized] attribute; cannot be applied to properties.</para>
      /// </summary>
      public static class BinarySerialization
      {
      	/// <summary>
      	/// Writes the given object instance to a binary file.
      	/// <para>Object type (and all child types) must be decorated with the [Serializable] attribute.</para>
      	/// <para>To prevent a variable from being serialized, decorate it with the [NonSerialized] attribute; cannot be applied to properties.</para>
      	/// </summary>
      	/// <typeparam name="T">The type of object being written to the XML file.</typeparam>
      	/// <param name="filePath">The file path to write the object instance to.</param>
      	/// <param name="objectToWrite">The object instance to write to the XML file.</param>
      	/// <param name="append">If false the file will be overwritten if it already exists. If true the contents will be appended to the file.</param>
      	public static void WriteToBinaryFile<T>(string filePath, T objectToWrite, bool append = false)
      	{
      		using (Stream stream = File.Open(filePath, append ? FileMode.Append : FileMode.Create))
      		{
      			var binaryFormatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
      			binaryFormatter.Serialize(stream, objectToWrite);
      		}
      	}
      
      	/// <summary>
      	/// Reads an object instance from a binary file.
      	/// </summary>
      	/// <typeparam name="T">The type of object to read from the XML.</typeparam>
      	/// <param name="filePath">The file path to read the object instance from.</param>
      	/// <returns>Returns a new instance of the object read from the binary file.</returns>
      	public static T ReadFromBinaryFile<T>(string filePath)
      	{
      		using (Stream stream = File.Open(filePath, FileMode.Open))
      		{
      			var binaryFormatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
      			return (T)binaryFormatter.Deserialize(stream);
      		}
      	}
      }
      

       

      And here is an example of how to use it:

      [Serializable]
      public class Person
      {
      	public string Name { get; set; }
      	public int Age = 20;
      	public Address HomeAddress { get; set;}
      	private string _thisWillGetWrittenToTheFileToo = "even though it is a private variable.";
      
      	[NonSerialized]
      	public string ThisWillNotBeWrittenToTheFile = "because of the [NonSerialized] attribute.";
      }
      
      [Serializable]
      public class Address
      {
      	public string StreetAddress { get; set; }
      	public string City { get; set; }
      }
      
      // And then in some function.
      Person person = new Person() { Name = "Dan", Age = 30; HomeAddress = new Address() { StreetAddress = "123 My St", City = "Regina" }};
      List<Person> people = GetListOfPeople();
      BinarySerialization.WriteToBinaryFile<Person>("C:\person.bin", person);
      BinarySerialization.WriteToBinaryFile<List<People>>("C:\people.bin", people);
      
      // Then in some other function.
      Person person = BinarySerialization.ReadFromBinaryFile<Person>("C:\person.bin");
      List<Person> people = BinarySerialization.ReadFromBinaryFile<List<Person>>("C:\people.bin");
      

       

      Writing and Reading an object to / from an XML file (Using System.Xml.Serialization.XmlSerializer in the System.Xml assembly)

      • Only writes and reads the Public properties and variables to / from the file.
      • Classes to be serialized must contain a public parameterless constructor.
      • The data saved to the file is human readable, so it can easily be edited outside of your application.
      • Use the [XmlIgnore] attribute to exclude a public property or variable from being written to the file.
      /// <summary>
      /// Functions for performing common XML Serialization operations.
      /// <para>Only public properties and variables will be serialized.</para>
      /// <para>Use the [XmlIgnore] attribute to prevent a property/variable from being serialized.</para>
      /// <para>Object to be serialized must have a parameterless constructor.</para>
      /// </summary>
      public static class XmlSerialization
      {
      	/// <summary>
      	/// Writes the given object instance to an XML file.
      	/// <para>Only Public properties and variables will be written to the file. These can be any type though, even other classes.</para>
      	/// <para>If there are public properties/variables that you do not want written to the file, decorate them with the [XmlIgnore] attribute.</para>
      	/// <para>Object type must have a parameterless constructor.</para>
      	/// </summary>
      	/// <typeparam name="T">The type of object being written to the file.</typeparam>
      	/// <param name="filePath">The file path to write the object instance to.</param>
      	/// <param name="objectToWrite">The object instance to write to the file.</param>
      	/// <param name="append">If false the file will be overwritten if it already exists. If true the contents will be appended to the file.</param>
      	public static void WriteToXmlFile<T>(string filePath, T objectToWrite, bool append = false) where T : new()
      	{
      		TextWriter writer = null;
      		try
      		{
      			var serializer = new XmlSerializer(typeof(T));
      			writer = new StreamWriter(filePath, append);
      			serializer.Serialize(writer, objectToWrite);
      		}
      		finally
      		{
      			if (writer != null)
      				writer.Close();
      		}
      	}
      
      	/// <summary>
      	/// Reads an object instance from an XML file.
      	/// <para>Object type must have a parameterless constructor.</para>
      	/// </summary>
      	/// <typeparam name="T">The type of object to read from the file.</typeparam>
      	/// <param name="filePath">The file path to read the object instance from.</param>
      	/// <returns>Returns a new instance of the object read from the XML file.</returns>
      	public static T ReadFromXmlFile<T>(string filePath) where T : new()
      	{
      		TextReader reader = null;
      		try
      		{
      			var serializer = new XmlSerializer(typeof(T));
      			reader = new StreamReader(filePath);
      			return (T)serializer.Deserialize(reader);
      		}
      		finally
      		{
      			if (reader != null)
      				reader.Close();
      		}
      	}
      }
      

       

      And here is an example of how to use it:

      public class Person
      {
      	public string Name { get; set; }
      	public int Age = 20;
      	public Address HomeAddress { get; set;}
      	private string _thisWillNotGetWrittenToTheFile = "because it is not public.";
      
      	[XmlIgnore]
      	public string ThisWillNotBeWrittenToTheFile = "because of the [XmlIgnore] attribute.";
      }
      
      public class Address
      {
      	public string StreetAddress { get; set; }
      	public string City { get; set; }
      }
      
      // And then in some function.
      Person person = new Person() { Name = "Dan", Age = 30; HomeAddress = new Address() { StreetAddress = "123 My St", City = "Regina" }};
      List<Person> people = GetListOfPeople();
      XmlSerialization.WriteToXmlFile<Person>("C:\person.txt", person);
      XmlSerialization.WriteToXmlFile<List<People>>("C:\people.txt", people);
      
      // Then in some other function.
      Person person = XmlSerialization.ReadFromXmlFile<Person>("C:\person.txt");
      List<Person> people = XmlSerialization.ReadFromXmlFile<List<Person>>("C:\people.txt");
      

       

      Writing and Reading an object to / from a Json file (using the Newtonsoft.Json assembly in the Json.NET NuGet package)

      • Only writes and reads the Public properties and variables to / from the file.
      • Classes to be serialized must contain a public parameterless constructor.
      • The data saved to the file is human readable, so it can easily be edited outside of your application.
      • Use the [JsonIgnore] attribute to exclude a public property or variable from being written to the file.
      /// <summary>
      /// Functions for performing common Json Serialization operations.
      /// <para>Requires the Newtonsoft.Json assembly (Json.Net package in NuGet Gallery) to be referenced in your project.</para>
      /// <para>Only public properties and variables will be serialized.</para>
      /// <para>Use the [JsonIgnore] attribute to ignore specific public properties or variables.</para>
      /// <para>Object to be serialized must have a parameterless constructor.</para>
      /// </summary>
      public static class JsonSerialization
      {
      	/// <summary>
      	/// Writes the given object instance to a Json file.
      	/// <para>Object type must have a parameterless constructor.</para>
      	/// <para>Only Public properties and variables will be written to the file. These can be any type though, even other classes.</para>
      	/// <para>If there are public properties/variables that you do not want written to the file, decorate them with the [JsonIgnore] attribute.</para>
      	/// </summary>
      	/// <typeparam name="T">The type of object being written to the file.</typeparam>
      	/// <param name="filePath">The file path to write the object instance to.</param>
      	/// <param name="objectToWrite">The object instance to write to the file.</param>
      	/// <param name="append">If false the file will be overwritten if it already exists. If true the contents will be appended to the file.</param>
      	public static void WriteToJsonFile<T>(string filePath, T objectToWrite, bool append = false) where T : new()
      	{
      		TextWriter writer = null;
      		try
      		{
      			var contentsToWriteToFile = Newtonsoft.Json.JsonConvert.SerializeObject(objectToWrite);
      			writer = new StreamWriter(filePath, append);
      			writer.Write(contentsToWriteToFile);
      		}
      		finally
      		{
      			if (writer != null)
      				writer.Close();
      		}
      	}
      
      	/// <summary>
      	/// Reads an object instance from an Json file.
      	/// <para>Object type must have a parameterless constructor.</para>
      	/// </summary>
      	/// <typeparam name="T">The type of object to read from the file.</typeparam>
      	/// <param name="filePath">The file path to read the object instance from.</param>
      	/// <returns>Returns a new instance of the object read from the Json file.</returns>
      	public static T ReadFromJsonFile<T>(string filePath) where T : new()
      	{
      		TextReader reader = null;
      		try
      		{
      			reader = new StreamReader(filePath);
      			var fileContents = reader.ReadToEnd();
      			return Newtonsoft.Json.JsonConvert.DeserializeObject<T>(fileContents);
      		}
      		finally
      		{
      			if (reader != null)
      				reader.Close();
      		}
      	}
      }
      

      And here is an example of how to use it:

      public class Person
      {
      	public string Name { get; set; }
      	public int Age = 20;
      	public Address HomeAddress { get; set;}
      	private string _thisWillNotGetWrittenToTheFile = "because it is not public.";
      
      	[JsonIgnore]
      	public string ThisWillNotBeWrittenToTheFile = "because of the [JsonIgnore] attribute.";
      }
      
      public class Address
      {
      	public string StreetAddress { get; set; }
      	public string City { get; set; }
      }
      
      // And then in some function.
      Person person = new Person() { Name = "Dan", Age = 30; HomeAddress = new Address() { StreetAddress = "123 My St", City = "Regina" }};
      List<Person> people = GetListOfPeople();
      JsonSerialization.WriteToJsonFile<Person>("C:\person.txt", person);
      JsonSerialization.WriteToJsonFile<List<People>>("C:\people.txt", people);
      
      // Then in some other function.
      Person person = JsonSerialization.ReadFromJsonFile<Person>("C:\person.txt");
      List<Person> people = JsonSerialization.ReadFromJsonFile<List<Person>>("C:\people.txt");
      

       

      As you can see, the Json example is almost identical to the Xml example, with the exception of using the [JsonIgnore] attribute instead of [XmlIgnore].

       

      Writing and Reading an object to / from a Json file (using the JavaScriptSerializer in the System.Web.Extensions assembly)

      There are many Json serialization libraries out there.  I mentioned the Newtonsoft.Json one because it is very popular, and I am also mentioning this JavaScriptSerializer one because it is built into the .Net framework.  The catch with this one though is that it requires the Full .Net 4.0 framework, not just the .Net Framework 4.0 Client Profile.

      The caveats to be aware of are the same between the Newtonsoft.Json and JavaScriptSerializer libraries, except instead of using [JsonIgnore] you would use [ScriptIgnore].

      Be aware that the JavaScriptSerializer is in the System.Web.Extensions assembly, but in the System.Web.Script.Serialization namespace.  Here is the code from the Newtonsoft.Json code snippet that needs to be replaced in order to use the JavaScriptSerializer:

      // In WriteFromJsonFile<T>() function replace:
      var contentsToWriteToFile = Newtonsoft.Json.JsonConvert.SerializeObject(objectToWrite);
      // with:
      var contentsToWriteToFile = new System.Web.Script.Serialization.JavaScriptSerializer().Serialize(objectToWrite);
      
      // In ReadFromJsonFile<T>() function replace:
      return Newtonsoft.Json.JsonConvert.DeserializeObject<T>(fileContents);
      // with:
      return new System.Web.Script.Serialization.JavaScriptSerializer().Deserialize<T>(fileContents);
      

       

      Happy Coding!

      Categories: C#, Json, XML Tags: , , , , , , , , , , ,

      “Agent lost communication with Team Foundation Server” TFS Build Server Error

      March 12th, 2014 1 comment

      We had recently started getting lots of error messages similar to the following on our TFS Build Servers:

      Exception Message: The build failed because the build server that hosts build agent TFS-BuildController001 - Agent4 lost communication with Team Foundation Server. (type FaultException`1) 
      

      This error message would appear randomly; some builds would pass, others would fail, and when they did fail with this error message it was often at different parts in the build process.

      After a bit of digging I found this post and this one, which discussed different error messages around their build process failing with some sort of error around the build controller losing connection to the TFS server.  They talked about different fixes relating to DNS issues and load balancing, so we had our network team update our DNS records and flush the cache, but were still getting the same errors.

      We have several build controllers, and I noticed that the problem was only happening on two of the three, so our network team updated the hosts file on the two with the problem to match the entries in the one that was working fine, and boom, everything started working properly again 🙂

      So the problem was that the hosts file on those two build controller machines somehow got changed.

      The hosts file can typically be found at "C:\Windows\System32\Drivers\etc\hosts", and here is an example of what we now have in our hosts file for entries (just the two entries):

      12.345.67.89	TFS-Server.OurDomain.local
      12.345.67.89	TFS-Server
      

      If you too are running into this TFS Build Server error I hope this helps.