Lately I'm using Unity and to me has one big problem.
But first I will talk a little about .Net Framework in particular the configuration.
In .Net Framework there are tree classes named ConfigurationElement, ConfigurationElementCollection and ConfigurationSection and these classes are available in System.Configuration. They exists so we can access configuration within our code, and change something's. I say something's because we could not change much because the .Net team put the usefully methods as protected and only useless methods are public.
So let's imagine that I have a class XXXConfigurationSection and that I want to serialize this class to another .config file different that web.config/app.config or I simple want to serialize and insert configuration on DB.
2 solutions, the person that create the class XXXConfigurationSection expose as public the SerializeSection and DeserializeSection methods or I have to create a SerializableXXXConfiguration that inheritance XXXConfigurationSection and then that class exposes the protected methods and do something like this:
public class SerializableXXXConfiguration : XXXConfigurationSection
{
private const string XXXConfigurationName = "xxx";
public void ReadConfiguration(XmlReader reader)
{
reader.Read();
DeserializeSection(reader);
}
public void WriteConfiguration(XmlWriter writer)
{
String serialized = SerializeSection(this, XXXConfigurationName, ConfigurationSaveMode.Full);
writer.WriteRaw(serialized);
}
}
But the story continues and let's imagine that I have a class XXXCollection : ConfigurationElementCollection and me programmer want to do this
XXXCollection xxxCol = new XXXCollection();
and then I want to add some elements to the collection I ask, where are the add methods???? well either the person that had done class XXXCollection expose the protected methods as public or I'm literally fu....
In conclusion this configuration is only to read and not to write. But then a question appear... How can I deploy my configuration dynamically??? Do I need to have different configuration files for every case, do I need inherit form every configuration class and expose the protected methods...
what is the best solution for this problem??? the answer is there is no best solution :(
And now I will enter again in Unity. The Unity configuration suffers the same problem.
if you want to use Unity configuration you will have to write configuration all by hand in the .config file, and now I ask again, what if I want to have different configurations depending on my deploy, how will I manage this problem.
A simple example:
imagine that I have a interface ILogger and I want to deploy 2 web applications one for customers and other for backoffice. For one web app want to resolve ILogger as class LoggerTrace that implements ILogger and in the other application I want resolve ILogger as class NullLogger that also implements ILogger.
Now, do I have to write configuration by hand ?
Do I have 2 configuration files prepared and the it's just copy the files ?
What do I need to do, what I want is just dynamic deploy the applications....
Personally I don't know any directly way to resolve this problem.
But... there is always a but, with help of Nuno Gomes there is a solution.
The solution consists in access directly the protected methods or properties depending in what we want to do.
So if I want to deploy my Unity configuration I will only need to have a few auxiliary methods that manage to access protected methods.
With this solution I don't have to inherit from anything.
So in case of Unity Configuration I had create this solution:
Create a class SerializableUnityConfiguration that enables me to Serialize and Deserialize Unity configuration, exposing PUBLIC methods
public class SerializableUnityConfiguration : UnityConfigurationSection
{
private const string UnityConfigurationName = "unity";
public void ReadConfiguration(XmlReader reader)
{
reader.Read();
DeserializeSection(reader);
}
public void WriteConfiguration(XmlWriter writer)
{
String serialized = SerializeSection(this, UnityConfigurationName, ConfigurationSaveMode.Full);
writer.WriteRaw(serialized);
}
public XmlReader GetReader()
{
String serialized = SerializeSection(this, UnityConfigurationName, ConfigurationSaveMode.Full);
StringReader strReader = new StringReader(serialized);
return XmlReader.Create(strReader);
}
}
Then I have a few methods that enable me to manipulate the protected methods that exists in configuration classes.
If I want to add a container to Unity configuration I just had to do this:
public UnityContainerElement AddContainer(UnityConfigurationSection unitySection, string containerName)
{
#region Validation
if (unitySection == null)
{
throw new ArgumentNullException("unitySection is null");
}
if (containerName == null)
{
throw new ArgumentNullException("containerName is null");
}
#endregion
if (unitySection.Containers.Count == 0 ||
unitySection.Containers[containerName] == null)
{
UnityContainerElement containerElement = new UnityContainerElement();
if (!string.IsNullOrEmpty(containerName))
{
containerElement.Name = containerName;
}
AddCollectionItem(unitySection.Containers, containerElement);
}
return unitySection.Containers[containerName];
}
public void AddCollectionItem(ConfigurationElementCollection collection, ConfigurationElement element)
{
if (collection == null)
{
throw new ArgumentNullException("collection is null");
}
if (element== null)
{
throw new ArgumentNullException("element is null");
}
MethodInfo baseAddMethod = collection.GetType().GetMethod("BaseAdd", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic, null, new Type[] { typeof(ConfigurationElement) }, null);
if (baseAddMethod != null)
{
baseAddMethod.Invoke(collection, new object[] { element });
return;
}
throw new ArgumentException("Not a collection", "collection");
}
SerializableUnityConfiguration config = new SerializableUnityConfiguration ();
AddContainer(config , "TestContainer");
config.WriteConfiguration(...)
And I will have the Unity configuration like this:
<unity>
<containers>
<clear />
<container name="TestContainer">
<types>
<clear />
</types>
<extensions>
<clear />
</extensions>
<instances>
<clear />
</instances>
<extensionConfig>
<clear />
</extensionConfig>
</container>
</containers>
<typeAliases>
<clear />
</typeAliases>
</unity>
Now it's just few methods and I will be enable to write to every section of the configuration :). And since this code will only run during deployment there will be no problems will speed because I'm using reflection :)