This week I will be one of the speakers at BASTA On Tour in Munich. One of the topics I am going to speak about is the Managed Extensibility Framework (MEF). In this blog post I want to share my slides and summarize the hands-on labs that I am going to go through with the participants.
Hands-On Lab 1: StyleCop Documentation Rules
Prerequisites:
Lab step by step description:
- Create a class library project StyleCopDemo.
- Add the following class to the newly created project:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace styleCopDemo
{
public class utils_Bucket<T>
{
public utils_Bucket()
{
}
public utils_Bucket(IEnumerable<Tuple<string, T>> data)
{
foreach (var item in data) {
this.data[item.Item1] = item.Item2;
}
}
private Dictionary<string, T> data = new Dictionary<string, T>();
public T this[string index]
{
get
{
if (data.ContainsKey(index))
{
return data[index];
}
else
return default(T);
}
}
public int GetLength()
{
return this.data.Keys.Count;
}
private string Dump()
{
return this.data.Aggregate<KeyValuePair<string, T>, StringBuilder>(
new StringBuilder(),
(agg, item) =>
{
if (agg.Length > 0)
{
agg.Append(", ");
}
agg.AppendFormat("{0}: {1}", item.Key, item.Value.ToString());
return agg;
}).ToString();
}
public override string ToString()
{
return this.Dump();
}
}
}
- Launch StyleCop Settings (right-click on project, select StyleCop settings).
- Enable all rules except Spacing Rules / SA1027
- Build your project and make sure that there are no errors and no warning.
- Run StyleCop on your project (right-click on project, select Run StyleCop). As you can see StyleCop generated a bunch of warnings.
- Correct all warnings appropriately.
- After that your file should look similar to the following implementation:
//-------------------------------------------------------
// <copyright file="Bucket.cs" company="Contoso Ltd.">
// Copyright (c) Contoso Ltd. All rights reserved.
// </copyright>
//-------------------------------------------------------
namespace StyleCopDemo
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
/// <summary>
/// Implements a bucket
/// </summary>
/// <typeparam name="T">Type of the elements in the bucket</typeparam>
public class Bucket<T>
{
/// <summary>
/// Internal helper to store data
/// </summary>
private Dictionary<string, T> data = new Dictionary<string, T>();
/// <summary>
/// Initializes a new instance of the Bucket class.
/// </summary>
public Bucket()
{
}
/// <summary>
/// Initializes a new instance of the Bucket class.
/// </summary>
/// <param name="data">The initial data.</param>
public Bucket(IEnumerable<Tuple<string, T>> data)
{
foreach (var item in data)
{
this.data[item.Item1] = item.Item2;
}
}
/// <summary>
/// Gets the element at the specified index.
/// </summary>
/// <param name="index">Index of the element to get.</param>
/// <value>Element at the specified index.</value>
public T this[string index]
{
get
{
if (this.data.ContainsKey(index))
{
return this.data[index];
}
else
{
return default(T);
}
}
}
/// <summary>
/// Gets the length.
/// </summary>
/// <returns>Length of the dictionary</returns>
public int GetLength()
{
return this.data.Keys.Count;
}
/// <summary>
/// Returns a <see cref="System.String"/> that represents this instance.
/// </summary>
/// <returns>
/// A <see cref="System.String"/> that represents this instance.
/// </returns>
public override string ToString()
{
return this.Dump();
}
/// <summary>
/// Dumps this instance.
/// </summary>
/// <returns>String representation of the dictionary</returns>
private string Dump()
{
return this.data.Aggregate<KeyValuePair<string, T>, StringBuilder>(
new StringBuilder(),
(agg, item) =>
{
if (agg.Length > 0)
{
agg.Append(", ");
}
agg.AppendFormat("{0}: {1}", item.Key, item.Value.ToString());
return agg;
}).ToString();
}
}
}
Hands-On Lab 2: StyleCop Build Integration
Prerequisites:
- Visual Studio 2010
- Download and install the latest version of the StyleCop from
http://stylecop.codeplex.com/
- Make sure that you have selected MSBuild integration files when installing StyleCop
- Complete Hands-On Lab 1 (see above)
Lab step by step description:
- Open the resulting solution from Hands-On Lab 1 (StyleCopDemo).
- Unload the StyleCopDemo project (right-click on project in Solution Explorer, select Unload Project).
- Edit StyleCopDemo project (right-click on project in Solution Explorer, select Edit StyleCopDemo.csproj).
- Scroll to the end of the file. Find the line <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />.
- Immediately after that line add the following line:
<Import Project="$(ProgramFiles)\MSBuild\Microsoft\StyleCop\v4.4\Microsoft.StyleCop.targets" />
- Reload the StyleCopDemo project (right-click on project in Solution Explorer, select Reload Project).
- Build your project to see that there are no errors and warnings.
- Break a StyleCop rule (e.g. remove the documentation of a method).
- Build your project.
- StyleCop should have run automatically, you should see an appropriate warning.
- Unload the StyleCopDemo project (right-click on project in Solution Explorer, select Unload Project).
- Edit StyleCopDemo project (right-click on project in Solution Explorer, select Edit StyleCopDemo.csproj).
- Find the first PropertyGroup in the project file and add the following setting:
<StyleCopTreatErrorsAsWarnings>false</StyleCopTreatErrorsAsWarnings>
- Reload the StyleCopDemo project (right-click on project in Solution Explorer, select Reload Project).
- Build your project.
- The StyleCop warning should now be an error.
- Suppress the warning/error using the SuppressMessage attribute:
[SuppressMessage("Microsoft.StyleCop.CSharp.DocumentationRules", "SA1600:ElementsMustBeDocumented",
Justification = "No time to write documentation...")]
public class Bucket<T>
{
...
}
Hands-On Lab 3: Code Analysis
Prerequisites:
- Visual Studio 2010
- Complete Hands-On Lab 1 (see above)
Lab step by step description:
- Add a new class library project to solution from Hands-On Lab 1 (StyleCopDemo).
- Add the following class to the newly created project:
namespace CodeAnalysisDemo
{
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.IO;
public interface ISwiftItem<T>
{
string ItemName { get; }
List<T> Values { get; }
}
public interface INamedItem
{
string ItemName { get; }
}
public class SwiftItem<T> : ISwiftItem<T>
{
public string ItemName
{
get;
set;
}
public List<T> Values
{
get;
set;
}
}
public class SwiftFile
{
private Stream underlying_File;
private string fileName;
private object header;
public SwiftFile(string fileName)
{
this.underlying_File = new FileStream(fileName, FileMode.Open);
this.fileName = fileName;
this.header = new object();
}
public List<Tuple<string, object>> Settings
{
get;
set;
}
public void AddObject<T>(SwiftItem<T> newObj)
{
lock (this.header)
{
var header = string.Format("{0}: {1}", newObj.ItemName, newObj.Values.Count);
foreach (var item in newObj.Values)
{
if (item is INamedItem)
{
var namedItem = item as INamedItem;
// do something special with namedItem
}
}
// do something with newObj
}
}
public void CopyFile(string source_file_name)
{
using (var reader = new StreamReader(new FileStream(source_file_name, FileMode.Open)))
{
// TODO: copy file here
}
}
public void WriteToDatabase(SqlConnection conn, string tenant)
{
var cmd = conn.CreateCommand();
cmd.CommandText = string.Format("INSERT INTO Target ( Tenant, Data ) VALUES ( {0}, @Data )", tenant);
// Build rest of the command and execute it
}
public void Close()
{
try
{
this.underlying_File.Close();
}
catch
{
Console.WriteLine("Error");
}
}
}
}
- Build your project and make sure that there are no error and no warnings.
- Enable
Code Analysis for the project and select rule set
Microsoft All Rules.
- You find the necessary options in the project's property window.
- Build your project. As you can see code analysis shows you a bunch of warnings.
- Try to correct all warnings appropriately. If you are not sure what a certain warning means or why it is important check the rule documentation in MSDN: http://msdn.microsoft.com/en-us/library/ee1hzekz.aspx
- After that your file should look similar to the following implementation:
using System;
[assembly: CLSCompliant(true)]
namespace CodeAnalysisDemo
{
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Data;
using System.Data.SqlClient;
using System.Diagnostics.CodeAnalysis;
using System.IO;
public interface ISwiftItem<T>
{
string ItemName { get; }
IEnumerable<T> Values { get; }
}
public interface INamedItem
{
string ItemName { get; }
}
public class SwiftItem<T> : ISwiftItem<T>
{
public string ItemName
{
get;
set;
}
public IEnumerable<T> Values
{
get;
set;
}
}
public class Setting
{
public string SettingName { get; set; }
public object SettingValue { get; set; }
}
public class SettingCollection : Collection<Setting>
{
}
public class SwiftFile : IDisposable
{
private Stream underlying_File;
private object header;
private bool disposed;
public SwiftFile(string fileName)
{
this.underlying_File = new FileStream(fileName, FileMode.Open);
this.header = new object();
this.Settings = new SettingCollection();
}
public SettingCollection Settings
{
get;
private set;
}
public void AddObject<T>(SwiftItem<T> newObj)
{
if (newObj != null)
{
lock (this.header)
{
foreach (var item in newObj.Values)
{
var namedItem = item as INamedItem;
if (namedItem != null)
{
// do something special with namedItem
}
}
// do something with newObj
}
}
}
[SuppressMessage("Microsoft.Performance", "CA1822", Justification = "Will reference 'this' later")]
public void CopyFile(string sourceFileName)
{
var stream = new FileStream(sourceFileName, FileMode.Open);
StreamReader reader;
try
{
reader = new StreamReader(stream);
}
catch
{
stream.Dispose();
throw;
}
try
{
// do something with reader
}
finally
{
reader.Dispose();
}
}
[SuppressMessage("Microsoft.Performance", "CA1822", Justification = "Will reference 'this' later")]
public void WriteToDatabase(SqlConnection conn, string tenant)
{
if (conn == null || (conn.State & ConnectionState.Open) == 0)
{
throw new ArgumentException("conn must not be null and must be open");
}
var cmd = conn.CreateCommand();
cmd.CommandText = "INSERT INTO Target ( Tenant, Data ) VALUES ( @Tenant, @Data )";
cmd.Parameters.Add("@Tenant", System.Data.SqlDbType.NVarChar, 100).Value = tenant;
// Build rest of the command and execute it
}
public void Close()
{
try
{
this.underlying_File.Close();
}
catch
{
Console.WriteLine("Error");
throw;
}
}
private void Dispose(bool disposing)
{
if (!this.disposed)
{
if (disposing)
{
this.underlying_File.Dispose();
}
disposed = true;
}
}
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
~SwiftFile()
{
Dispose(false);
}
}
}
comments powered by