Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / XML

Strongly typed AppSettings with Genuilder.Extensibility

4.71/5 (5 votes)
26 Jun 2010Ms-PL2 min read 21.6K   88  
Practical use case of Genuilder.Extensibility: Strongly typed AppSettings.

Image 1

Table of Contents

Introduction

My last article on Genuilder.Extensibility did not have much success, a reader on my blog told me it was because there is no real use case shown in it. So here it is!

The goal is to create a code generator which allows you to use AppSettings in a safely typed manner. I already have done an article on that and integrated it to Genuilder. The problem is that to understand how I have done it, you have to understand how MSBuild works. So today, I'll show you how to implement this feature yourself with Genuilder.Extensibility.

Installation

I won't explain again how to install Genuilder, since it's fully described on CodePlex and in my last articles.

Create the plug-in

So let's create two projects: the first one is a console application with a config file and some appSettings inside. The second one is our feature project, a Class Library.

Image 2

Do not forget, the feature project mus end with .Gen.

Let's create the plug-in StronglyTypedAppSettingsPlugin in the feature project.

In this code snippet, I subscribe to the CodeItemCreated event of the CodeRepository. This event is fired for every source code file + config file in the project.

Then I create a dependency between my config file and a target file, named StronglyTypedSettings.cs.

C#
public class StronglyTypedAppSettingsPlugin : IPlugin
{
    #region IPlugin Members

    public void Initialize(ICodeRepository repository)
    {
        repository.CodeItemCreated += 
           new CodeItemCreatedHandler(repository_CodeItemCreated);
    }

    void repository_CodeItemCreated(ICodeRepository sender, CodeItem item)
    {
        if(item.Name.EndsWith(".config"))
        {
            var dependency = item.SourceOf("StronglyTypedSettings.cs");
            dependency.ShouldUpdateTarget += 
                new CodeDependencyHandler(dependency_ShouldUpdateTarget);
        }
    }

dependency_ShouldUpdateTarget generates the code in StronglyTypedSettings.cs based on the config file.

C#
void dependency_ShouldUpdateTarget(CodeDependency sender, CodeItem target)
{
    var configuration = 
        ConfigurationManager.OpenMappedExeConfiguration(new ExeConfigurationFileMap()
    {
        ExeConfigFilename = ((FileCodeItem)sender.Source).File.FullName
    }, ConfigurationUserLevel.None);

    string generatedClass = "public static class AppConfigSettings{";

    foreach(KeyValueConfigurationElement setting in configuration.AppSettings.Settings)
    {

        string getter = "public static System.String " + setting.Key + 
          " { get{ return System.Configuration.ConfigurationManager.AppSettings[\"" + 
          setting.Key + "\"]; } }";
        generatedClass += getter;
    }
    generatedClass += "}";
    target.Content = generatedClass;

To show how to send warning and error notification to the build, let's send a warning each time StronglyTypedSettings.cs is updated.

C#
target.Logger.Send("Settings generated !!", 
              Genuilder.Extensibility.Utilities.LogType.Warning);
    }

    #endregion
}

Here is the config file in the console application:

XML
<?xml version="1.0"?>
<configuration>
    <appSettings>
        <add key="SomeKey" value="SomeValue"/>
    </appSettings>
</configuration>

Build ConsoleApplication1 and enjoy the show.

Image 3

You can even see the warning message.

Image 4

As you can see, when you rebuild, the warning does not appear because the source file (App.config) has not changed. If you modify your feature project, you must clean the project prior to build.

This plug-in works only in C#, how can I make it work in VB.NET too?

We will just modify dependency_ShouldUpdateTarget to use NRefactory extension. But before that, let's reference the NRefactory integration assembly in our project.

Image 5

Here is the code of dependency_ShouldUpdateTarget using CompilationUnitExtension (NRefactory extension)... A bit more complicated, but works in both C# and VB.NET !!

C#
void dependency_ShouldUpdateTarget(CodeDependency sender, CodeItem target)
{
    var configuration = 
        ConfigurationManager.OpenMappedExeConfiguration(
        new ExeConfigurationFileMap()
    {
        ExeConfigFilename = ((FileCodeItem)sender.Source).File.FullName
    }, ConfigurationUserLevel.None);


    var targetNRefactoryExtension = 
      target.GetExtension<Genuilder.Extensibility.NRefactory.CompilationUnitExtension>();

    targetNRefactoryExtension.CompilationUnit.Children.Clear();
    var typeDeclaration = new TypeDeclaration(Modifiers.Public | Modifiers.Static, null)
    {
        Name = "AppConfigSettings"
    };
    targetNRefactoryExtension.CompilationUnit.Children.Add(typeDeclaration);



    foreach(KeyValueConfigurationElement setting in configuration.AppSettings.Settings)
    {

        var propertyDeclaration = new PropertyDeclaration(
               Modifiers.Public | Modifiers.Static, null, setting.Key, null)
        {
            TypeReference = new TypeReference(typeof(String).FullName)
        };
        var indexer = new IndexerExpression(
            new MemberReferenceExpression(
                new TypeReferenceExpression(
                    new TypeReference(typeof(ConfigurationManager).FullName)), "AppSettings")
                        , new List<Expression>()
                        {
                            new ICSharpCode.NRefactory.Ast.PrimitiveExpression(setting.Key)
                        });

        propertyDeclaration.GetRegion = new PropertyGetRegion(new BlockStatement(), null);
        propertyDeclaration.GetRegion.Block.Children.Add(new ReturnStatement(indexer));
        typeDeclaration.Children.Add(propertyDeclaration);

    }
    targetNRefactoryExtension.Save();
    target.Logger.Send("Settings generated !!", 
           Genuilder.Extensibility.Utilities.LogType.Warning);
}

Conclusion

Well, that's all. I'm proud that my articles are short and fast to do, it shows that they are not complicated!!

I'm very interested to hear about new plug-in ideas from you. I'll be glad to implement them if I'm motivated! ;)

License

This article, along with any associated source code and files, is licensed under The Microsoft Public License (Ms-PL)