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

Strongly typed AppSettings with Genuilder.Extensibility

By , 26 Jun 2010
 

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

License

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

About the Author

Nicolas Dorier
Software Developer Freelance
France France
Member
I develop to make you forget what's between you and your work.
 
I teach and, with delight, you'll see that the best castles are done with the dumbest concepts.

Curiosity is the best teacher.
 
If you are interested for working with me, this way Smile | :)

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
Hint: For improved responsiveness ensure Javascript is enabled and choose 'Normal' from the Layout dropdown and hit 'Update'.
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralT4 alternativememberGigaGeorge28 Jul '10 - 6:06 
Hi, good idea the wrapper,
I've done something similar but using T4 (the post is in my blog). I find it a good tool for code generation and it is already in Visual Studio, give it a try !
cheers

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

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