AutoMapper for Silverlight 3.0 Alpha
In between workshops here at the MVP Summit, I’ve been working on pushing out an early Alpha for an AutoMapper version built against Silverlight 3.0. Thanks to some existing patches from the community, it was pretty straightforward to get things going. I’ve also started working against an AutoMapper github repository, which also made some other things much, much easier (which I’ll touch on soon). To get the alpha, grab the binaries from the CodePlex site:
AutoMapper for Silverlight 3.0 Alpha
All existing features of AutoMapper 1.0 are supported, except for:
- IDataReader
- IListSource
Neither of which even exist in Silverlight 3.0. Since I don’t do any Silverlight development, I labeled this one as “Alpha” as I’ll need to rely on folks in the wild to let me know if it actually works or not.
Supporting two runtimes
Before this whole conversion process, I had almost zero experience with Silverlight 3.0. I vaguely remembered that there was a separate runtime, but it’s really interesting how it all works out. To get AutoMapper working with Silverlight, I had several big obstacles:
- Some assemblies don’t exist in Silverlight
- Some types don’t exist in Silverlight
- Some types have only partial members defined
- Some types only have some method overloads defined
Some goals I have is that going foward:
- Any feature added to AutoMapper will also get added to the Silverlight version
- Unit tests are executed against the Silverlight version
- Any test added to AutoMapper will also get added to the Silverlight version
Project setup
To support two runtimes against basically one codebase, I opted for a model where I create Silverlight class libraries, then use linked files to keep the source the same. Linked files allow me to keep only one source code file on disk (and in source control), but reference the same file from two projects. I created a new project, AutoMapper.Silverlight, and a new unit tests project as well. Some files weren’t needed as the feature wouldn’t be supported (such as IDataReader), so I just didn’t link that file in.
When things don’t line up
If a whole file isn’t supported, that’s fine, I just don’t add it. But what if the API is different? For example, Silverlight has no TypeDescriptor class. In those cases, I relied on conditional compilation to provide two implementations in one file:
private static TypeConverter GetTypeConverter(ResolutionContext context) { #if !SILVERLIGHT return TypeDescriptor.GetConverter(context.SourceType); #else var attributes = context.SourceType.GetCustomAttributes(typeof(TypeConverterAttribute), false); if (attributes.Length != 1) return new TypeConverter(); var converterAttribute = (TypeConverterAttribute)attributes[0]; var converterType = Type.GetType(converterAttribute.ConverterTypeName); if (converterType == null) return new TypeConverter(); return Activator.CreateInstance(converterType) as TypeConverter; #endif }
Because I opened this file from the regular project, the Silverlight code is grayed out. Opening the file from the Silverlight project makes all that extra code available, while the top part is commented out. For places where I just couldn’t support features, I’d just leave them out:
public static Func<IEnumerable<IObjectMapper>> AllMappers = () => new IObjectMapper[] { #if !SILVERLIGHT new DataReaderMapper(), #endif new TypeMapMapper(TypeMapObjectMapperRegistry.AllMappers()), new StringMapper(), new FlagsEnumMapper(), new EnumMapper(), new ArrayMapper(), new EnumerableToDictionaryMapper(), new DictionaryMapper(), #if !SILVERLIGHT new ListSourceMapper(), #endif new EnumerableMapper(), new AssignableMapper(), new TypeConverterMapper(), new NullableMapper() }; }
It’s slightly ugly, but the places where I have to do this are very small. Luckily, things are reasonably factored out that individual features are usually individual classes.
Third-party libraries
AutoMapper uses proxy classes to support mapping to interfaces. Originally, I went with LinFu to do this dynamic proxy stuff because it was small, targeted and focused on what I needed to do. Unfortunately, there is not a Silverlight version, so I switched to Castle Dynamic Proxy to get things going.
It was a very straightforward switch, as the APIs are quite similar. The only big fix I had to do was update the IL Merge business, and make sure that I excluded the right types so that interface mapping worked when everything was merged up.
Testing
Silverlight unit testing is…weird. A lot of the documentation out there talks about executing unit tests in a Silverlight application, which is not what I’m interested in. For NUnit, I merely create a regular class library, and test runners run the test in whatever environment they want. I needed to find an NUnit version supported for Silverlight, and that was not easy to find. I went through around three or four different builds out there before I found one I liked:
http://wesmcclure.tumblr.com/post/152727000
I also use NBehave, but I didn’t really feel like porting NBehave over as well, so I just grabbed the source files I needed and included them directly in my testing project.
Executing the tests from NAnt was also completely straightforward, and worked as soon as my TestDriven.NET worked.
Packaging
Based on community feedback, I built the Silverlight-based AutoMapper assembly as a new assembly name, “AutoMapper.Silverlight.dll”. The only other work I needed to do was duplicate the packaging NAnt tasks, and make sure I included the relevant Silverlight assemblies into my source repository so that the automated build would work. Installing Silverlight on a build machine is just a bad idea.
Final thoughts
The conversion was smoother than I thought it would be. The biggest hurdles I had were just getting the unit tests running. The unit testing framework I use doesn’t have a supported Silverlight build, so I had to do quite a bit of hunting to find one that works. But other than that, linked files and conditional compilation made things quite easy to do. Also, playing around on github with easy branching and merging made adding intermittent patches to master very easy to do. In fact, I was even able to cherry pick a single commit to push back to master from a Silverlight branch…but my git experience should wait for another post.
Thanks again to everyone that sent me patches to get Silverlight working with AutoMapper.