August 2005 - Posts

While playing around some more with my ReflectionSerializationSurrogate I found that it did a good job on deserializing objects without default constructors. That’s a pretty cool but strange behavior, don’t you think?

After having another look in the documentation, I found the following method ‘FormatterServices. GetSafeUninitializedObject(Type)’. This method allows IFormatters to create an uninitialized Object before unserializing its contents.

Off course there aren’t a lot of scenarios this might be useful, in most scenarios you even would like to avoid calling such a method.
From the MSDN documentation:
“Because the new instance of the object is initialized to zero and no constructors are run, the object might not represent a state that is regarded as valid by that object. The current method should only be used for deserialization when the user intends to immediately populate all fields. It will not create an uninitialized string, since creating an empty instance of an immutable type serves no purpose.”

I found a useful scenario in dealing with circular references in my ServiceContainer, which uses constructor injection as its method to inject dependencies. I'll implement circular references on its second iteration

In addition to my last post where I introduced an EntLib ConfigurationConsole plugin called ‘EnvironmentalOverrides’, I would like to present some scenario’s where you might find this particularly useful. I also would like you to tell me where you would like to use this plugin for!

All examples require you to have the plugin downloaded, compiled and placed in the Enterprise Library’s bin directory. Next you should add the ‘Environmental Overrides’ node to your application by right-clicking your application, choose ‘new’ -> ‘Environmental Overrides’.

The plugin will add 3 environments by default (Test, Staging and production), the settings you enter directly in the configuration console will apply on your development environment.
Environments can be added/removed or renamed, on the ‘Environmental overrides’ node under the application node.

Please note that the plugin creates different subdirectories per environment (in the image above, the subdirectory for the production environment is 'PRO'), each containing a complete set of .config files. Distribution of the configuration files is beyond the scope of the plugin. Typically a buildserver should take care of packaging the different configuration files into separate distributions.


First scenario: Your application uses different database servers on your production and staging environment

Your application uses databases, though the production database is fit for development and debugging purposes. Hopefully it is not even accessible by you, from your development environment. Every environment has its own connectionstring to the database, in this example it the “server” property is different for both the production and staging environment, the test environment is configured to use the same server as your development environment.

EnvironmentDatabase server
Development                               DevelopmentDB
Test  [same as on Development environment]
Staging StagingDB
Production ProductionDB

This would look like the following in the EntLib configuration console


The value property for the 'Test environment' is grayed out and kept in sync with your configurations default (the development environment).


Second scenario: Your application uses the Caching application block, which you would like to disable on your development environment

Caching is a good thing… on production environments! It pretty much gets in your way when someone is testing your application. Ever heard yourself say: “Err…. It might be the cache you are looking at…. Why don’t you keep trying the following 300000 milliseconds?”?

The thing you might want to do is disable caching on your development and testing environment and only cache your production and staging environments. This is done by setting your ‘cache managers’ properties to do minimal caching by default and overriding the values with the actual onces for the production and staging environments.


Third Scenario: You are required to encrypt database settings on the production environments

Lucky you! Entlib can encrypt any application blocks settings out of the box. Even though you agreed to comply with this requirement, you might want to be able to edit the configuration file from any text editor on your development machine.

Add a new key/algorithm pair to your encryption settings (under the ‘Configuration Application Block’) as you would do normally. Next, select the ‘dataConfiguration’ section (just above) and leave the main ‘Encrypt’ value to ‘false’. Override the production environments settings and for the ‘Encrypt’ property, choose false.

Next you should encrypt all copies of your ‘EnvironmentConfig’ section (again under ‘Configuration Application Block’). This is because for all overridden settings, the values are stored in this configuration file.

If you failed to add a key/algorithm pair to you’re the encryption settings, this will raise a configuration error trying to save the configuration on your development environment already. Pretty convenient, huh?



Good luck playing around with the plugin! if you used the plugin for less common scenarios, please share your experiences.

As promised in an earlier post I would have a look into a way to manage configuration from within the EntLib Configuration Console over multiple environments. Just recently I had to deal with this very same problem on the project I worked at and found a solution I think works pretty decently. The solution is implemented as an ‘application block’ and uses my very favorite PropertyGrid to throw some extra Extenders and TypeDescriptors at the configuration nodes in the console.

 

The ‘application block’ can be added to any project within the configuration console. To declare a new environment[1] (places where you might want to have alternative configuration), you simply add a configuration node to the “Environment overrides”.

All configuration nodes are then extended with new properties for that apply on that specific environment[2]. Properties are automatically kept in sync with the enterprise library property when the ‘Override on Xxxx environment’ property is left as “Don’t override properties” [3].

 

After saving your configuration to disk a subdirectory will be created for each environment, containing a complete configuration setup for that specific environment. Commonly a buildserver should be responsible for distributing the configuration files from the subdirectories to the different physical environments.

The application block is only tested with the June version of EntLib and not tested with any custom application blocks. Please backup your configuration, as I would hate to see anyone losing their work because of flaws in the plugin.

The plugin can be deployed by recompiling the sources against your EntLib binaries and then copying the “EnvironmentalOverrides.dll” to EntLib’s bin directory.

The source of this EntLib plugin can be found here.

Recently I was stuck on a serialization problem. I needed to serialize both complex structures (which the XmlSerializer could not handle, such as IDictionary’s) from a 3rd party codebase (which the Binary-/SoapSerializer could not handle, because the types weren’t decorated with [Serializable] attributes).

 

What I needed was a serializer that had both the intelligence to serialize complex structures and do it through reflection.

 

While I was studying the IFormatter interface, to solve this problem through AOP (inject advice on the serialize method, mix the ISerializable interface into the foreign objects and probably Reflection.Emit my way out of decorating the types with the [Serializable] attribute :o)...) I stumbled into the IFormatter.SurrogateSelector property.

 
The SurrogateSelector allows you to extend an IFormatter with your own serialization logic, which is called when the IFormatter isn’t able to serialize the object itself. Even though this would have been a cool AOP demo (which I might write anyway, if someone is interested anyway) I decided to read the fucking documentation and within a half hour cooked myself a ReflectionSurrogateSelector.

 

The ReflectionSurrogateSelector extends any IFormatter with the logic to serialize through reflection, just as the XmlSerializer does.


An example of how to use the ReflectionSurrgateSelector (though not that pretty, SurrogateSelectors should be chained):
SoapFormatter soapFormatter = new SoapFormatter();
soapFormatter.SurrogateSelector = new ReflectionSurrogateSelector();
using(MemoryStream ms = new MemoryStream())
{
 soapFormatter.Serialize(ms, _someInstance);
}


And... the code for the ReflectionSurrogateSelector class itself:
public class ReflectionSurrogateSelector: ISurrogateSelector
{
 Hashtable _surrogatesByType = new Hashtable();
 ISurrogateSelector _nextSelector;

 public void ChainSelector(ISurrogateSelector selector)
 {
  _nextSelector = selector;
 }

 public ISerializationSurrogate GetSurrogate(Type type, StreamingContext context, out ISurrogateSelector selector)
 {
  if (_surrogatesByType.ContainsKey(type))
  {
   selector = this;
   return (ISerializationSurrogate) _surrogatesByType[type];
  }
  
  if (!type.IsSerializable)
  {
   System.Diagnostics.Debug.Write(type.Name);

   ISerializationSurrogate surrogate = new ReflectionSurogate(type);
   _surrogatesByType[type] = surrogate;
   
   selector = this;
   return surrogate;
  }
  selector = _nextSelector;
  return null;
 }

 public ISurrogateSelector GetNextSelector()
 {
  return _nextSelector;
 }

 private sealed class ReflectionSurogate :ISerializationSurrogate
 {
  Type _t;
  FieldInfo[] _fs;
  PropertyInfo[] _ps;

  public ReflectionSurogate(Type t)
  {
   _t = t;
   _fs = _t.GetFields();
   _ps = _t.GetProperties();
  }

  #region ISerializationSurrogate Members

  public void GetObjectData(object obj, SerializationInfo info, StreamingContext context)
  {
   foreach(FieldInfo f in _fs)
   {
    info.AddValue(f.Name, f.GetValue(obj));
   }

   foreach(PropertyInfo p in _ps)
   {
    if (p.CanRead && p.CanWrite && p.GetIndexParameters().Length == 0)
    {
     info.AddValue(p.Name, p.GetValue(obj, new object[0]));
    }
   }
  }

  public object SetObjectData(object obj, SerializationInfo info, StreamingContext context, ISurrogateSelector selector)
  {
   foreach(PropertyInfo pi in _ps)
   {
    object pValue = info.GetValue(pi.Name, pi.PropertyType);
    if (pValue != null)
    {
     pi.SetValue(obj, pValue, new object[0]);
    }
   }

   foreach(FieldInfo fi in _fs)
   {
    object fValue = info.GetValue(fi.Name, fi.FieldType);
    if (fValue != null)
    {
     fi.SetValue(obj, fValue);
    }
   }

   return obj;
  }

  #endregion
 }
}