U4-8984 - Upgrade AutoMapper to latest 3.x

Created by Claus Jensen 16 Sep 2016, 19:05:11 Updated by Sebastiaan Janssen 27 Oct 2016, 16:40:31

Relates to: U4-9109

Note: this is marked as not backwards compatible because something seems to have changed in AutoMapper when used in conjunction with AutoFac. That specific problem has been fixed for 7.5.5. Make sure to read this comment when you're using 7.5.4 and AutoFac (possibly other IoC containers might have the same problem): http://issues.umbraco.org/issue/U4-9109#comment=67-32150

Note2: the API for ResolveUsing has changed, look here if you get any build errors: https://www.odity.co.uk/articles/automapper-3-2-1-the-call-is-ambiguous-between-the-following-methods-or-properties/

... without breaking compatibility with packages built against v.3.0.0 (unsigned assembly)

Comments

Claus Jensen 16 Sep 2016, 19:27:58

PR: https://github.com/umbraco/Umbraco-CMS/pull/1483

The changes in UmbracoApplicationBase allows us to load the signed assembly without having to recompile packages compiled against v. 3.0.0 of AutoMapper.

We however still need a binding redirect in the web.config for packages that are built against 3.0.0, to be redirected to this version. I'm not sure how we usually handle these .. do they go in minor versions or?


Sebastiaan Janssen 19 Sep 2016, 07:22:27

For review: this requires a thorough test:

  • Create a 7.5.3 site
  • Have a separate class library which references UmbracoCms.Core
  • In the class library use a method in Automapper, compile, drop the dll in the 7.5.3 site
  • Upgrade the 7.5.3 site to this commit, do not upgrade the class library - the class libary dll has to be the same as it was before
  • Check if the site runs and if the Automapper method still works


Sebastiaan Janssen 19 Sep 2016, 07:27:37

Re-opened because of the missing binding redirect.

Do you have any information as to why this change to UmbracoApplicationBase would make dlls compiled against v3.0.0 still work (the comments mention stackoverflow, got a link)?


Claus Jensen 19 Sep 2016, 07:47:48

Cool - I'll fix up the config transform .. that's the part I wasn't really sure how we needed to handle.

The reason why it doesn't work is because from version 3.1+, AutoMapper changed to using a signed assembly instead of being unsigned (meaning the PublicKeyToken is now a guid instead of just being null.

Binding redirects will only affect version numbers, so even if you have a binding redirect covering v. 3.0.0 redirecting it to 3.3.1 (which would normally make packages built against the old version number work with the newer one without having to recompile) - it will try to load this new assembly with the same signature (without the PublicKeyToken) which will obviously fail as the new signed assembly requires that.

This is what has been keeping us from at least upgrading to the latest v.3 (3.3.1).

The change in UmbracoApplicationBase hooks into a method that only runs when an assembly fails to resolve - and here you can do some retry logic to try to locate the assembly elsewhere in case of a missing assembly... or you can try to load the assembly with an added signature (as it requires in our case).

Yes - the comments mention stackoverflow ... as in https://msdn.microsoft.com/en-us/library/system.stackoverflowexception ... due to the infinite loop you could be causing if you don't return null (a failed resolve here will just trigger the resolver again so we have to return null at some point).


Claus Jensen 20 Sep 2016, 07:13:14

Added a config transform for nuget upgrade: https://github.com/umbraco/Umbraco-CMS/pull/1483/commits/c9106065fda4ff535e6bf8f7f3a80bf926b5264f


Sebastiaan Janssen 20 Sep 2016, 15:51:27

Okay, sounds plausible! ;-)

FYI I think we'll likely target this for 7.6.0 because if people forget to apply the binding redirect they're in upgrade hell and we want to keep patch releases as smooth and easy as possible. Let's discuss tomorrow.


Claus Jensen 20 Sep 2016, 17:36:46

7.6 is fine with me - it's not really an urgent upgrade ... we've been on 3.0.0 forever anyways :)


Shannon Deminick 22 Sep 2016, 13:28:54

This is the code I'm using for NH re-mappings in Courier:

[assembly: PreApplicationStartMethod(typeof(NhRebinding), "Initialize")]

namespace Umbraco.Courier.Persistence.V6.NHibernate
{

    public static class NhRebinding
    {
        public static void Initialize()
        {
            // this only gets called when an assembly can't be resolved
            AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
        }

        private static readonly Regex NhAssemblyPattern = new Regex("NHibernate, Version=([\\d\\.]+?), Culture=neutral, PublicKeyToken=aa95f207798dfdb4", RegexOptions.Compiled);
        private const string NhReplacement = "NHibernate, Version=3.4.0.0, Culture=neutral, PublicKeyToken=aa95f207798dfdb4";
        /// <summary>
        /// This is used to do an assembly binding redirect of NHibernate if it does not exist in the web.config
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        /// <returns></returns>
        private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
        {
            if (NhAssemblyPattern.IsMatch(args.Name))
            {
                return Assembly.Load(NhAssemblyPattern.Replace(args.Name, NhReplacement));
            }
    
            return null;
        }
    }


Shannon Deminick 28 Sep 2016, 13:39:21

Have tested upgrade + new installs. Had to fix some changes with regards to config transforms, see rev: 248177f518908dd531e6d36553c4d4dc726f8396


Jeroen Breuer 24 Oct 2016, 11:47:20

I think that upgrading to Automapper 3.3 should be marked as a breaking change. After the upgrade our code didn't build anymore. We had this error: https://www.odity.co.uk/articles/automapper-3-2-1-the-call-is-ambiguous-between-the-following-methods-or-properties/. It was easy to fix, but still a breaking change.


Dave Woestenborghs 24 Oct 2016, 11:49:37

This also breaks this package on 7.5.4 because of Automapper usage : https://github.com/dawoe/umbraco-loadbalancing-dashboard/blob/master/Sources/Our.Umbraco.LoadBalancingDashboard/BootStrapper.cs


Jonathan Roberts 24 Oct 2016, 12:06:01

Hi, Latest version of Umbraco 7.5.4 Automapper breaks Archetype. I am getting a 500 internal server error when I try to edit my Archetype blocks.


Jeroen Breuer 24 Oct 2016, 12:07:57

After upgrading to Umbraco 7.5.4 I get this error on each node that I open:

An error occured

Mapping types: Content -> UserBasic Umbraco.Core.Models.Content -> Umbraco.Web.Models.ContentEditing.UserBasic

Destination path: ContentItemDisplay.Owner.Owner

Source value: Umbraco.Core.Models.Content

EXCEPTION DETAILS

AutoMapper.AutoMapperMappingException:

Mapping types: Content -> UserBasic Umbraco.Core.Models.Content -> Umbraco.Web.Models.ContentEditing.UserBasic

Destination path: ContentItemDisplay.Owner.Owner

Source value: Umbraco.Core.Models.Content STACKTRACE

at Umbraco.Web.Editors.ContentController.GetById(Int32 id) at lambda_method(Closure , Object , Object[] ) at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.<>c__DisplayClass10.b__9(Object instance, Object[] methodParameters) at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ExecuteAsync(HttpControllerContext controllerContext, IDictionary`2 arguments, CancellationToken cancellationToken) --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Web.Http.Controllers.ApiControllerActionInvoker.d__0.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Web.Http.Filters.ActionFilterAttribute.d__5.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Web.Http.Filters.ActionFilterAttribute.d__5.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Web.Http.Filters.ActionFilterAttribute.d__0.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Web.Http.Filters.ActionFilterAttribute.d__5.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Web.Http.Filters.ActionFilterAttribute.d__5.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Web.Http.Filters.ActionFilterAttribute.d__0.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Web.Http.Filters.ActionFilterAttribute.d__5.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Web.Http.Filters.ActionFilterAttribute.d__5.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Web.Http.Filters.ActionFilterAttribute.d__0.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Web.Http.Filters.ActionFilterAttribute.d__5.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Web.Http.Filters.ActionFilterAttribute.d__5.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Web.Http.Filters.ActionFilterAttribute.d__0.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Web.Http.Filters.ActionFilterAttribute.d__5.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Web.Http.Filters.ActionFilterAttribute.d__5.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Web.Http.Filters.ActionFilterAttribute.d__0.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Web.Http.Filters.ActionFilterAttribute.d__5.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Web.Http.Filters.ActionFilterAttribute.d__5.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Web.Http.Filters.ActionFilterAttribute.d__0.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Web.Http.Controllers.ActionFilterResult.d__2.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Web.Http.Filters.AuthorizationFilterAttribute.d__2.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Web.Http.Filters.AuthorizationFilterAttribute.d__2.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Web.Http.Filters.AuthorizationFilterAttribute.d__2.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Web.Http.Dispatcher.HttpControllerDispatcher.d__1.MoveNext() INNER EXCEPTION

Autofac.Core.Registration.ComponentNotRegisteredException: The requested service 'Umbraco.Web.Models.Mapping.OwnerResolver1[[Umbraco.Core.Models.IContent, Umbraco.Core, Version=1.0.6136.27241, Culture=neutral, PublicKeyToken=null]]' has not been registered. To avoid this exception, either register a component to provide the service, check for service registration using IsRegistered(), or use the ResolveOptional() method to resolve an optional dependency. at Autofac.ResolutionExtensions.ResolveService(IComponentContext context, Service service, IEnumerable1 parameters) at Autofac.ResolutionExtensions.Resolve(IComponentContext context, Type serviceType) at AutoMapper.MappingExpression2.<>c__DisplayClass3a1.b__39(ResolutionContext context) in c:\dev\AutoMapper\src\AutoMapper\Internal\MappingExpression.cs:line 554 at AutoMapper.DeferredInstantiatedResolver.Resolve(ResolutionResult source) in c:\dev\AutoMapper\src\AutoMapper\Internal\DeferredInstantiatedResolver.cs:line 16 at System.Linq.Enumerable.Aggregate[TSource,TAccumulate](IEnumerable1 source, TAccumulate seed, Func3 func) at AutoMapper.Mappers.TypeMapObjectMapperRegistry.PropertyMapMappingStrategy.MapPropertyValue(ResolutionContext context, IMappingEngineRunner mapper, Object mappedObject, PropertyMap propertyMap) in c:\dev\AutoMapper\src\AutoMapper\Mappers\TypeMapObjectMapperRegistry.cs:line 116


Umbraco 24 Oct 2016, 12:18:06

Can you confirm that the assembly binding redirect is in your web.config?


Mikkel Holck Madsen 24 Oct 2016, 12:19:22

The assemblyBinding should look like this

      <dependentAssembly>
        <assemblyIdentity name="AutoMapper" publicKeyToken="be96cd2c38ef1005" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-3.3.1.0" newVersion="3.3.1.0" />
      </dependentAssembly>


Jeroen Breuer 24 Oct 2016, 12:20:01

I added the assemblyBinding, but that didn't help. Looks like it's an issue with autofac and the newest automapper.


Jonathan Roberts 24 Oct 2016, 12:30:39

I added it and it worked


Sebastiaan Janssen 24 Oct 2016, 12:30:59

Created a new issue to look into this http://issues.umbraco.org/issue/U4-9109


Priority: Normal

Type: Bug

State: Fixed

Assignee:

Difficulty: Normal

Category:

Backwards Compatible: False

Fix Submitted:

Affected versions: 7.5.3

Due in version: 7.5.4

Sprint: Sprint 43

Story Points:

Cycle: