Click here to Skip to main content
Click here to Skip to main content

Strongly typed AppSettings with Genuilder.Extensibility

, 26 Jun 2010 Ms-PL
Rate this:
Please Sign up or sign in to vote.
Practical use case of Genuilder.Extensibility: Strongly typed AppSettings.

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.

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.

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.

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.

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

    #endregion
}

Here is the config file in the console application:

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

Build ConsoleApplication1 and enjoy the show.

You can even see the warning message.

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.

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

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! Wink | ;)

License

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

Share

About the Author

Nicolas Dorier
Software Developer Freelance
France France
I am a trainer and a curious developer.
 
CEO of AO-IS, we created a tool to make IaaS on Azure more easy IaaS Management Studio.
 
If you are interested for working with me, for fun coding stuff, for freelance stuff, or interested in using our cloud training infrastructure freely for a kickass presentation for the dev community ? this way Smile | :)

Comments and Discussions

 
GeneralT4 alternative PinmemberGigaGeorge28-Jul-10 7:06 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web04 | 2.8.1411023.1 | Last Updated 26 Jun 2010
Article Copyright 2010 by Nicolas Dorier
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid