Tuesday, November 4, 2008

Using dictionaries for constant Identifiers

I wanted to have a constant string so that I could have a list of roles, however I also wanted to have a friendly description, so how do you go about this? The reason for using a constant string is so that we are consistent when we are checking our roles. e.g.
if (_user.IsInRole(Role.Practitioner))
{
. . .
}

But what happens when you want to get a friendly description too? Well firstly I wanted to be able to put all the roles I had defined into a drop down list, so it would be nice if all the roles were defined in one place. So I wanted to be able to do this

// Put the roles in the role dropdownlist
RoleDropDownList.DataSource = Role.Roles;
RoleDropDownList.DataTextField = "Value";
RoleDropDownList.DataValueField = "Key";
RoleDropDownList.DataBind();
RoleDropDownList.Items.Insert(0, new ListItem("All", ""));

So how do we do it? Simply use a dictionary. Have the key be the constant string and the value a friendly description. Now the important point to note here is that we could be tempted to use StringDictionary as it is a specialization of a Dictionary, but if you look carefully in the documentation, the key will be converted to a lower case string and that *is not what we want*.

/// <summary>
///
These are the membership roles that users can have
/// </summary>
public static class Role
{
/// <summary>A basic user (frozen user)</summary>
public const string Basic = "Basic";

/// <summary>A standard practitioner</summary>
public const string Practitioner = "Practitioner";

/// <summary>An IP administrator</summary>
public const string Administrator = "Administrator";

/// <summary>A training manager</summary>
public const string TrainingManager = "TrainingManager";

/// <summary>A submissions manager</summary>
public const string SubmissionManager = "SubmissionManager";

/// <summary>A submission assessor</summary>
public const string Assessor = "Assessor";

public const string Mentor = "Mentor";

/// <summary>
///
Create a friendly description of each role
/// </summary>
public static Dictionary<String, String> Roles
= new Dictionary<String, String>()
{
{ Basic, "Frozen User" },
{ Practitioner, "Practitioner" },
{ Administrator, "Administrator" },
{ TrainingManager, "Training Manager" },
{ SubmissionManager, "Submission Manager" },
{ Assessor, "Assessor"},
{ Mentor, "Mentor"}
};
}

Wednesday, September 10, 2008

Enum Parsing

I was getting so frustrated with writing this
ViewMode viewType = (ViewMode)Enum.Parse(typeof(ViewMode), value);

all the time, so I created a class so I can do

ViewMode viewType = EnumParser.Parse<ViewMode>(value);

but the came along extension methods and now we can then do

ViewMode viewType = value.ParseEnum<ViewMode>();

then lastly wouldn't it be cool to have the default value if you can't parse it?

ViewMode viewType = value.GetEnumOrDefault<ViewMode>(ViewMode.None);

so here is the class. There is also some goodies so that you can decorate your enum's with attributes and then GetDescription to have a friendly version for them!

/// <summary>
///
Helpful helper class for all things with enumerations
/// </summary>
public static class EnumParser
{
/// <summary>
///
Parses the specified enum value - ignoring case.
/// </summary>
/// <param name="enumValue">
The enum value.</param>
/// <returns></returns>
[SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter")]
public static T Parse<T>(string enumValue)
{
return Parse<T>(enumValue, true);
}

/// <summary>
///
Parses the specified enum value.
/// </summary>
/// <param name="enumValue">
The enum value.</param>
/// <param name="ignoreCase">
if set to <c>true</c> [ignore case].</param>
/// <returns></returns>
[SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter")]
public static T Parse<T>(string enumValue, bool ignoreCase)
{
if (string.IsNullOrEmpty(enumValue))
throw new ArgumentNullException("enumValue", CoreResources.NullValue);

enumValue = enumValue.Trim();

Type t = typeof(T);

if (!t.IsEnum)
throw new ArgumentException(CoreResources.InvalidType, "enumValue");

return (T)Enum.Parse(t, enumValue, ignoreCase);
}

/// <summary>
///
Parses the enum.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="value">
The value.</param>
/// <returns></returns>
[SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter")]
public static T ParseEnum<T>(this string value)
{
if (string.IsNullOrEmpty(value))
throw new ArgumentNullException("value", CoreResources.NullValue);

return ParseEnum<T>(value, false);
}

/// <summary>
///
Parses the enum.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="value">
The value.</param>
/// <param name="ignoreCase">
if set to <c>true</c> [ignore case].</param>
/// <returns></returns>
[SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter")]
public static T ParseEnum<T>(this string value, bool ignoreCase)
{
if (string.IsNullOrEmpty(value))
throw new ArgumentNullException("value", CoreResources.NullValue);

return Parse<T>(value, ignoreCase);
}

/// <summary>
///
Gets the enum or default.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="value">
The value.</param>
/// <param name="defaultValue">
The default value.</param>
/// <returns></returns>
[SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter")]
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes",
Justification = "Never throw the exception in this case")]
public static T GetEnumOrDefault<T>(this string value, T defaultValue)
{
if (string.IsNullOrEmpty(value))
return defaultValue;

try
{
return Parse<T>(value, true);
}
catch
{
return defaultValue;
}
}

/// <summary>
///
Tries to parse the enum. Case insensitive. Returns true/false for errors (or return InvalidArgumentException?)
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="value">
The value.</param>
/// <param name="enumInstance">
The enum instance.</param>
/// <returns></returns>
[SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter")]
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes",
Justification = "Never throw the exception in this case")]
public static bool TryParseEnum<T>(this string value, out T enumInstance)
{
try
{
enumInstance = Parse<T>(value, true);
return true;
}
catch
{
enumInstance = default(T);
return false;
}
}


/// <summary>
///
Gets the <see cref="DescriptionAttribute" /> of an <see cref="Enum" />
///
type value.
/// </summary>
/// <param name="value">
The <see cref="Enum" /> type value.</param>
/// <returns>
A string containing the text of the
/// <see cref="DescriptionAttribute"/>.</returns>
public static string GetDescription(this Enum value)
{
if (value == null)
throw new ArgumentNullException("value", CoreResources.NullValue);

string description = value.ToString();
FieldInfo fieldInfo = value.GetType().GetField(description);
EnumDescriptionAttribute[] attributes =
(EnumDescriptionAttribute[])
fieldInfo.GetCustomAttributes(typeof(EnumDescriptionAttribute), false);

if (attributes != null && attributes.Length > 0)
{
description = attributes[0].Description;
}
return description;
}

/// <summary>
///
Converts the <see cref="Enum" /> type to an <see cref="IList" />
///
compatible object.
/// </summary>
/// <param name="type">
The <see cref="Enum"/> type.</param>
/// <returns>
An <see cref="IList"/> containing the enumerated
/// type value and description.</returns>
public static IList ToList(this Type type)
{
if (type == null)
throw new ArgumentNullException("type", CoreResources.InvalidType);

ArrayList list = new ArrayList();
Array enumValues = Enum.GetValues(type);

foreach (Enum value in enumValues)
{
list.Add(new KeyValuePair<Enum, string>(value, GetDescription(value)));
}

return list;
}
}




Wednesday, July 30, 2008

AfterBuild copy task

Have you ever had to get a licence (.lic) file copied to your bin directory as part of you build?

Well annoyingly enough you don't want it to be marked as "Content" and you cant compile it, so I placed the following line into my .csproj file quite near the bottom. I have included the targets above so you can see where I placed it in bold.

<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
<PreBuildEvent>
</PreBuildEvent>
<PostBuildEvent>
</PostBuildEvent>
</PropertyGroup>
<Target Name="AfterBuild">
<CreateItem Include="$(ProjectDir)ThirdPartyLibraries\PowerUp.Web.UI.WebTree.lic">
<Output TaskParameter="Include" ItemName="LicenceFile" />
</CreateItem>
<Copy SourceFiles="@(LicenceFile)" DestinationFolder="$(OutputPath)" ContinueOnError="false" />
</Target>


I created a target on the fly using the CreateItem step which also allows me to extend it later if I want to place more items inside it, then just referenced that item and copied it

Wednesday, June 18, 2008

Work Item Templates

My findings on creating and customising your own Work Item Templates in TFS 2008

First off - here is some background reading and resources

Tools to capture “Defects”
http://www.techsmith.com/snagit/accessories/teamsystem.asp
http://ozgrant.com/2006/06/17/tfsbugsnapper/

Extending TFS Templates using TFS Power Tools
http://msdn.microsoft.com/en-us/tfs2008/bb980963.aspx
http://msdn.microsoft.com/en-us/library/ms400654(VS.80).aspx
http://blogs.msdn.com/narend/archive/2007/07/31/nice-trick-to-enforce-certain-rules-on-area-path-and-iteration-path.aspx

Enhancing VSTS – Setting up TFS groups and changing the Bug Work Item Template
http://visualstudiomagazine.com/columns/article.aspx?editorialsid=2046

Differences between WIWA and TSWA
http://www.microsoft.com/downloads/details.aspx?FamilyId=FAED8359-F54D-480E-8A86-F154D3DEA07E&displaylang=en
http://blogs.msdn.com/buckh/archive/2008/04/09/team-system-web-access-2008-sp1-ctp-and-work-item-web-access-2008-ctp-are-now-available.aspx

After I had created my own custom "Incident" my boss didn't want to use Bug as then he couldn't charge for it! I then wanted to delete the templates we don't use so use the following command

C:\Program Files\Microsoft Team Foundation Server 2008 Power Tools>
tfpt destroywitd