Getting the Windows Product ID using WMI

by Alex Meyer-Gleaves 21 September 2010 - 7:29 PM

I needed to get the Windows Product ID from managed code recently and decided that using WMI would be the most reliable and stable way to get the job done. Do not follow any examples that show you how to get this information from the registry. You simply cannot rely on those registry keys being present in the many different versions of Windows. There is also no guarantee that if they are present, that this will continue to be the case into the future. Using an official and supported API means that maintaining compatibility is Microsoft’s problem and not yours.

The WMI class you want is Win32_OperatingSystem and the property is called SerialNumber. The relevant documentation can be found on MSDN.

SerialNumber
Data type: string
Access type: Read-only
Operating system product serial identification number.

Example: "10497-OEM-0031416-71674"

To query the property value you will need to add a reference to System.Management and create an instance of ManagementObjectSearcher with the appropriate query. You can then use LINQ to extract the SerialNumber property value.

const string queryString = "SELECT SerialNumber FROM Win32_OperatingSystem";

string productId = (from ManagementObject managementObject in new ManagementObjectSearcher(queryString).Get()
                    from PropertyData propertyData in managementObject.Properties
                    where propertyData.Name == "SerialNumber"
                    select (string)propertyData.Value).FirstOrDefault();

Console.WriteLine(productId ?? "What!? WMI is broken!");

That will give you the result you are after and will work on any version of Windows that you are likely to be using. I have successfully tested the code on the following operating systems.

  • Windows XP SP3 (32-bit)
  • Windows Vista Ultimate SP2 (64-bit)
  • Windows 7 Ultimate (64-bit)
  • Windows Server 2008 R2 (64-bit)

Tags: , ,

Microsoft .NET

CodeDomProvider and Compiler Warning Levels

by Alex Meyer-Gleaves 1 September 2010 - 6:43 PM

When generating code using the CodeDomProvider you may noticed that by default you do not get the same warning messages in the CompilerResults that you receive when compiling in Visual Studio. For example, lets take the simple code below that has a static method with two unused variables.

public class Test
{
    static void testMethod()
    {
        int a;
        int b = 1;
    }
}

The code above when compiled in Visual Studio will result in two warning messages appearing in the Error List window.

Warnings in Error List

On the CodeDOM side we can confirm with a simple unit test that the default value for CompilerParameters.WarningLevel does not cause the two warnings to be returned when the same code is compiled with the CodeDomProvider.

[Test]
public void DefaultWarningLevel()
{
    StringBuilder snippet = new StringBuilder();
    snippet.AppendLine("public class Test");
    snippet.AppendLine("{");
    snippet.AppendLine("    static void testMethod()");
    snippet.AppendLine("    {");
    snippet.AppendLine("        int a;");
    snippet.AppendLine("        int b = 1;");
    snippet.AppendLine("    }");
    snippet.AppendLine("}");

    CodeDomProvider provider = CodeDomProvider.CreateProvider("C#");
    CompilerParameters parameters = new CompilerParameters {GenerateInMemory = true};

    CompilerResults results = provider.CompileAssemblyFromSource(parameters, snippet.ToString());
    Assert.That(results.Errors.Cast<CompilerError>().Any(r => r.IsWarning), Is.False);
}

If you check the Build tab in the Properties window for a new project in Visual Studio you can see that the default warning level is 4.

Default Warning Level

The MSDN documentation tells us that the valid warning levels are 0 through 4.

  • 0 - Turns off emission of all warning messages.
  • 1 - Displays severe warning messages.
  • 2 - Displays level 1 warnings plus certain, less-severe warnings, such as warnings about hiding class members.
  • 3 - Displays level 2 warnings plus certain, less-severe warnings, such as warnings about expressions that always evaluate to true or false.
  • 4 - Displays all level 3 warnings plus informational warnings. This is the default warning level at the command line.

Another simple unit test shows that setting CompilerParameters.WarningLevel to 4 will result in the same warnings as Visual Studio being returned.

[Test]
public void WarningLevelFour()
{
    StringBuilder snippet = new StringBuilder();
    snippet.AppendLine("public class Test");
    snippet.AppendLine("{");
    snippet.AppendLine("    static void testMethod()");
    snippet.AppendLine("    {");
    snippet.AppendLine("        int a;");
    snippet.AppendLine("        int b = 1;");
    snippet.AppendLine("    }");
    snippet.AppendLine("}");

    CodeDomProvider provider = CodeDomProvider.CreateProvider("C#");
    CompilerParameters parameters = new CompilerParameters
    {
        GenerateInMemory = true,
        WarningLevel = 4
    };

    CompilerResults results = provider.CompileAssemblyFromSource(parameters, snippet.ToString());
    Assert.That(results.Errors.Cast<CompilerError>().Count(r => r.IsWarning), Is.EqualTo(2));
    Assert.That(results.Errors[0].ErrorText, Is.StringContaining("The variable 'a' is declared but never used"));
    Assert.That(results.Errors[1].ErrorText, Is.StringContaining("The variable 'b' is assigned but its value is never used"));
}

We now have the same warnings that Visual Studio provides, even though the documentation for CompilerParameters.WarningLevel appears to be a little misleading.

Gets or sets the warning level at which the compiler aborts compilation.

The compilation will not be aborted for a warning unless the /warnaserror option is provided to the compiler. When compiling with the CodeDOM you use CompilerParameters.TreatWarningsAsErrors to abort the compilation for warnings. Needless to say Visual Studio has the same option available on the Build tab in the project Properties window.

Tags: ,

Microsoft .NET

Attaching the Debugger only in Debug

by Alex Meyer-Gleaves 16 June 2010 - 6:41 PM

I noticed an article on the Infinite Codex blog that demonstrates how to debug CLR Stored Procedures. The example uses a #if preprocessor directive to compile the debugging code only if the DEBUG symbol is defined. Personally, I find using the #if directive makes your code look rather ugly, and accidentally including code inside the directive that you did not intend to becomes a real possibility. My preferred solution is to use the ConditionalAttribute to refactor such code out into a separate method.

Applying ConditionalAttribute to a method indicates to compilers that a call to the method should not be compiled into Microsoft intermediate language (MSIL) unless the conditional compilation symbol that is associated withConditionalAttribute is defined.

Using this approach you can create a helper class that includes methods that should only be called when the DEBUG symbol is defined. When you compile with the RELEASE symbol defined all calls to the methods will simply be excluded from the generated MSIL.

public static class Debugging
{
    [Conditional("DEBUG")]
    public static void Break()
    {
        Debugger.Break();
    }
}

If you run the code below in Debug mode with the debugger attached, a breakpoint will occur immediately after the first Console.WriteLine method call.

class Program
{
    static void Main()
    {
        Console.WriteLine("Before the Debugging.Break() method.");
        Debugging.Break();
        Console.WriteLine("After the Debugging.Break() method.");
        Console.ReadLine();
    }
}

When the debugger is not attached and you compiled in Debug mode, the Visual Studio Just-In-Time Debugger dialog is presented for you to selected a debugger to attach with.

Visual Studio Just-In-Time Debugger Dialog

If you run in Release mode no breakpoint will be hit when the debugger is attached, and the Visual Studio Just-In-Time Debugger dialog will not be presented when you run without the debugger attached.

Tags: ,

Development Tools | Microsoft .NET

Framework Design Guidelines Second Edition

by Alex Meyer-Gleaves 6 April 2010 - 5:33 PM

Framework Design Guidelines Second Edition I have just finished reading Framework Design Guidelines Second Edition and must say that I enjoyed the read. The first edition of this book was good enough to find a place on my shelf at the office and the second edition expands and improves upon the first. The many annotations dispersed throughout the book by various experts provide some interesting insight into the challenges they have faced. Best practice is not something that is immediately apparent, and often comes from having made mistakes and seeing how things pan out in the real world. It is very refreshing to see people like Brad Abrams and Krzysztof Cwalina drawing attention to the mistakes they made in designing the .NET Framework in order to help others avoid similar issues.

Brad Abrams: We added a set of controls to ASP.NET late in the ship cycle for V1.0 of the .NET Framework that rendered for mobile devices. Because these controls came from a team in a different division, our immediate reaction was to put them in a different namespace (System.Web.MobileControls). Then, after a couple of reorganizations and .NET Framework versions, we realized a better engineering trade-off was to fold that functionality into the existing controls in System.Web.Controls. In retrospect, we let internal organizational differences affect the public exposure of the APIs, and we came to regret that later. Avoid this type of mistake in your designs.

You can now see in the documentation that every type in the System.Web.UI.MobileControls namespace has been marked as obsolete. Working on a large product with a substantial investment in the underlying framework I can certainly appreciate that you will make mistakes despite your best efforts. Creating guidance that reflects what you have learned from past issues is a great way of limiting the accumulation of future technical debt. When the code you write represents a public API you need to very carefully consider each of your decisions. I too have a laundry list of things that I wish had been done differently, and I think anyone who has worked on a product or framework of a substantial size would as well. It is important to not just dismiss your mistakes, but to highlight them along with a means of avoiding in them in the future, and make sure they everyone on your team can learn from them.

Tags:

Microsoft .NET

Registering open generic types in Autofac 1.4

by Alex Meyer-Gleaves 3 January 2010 - 5:34 PM

UPDATE (5 January 2010): This feature has now been added to the Autofac 1.4 codebase. I had intended to get this one directly into the codebase but Nick and I got our wires crossed, and I ended up posting it as an extension instead. Regardless, this post remains a valid example of extending Autofac 1.4. The RegisterClosedTypesOf method will appear on the ContainerBuilder in the next 1.4 maintenance release. Until then you can use the extension below to register your open generic types.

This is a follow up to my recent post about writing an extension for registering open generic interface types in Autofac 2. The feature described in the first post, along with support for open generic classes, has since been added to the Autofac 2 codebase. You can grab the current 2.1 preview release on the download page and test it out.

I personally feel that the latest preview version of Autofac 2 is stable enough to start using, but I know that even when released not everyone will be able to adopt the new version as soon as they would like. For that reason I have decided to write a similar extension for Autofac 1.4 that supports both open generic interfaces and classes.

Before I move onto the code I would like to draw a distinction between this feature and the RegisterGeneric method found on instances of the the ContainerBuilder class in Autofac 1.4. The RegisterGeneric method allows you to register an open generic type and have a closed generic type created for you when requested. For example, registering List<> and then resolving List<string> will cause Autofac to create a new List<string> instance for you. The difference is that the RegisterGeneric feature does not locate existing types that close the open generic type being registered.

In the unit test below we are ensuring that a closing type is provided for an open generic interface. It is similar to that from the original post, except the extension method is called RegisterClosedTypesOf and extends ContainerBuilder instances instead of RegistrationBuilder instances. The extension method also has a parameter for the assembly that will be scanned to find the closing types. This is different from the Autofac 2 implementation were the assembly is provided to the RegisterAssemblyTypes method, and the containing types are filtered using a delegate provided to the RegistrationBuilder.

[Test]
public void RegisterClosedTypesOf_OpenGenericInterfaceTypeProvided_ClosingGenericTypesRegistered()
{
    ContainerBuilder builder = new ContainerBuilder();
    Assembly assembly = typeof(ICommand<>).Assembly;
    builder.RegisterClosedTypesOf(typeof(ICommand<>), assembly);
    IContainer container = builder.Build();

    Assert.That(container.Resolve<ICommand<SaveCommandData>>(), Is.InstanceOf<SaveCommand>());
    Assert.That(container.Resolve<ICommand<DeleteCommandData>>(), Is.InstanceOf<DeleteCommand>());
}

In addition to the types I used for unit testing in the previous post, there are two new types used for the testing of open generic classes.

/// <summary>
/// An abstract open generic base class.
/// </summary>
public abstract class Message<T>
{
}

/// <summary>
/// A class that closed the open generic type.
/// </summary>
public class StringMessage : Message<string>
{
}

The new message types are used in the next unit test to make sure that support for open generic classes is working.

[Test]
public void RegisterClosedTypesOf_OpenGenericAbstractClassTypeProvided_ClosingGenericTypesRegistered()
{
    ContainerBuilder builder = new ContainerBuilder();
    Assembly assembly = typeof(Message<>).Assembly;
    builder.RegisterClosedTypesOf(typeof(Message<>), assembly);
    IContainer container = builder.Build();

    Assert.That(container.Resolve<Message<string>>(), Is.InstanceOf<StringMessage>());
}

Now that we know how the extension method is used we can move onto the implementation.

/// <summary>
/// Extension methods for the <see cref="ContainerBuilder"/> class.
/// </summary>
public static class ContainerBuilderExtensions
{
    /// <summary>
    /// Scans the types in an assembly and registers those that support any base or interface that closes the 
    /// provided open generic service type.
    /// </summary>
    /// <param name="builder">The container builder being extended.</param>
    /// <param name="openGenericServiceType">The open generic interface or base class type for which implementations will be found.</param>
    /// <param name="assembly">The assembly to scan for the matching types.</param>
    public static void RegisterClosedTypesOf(this ContainerBuilder builder, Type openGenericServiceType, Assembly assembly)
    {
        if (openGenericServiceType == null) throw new ArgumentNullException("openGenericServiceType");

        if (!(openGenericServiceType.IsGenericTypeDefinition || openGenericServiceType.ContainsGenericParameters))
        {
            throw new ArgumentException(
                string.Format("The type '{0}' is not an open generic class or interface type.",
                              openGenericServiceType.FullName));
        }

        foreach (Type candidateType in assembly.GetTypes())
        {
            Type closedServiceType;
            if (findAssignableTypeThatCloses(candidateType, openGenericServiceType, out closedServiceType))
            {
                builder.Register(candidateType).As(closedServiceType);
            }
        }
    }

    /// <summary>
    /// Looks for an interface on the candidate type that closes the provided open generic interface type.
    /// </summary>
    /// <param name="candidateType">The type that is being checked for the interface.</param>
    /// <param name="openGenericServiceType">The open generic service type to locate.</param>
    /// <param name="closedServiceType">The type of the closed service if found.</param>
    /// <returns>True if a closed implementation was found; otherwise false.</returns>
    private static bool findAssignableTypeThatCloses(Type candidateType, Type openGenericServiceType, out Type closedServiceType)
    {
        closedServiceType = null;

        if (candidateType.IsAbstract) return false;

        foreach (Type interfaceType in getTypesAssignableFrom(candidateType))
        {
            if (interfaceType.IsGenericType && interfaceType.GetGenericTypeDefinition() == openGenericServiceType)
            {
                closedServiceType = interfaceType;
                return true;
            }
        }

        return false;
    }

    /// <summary>
    /// Returns the interface and base types that given a type is assignable from.
    /// </summary>
    /// <param name="candidateType">The type to find assignable types for.</param>
    /// <returns>A list of the assignable interface and base types.</returns>
    private static IEnumerable<Type> getTypesAssignableFrom(Type candidateType)
    {
        foreach (Type interfaceType in candidateType.GetInterfaces())
        {
            yield return interfaceType;
        }

        Type nextType = candidateType;
        while (nextType != typeof(object))
        {
            yield return nextType;
            nextType = nextType.BaseType;
        }
    }
}

After the usual sort of argument checking the types in the assembly are enumerated and tested to see if they close the open generic type. The findAssignableTypeThatCloses method does the work of locating a possible match and returns a value indicating if a match was found. When a match is found the out parameter is assigned the closing type that was located, and the registration is added to the ContainerBuilder. The getTypesAssignableFrom method helps out by returning all the interface and base types assignable from the type it is provided.

That is all that is needed to add support for open generic types in Autofac 1.4.

ContainerBuilderExtensions.cs (3.71 kb)

ContainerBuilderExtensionsTests.cs (3.93 kb)

Tags:

Garage Sale Code | Microsoft .NET | Autofac

Is64BitOperatingSystem and Is64BitProcess in .NET 4.0

by Alex Meyer-Gleaves 21 December 2009 - 5:07 PM

The Environment class in .NET 4.0 contains two new static properties for checking bitness: Is64BitOperatingSystem and Is64BitProcess. In the past I have had to answer these questions in code and wondered how the new properties ended up being implemented. It turns out that the code is actually quite elegant and simple to follow. They have taken advantage of the fact that there are two versions of the mscorlib.dll assembly: one for the x86 version of the framework and another for the x64 version.

The code in the 64-bit version of mscorlib.dll is really simple. If the 64-bit version is loaded, you must be running in a 64-bit process. And if your running in a 64-bit process, you must be running on a 64-bit version of Windows.

public static bool Is64BitOperatingSystem
{
    [SecuritySafeCritical]
    get
    {
        return true;
    }
}

public static bool Is64BitProcess
{
    get
    {
        return true;
    }
}

The code in the 32-bit version of mscorlib.dll is almost as simple. If the 32-bit version is loaded, you must be running in a 32-bit process. In this case though it does not automatically mean you are running on a 32-bit version of Windows. It is possible that the process is running under the WOW64 (Windows-on-Windows 64-bit) subsystem on a 64-bit version of Windows.

The IsWow64Process function is used to determine if the process is running under WOW64. When this returns true you must be running on a 64-bit version of Windows because your process is running under the WOW64 subsystem. It is worth noting that the IsWow64Process function in kernel32.dll is present in current 32-bit versions of Windows but you need to check for its existence to maintain compatibility with versions where it is not present.

public static bool Is64BitOperatingSystem
{
    [SecuritySafeCritical]
    get
    {
        bool flag;
        return ((Win32Native.DoesWin32MethodExist("kernel32.dll", "IsWow64Process") 
            && Win32Native.IsWow64Process(Win32Native.GetCurrentProcess(), out flag)) && flag);
    }
}

public static bool Is64BitProcess
{
    get
    {
        return false;
    }
}

I really like the implementation; it is easy to follow and reliable. You will no longer need for check the IntPtr.Size value that gets hardcoded into mscorlib.dll to determine the bitness of your process, and you wont have to write your own platform invoke code to check the bitness of the operating system either. What joy a couple of simple static properties can bring!

Tags:

Microsoft .NET

Registering open generic interface types in Autofac

by Alex Meyer-Gleaves 20 December 2009 - 12:22 PM

UPDATE (22 December 2009): I have submitted a patch to Nick and this feature has now been added to the Autofac V2 codebase. While including the patch Nick added support for open generic classes and renamed the extension method to AsClosedTypesOf. This post will be left in its current form and remains a valid example of extending Autofac.

There has been some discussion lately around connecting an open generic type to its implementation types in a number of different dependency injection containers including StructureMap and Unity:

It looks like this is not the first time this scenario has been discussed:

I also recently noticed a posting on the Autofac Google Group asking if it was possible for Autofac to automatically register an open generic interface type against its implementations. Nicholas Blumhardt (the creator of Autofac and all round nice guy) suggested that this would be easy to implement using the new RegisterAssemblyTypes method found on the ContainerBuilder in the upcoming V2 release. Since I have been meaning to take a closer look at the preview of Autofac V2, I decided that having a look into solving this would be a good way for me to dip my toes into the water. I decided to limit the scope to supporting only automatic registrations for open generic interface types. There is no doubt you could use the extensibility provided by the RegisterAssemblyTypes method to take things much further and add features like those supported in the Unity Auto Registration library.

Time to introduce some types that will be used in the unit tests. First is the open generic interface type.

/// <summary>
/// An open generic interface type.
/// </summary>
public interface ICommand<T>
{
    void Execute(T data);
}

Next we have a couple of simple types that will be used as the generic type parameters.

/// <summary>
/// A type to use as a generic parameter.
/// </summary>
public class SaveCommandData
{
}

/// <summary>
/// A type to use as a generic parameter.
/// </summary>
public class DeleteCommandData
{
}

To keep things interesting I decided to include an abstract base class that implements the ICommand interface.

/// <summary>
/// An abstract base class that implements the open generic 
/// interface type.
/// </summary>
public abstract class CommandBase<T> : ICommand<T>
{
    public abstract void Execute(T data);
}

There will be two command implementations. The first will directly implement the ICommand interface.

/// <summary>
/// A command class that directly implements the open 
/// generic interface type.
/// </summary>
public class SaveCommand : ICommand<SaveCommandData>
{
    public void Execute(SaveCommandData data)
    {
    }
}

The second will implement the ICommand interface by inheriting from the CommandBase<T> abstract class.

/// <summary>
/// A command class that implements the open generic interface 
/// type by inheriting from the abstract base class.
/// </summary>
public class DeleteCommand : CommandBase<DeleteCommandData>
{
    public override void Execute(DeleteCommandData data)
    {
    }
}

I will use the first unit test to define the name and signature of the extension method that be will added to the RegistrationBuilder. The extension method will be named WhereTypeClosesOpenGenericInterface and will be available on the RegistrationBuilder returned from the RegisterAssemblyTypes method. It will take a single parameter for the Type that represents the open generic interface type that automatic registrations will be created for.

This first unit test will actually ensure that passing a null value as the method parameter will result in an ArgumentNullException being thrown.

[Test]
public void WhereTypeClosesOpenGenericInterface_NullTypeProvided_ThrowsException()
{
    ContainerBuilder builder = new ContainerBuilder();
    Assert.Throws<ArgumentNullException>(() => builder.RegisterAssemblyTypes(typeof(ICommand<>).Assembly).
        WhereTypeClosesOpenGenericInterface(null));
}

The next unit test will ensure that passing a non-generic type into the method will result in an ArgumentException being thrown.

[Test]
public void WhereTypeClosesOpenGenericInterface_NonGenericTypeProvided_ThrowsException()
{
    ContainerBuilder builder = new ContainerBuilder();
    Assert.Throws<ArgumentException>(() => builder.RegisterAssemblyTypes(typeof(ICommand<>).Assembly).
        WhereTypeClosesOpenGenericInterface(typeof(SaveCommandData)));
}

Another simple unit test will ensure that passing in a closed generic type will also result in an ArgumentException being thrown.

[Test]
public void WhereTypeClosesOpenGenericInterface_ClosedGenericTypeProvided_ThrowsException()
{
    ContainerBuilder builder = new ContainerBuilder();
    Assert.Throws<ArgumentException>(() => builder.RegisterAssemblyTypes(typeof(ICommand<>).Assembly).
        WhereTypeClosesOpenGenericInterface(typeof(ICommand<SaveCommandData>)));
}

The last of the boring unit tests ensures that passing in an open generic type that is not an interface will again result in an ArgumentException being thrown.

[Test]
public void WhereTypeClosesOpenGenericInterface_NonInterfaceOpenGenericTypeProvided_ThrowsException()
{
    ContainerBuilder builder = new ContainerBuilder();
    Assert.Throws<ArgumentException>(() => builder.RegisterAssemblyTypes(typeof(ICommand<>).Assembly).
        WhereTypeClosesOpenGenericInterface(typeof(List<>)));
}

Now onto the interesting unit test that will ensure our registrations are wired up correctly. We use our new WhereTypeClosesOpenGenericInterface extension method and pass it the ICommand<> open generic interface type. Obviously when we resolve a closed generic interface type we except the returned instance to be the type that implements it. In the case of the SaveCommand the implementation of the interface is direct, and with the DeleteCommand the implementation of the interface is through its inheritance of CommandBase<T>.

[Test]
public void WhereTypeClosesOpenGenericInterface_OpenGenericInterfaceTypeProvided_ClosingGenericTypesRegistered()
{
    ContainerBuilder builder = new ContainerBuilder();
    builder.RegisterAssemblyTypes(typeof(ICommand<>).Assembly)
        .WhereTypeClosesOpenGenericInterface(typeof(ICommand<>));
    IContainer container = builder.Build();

    Assert.That(container.Resolve<ICommand<SaveCommandData>>(), Is.TypeOf<SaveCommand>());
    Assert.That(container.Resolve<ICommand<DeleteCommandData>>(), Is.TypeOf<DeleteCommand>());
}

Finally we arrive at the implementation code. I looked at the StructureMap implementation before writing this to keep an eye out for details that I might have otherwise forgotten. Doing so seemed like a good idea considering their code has already been put through its paces. Take a quick look over the code and I will explain what is going on below.

/// <summary>
/// Extension methods for the <see cref="RegistrationBuilder{TLimit,TActivatorData,TRegistrationStyle}"/> class.
/// </summary>
public static class RegistrationBuilderExtensions
{
    /// <summary>
    /// Specifies that a type from a scanned assembly is registered if it implements an interface
    /// that closes the provided open generic interface type.
    /// </summary>
    /// <typeparam name="TLimit">Registration limit type.</typeparam>
    /// <typeparam name="TRegistrationStyle">Registration style.</typeparam>
    /// <typeparam name="TScanningActivatorData">Activator data type.</typeparam>
    /// <param name="registration">Registration to set service mapping on.</param>
    /// <param name="openGenericInterfaceType">The open generic interface type for which implementations will be found.</param>
    /// <returns>Registration builder allowing the registration to be configured.</returns>
    public static RegistrationBuilder<TLimit, TScanningActivatorData, TRegistrationStyle>
        WhereTypeClosesOpenGenericInterface<TLimit, TScanningActivatorData, TRegistrationStyle>(
            this RegistrationBuilder<TLimit, TScanningActivatorData, TRegistrationStyle> registration, Type openGenericInterfaceType)
        where TScanningActivatorData : ScanningActivatorData
    {
        if (openGenericInterfaceType == null)
        {
            throw new ArgumentNullException("openGenericInterfaceType");
        }

        if (!(openGenericInterfaceType.IsGenericTypeDefinition || openGenericInterfaceType.ContainsGenericParameters) || !openGenericInterfaceType.IsInterface)
        {
            throw new ArgumentException("The type '" + openGenericInterfaceType.FullName + "' is not an open generic interface type.");
        }

        return registration.Where(candidateType => findInterfaceThatCloses(candidateType, openGenericInterfaceType) != null)
            .As(candidateType => findInterfaceThatCloses(candidateType, openGenericInterfaceType));
    }

    /// <summary>
    /// Looks for an interface on the candidate type that closes the provided open generic interface type.
    /// </summary>
    /// <param name="candidateType">The type that is being checked for the interface.</param>
    /// <param name="openGenericInterfaceType">The open generic interface type to locate.</param>
    /// <returns>The type of the interface if found; otherwise, <c>null</c>.</returns>
    private static Type findInterfaceThatCloses(Type candidateType, Type openGenericInterfaceType)
    {
        if (candidateType.IsAbstract) return null;

        foreach (Type interfaceType in candidateType.GetInterfaces())
        {
            if (interfaceType.IsGenericType && interfaceType.GetGenericTypeDefinition() == openGenericInterfaceType)
            {
                return interfaceType;
            }
        }

        return (candidateType.BaseType == typeof(object)) 
            ? null
            : findInterfaceThatCloses(candidateType.BaseType, openGenericInterfaceType);
    }
}

First the parameter for the open generic interface type is checked to ensure that it is not null and that it is indeed an open generic interface type. Next we use the RegistrationBuilder instance that is being extended to determine what types we want registered and what their service mappings will be. The important methods on the RegistrationBuilder that enable this are the Where and As methods. The Where method takes a predicate that is used to filter the list of scanned types down to only those you are interested in registering. The As method is used to provide the service mappings for the types that got included for registration after the filter was applied.

The Func<Type,bool> predicate provided to the Where method on the RegistrationBuilder instance utilizes a private method named findInterfaceThatCloses. When the findInterfaceThatCloses method is called it will look for an interface on the type provided as the first parameter, that matches the type provided as the second parameter. In our case we are passing in the candidate type that was provided by the assembly scanning process, and the open generic interface type we are interested in matching. When no matching interface is found null is returned. When used in the delegate parameter provided to the Where method for filtering we check for the return from findInterfaceThatCloses being not null, and use the actual type returned from the method for the delegate parameter provided to the As method for mapping the services. We know that when the As method is called it will only be provided with types that were included by the filter, so we need not worry about receiving a type that does not implement the interface at this point.

The implementation of the findInterfaceThatCloses method ensures that only types which are not abstract are checked for matching interfaces. It then iterates through the available interfaces and checks if any match the open generic interface type provided. If no matching interface is found we recursively check if the type’s base class implements the interface until we reach a type that inherits directly from object.

As you can see Nick has done a great job making Autofac extensible, allowing additional requirements for your container to be met with very little effort on your part. I think the next version of Autofac is shaping up nicely and I look forward to posting more about it in the future.

Tags:

Garage Sale Code | Microsoft .NET | Autofac

What is a "turd" configuration file?

by Alex Meyer-Gleaves 14 July 2009 - 4:33 PM

I stumbled across an interesting response to an issue on Microsoft Connect and couldn’t help but to laugh:

As a workaround, your add-in might want to store these settings on a per-project basis, or alternatively, you could keep a small turd configuration file next to the .sln that has your settings.

Maybe I need to get some more sleep, but it certainly seems funny at the moment. ;p

Tags:

Microsoft .NET | Random

Velocity DataCache Extension Methods

by Alex Meyer-Gleaves 11 June 2009 - 5:53 PM

If you are using the local cache configuration in Velocity you need to be careful how you use objects returned from the cache. The instance returned is a reference to the object stored inside the local cache, and is not a new instance that has been deserialized from the cache host. This means that if you make a change to the object, the change will be seen by any other caller retrieving the same object from the cache.

The current API does not provide a means to determine if the cache you are using has been configured with local caching. When the local caching option is used the type of the DataCache instance returned from the DataCacheFactory is actually an internal type called Microsoft.Data.Caching.LocalCache. The first extension method called IsLocalCache uses this knowledge to determine if you are working with a local cache.

/// <summary>
///        Determines whether the cache is configured as a local cache.
/// </summary>
/// <param name="dataCache">
///        The data cache being extended.
/// </param>
/// <returns>
///     <c>true</c> if configured as a local cache; otherwise, <c>false</c>.
/// </returns>
public static bool IsLocalCache(this DataCache dataCache)
{
    return (dataCache.GetType().FullName == "Microsoft.Data.Caching.LocalCache");
}

The next extension method is another Get method implementation that uses generics to avoid casting in the calling code. This method also accepts a Func<T> delegate that is used to create the object and add it to the cache if the entry for the specified key is not found. To make sure that the object returned from the local cache is not accidently modified the object is cloned before being returned. You can see in the code below that the cloning is performed in the clone method which I will explain in more detail next.

/// <summary>
///        Gets the cached object for the specified key.
/// </summary>
/// <typeparam name="T">
///        The type of the cached object.
/// </typeparam>
/// <param name="dataCache">
///        The data cache being extended.
/// </param>
/// <param name="key">
///        The key for the cached object.
/// </param>
/// <param name="creator">
///        A <see cref="Func{TResult}"/> delegate used to create the object if not found in the cache.
/// </param>
/// <returns></returns>
public static T Get<T>(this DataCache dataCache, string key, Func<T> creator)
{
    object value = dataCache.Get(key);
    if (value == null)
    {
        value = creator();
        dataCache.Put(key, value);
    }
    return dataCache.IsLocalCache() ? clone((T)value) : (T)value;
}

In the private clone method, I have used the DataContractSerializer class to perform the cloning as it works on types that are not explicitly marked as being Serializable or DataContract. The support for serializing POCO objects was added in .NET Framework 3.5 SP1. You can find out more about it here on Aaron Skonnard’s blog. You can see in the implementation below that the serialization cost can be avoided if the type being cloned is a value type or a string. The string class is not cloned because it is immutable and cannot be modified, and value types are of course always passed by value anyway.

private static T clone<T>(T instance)
{
    Type instanceType = typeof(T);
    if (instanceType.IsValueType || instanceType == typeof(string))
    {
        return instance;
    }

    DataContractSerializer serializer = new DataContractSerializer(instanceType);
    using (MemoryStream memoryStream = new MemoryStream())
    {
        serializer.WriteObject(memoryStream, instance);
        memoryStream.Position = 0;
        return (T)serializer.ReadObject(memoryStream);
    }
}

The final extension method is called CreateKey, and is a simple helper method that can be used to create keys for cache entries. It accepts a params array of object values that are concatenated with a pipe character as a separator between them. The ToString method is called on each object to obtain the string value used in the key.

/// <summary>
///        Creates a key to use for caching objects.
/// </summary>
/// <param name="dataCache">
///        The data cache being extended.
/// </param>
/// <param name="values">
///        The values to build the key from.
/// </param>
/// <returns>
///        A key to use for caching objects.
/// </returns>
public static string CreateKey(this DataCache dataCache, params object[] values)
{
    StringBuilder combinedValues = new StringBuilder();
    foreach (object value in values)
    {
        if (combinedValues.Length > 0)
        {
            combinedValues.Append("|");
        }
        combinedValues.Append(value);
    }
    return combinedValues.ToString();
}

Here are some code samples of how to call the extension methods based on some unit tests I created while writing the extensions. I have attached the code file for the extensions to the end of the post. The actual unit tests are not attached as those require Velocity to be installed and configured appropriately.

// Get the default cache using the factory.
DataCacheFactory factory = new DataCacheFactory();
DataCache cache = factory.GetDefaultCache();

// Test if the cache is configured as a local cache.
Assert.That(cache.IsLocalCache(), Is.True);

// Test that objects returned from the local cache are cloned.
Customer customer1 = cache.Get("customer", () => new Customer {FirstName = "John", LastName = "Smith"});
customer1.FirstName = "Frank";

Customer customer2 = cache.Get("customer", () => new Customer {FirstName = "John", LastName = "Smith"});

Assert.That(customer1.FirstName, Is.EqualTo("Frank"));
Assert.That(customer2.FirstName, Is.EqualTo("John"));

// Test that value types are supported.
int integer = cache.Get("integer", () => 123);
Assert.That(integer, Is.EqualTo(123));

// Test that cache keys are created correctly.
Assert.That(cache.CreateKey("customer", 123), Is.EqualTo("customer|123"));

DataCacheExtensions.cs (2.68 kb)

Tags:

Garage Sale Code | Microsoft .NET

Mocking support in Microsoft Velocity

by Alex Meyer-Gleaves 3 June 2009 - 7:06 PM

I have been looking at the Microsoft Distributed Cache and have found the been fairly impressed with the project. The download page offers a brief description of “Velocity”:

"Velocity" is a distributed in-memory application cache platform for developing scalable, high-performance applications. "Velocity" can be used to cache any common language runtime (CLR) object and provides access through simple APIs. The key aspects of "Velocity" are distributed cache performance, scalability, and availability.

Being a fan of TDD (Test-Driven Development) and mocking in my unit tests I was keen to see how well the API supported mocking. It turns out that the support for mocking is no where to be found. None of the classes implement interfaces and the important methods that support general caching operations are not virtual. Looking at the sample code below you can see how not having an IDataCache interface on the DataCache class will make unit testing the CustomerRepository class in isolation difficult. Even though the DataCache instance is passed into the constructor the interaction with it cannot be mocked.

public class CustomerRepository : ICustomerRepository
{
    private readonly DataCache _dataCache;

    public CustomerRepository(DataCache dataCache)
    {
        _dataCache = dataCache;
    }

    public Customer GetCustomer(int customerId)
    {
        Customer customer = (Customer)_dataCache.Get(customerId.ToString());
        if (customer == null)
        {
            customer = // Code that gets customer from the database.
            _dataCache.Put(customerId.ToString(), customer);
        }
        return customer;
    }
}

The idea of having my unit tests dependant on a running cache service is certainly not attractive, and while it would be possible to wrap the classes and provide an interface implementation of my own, this is not an attractive option either. The support for unit testing is now expected in the design of an API and Microsoft should be providing this for something as core as an application cache. I hope that the team at Microsoft sees this as a problem and provides some interfaces, or at very least makes the methods that support the primary caching operations virtual.

Tags:

Microsoft .NET

About the author

Alex Meyer-Gleaves I'm a Technical Architect living in Australia (that island like continent in the southern hemisphere). I love Microsoft .NET and C#. I hate early mornings, slow drivers and Lotus Notes.

Twitter

Google Shared

 

Month List

Recent Comments

Comment RSS

Links

Disclaimer

The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.

© Copyright 2010