Castle DictionaryAdapter

June 10, 2008 at 3:02 PMAndre Loker

DictionaryAdapter is a component of the Castle stack which can create an adapter between an interface and a dictionary at runtime. The DictionaryAdapter combines the flexibility of a dictionary with the type safety and convenience provided by a strong API. Here's an example:

   1: // the interface that will be used to access the content of the dictionary
   2: public interface ISimple {
   3:   string Name { get; set; }
   4:   int Age { get; set; }
   5: }
   6:  
   7: //...
   8:  
   9: // the backing store
  10: IDictionary dict = new Hashtable(); 
  11:  
  12: // dynamically create an adapter around dict 
  13: ISimple wrapper = new DictionaryAdapterFactory().GetAdapter<ISimple>(dict);
  14: wrapper.Name = "Andre"; // will be written to dict["Name"];
  15: wrapper.Age = 26;       // will be written to dict["Age"];
  16:  
  17: Assert.AreEqual("Andre", dict["Name"]);
  18: Assert.AreEqual("Andre", wrapper.Name);
  19:  
  20: Assert.AreEqual(26, dict["Age"]);
  21: Assert.AreEqual(26, wrapper.Age);

How to get the DictionaryAdapter

The DictionaryAdapter can be downloaded as part of the Castle project:

How does it work?

Calling DictionaryAdapterFactory.GetAdapter will create a new type on the fly. This type implements the interface that was provided as the type argument (here: ISimple). For each property on the interface a getter and/or setter will be implemented that reads a value from the provided dictionary or writes a value to the dictionary. The key that is used is based on the property name (the default setting is to use the property name as the key, later we will see how we can influence the key being used).

Creating an adapter is rather costly. It requires the creation of a new in-memory assembly containing the dynamic adapter type. However, this performance only exists the first time GetAdapter is called for a specific type. Subsequent requests for the same adapter type will reuse the generated assembly.

More examples and options

DictionaryAdapter has plenty of options to customize how the mapping between the adapter interface and the underlying dictionary happens. In this section I'll give some examples of what DictionaryAdapter is capable of without covering every detail (for a complete reference I'd recommend the Castle source code).

I will provide code examples in the form of an MbUnit test with the following skeleton:

   1: using System.Collections;
   2: using MbUnit.Framework;
   3: using Castle.Components.DictionaryAdapter;
   4:  
   5: [TestFixture]
   6: public class DictionaryAdapterTests {
   7:   private DictionaryAdapterFactory factory;
   8:   private IDictionary dict;
   9:  
  10:   [SetUp]
  11:   public void SetUp() {
  12:     factory = new DictionaryAdapterFactory();
  13:     dict = new Hashtable();
  14:   }
  15: }

The IDictionary instance is used as the backing store for which an adapter will created. The factory handles the dynamic creation of the adapters.

Download the source code of the examples (DictionaryAdapterTests.cs)

Basic example

Ones again, here's the most basic example:

   1: public interface ISimple {
   2:   string Name { get; set; }
   3:   int Age { get; set; }
   4: }
   5:  
   6: [Test]
   7: public void SimpleTest() {
   8:   var wrapper = factory.GetAdapter<ISimple>(dict);
   9:   wrapper.Name = "Andre";
  10:   wrapper.Age = 26;
  11:  
  12:   Assert.AreEqual("Andre", dict["Name"]);
  13:   Assert.AreEqual(26, dict["Age"]);
  14: }

Type prefix

Especially if you want to use multiple adapters on the same dictionary naming collisions can occur if a property with the same name is present in more than one adapter interface. To avoid this problem you can specify key prefixes using attributes. One possible prefix is the (full) name of the interface type:

   1: [DictionaryTypeKeyPrefix]
   2: public interface IWithTypePrefix {
   3:   string Name { get; set; }
   4:   int Age { get; set; }
   5: }
   6:  
   7: [Test]
   8: public void WithTypePrefix() {
   9:   var wrapper = factory.GetAdapter<IWithTypePrefix>(dict);
  10:   wrapper.Name = "Andre";
  11:   wrapper.Age = 26;
  12:  
  13:   Assert.AreEqual("Andre", dict[typeof (IWithTypePrefix).FullName + "#Name"]);
  14:   Assert.AreEqual(26, dict[typeof (IWithTypePrefix).FullName + "#Age"]);
  15: }

Custom prefix

The type prefix can be quite lengthy, so you might prefer setting your own prefix:

   1: [DictionaryKeyPrefix("My")]
   2: public interface IWithCustomPrefix {
   3:   string Name { get; set; }
   4:   int Age { get; set; }
   5: }
   6:  
   7: [Test]
   8: public void WithCustomPrefix() {
   9:   var wrapper = factory.GetAdapter<IWithCustomPrefix>(dict);
  10:   wrapper.Name = "Andre";
  11:   wrapper.Age = 26;
  12:  
  13:   Assert.AreEqual("Andre", dict["MyName"]);
  14:   Assert.AreEqual(26, dict["MyAge"]);
  15: }

Custom keys

You can opt for not using a prefix altogether and define the keys manually:

   1: public interface ICustomKeys {
   2:   [DictionaryKey("Foo")]
   3:   string Name { get; set; }
   4:  
   5:   [DictionaryKey("Bar")]
   6:   int Age { get; set; }
   7: }
   8:  
   9: [Test]
  10: public void WithCustomKey() {
  11:   var wrapper = factory.GetAdapter<ICustomKeys>(dict);
  12:   wrapper.Name = "Andre";
  13:   wrapper.Age = 26;
  14:  
  15:   Assert.AreEqual("Andre", dict["Foo"]);
  16:   Assert.AreEqual(26, dict["Bar"]);
  17: }

Convert property to string

In some scenarios you might need all values in the dictionary to be strings. This can be enforced by using the DictionaryStringValuesAttribute:

   1: public interface IPropertyAsString {
   2:   string Name { get; set; }
   3:  
   4:   [DictionaryStringValues]
   5:   int Age { get; set; }
   6: }
   7:  
   8: [Test]
   9: public void ConvertPropertyToString() {
  10:   var wrapper = factory.GetAdapter<IPropertyAsString>(dict);
  11:   wrapper.Name = "Andre";
  12:   wrapper.Age = 26;
  13:  
  14:   Assert.AreEqual("Andre", dict["Name"]);
  15:   Assert.AreEqual("26", dict["Age"]);
  16: }

This attribute can be applied to the interface itself as well to force all properties to be converted to strings.

Collection as string list

If your property implements IEnumerable you can have it converted to a string containing a list of all values:

   1: public interface IAsStringList {
   2:   [DictionaryStringList]
   3:   int[] Values { get; set; }
   4: }
   5:  
   6: [Test]
   7: public void ConvertPropertyToStringList() {
   8:   var wrapper = factory.GetAdapter<IAsStringList>(dict);
   9:   wrapper.Values = new[] {1, 2, 3, 4, 5};
  10:  
  11:   Assert.AreEqual("1,2,3,4,5", dict["Values"]);
  12: }

Several aspects are configurable, like the separator character.

Adapting complex properties

You are not limited to using "flat" interfaces with only simple types. You can also create an adapter that automatically adapts complex properties:

   1: public interface IWithComponent {
   2:   [DictionaryComponent]
   3:   ISimple Simple { get; }
   4: }
   5:  
   6: [Test]
   7: public void CanUseComponent() {
   8:   var wrapper = factory.GetAdapter<IWithComponent>(dict);
   9:   wrapper.Simple.Name = "Andre";
  10:   wrapper.Simple.Age = 26;
  11:   Assert.AreEqual("Andre", dict["Simple_Name"]);
  12:   Assert.AreEqual(26, dict["Simple_Age"]);
  13: }

As you can see IWithComponent.Simple is automatically populated with an adapter itself.

Conclusion

DictionaryAdapter provides means to access the content of a dictionary in an elegant, type safe way. It is a great tool to get rid of strings and to to make your source code robust and refactorable.

Pro

  • Easy to use
  • Very flexible
  • Type safe access even to IDictionary
  • Great tool to improve robustness of code

Cons

  • One time overhead (creating adapters)
  • Memory overhead (in-memory assemblies created by the factory)

Download source code: DictionaryAdapterTests.cs (2.97 kb)

Posted in: Patterns | Snippets | Tools

Tags: , ,

.NET 3.5 setup woes

June 5, 2008 at 1:32 AMAndre Loker

Today I tried to install .NET 3.5 on one of my web servers. It's a virtual server running Windows Server 2003 Standard x64 under Virtuozzo. I downloaded the complete redistributable (197 MB). The setup, however, failed with an error message that was not too comprehensible:

[06/05/08,00:51:10] XPSEPSC x64 Installer: [2] Error code 1603 for this component means "Fatal error during installation."
[06/05/08,00:51:10] XPSEPSC x64 Installer: [2] Setup Failed on component XPSEPSC x64 Installer

So there was something wrong with the XPSEPSC component (until know I never even heard of it). After digging through heaps of installation logs I found a file called dd_XPS_x64.txt in %USER%\Local Settings\Temp\28. That file appeared to be the installation log file for the XPSEPSC component. At least it was a bit more chatty:

3.078: XpsEPSC Setup encountered an error:  Setup could not verify the integrity of the file Update.inf.  Make sure the Cryptographic service is running on this computer.

Nice clue, I thought, expect that the cryptographic service was already running in automatic mode. At least I somehow managed to find this knowledge base article that referred to the error message. Already the first method that was described in there helped a lot:

Method 1: Rename the Edb.log file

Rename the Edb.log file, and then try to install the program again. To rename the Edb.log file, follow these steps:

1. Click Start, click Run, type cmd in the Open box, and then OK.
Note On a Windows Vista-based computer, click Start, type cmd in the Start Search text box, right-click cmd.exe, and then click Run as administrator.

2.At the command prompt, type the following command, and then press ENTER:

ren %systemroot%\system32\catroot2\Edb.log *.tst

After renaming Edb.log I ran the setup again. During installation a different error message popped up complaining about the VC++ runtime:

The application has requested the Runtime to terminate it in an unusual way.

I already thought setup was to fail again, but interestingly it completed successfully and told me that .NET 3.5 was installed correctly. Really, I had never come up with this myself, so I'm really glad I found the KB article.

By the way: the reason why the VC++ runtime error occurred was likely that the printer spooler service was disabled (and it is on my web server). Right, the printer spooler. Who on earth should predict that? At least there are blog posts about it, e.g. here and here. Just out of curiosity I started the printer spooler, re-ran the .NET 3.5 installer and chose the repair function (go get some coffee, it takes aeons to complete). At least during the repair no error occurred, so I assume everything's fine now.

Posted in: Other | Tools

Tags: , ,

MonoRail, AspView and ReSharper: skip ViewAtDesignTime

May 24, 2008 at 1:53 AMAndre Loker

AspView and Intellisense

AspView is a MonoRail viewengine which provides compile time checking of views and Intellisense support in Visual Studio. The latter is achieved by using the known ASP.NET WebForms syntax to define views, see below for an example:

aspview_viewatdesigntime

It uses the @Page directive and sets the Inherits attribute to a base class called ViewAtDesignTime defined by AspView. This base class contains fake methods and properties that are similar to the ones that are available to the runtime view class - Caslte.MonoRail.Views.AspView.AspViewBase. The only purpose of the ViewAtDesignTimeClass is to make Visual Studio's Intellisense work. It does, as can be seen in the picture. Additionally the user can provide a custom pair of design time and runtime classes that derive from ViewAtDesignTime and AspViewBase.

However, there is a caveat. ViewAtDesignTime derives from System.Web.UI.Page. This is a requirement of Visual Studio to make Intellisense work. As a result Intellisense will show all members of Page, which is at least confusing. More likely, it's misleading as many of the Members won't be available in AspViewBase and will let compilation fail.

aspview_intellisense

In the example above Intellisense shows members like DataBind which will not be available during compilation.

ReSharper to the rescue: skip ViewAtDesignTime

However, as Ken Egozi pointed out on the Castle Developer mailing list, if you use ReSharper (and you should) you can skip the ViewAtDesignTime class altogether and just use the AspViewBase class (or a derived class) in the view. ReSharper will still provide Intellisense. The list of members is much more reliable as those members will definitively be available during compilation.

aspview_resharper

Advantages of using the AspViewBase based class instead of ViewAtDesignTime:

  • Get better Intellisense: Page members are ommitted
  • Less code: if you provide your own base classes for views because you want to provide some extra members to the view (a good example would be the code generated by CodeGenerator) you do not have to provide a design time counterpart as well.
  • More robust code: similarly, if you have to provide a runtime class and a design time class you have to ensure that both interfaces match exactly, otherwise the design support is misleading (this problem does actually happen). Without the design time view, only one interface has to be maintained.

Disadvantages:

  • No Intellisense on dev machines without ReSharper (which is a good excuse to ask your boss for a R# licence anyway)

Thanks to Ken Egozi for this neat hint.

Posted in: Castle | Tools

Tags: , , ,

Web Deployment Projects in Visual Studio 2008

April 14, 2008 at 10:27 PMAndre Loker

Seit kurzem hatte ich das Problem, dass beim Öffnen von Projektmappen, die ein oder mehrere Web Deployment Projects enthielten, jedes Mal der Conversion Wizard von Visual Studio erschien, um das Projekt in das "aktuelle" Format zu übertragen.

Offensichtlich war ich damit nicht alleine, wie aus den Kommentaren der WDP Ankündigung zu lesen war. Glücklicherweise bot Vishal R. Joshi an, auf Anfrage per Mail eine gepatchte DLL zu versenden, mit der das Problem behoben werden sollte. Gesagt, getan. Innerhalb weniger Stunden erhielt ich eine freundliche Mail von Herrn Joshi inklusive der DLL. Mit letzterer brauchte nur noch die ältere Version in %Program Files%\Microsoft Visual Studio 9.0\Common7\Packages überschrieben werden. Und siehe da: der Conversion Wizard erscheint nicht mehr.

Vielen Dank an Vishal R. Joshi für seine schnelle und freundliche Unterstützung. Vermutlich wird eine Lösung für das Problem kurzfristig auch offiziell veröffentlicht. Laut Mr Joshi gibt es zurzeit lediglich noch einige logistische Verzögerungen.

Posted in: Tools | Visual Studio

Tags: ,

Webcam verhindert Update von Thunderbird

February 27, 2008 at 10:14 AMAndre Loker

Bei mir brach heute das Update von Thunderbird auf Version 2.0.0.12 mit folgender Meldung ab:

Software-Update fehlgeschlagen. Eine oder mehrere Dateien konnte nicht aktualisiert werden. Bitte schließen Sie alle anderen Anwendungen, und stellen Sie sicher, dass Sie ausreichende Benutzerrechte besitzen, um Dateien zu verändert. Starten Sie Thunderbird dann nochmals, um es erneut zu versuchen.

Nach diversen fehlgeschlagenen Versuchen bin ich in einem Forum auf den Tipp gestoßen, dass ein WebCam-Treiber womöglich Dateien von Thunderbird blockiert. Und tatsächlich, nachdem ich den Prozess "Webcam10.exe" mittels Taskmanager beendet hatte, funktionierte auch wieder das Update. Man muss nur mal drauf kommen...

Posted in: Tools

Tags: