More cleanup, cleans up tests, cleans up the BuildManagerAssemblyProvider (even though it's not used)

This commit is contained in:
Shannon
2020-03-09 13:31:56 +11:00
parent 0c8426f308
commit d92fc8736a
29 changed files with 426 additions and 378 deletions

View File

@@ -0,0 +1,102 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Security;
using Umbraco.Core.Exceptions;
namespace Umbraco.Core.Composing
{
/// <summary>
/// lazily load a reference to all local assemblies and gac assemblies
/// </summary>
/// <remarks>
/// This is a modified version of: http://www.dominicpettifer.co.uk/Blog/44/how-to-get-a-reference-to-all-assemblies-in-the--bin-folder
///
/// We do this because we cannot use AppDomain.Current.GetAssemblies() as this will return only assemblies that have been
/// loaded in the CLR, not all assemblies.
/// See these threads:
/// http://issues.umbraco.org/issue/U5-198
/// http://stackoverflow.com/questions/3552223/asp-net-appdomain-currentdomain-getassemblies-assemblies-missing-after-app
/// http://stackoverflow.com/questions/2477787/difference-between-appdomain-getassemblies-and-buildmanager-getreferencedassembl
/// </remarks>
public class BruteForceAssemblyProvider : IAssemblyProvider
{
public BruteForceAssemblyProvider()
{
_allAssemblies = new Lazy<HashSet<Assembly>>(() =>
{
HashSet<Assembly> assemblies = null;
try
{
//NOTE: we cannot use AppDomain.CurrentDomain.GetAssemblies() because this only returns assemblies that have
// already been loaded in to the app domain, instead we will look directly into the bin folder and load each one.
var binFolder = GetRootDirectorySafe();
var binAssemblyFiles = Directory.GetFiles(binFolder, "*.dll", SearchOption.TopDirectoryOnly).ToList();
assemblies = new HashSet<Assembly>();
foreach (var a in binAssemblyFiles)
{
try
{
var assName = AssemblyName.GetAssemblyName(a);
var ass = Assembly.Load(assName);
assemblies.Add(ass);
}
catch (Exception e)
{
if (e is SecurityException || e is BadImageFormatException)
{
//swallow these exceptions
}
else
{
throw;
}
}
}
//Since we are only loading in the /bin assemblies above, we will also load in anything that's already loaded (which will include gac items)
foreach (var a in AppDomain.CurrentDomain.GetAssemblies())
{
assemblies.Add(a);
}
}
catch (InvalidOperationException e)
{
if (e.InnerException is SecurityException == false)
throw;
}
return assemblies;
});
}
private readonly Lazy<HashSet<Assembly>> _allAssemblies;
private string _rootDir = string.Empty;
public IEnumerable<Assembly> Assemblies => _allAssemblies.Value;
// FIXME - this is only an interim change, once the IIOHelper stuff is merged we should use IIOHelper here
private string GetRootDirectorySafe()
{
if (string.IsNullOrEmpty(_rootDir) == false)
{
return _rootDir;
}
var codeBase = Assembly.GetExecutingAssembly().CodeBase;
var uri = new Uri(codeBase);
var path = uri.LocalPath;
var baseDirectory = Path.GetDirectoryName(path);
if (string.IsNullOrEmpty(baseDirectory))
throw new PanicException("No root directory could be resolved.");
_rootDir = baseDirectory.Contains("bin")
? baseDirectory.Substring(0, baseDirectory.LastIndexOf("bin", StringComparison.OrdinalIgnoreCase) - 1)
: baseDirectory;
return _rootDir;
}
}
}

View File

@@ -0,0 +1,36 @@
using System;
using System.Collections.Generic;
using System.Reflection;
namespace Umbraco.Core.Composing
{
/// <summary>
/// Returns a list of scannable assemblies based on an entry point assembly and it's references
/// </summary>
/// <remarks>
/// This will recursively search through the entry point's assemblies and Umbraco's core assemblies (Core/Web) and their references
/// to create a list of scannable assemblies based on whether they themselves or their transitive dependencies reference Umbraco core assemblies.
/// </remarks>
public class DefaultUmbracoAssemblyProvider : IAssemblyProvider
{
private readonly Assembly _entryPointAssembly;
private static readonly string[] UmbracoCoreAssemblyNames = new[] { "Umbraco.Core", "Umbraco.Web" };
public DefaultUmbracoAssemblyProvider(Assembly entryPointAssembly)
{
_entryPointAssembly = entryPointAssembly ?? throw new ArgumentNullException(nameof(entryPointAssembly));
}
public IEnumerable<Assembly> Assemblies
{
get
{
var finder = new FindAssembliesWithReferencesTo(new[] { _entryPointAssembly }, UmbracoCoreAssemblyNames, true);
foreach(var found in finder.Find())
{
yield return found;
}
}
}
}
}

View File

@@ -0,0 +1,55 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace Umbraco.Core.Composing
{
/// <summary>
/// Finds Assemblies from the entry point assemblies, it's dependencies and it's transitive dependencies that reference that targetAssemblyNames
/// </summary>
/// <remarkes>
/// borrowed and modified from here https://github.com/dotnet/aspnetcore-tooling/blob/master/src/Razor/src/Microsoft.NET.Sdk.Razor/FindAssembliesWithReferencesTo.cs
/// </remarkes>
internal class FindAssembliesWithReferencesTo
{
private readonly Assembly[] _referenceAssemblies;
private readonly string[] _targetAssemblies;
private readonly bool _includeTargets;
/// <summary>
/// Constructor
/// </summary>
/// <param name="referenceAssemblies">Entry point assemblies</param>
/// <param name="targetAssemblyNames">Used to check if the entry point or it's transitive assemblies reference these assembly names</param>
/// <param name="includeTargets">If true will also use the target assembly names as entry point assemblies</param>
public FindAssembliesWithReferencesTo(Assembly[] referenceAssemblies, string[] targetAssemblyNames, bool includeTargets)
{
_referenceAssemblies = referenceAssemblies;
_targetAssemblies = targetAssemblyNames;
_includeTargets = includeTargets;
}
public IEnumerable<Assembly> Find()
{
var referenceItems = new List<Assembly>();
foreach (var assembly in _referenceAssemblies)
{
referenceItems.Add(assembly);
}
if (_includeTargets)
{
foreach(var target in _targetAssemblies)
{
referenceItems.Add(Assembly.Load(target));
}
}
var provider = new ReferenceResolver(_targetAssemblies, referenceItems);
var assemblyNames = provider.ResolveAssemblies();
return assemblyNames.ToList();
}
}
}

View File

@@ -0,0 +1,13 @@
using System.Collections.Generic;
using System.Reflection;
namespace Umbraco.Core.Composing
{
/// <summary>
/// Provides a list of assemblies that can be scanned
/// </summary>
public interface IAssemblyProvider
{
IEnumerable<Assembly> Assemblies { get; }
}
}

View File

@@ -0,0 +1,115 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
namespace Umbraco.Core.Composing
{
/// <summary>
/// Resolves assemblies that reference one of the specified "targetAssemblies" either directly or transitively.
/// </summary>
/// <remarks>
/// Borrowed and modified from https://github.com/dotnet/aspnetcore-tooling/blob/master/src/Razor/src/Microsoft.NET.Sdk.Razor/ReferenceResolver.cs
/// </remarks>
internal class ReferenceResolver
{
private readonly HashSet<string> _umbracoAssemblies;
private readonly IReadOnlyList<Assembly> _assemblyItems;
private readonly Dictionary<Assembly, Classification> _classifications;
private readonly List<Assembly> _lookup = new List<Assembly>();
public ReferenceResolver(IReadOnlyList<string> targetAssemblies, IReadOnlyList<Assembly> assemblyItems)
{
_umbracoAssemblies = new HashSet<string>(targetAssemblies, StringComparer.Ordinal);
_assemblyItems = assemblyItems;
_classifications = new Dictionary<Assembly, Classification>();
foreach (var item in assemblyItems)
{
_lookup.Add(item);
}
}
public IEnumerable<Assembly> ResolveAssemblies()
{
var applicationParts = new List<Assembly>();
foreach (var item in _assemblyItems)
{
var classification = Resolve(item);
if (classification == Classification.ReferencesUmbraco || classification == Classification.IsUmbraco)
{
applicationParts.Add(item);
}
}
return applicationParts;
}
private Classification Resolve(Assembly assemblyItem)
{
if (_classifications.TryGetValue(assemblyItem, out var classification))
{
return classification;
}
// Initialize the dictionary with a value to short-circuit recursive references.
classification = Classification.Unknown;
_classifications[assemblyItem] = classification;
if (_umbracoAssemblies.Contains(assemblyItem.GetName().Name))
{
classification = Classification.IsUmbraco;
}
else
{
classification = Classification.DoesNotReferenceUmbraco;
foreach (var reference in GetReferences(assemblyItem))
{
// recurse
var referenceClassification = Resolve(reference);
if (referenceClassification == Classification.IsUmbraco || referenceClassification == Classification.ReferencesUmbraco)
{
classification = Classification.ReferencesUmbraco;
break;
}
}
}
Debug.Assert(classification != Classification.Unknown);
_classifications[assemblyItem] = classification;
return classification;
}
protected virtual IEnumerable<Assembly> GetReferences(Assembly assembly)
{
foreach (var referenceName in assembly.GetReferencedAssemblies())
{
// don't include if this is excluded
if (TypeFinder.KnownAssemblyExclusionFilter.Any(f => referenceName.FullName.StartsWith(f)))
continue;
var reference = Assembly.Load(referenceName);
if (!_lookup.Contains(reference))
{
// A dependency references an item that isn't referenced by this project.
// We'll construct an item for so that we can calculate the classification based on it's name.
_lookup.Add(reference);
yield return reference;
}
}
}
protected enum Classification
{
Unknown,
DoesNotReferenceUmbraco,
ReferencesUmbraco,
IsUmbraco,
}
}
}

View File

@@ -1,16 +1,11 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Metadata;
using System.Reflection.PortableExecutable;
using System.Security;
using System.Text;
using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Core.Exceptions;
using Umbraco.Core.Logging;
namespace Umbraco.Core.Composing
@@ -21,11 +16,6 @@ namespace Umbraco.Core.Composing
private readonly ILogger _logger;
private readonly IAssemblyProvider _assemblyProvider;
//public TypeFinder(ILogger logger, ITypeFinderConfig typeFinderConfig = null)
// : this(logger, new DefaultUmbracoAssemblyProvider(Assembly.GetEntryAssembly()?.GetName()?.Name), typeFinderConfig)
//{
//}
public TypeFinder(ILogger logger, IAssemblyProvider assemblyProvider, ITypeFinderConfig typeFinderConfig = null)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
@@ -151,7 +141,9 @@ namespace Umbraco.Core.Composing
"WebDriver,",
"itextsharp,",
"mscorlib,",
"nunit.framework,",
"NUnit,",
"NUnit3TestAdapter,",
"Selenium.",
};
/// <summary>
@@ -446,290 +438,4 @@ namespace Umbraco.Core.Composing
#endregion
}
/// <summary>
/// lazily load a reference to all local assemblies and gac assemblies
/// </summary>
/// <remarks>
/// This is a modified version of: http://www.dominicpettifer.co.uk/Blog/44/how-to-get-a-reference-to-all-assemblies-in-the--bin-folder
///
/// We do this because we cannot use AppDomain.Current.GetAssemblies() as this will return only assemblies that have been
/// loaded in the CLR, not all assemblies.
/// See these threads:
/// http://issues.umbraco.org/issue/U5-198
/// http://stackoverflow.com/questions/3552223/asp-net-appdomain-currentdomain-getassemblies-assemblies-missing-after-app
/// http://stackoverflow.com/questions/2477787/difference-between-appdomain-getassemblies-and-buildmanager-getreferencedassembl
/// </remarks>
public class BruteForceAssemblyProvider : IAssemblyProvider
{
public BruteForceAssemblyProvider()
{
_allAssemblies = new Lazy<HashSet<Assembly>>(() =>
{
HashSet<Assembly> assemblies = null;
try
{
//NOTE: we cannot use AppDomain.CurrentDomain.GetAssemblies() because this only returns assemblies that have
// already been loaded in to the app domain, instead we will look directly into the bin folder and load each one.
var binFolder = GetRootDirectorySafe();
var binAssemblyFiles = Directory.GetFiles(binFolder, "*.dll", SearchOption.TopDirectoryOnly).ToList();
assemblies = new HashSet<Assembly>();
foreach (var a in binAssemblyFiles)
{
try
{
var assName = AssemblyName.GetAssemblyName(a);
var ass = Assembly.Load(assName);
assemblies.Add(ass);
}
catch (Exception e)
{
if (e is SecurityException || e is BadImageFormatException)
{
//swallow these exceptions
}
else
{
throw;
}
}
}
//Since we are only loading in the /bin assemblies above, we will also load in anything that's already loaded (which will include gac items)
foreach (var a in AppDomain.CurrentDomain.GetAssemblies())
{
assemblies.Add(a);
}
}
catch (InvalidOperationException e)
{
if (e.InnerException is SecurityException == false)
throw;
}
return assemblies;
});
}
private readonly Lazy<HashSet<Assembly>> _allAssemblies;
private string _rootDir = string.Empty;
public IEnumerable<Assembly> Assemblies => _allAssemblies.Value;
// FIXME - this is only an interim change, once the IIOHelper stuff is merged we should use IIOHelper here
private string GetRootDirectorySafe()
{
if (string.IsNullOrEmpty(_rootDir) == false)
{
return _rootDir;
}
var codeBase = Assembly.GetExecutingAssembly().CodeBase;
var uri = new Uri(codeBase);
var path = uri.LocalPath;
var baseDirectory = Path.GetDirectoryName(path);
if (string.IsNullOrEmpty(baseDirectory))
throw new PanicException("No root directory could be resolved.");
_rootDir = baseDirectory.Contains("bin")
? baseDirectory.Substring(0, baseDirectory.LastIndexOf("bin", StringComparison.OrdinalIgnoreCase) - 1)
: baseDirectory;
return _rootDir;
}
}
/// <summary>
/// Provides a list of assemblies that can be scanned
/// </summary>
public interface IAssemblyProvider
{
IEnumerable<Assembly> Assemblies { get; }
}
/// <summary>
/// Returns a list of scannable assemblies based on an entry point assembly and it's references
/// </summary>
/// <remarks>
/// This will recursively search through the entry point's assemblies and Umbraco's core assemblies (Core/Web) and their references
/// to create a list of scannable assemblies based on whether they themselves or their transitive dependencies reference Umbraco core assemblies.
/// </remarks>
public class DefaultUmbracoAssemblyProvider : IAssemblyProvider
{
private readonly Assembly _entryPointAssembly;
private static readonly string[] UmbracoCoreAssemblyNames = new[] { "Umbraco.Core", "Umbraco.Web" };
public DefaultUmbracoAssemblyProvider(Assembly entryPointAssembly)
{
_entryPointAssembly = entryPointAssembly ?? throw new ArgumentNullException(nameof(entryPointAssembly));
}
public IEnumerable<Assembly> Assemblies
{
get
{
var finder = new FindAssembliesWithReferencesTo(new[] { _entryPointAssembly }, UmbracoCoreAssemblyNames, true);
foreach(var found in finder.Find())
{
yield return found;
}
}
}
}
/// <summary>
/// Resolves assemblies that reference one of the specified "targetAssemblies" either directly or transitively.
/// </summary>
/// <remarks>
/// Borrowed and modified from https://github.com/dotnet/aspnetcore-tooling/blob/master/src/Razor/src/Microsoft.NET.Sdk.Razor/ReferenceResolver.cs
/// </remarks>
internal class ReferenceResolver
{
private readonly HashSet<string> _umbracoAssemblies;
private readonly IReadOnlyList<Assembly> _assemblyItems;
private readonly Dictionary<Assembly, Classification> _classifications;
private readonly List<Assembly> _lookup = new List<Assembly>();
public ReferenceResolver(IReadOnlyList<string> targetAssemblies, IReadOnlyList<Assembly> assemblyItems)
{
_umbracoAssemblies = new HashSet<string>(targetAssemblies, StringComparer.Ordinal);
_assemblyItems = assemblyItems;
_classifications = new Dictionary<Assembly, Classification>();
foreach (var item in assemblyItems)
{
_lookup.Add(item);
}
}
public IEnumerable<Assembly> ResolveAssemblies()
{
var applicationParts = new List<Assembly>();
foreach (var item in _assemblyItems)
{
var classification = Resolve(item);
if (classification == Classification.ReferencesUmbraco || classification == Classification.IsUmbraco)
{
applicationParts.Add(item);
}
}
return applicationParts;
}
private Classification Resolve(Assembly assemblyItem)
{
if (_classifications.TryGetValue(assemblyItem, out var classification))
{
return classification;
}
// Initialize the dictionary with a value to short-circuit recursive references.
classification = Classification.Unknown;
_classifications[assemblyItem] = classification;
if (_umbracoAssemblies.Contains(assemblyItem.GetName().Name))
{
classification = Classification.IsUmbraco;
}
else
{
classification = Classification.DoesNotReferenceUmbraco;
foreach (var reference in GetReferences(assemblyItem))
{
// recurse
var referenceClassification = Resolve(reference);
if (referenceClassification == Classification.IsUmbraco || referenceClassification == Classification.ReferencesUmbraco)
{
classification = Classification.ReferencesUmbraco;
break;
}
}
}
Debug.Assert(classification != Classification.Unknown);
_classifications[assemblyItem] = classification;
return classification;
}
protected virtual IEnumerable<Assembly> GetReferences(Assembly assembly)
{
foreach (var referenceName in assembly.GetReferencedAssemblies())
{
// don't include if this is excluded
if (TypeFinder.KnownAssemblyExclusionFilter.Any(f => referenceName.FullName.StartsWith(f)))
continue;
var reference = Assembly.Load(referenceName);
if (!_lookup.Contains(reference))
{
// A dependency references an item that isn't referenced by this project.
// We'll construct an item for so that we can calculate the classification based on it's name.
_lookup.Add(reference);
yield return reference;
}
}
}
protected enum Classification
{
Unknown,
DoesNotReferenceUmbraco,
ReferencesUmbraco,
IsUmbraco,
}
}
/// <summary>
/// Finds Assemblies from the entry point assemblies, it's dependencies and it's transitive dependencies that reference that targetAssemblyNames
/// </summary>
/// <remarkes>
/// borrowed and modified from here https://github.com/dotnet/aspnetcore-tooling/blob/master/src/Razor/src/Microsoft.NET.Sdk.Razor/FindAssembliesWithReferencesTo.cs
/// </remarkes>
internal class FindAssembliesWithReferencesTo
{
private readonly Assembly[] _referenceAssemblies;
private readonly string[] _targetAssemblies;
private readonly bool _includeTargets;
/// <summary>
/// Constructor
/// </summary>
/// <param name="referenceAssemblies">Entry point assemblies</param>
/// <param name="targetAssemblyNames">Used to check if the entry point or it's transitive assemblies reference these assembly names</param>
/// <param name="includeTargets">If true will also use the target assembly names as entry point assemblies</param>
public FindAssembliesWithReferencesTo(Assembly[] referenceAssemblies, string[] targetAssemblyNames, bool includeTargets)
{
_referenceAssemblies = referenceAssemblies;
_targetAssemblies = targetAssemblyNames;
_includeTargets = includeTargets;
}
public IEnumerable<Assembly> Find()
{
var referenceItems = new List<Assembly>();
foreach (var assembly in _referenceAssemblies)
{
referenceItems.Add(assembly);
}
if (_includeTargets)
{
foreach(var target in _targetAssemblies)
{
referenceItems.Add(Assembly.Load(target));
}
}
var provider = new ReferenceResolver(_targetAssemblies, referenceItems);
var assemblyNames = provider.ResolveAssemblies();
return assemblyNames.ToList();
}
}
}

View File

@@ -13,7 +13,6 @@
<ItemGroup>
<PackageReference Include="System.ComponentModel.Annotations" Version="4.6.0" />
<PackageReference Include="System.Reflection.Emit.Lightweight" Version="4.3.0" />
<PackageReference Include="System.Reflection.Metadata" Version="1.8.0" />
<PackageReference Include="System.Runtime.Caching" Version="4.6.0" />
</ItemGroup>

View File

@@ -13,6 +13,7 @@ using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Entities;
using Umbraco.Tests.Collections;
using Umbraco.Tests.TestHelpers;
using Umbraco.Web.Cache;
namespace Umbraco.Tests.Cache
@@ -28,7 +29,7 @@ namespace Umbraco.Tests.Cache
public override void Setup()
{
base.Setup();
var typeFinder = new TypeFinder(Mock.Of<ILogger>(), new DefaultUmbracoAssemblyProvider(GetType().Assembly));
var typeFinder = TestHelper.GetTypeFinder();
_memberCache = new ObjectCacheAppCache(typeFinder);
_provider = new DeepCloneAppCache(_memberCache);

View File

@@ -16,7 +16,7 @@ namespace Umbraco.Tests.Cache
public override void Setup()
{
base.Setup();
var typeFinder = new TypeFinder(Mock.Of<ILogger>(), new DefaultUmbracoAssemblyProvider(GetType().Assembly));
var typeFinder = TestHelper.GetTypeFinder();
_ctx = new FakeHttpContextFactory("http://localhost/test");
_appCache = new HttpRequestAppCache(() => _ctx.HttpContext.Items, typeFinder);
}

View File

@@ -1,10 +1,7 @@
using System.Collections.Generic;
using System.Linq;
using Moq;
using System.Linq;
using NUnit.Framework;
using Umbraco.Core.Cache;
using Umbraco.Core.Composing;
using Umbraco.Core.Logging;
using Umbraco.Tests.TestHelpers;
namespace Umbraco.Tests.Cache
{
@@ -21,7 +18,7 @@ namespace Umbraco.Tests.Cache
public override void Setup()
{
base.Setup();
var typeFinder = new TypeFinder(Mock.Of<ILogger>(), new DefaultUmbracoAssemblyProvider(GetType().Assembly));
var typeFinder = TestHelper.GetTypeFinder();
_provider = new ObjectCacheAppCache(typeFinder);
}

View File

@@ -32,7 +32,7 @@ namespace Umbraco.Tests.Components
var mock = new Mock<IFactory>();
var logger = Mock.Of<ILogger>();
var typeFinder = new TypeFinder(logger, new DefaultUmbracoAssemblyProvider(typeof(ComponentTests).Assembly));
var typeFinder = TestHelper.GetTypeFinder();
var f = new UmbracoDatabaseFactory(logger, new Lazy<IMapperCollection>(() => new MapperCollection(Enumerable.Empty<BaseMapper>())), TestHelper.GetConfigs(), TestHelper.DbProviderFactoryCreator);
var fs = new FileSystems(mock.Object, logger, TestHelper.IOHelper, SettingsForTests.GenerateMockGlobalSettings());
var coreDebug = Mock.Of<ICoreDebug>();
@@ -371,7 +371,7 @@ namespace Umbraco.Tests.Components
public void AllComposers()
{
var ioHelper = TestHelper.IOHelper;
var typeFinder = new TypeFinder(Mock.Of<ILogger>(), new DefaultUmbracoAssemblyProvider(GetType().Assembly));
var typeFinder = TestHelper.GetTypeFinder();
var typeLoader = new TypeLoader(ioHelper, typeFinder, AppCaches.Disabled.RuntimeCache, new DirectoryInfo(ioHelper.MapPath("~/App_Data/TEMP")), Mock.Of<IProfilingLogger>());
var register = MockRegister();

View File

@@ -22,7 +22,7 @@ namespace Umbraco.Tests.Composing
{
ProfilingLogger = new ProfilingLogger(Mock.Of<ILogger>(), Mock.Of<IProfiler>());
var typeFinder = new TypeFinder(Mock.Of<ILogger>(), new DefaultUmbracoAssemblyProvider(GetType().Assembly));
var typeFinder = TestHelper.GetTypeFinder();
var ioHelper = TestHelper.IOHelper;
TypeLoader = new TypeLoader(ioHelper, typeFinder, NoAppCache.Instance, new DirectoryInfo(ioHelper.MapPath("~/App_Data/TEMP")), ProfilingLogger, false, AssembliesToScan);
}

View File

@@ -38,7 +38,7 @@ namespace Umbraco.Tests.Composing
.Returns(() => factoryFactory?.Invoke(mockedFactory));
var logger = new ProfilingLogger(Mock.Of<ILogger>(), Mock.Of<IProfiler>());
var typeFinder = new TypeFinder(Mock.Of<ILogger>(), new DefaultUmbracoAssemblyProvider(GetType().Assembly));
var typeFinder = TestHelper.GetTypeFinder();
var ioHelper = TestHelper.IOHelper;
var typeLoader = new TypeLoader(ioHelper, typeFinder, Mock.Of<IAppPolicyCache>(), new DirectoryInfo(ioHelper.MapPath("~/App_Data/TEMP")), logger);
var composition = new Composition(mockedRegister, typeLoader, logger, Mock.Of<IRuntimeState>(), TestHelper.GetConfigs(), TestHelper.IOHelper, AppCaches.NoCache);

View File

@@ -27,7 +27,7 @@ namespace Umbraco.Tests.Composing
public void Initialize()
{
// this ensures it's reset
var typeFinder = new TypeFinder(Mock.Of<ILogger>(), new DefaultUmbracoAssemblyProvider(GetType().Assembly));
var typeFinder = TestHelper.GetTypeFinder();
_typeLoader = new TypeLoader(TestHelper.IOHelper, typeFinder, NoAppCache.Instance,
new DirectoryInfo(TestHelper.IOHelper.MapPath("~/App_Data/TEMP")),
new ProfilingLogger(Mock.Of<ILogger>(), Mock.Of<IProfiler>()), false,

View File

@@ -17,7 +17,7 @@ namespace Umbraco.Tests.Macros
[SetUp]
public void Setup()
{
var typeFinder = new TypeFinder(Mock.Of<ILogger>(), new DefaultUmbracoAssemblyProvider(GetType().Assembly));
var typeFinder = TestHelper.GetTypeFinder();
//we DO want cache enabled for these tests
var cacheHelper = new AppCaches(
new ObjectCacheAppCache(typeFinder),

View File

@@ -22,6 +22,7 @@ using Umbraco.Tests.TestHelpers.Entities;
using Umbraco.Tests.TestHelpers.Stubs;
using Umbraco.Tests.Testing;
using Umbraco.Web.PropertyEditors;
using Umbraco.Tests.TestHelpers;
namespace Umbraco.Tests.Models
{
@@ -269,7 +270,8 @@ namespace Umbraco.Tests.Models
content.UpdateDate = DateTime.Now;
content.WriterId = 23;
var runtimeCache = new ObjectCacheAppCache(new TypeFinder(Mock.Of<ILogger>(), new DefaultUmbracoAssemblyProvider(GetType().Assembly)));
var typeFinder = TestHelper.GetTypeFinder();
var runtimeCache = new ObjectCacheAppCache(typeFinder);
runtimeCache.Insert(content.Id.ToString(CultureInfo.InvariantCulture), () => content);
var proflog = GetTestProfilingLogger();

View File

@@ -127,7 +127,7 @@ namespace Umbraco.Tests.Published
var setType1 = publishedContentTypeFactory.CreateContentType(1000, "set1", CreatePropertyTypes);
var typeFinder = new TypeFinder(Mock.Of<ILogger>(), new DefaultUmbracoAssemblyProvider(GetType().Assembly));
var typeFinder = TestHelper.GetTypeFinder();
var elementsCache = new FastDictionaryAppCache(typeFinder);
var snapshotCache = new FastDictionaryAppCache(typeFinder);

View File

@@ -143,7 +143,7 @@ namespace Umbraco.Tests.PublishedContent
// create a data source for NuCache
_source = new TestDataSource(kits);
var typeFinder = new TypeFinder(Mock.Of<ILogger>(), new DefaultUmbracoAssemblyProvider(GetType().Assembly));
var typeFinder = TestHelper.GetTypeFinder();
var settings = Mock.Of<INuCacheSettings>();

View File

@@ -184,7 +184,7 @@ namespace Umbraco.Tests.PublishedContent
// create a variation accessor
_variationAccesor = new TestVariationContextAccessor();
var typeFinder = new TypeFinder(Mock.Of<ILogger>(), new DefaultUmbracoAssemblyProvider(GetType().Assembly));
var typeFinder = TestHelper.GetTypeFinder();
var settings = Mock.Of<INuCacheSettings>();
// at last, create the complete NuCache snapshot service!

View File

@@ -256,7 +256,7 @@ namespace Umbraco.Tests.Runtimes
var profilingLogger = new ProfilingLogger(logger, profiler);
var appCaches = AppCaches.Disabled;
var databaseFactory = Mock.Of<IUmbracoDatabaseFactory>();
var typeFinder = new TypeFinder(Mock.Of<ILogger>(), new DefaultUmbracoAssemblyProvider(GetType().Assembly));
var typeFinder = TestHelper.GetTypeFinder();
var ioHelper = TestHelper.IOHelper;
var typeLoader = new TypeLoader(ioHelper, typeFinder, appCaches.RuntimeCache, new DirectoryInfo(ioHelper.MapPath("~/App_Data/TEMP")), profilingLogger);
var runtimeState = Mock.Of<IRuntimeState>();

View File

@@ -85,7 +85,7 @@ namespace Umbraco.Tests.Scoping
var memberRepository = Mock.Of<IMemberRepository>();
var hostingEnvironment = TestHelper.GetHostingEnvironment();
var typeFinder = new TypeFinder(Mock.Of<ILogger>(), new DefaultUmbracoAssemblyProvider(GetType().Assembly));
var typeFinder = TestHelper.GetTypeFinder();
var settings = Mock.Of<INuCacheSettings>();
return new PublishedSnapshotService(

View File

@@ -58,7 +58,7 @@ namespace Umbraco.Tests.Services
var memberRepository = Mock.Of<IMemberRepository>();
var hostingEnvironment = Mock.Of<IHostingEnvironment>();
var typeFinder = new TypeFinder(Mock.Of<ILogger>(), new DefaultUmbracoAssemblyProvider(GetType().Assembly));
var typeFinder = TestHelper.GetTypeFinder();
var settings = Mock.Of<INuCacheSettings>();
return new PublishedSnapshotService(

View File

@@ -38,7 +38,7 @@ namespace Umbraco.Tests.TestHelpers
var ioHelper = TestHelper.IOHelper;
var logger = new ProfilingLogger(Mock.Of<ILogger>(), Mock.Of<IProfiler>());
var typeFinder = new TypeFinder(Mock.Of<ILogger>(), new DefaultUmbracoAssemblyProvider(GetType().Assembly));
var typeFinder = TestHelper.GetTypeFinder();
var typeLoader = new TypeLoader(ioHelper, typeFinder, NoAppCache.Instance,
new DirectoryInfo(ioHelper.MapPath("~/App_Data/TEMP")),
logger,

View File

@@ -40,7 +40,7 @@ namespace Umbraco.Tests.TestHelpers.Stubs
{
if (_factory != null) return _factory(requestContext);
var typeFinder = new TypeFinder(Mock.Of<ILogger>(), new DefaultUmbracoAssemblyProvider(GetType().Assembly));
var typeFinder = TestHelper.GetTypeFinder();
var types = typeFinder.FindClassesOfType<ControllerBase>(new[] { Assembly.GetExecutingAssembly() });
var controllerTypes = types.Where(x => x.Name.Equals(controllerName + "Controller", StringComparison.InvariantCultureIgnoreCase));

View File

@@ -43,6 +43,12 @@ namespace Umbraco.Tests.TestHelpers
public static class TestHelper
{
public static ITypeFinder GetTypeFinder()
{
var typeFinder = new TypeFinder(Mock.Of<ILogger>(), new DefaultUmbracoAssemblyProvider(typeof(TestHelper).Assembly));
return typeFinder;
}
public static TypeLoader GetMockedTypeLoader()
{
return new TypeLoader(IOHelper, Mock.Of<ITypeFinder>(), Mock.Of<IAppPolicyCache>(), new DirectoryInfo(IOHelper.MapPath("~/App_Data/TEMP")), Mock.Of<IProfilingLogger>());

View File

@@ -28,7 +28,7 @@ namespace Umbraco.Tests.Web
{
// FIXME: bad in a unit test - but Udi has a static ctor that wants it?!
var container = new Mock<IFactory>();
var typeFinder = new TypeFinder(Mock.Of<ILogger>(), new DefaultUmbracoAssemblyProvider(GetType().Assembly));
var typeFinder = TestHelper.GetTypeFinder();
var ioHelper = TestHelper.IOHelper;
container
.Setup(x => x.GetInstance(typeof(TypeLoader)))

View File

@@ -0,0 +1,70 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Security;
using System.Web.Compilation;
using Umbraco.Core.Composing;
using Umbraco.Core.Hosting;
using Umbraco.Core.IO;
using Umbraco.Core.Logging;
namespace Umbraco.Web.Composing
{
/// <summary>
/// Uses the BuildManager to provide a list of assemblies to scan
/// </summary>
internal class BuildManagerAssemblyProvider : BruteForceAssemblyProvider, IAssemblyProvider
{
private readonly Lazy<HashSet<Assembly>> _allAssemblies;
public BuildManagerAssemblyProvider(IIOHelper ioHelper,
IHostingEnvironment hostingEnvironment,
ILogger logger)
{
_allAssemblies = new Lazy<HashSet<Assembly>>(() =>
{
var isHosted = hostingEnvironment.IsHosted;
try
{
if (isHosted)
{
var assemblies = new HashSet<Assembly>(BuildManager.GetReferencedAssemblies().Cast<Assembly>());
//here we are trying to get the App_Code assembly
var fileExtensions = new[] { ".cs", ".vb" }; //only vb and cs files are supported
var appCodeFolder = new DirectoryInfo(ioHelper.MapPath(ioHelper.ResolveUrl("~/App_code")));
//check if the folder exists and if there are any files in it with the supported file extensions
if (appCodeFolder.Exists && fileExtensions.Any(x => appCodeFolder.GetFiles("*" + x).Any()))
{
try
{
var appCodeAssembly = Assembly.Load("App_Code");
if (assemblies.Contains(appCodeAssembly) == false) // BuildManager will find App_Code already
assemblies.Add(appCodeAssembly);
}
catch (FileNotFoundException ex)
{
//this will occur if it cannot load the assembly
logger.Error(typeof(TypeFinder), ex, "Could not load assembly App_Code");
}
}
return assemblies;
}
}
catch (InvalidOperationException e)
{
if (e.InnerException is SecurityException == false)
throw;
}
// Not hosted, just use the default implementation
return new HashSet<Assembly>(base.Assemblies);
});
}
IEnumerable<Assembly> IAssemblyProvider.Assemblies => _allAssemblies.Value;
}
}

View File

@@ -1,19 +1,15 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Security;
using System.Web.Compilation;
using Umbraco.Core.Configuration;
using Umbraco.Core.Composing;
using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Core.Hosting;
using Umbraco.Core.IO;
using Umbraco.Core.Logging;
namespace Umbraco.Web.Composing
{
/// <summary>
/// An implementation of TypeFinder that uses the BuildManager to resolve references for aspnet framework hosted websites
/// </summary>
@@ -24,60 +20,13 @@ namespace Umbraco.Web.Composing
{
public BuildManagerTypeFinder(
IIOHelper ioHelper,
IHostingEnvironment hostingEnvironment,
ILogger logger,
IAssemblyProvider assemblyProvider,
ITypeFinderConfig typeFinderConfig = null) : base(logger, assemblyProvider, typeFinderConfig)
{
if (ioHelper == null) throw new ArgumentNullException(nameof(ioHelper));
if (hostingEnvironment == null) throw new ArgumentNullException(nameof(hostingEnvironment));
if (logger == null) throw new ArgumentNullException(nameof(logger));
_allAssemblies = new Lazy<HashSet<Assembly>>(() =>
{
var isHosted = hostingEnvironment.IsHosted;
try
{
if (isHosted)
{
var assemblies = new HashSet<Assembly>(BuildManager.GetReferencedAssemblies().Cast<Assembly>());
//here we are trying to get the App_Code assembly
var fileExtensions = new[] { ".cs", ".vb" }; //only vb and cs files are supported
var appCodeFolder = new DirectoryInfo(ioHelper.MapPath(ioHelper.ResolveUrl("~/App_code")));
//check if the folder exists and if there are any files in it with the supported file extensions
if (appCodeFolder.Exists && fileExtensions.Any(x => appCodeFolder.GetFiles("*" + x).Any()))
{
try
{
var appCodeAssembly = Assembly.Load("App_Code");
if (assemblies.Contains(appCodeAssembly) == false) // BuildManager will find App_Code already
assemblies.Add(appCodeAssembly);
}
catch (FileNotFoundException ex)
{
//this will occur if it cannot load the assembly
logger.Error(typeof(TypeFinder), ex, "Could not load assembly App_Code");
}
}
return assemblies;
}
}
catch (InvalidOperationException e)
{
if (e.InnerException is SecurityException == false)
throw;
}
// Not hosted, just use the default implementation
return new HashSet<Assembly>(base.AssembliesToScan);
});
}
private readonly Lazy<HashSet<Assembly>> _allAssemblies;
/// <summary>
/// Explicitly implement and return result from BuildManager
/// </summary>
@@ -85,10 +34,6 @@ namespace Umbraco.Web.Composing
/// <returns></returns>
Type ITypeFinder.GetTypeByName (string name) => BuildManager.GetType(name, false);
/// <summary>
/// Explicitly implement and return result from BuildManager
/// </summary>
IEnumerable<Assembly> ITypeFinder.AssembliesToScan => _allAssemblies.Value;
/// <summary>
/// TypeFinder config via appSettings

View File

@@ -143,6 +143,7 @@
<Compile Include="Compose\AuditEventsComposer.cs" />
<Compile Include="Compose\BackOfficeUserAuditEventsComponent.cs" />
<Compile Include="Compose\BackOfficeUserAuditEventsComposer.cs" />
<Compile Include="Composing\BuildManagerAssemblyProvider.cs" />
<Compile Include="Composing\BuildManagerTypeFinder.cs" />
<Compile Include="Composing\CompositionExtensions\Installer.cs" />
<Compile Include="Composing\LightInject\LightInjectContainer.cs" />