diff --git a/src/Umbraco.Core/Packaging/PackageBinaryInspector.cs b/src/Umbraco.Core/Packaging/PackageBinaryInspector.cs index 1d2ef10c10..9e68cda0fb 100644 --- a/src/Umbraco.Core/Packaging/PackageBinaryInspector.cs +++ b/src/Umbraco.Core/Packaging/PackageBinaryInspector.cs @@ -62,28 +62,39 @@ namespace Umbraco.Core.Packaging var files = Directory.GetFiles(dllPath, "*.dll"); var dllsWithReference = new List(); var errors = new List(); - var assembliesWithErrors = new List(); + var assembliesWithErrors = new List(); //we need this handler to resolve assembly dependencies below AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += (s, e) => { - var a = Assembly.ReflectionOnlyLoadFrom(GetAssemblyPath(Assembly.ReflectionOnlyLoad(e.Name))); + var a = Assembly.ReflectionOnlyLoad(e.Name); if (a == null) throw new TypeLoadException("Could not load assembly " + e.Name); return a; }; - //First load each dll file into the context - foreach (var f in files) Assembly.ReflectionOnlyLoadFrom(f); - - //Then load each referenced assembly into the context - foreach (var f in files) + //First load each dll file into the context + var loaded = files.Select(Assembly.ReflectionOnlyLoadFrom).ToList(); + + //load each of the LoadFrom assemblies into the Load context too + foreach (var a in loaded) { - var reflectedAssembly = Assembly.ReflectionOnlyLoadFrom(f); - foreach (var assemblyName in reflectedAssembly.GetReferencedAssemblies()) + Assembly.ReflectionOnlyLoad(a.FullName); + } + + //get the list of assembly names to compare below + var loadedNames = loaded.Select(x => x.GetName().Name).ToArray(); + + //Then load each referenced assembly into the context + foreach (var a in loaded) + { + //don't load any referenced assemblies that are already found in the loaded array - this is based on name + // regardless of version. We'll assume that if the assembly found in the folder matches the assembly name + // being looked for, that is the version the user has shipped their package with and therefore it 'must' be correct + foreach (var assemblyName in a.GetReferencedAssemblies().Where(ass => loadedNames.Contains(ass.Name) == false)) { try { - Assembly.ReflectionOnlyLoadFrom(GetAssemblyPath(Assembly.ReflectionOnlyLoad(assemblyName.FullName))); + Assembly.ReflectionOnlyLoad(assemblyName.FullName); } catch (FileNotFoundException) { @@ -92,7 +103,7 @@ namespace Umbraco.Core.Packaging string.Concat("This package references the assembly '", assemblyName.Name, "' which was not found")); - assembliesWithErrors.Add(f); + assembliesWithErrors.Add(a); } catch (Exception ex) { @@ -101,7 +112,7 @@ namespace Umbraco.Core.Packaging string.Concat("This package could not be verified for compatibility. An error occurred while loading a referenced assembly '", assemblyName.Name, "' see error log for full details.")); - assembliesWithErrors.Add(f); + assembliesWithErrors.Add(a); LogHelper.Error("An error occurred scanning package assemblies", ex); } } @@ -110,34 +121,62 @@ namespace Umbraco.Core.Packaging var contractType = GetLoadFromContractType(); //now that we have all referenced types into the context we can look up stuff - foreach (var f in files.Except(assembliesWithErrors)) + foreach (var a in loaded.Except(assembliesWithErrors)) { //now we need to see if they contain any type 'T' - var reflectedAssembly = Assembly.ReflectionOnlyLoadFrom(f); - - var found = reflectedAssembly.GetExportedTypes() - .Where(contractType.IsAssignableFrom); - - if (found.Any()) + var reflectedAssembly = a; + + try { - dllsWithReference.Add(reflectedAssembly.FullName); + var found = reflectedAssembly.GetExportedTypes() + .Where(contractType.IsAssignableFrom); + + if (found.Any()) + { + dllsWithReference.Add(reflectedAssembly.FullName); + } } + catch (Exception ex) + { + //This is a hack that nobody can seem to get around, I've read everything and it seems that + // this is quite a common thing when loading types into reflection only load context, so + // we're just going to ignore this specific one for now + var typeLoadEx = ex as TypeLoadException; + if (typeLoadEx != null) + { + if (typeLoadEx.Message.InvariantContains("does not have an implementation")) + { + //ignore + continue; + } + } + else + { + errors.Add( + string.Concat("This package could not be verified for compatibility. An error occurred while scanning a packaged assembly '", + a.GetName().Name, + "' see error log for full details.")); + assembliesWithErrors.Add(a); + LogHelper.Error("An error occurred scanning package assemblies", ex); + } + } + } errorReport = errors.ToArray(); return dllsWithReference; } + /// /// In order to compare types, the types must be in the same context, this method will return the type that - /// we are checking against but from the LoadFrom context. + /// we are checking against but from the Load context. /// /// /// private static Type GetLoadFromContractType() { - var contractAssemblyLoadFrom = Assembly.ReflectionOnlyLoadFrom( - GetAssemblyPath(Assembly.ReflectionOnlyLoad(typeof (T).Assembly.FullName))); + var contractAssemblyLoadFrom =Assembly.ReflectionOnlyLoad(typeof (T).Assembly.FullName); var contractType = contractAssemblyLoadFrom.GetExportedTypes() .FirstOrDefault(x => x.FullName == typeof(T).FullName && x.Assembly.FullName == typeof(T).Assembly.FullName);