AutoMapper 3.0, Portable Class Libraries and PlatformNotSupportedException

One of the major additions of AutoMapper 3.0 is the support of multiple frameworks through Portable Class Libraries. A Portable Class Library (PCL) allows a library developer to easily target multiple frameworks, as Scott Hanselman pointed on in a blog on cross platform development. This allows me as the developer of AutoMapper to target, in 3.0:

  • .NET 4 and higher
  • Silverlight 4 and higher (yes I know it’s pretty much dead, but this was as easy as me checking a checkbox)
  • Windows Phone 8
  • Windows Store apps
  • And in AutoMapper 3.1 pre-release, MonoDroid (and hopefully soon MonoTouch)

I had Silverlight 4 support a while back, but it was a huge pain to support as I had to effectively manage two codebases and a bunch of compiler directives separating the two.

With PCLs, I manage one codebase and let the PCL itself manage API differences. Another big plus is the compiler letting you know when members and types aren’t available based on the frameworks you’ve chosen.

Which brings me to the above exception, “Platform Not Supported”.

When things are different

In my previous mode, if I had differences between libraries I’d use compiler directives (#if) to conditionally include code. In PCLs, those compiler directives don’t exist. So how do I include different code for different runtimes? For example, AutoMapper supports mapping to interfaces, but that requires Reflection.Emit, which is only included in the .NET 4 target pack.

But I don’t want to lose that feature!

There are many other examples of missing types, but the end result is that I have to create separate targeting assemblies for “Extra Features” supported by your runtime. So the assemblies created from the build are:

  • AutoMapper.dll <- the core PCL
  • AutoMapper.Net4.dll
  • AutoMapper.SL4.dll
  • AutoMapper.WP75.dll
  • AutoMapper.WinRT.dll

Those are all easy to manage, just a handful of different classes in each. It’s small things like .NET 4 having System.Data, the others not, to bigger things like SL4 using the slower reflection calls and the other platforms using highly optimized calls.

In order to dynamically load different behavior at runtime, I use dependency injection to test for the existence of those other assemblies, and check for classes that override behavior. Sometimes, I don’t find what I need, and I throw a PlatformNotSupportedException – something that should never, ever be seen at runtime.

MSBuild and copying references

MSBuild has an interesting (ahem) algorithm in how it determines which assembly references to copy for project references. It actually doesn’t use what your project references, but what your assembly references. That’s extremely problematic for types discovered at runtime. Suppose you have the following project structure/references:

  • Core
    • AutoMapper.dll
    • AutoMapper.Net4.dll
  • UI
    • Core

Because Core references AutoMapper, then AutoMapper.dll will be copied over. However, AutoMapper.Net4.dll is NOT directly references, it only contains classes discovered at runtime. At compile time, UI looks like this:

  • UI
    • Core
    • AutoMapper.dll

We have an incomplete assembly, and pieces in Core trying to use features that don’t exist in UI. So how can we fix this? Rather easily, add a reference to AutoMapper in UI:

  • UI
    • Core
    • AutoMapper.dll
    • AutoMapper.Net4.dll

That’s a fix that anyone using AutoMapper 3.0 can do. I’ve run into this problem before in .NET for many, many years. Assemblies references in web.config, but not in code, are not copied over either. It’s just funny in my situation.

Fix going forward

I intend to fix this issue in AutoMapper 3.1. I never ran into it simply because I just reference AutoMapper in my application projects. However, this behavior is not completely obvious, nor is the exception helpful (although it is semantically correct). My options include:

  • A build warning, saying you must add a reference in every project. This is what the Microsoft BCL team does.
  • Modify the NuGet package to also treat AutoMapper.Net4.dll as content, copied always. This seems like a viable, but weird, strategy
  • Force users of AutoMapper to reference the derived package by moving some core, always used class (like the Mapper class) to the .Net4.dll, .SL4.dll etc. assemblies. I REALLY don’t like this approach – clients that have a “Core” PCL project now can’t use AutoMapper there – and that sort of defeats the purpose

Likely I’ll go either the first or second option, but for now, just add the AutoMapper package to the applications that use them.

And if you whine about UI projects shouldn’t reference this library directly because of some silly faux architect-y reason (even referencing a certain smelly round vegetable), I will drive to your house and slap you silly. Get off your high horse and start being productive.

About Jimmy Bogard

I'm a technical architect with Headspring in Austin, TX. I focus on DDD, distributed systems, and any other acronym-centric design/architecture/methodology. I created AutoMapper and am a co-author of the ASP.NET MVC in Action books.
This entry was posted in AutoMapper. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • “And if you whine about UI projects shouldn’t reference this library directly because of some silly faux architect-y reason (even referencing a certain smelly round vegetable), I will drive to your house and slap you silly. Get off your high horse and start being productive.”

    Thank you for that. :-)

  • Pingback: The Morning Brew - Chris Alcock » The Morning Brew #1435()

  • NuGet is capable of installing different references per-platform. I use this in my PCL libraries to install the core everywhere and the platform-specific dll just in the appropriate platforms.

    This has a nice side effect that my library supports PCLs (just installing the PCL core of my library), and then if those PCLs also use NuGet, they just have to declare a dependency on my library. Then when an end-user with a concrete application installs their PCL, it pulls in my library with both core and platform dlls.

    Details on my blog:

    • jbogard

      I’m not sure that would help folks getting this error.

      • OK, I took a look at your nuspec and re-read your blog post. You’re saying that both “Core” and “UI” are end-user projects. I see the problem now.

        One thing you should consider is Microsoft.Bcl.Build, which provides the groundwork for the build errors. I *think* just adding that package as a dependency will handle the build error for you, but if not it shouldn’t be hard to hook into.

  • Pingback: Windows Store Developer Links – 2013-09-06 | Dan Rigby()

  • Carlos Ribas

    “And if you whine about UI projects shouldn’t reference this library directly because of some silly faux architect-y reason (even referencing a certain smelly round vegetable), I will drive to your house and slap you silly. Get off your high horse and start being productive.”

    Mapping is a cross-cutting concern. Using a cross-cutting concern to poo-poo layered software design is disingenuous. Faux-pragmatism isn’t any better…

    With great power comes great responsibility. Be mindful of what you endorse :)

    • jbogard

      I have absolutely nothing against layered software design. Using project structure to do so is a waste of time if you only have 1 app you’re deploying (I was getting complaints from people that they didn’t want their UI project referencing AutoMapper, in a single, standalone web app. Silly.)

      • Carlos Ribas

        I’m saying every layer should reference things like mapping helpers. Thus, I think we agree on that.

        I’m only objecting to the apparent context-insensitive dismissal of relatively important architectural/design principles. Some might read your closing comment as an endorsement to just use whatever you need in any layer if it helps you “start being productive.” A more nuanced piece of advice might be to recognize cross-cutting concerns and treat them appropriately.

        There’s room for both cross-cutting concerns and guiding principles like SOLID in a productive environment. Especially if productivity is measured over longer-term, not just pre-launch.

        • jbogard

          Yeah, but not wanting to add a reference because you don’t want that layer to have a direct reference is the silly thing. That’s what I was referring to, not that layers are bad or anything like that.

          I also run into this when folks don’t want to reference NHibernate/EF from a UI project, just silly. Adding references versus USING the references are two separate concerns.

  • Rajesh Patel

    Sweet. I was having the same problem with my Business Domain project. So by adding an additional reference into my UI I have resolved the problem. Cheers!

  • Pingback: Unit testing with AutoMapper using DeploymentItem attribute not working – PlatformNotSupportedException | Technology & Programming Answers()

  • Pingback: Converting AutoMapper to a Portable Class Library | Jimmy Bogard's Blog()

  • Nilesh Ahir

    Hi Jimmy,
    What is use of automapper.net4.dll?


    • jbogard

      It’s for platform-specific extensions. For example, IDataReader doesn’t exist in Windows Phone, so trying to use it means my project won’t even compile. That assembly provides extensions available on the .NET 4 framework that don’t exist across _all_ platforms.