Modify controller discovery to only look for types that reference Mvc.Core

assembly
This commit is contained in:
Pranav K 2014-03-13 17:55:53 -07:00
parent 683c5bf9b3
commit 8ea196023e
17 changed files with 225 additions and 47 deletions

View File

@ -1,30 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace Microsoft.AspNet.Mvc
{
public class AppDomainControllerAssemblyProvider : IControllerAssemblyProvider
{
public IEnumerable<Assembly> Assemblies
{
get
{
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies().Where(AllowAssembly))
{
yield return assembly;
}
}
}
private bool AllowAssembly(Assembly assembly)
{
// consider mechanisms to filter assemblies upfront, so scanning cost is minimized and startup improved.
// 1 - Does assembly reference the WebFx assembly (directly or indirectly). - Down side, object only controller not supported.
// 2 - Remove well known assemblies (maintenance and composability cost)
return true;
}
}
}

View File

@ -0,0 +1,57 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Microsoft.Net.Runtime;
namespace Microsoft.AspNet.Mvc
{
public class DefaultControllerAssemblyProvider : IControllerAssemblyProvider
{
// List of Mvc assemblies that we'll use as roots for controller discovery.
private static readonly HashSet<string> _mvcAssemblyList = new HashSet<string>(StringComparer.Ordinal)
{
"Microsoft.AspNet.Mvc",
"Microsoft.AspNet.Mvc.Core",
"Microsoft.AspNet.Mvc.ModelBinding",
"Microsoft.AspNet.Mvc.Razor",
"Microsoft.AspNet.Mvc.Razor.Host",
"Microsoft.AspNet.Mvc.Rendering",
};
private readonly ILibraryManager _libraryManager;
private readonly Func<ILibraryInformation, Assembly> _assemblyLoader;
public DefaultControllerAssemblyProvider(ILibraryManager libraryManager)
{
_libraryManager = libraryManager;
}
public IEnumerable<Assembly> CandidateAssemblies
{
get
{
return GetCandiateLibraries().Select(Load);
}
}
internal IEnumerable<ILibraryInformation> GetCandiateLibraries()
{
// GetReferencingLibraries returns the transitive closure of referencing assemblies
// for a given assembly. In our case, we'll gather all assemblies that reference
// any of the primary Mvc assemblies while ignoring Mvc assemblies.
return _mvcAssemblyList.SelectMany(_libraryManager.GetReferencingLibraries)
.Distinct()
.Where(IsCandidateLibrary);
}
private static Assembly Load(ILibraryInformation library)
{
return Assembly.Load(new AssemblyName(library.Name));
}
private static bool IsCandidateLibrary(ILibraryInformation library)
{
return !_mvcAssemblyList.Contains(library.Name);
}
}
}

View File

@ -5,6 +5,6 @@ namespace Microsoft.AspNet.Mvc
{
public interface IControllerAssemblyProvider
{
IEnumerable<Assembly> Assemblies { get; }
IEnumerable<Assembly> CandidateAssemblies { get; }
}
}

View File

@ -34,3 +34,4 @@ using System.Runtime.InteropServices;
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: InternalsVisibleTo("Microsoft.AspNet.Mvc.Core.Test")]

View File

@ -43,7 +43,7 @@ namespace Microsoft.AspNet.Mvc
public IEnumerable<ActionDescriptor> GetDescriptors()
{
var assemblies = _controllerAssemblyProvider.Assemblies;
var assemblies = _controllerAssemblyProvider.CandidateAssemblies;
var types = assemblies.SelectMany(a => a.DefinedTypes);
var controllers = types.Where(_conventions.IsController);
var controllerDescriptors = controllers.Select(t => _controllerDescriptorFactory.CreateControllerDescriptor(t)).ToArray();

View File

@ -0,0 +1,11 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Microsoft.Net.Runtime
{
[AssemblyNeutral]
public class AssemblyNeutralAttribute : Attribute { }
}

View File

@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
namespace Microsoft.Net.Runtime
{
[AssemblyNeutral]
public interface ILibraryExport
{
IList<IMetadataReference> MetadataReferences { get; }
IList<ISourceReference> SourceReferences { get; }
}
}

View File

@ -0,0 +1,12 @@
using System.Collections.Generic;
namespace Microsoft.Net.Runtime
{
[AssemblyNeutral]
public interface ILibraryInformation
{
string Name { get; }
IEnumerable<string> Dependencies { get; }
}
}

View File

@ -0,0 +1,16 @@
using System.Collections.Generic;
namespace Microsoft.Net.Runtime
{
[AssemblyNeutral]
public interface ILibraryManager
{
ILibraryExport GetLibraryExport(string name);
IEnumerable<ILibraryInformation> GetReferencingLibraries(string name);
ILibraryInformation GetLibraryInformation(string name);
IEnumerable<ILibraryInformation> GetLibraries();
}
}

View File

@ -0,0 +1,9 @@

namespace Microsoft.Net.Runtime
{
[AssemblyNeutral]
public interface IMetadataReference
{
string Name { get; }
}
}

View File

@ -0,0 +1,7 @@
namespace Microsoft.Net.Runtime
{
[AssemblyNeutral]
public interface ISourceReference
{
}
}

View File

@ -12,17 +12,17 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
{
public class RoslynCompilationService : ICompilationService
{
private readonly ILibraryExportProvider _exportProvider;
private readonly ILibraryManager _libraryManager;
private readonly IApplicationEnvironment _environment;
private readonly IAssemblyLoaderEngine _loader;
public RoslynCompilationService(IApplicationEnvironment environment,
IAssemblyLoaderEngine loaderEngine,
ILibraryExportProvider exportProvider)
ILibraryManager libraryManager)
{
_environment = environment;
_loader = loaderEngine;
_exportProvider = exportProvider;
_libraryManager = libraryManager;
}
public Task<CompilationResult> Compile(string content)
@ -67,7 +67,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
{
var references = new List<MetadataReference>();
var export = _exportProvider.GetLibraryExport(_environment.ApplicationName, _environment.TargetFramework);
var export = _libraryManager.GetLibraryExport(_environment.ApplicationName);
foreach (var metadataReference in export.MetadataReferences)
{

View File

@ -1,10 +0,0 @@
using System.Runtime.Versioning;
namespace Microsoft.Net.Runtime
{
[AssemblyNeutral]
public interface ILibraryExportProvider
{
ILibraryExport GetLibraryExport(string name, FrameworkName targetFramework);
}
}

View File

@ -0,0 +1,12 @@
using System.Collections.Generic;
namespace Microsoft.Net.Runtime
{
[AssemblyNeutral]
public interface ILibraryInformation
{
string Name { get; }
IEnumerable<string> Dependencies { get; }
}
}

View File

@ -0,0 +1,16 @@
using System.Collections.Generic;
namespace Microsoft.Net.Runtime
{
[AssemblyNeutral]
public interface ILibraryManager
{
ILibraryExport GetLibraryExport(string name);
IEnumerable<ILibraryInformation> GetReferencingLibraries(string name);
ILibraryInformation GetLibraryInformation(string name);
IEnumerable<ILibraryInformation> GetLibraries();
}
}

View File

@ -28,7 +28,7 @@ namespace Microsoft.AspNet.Mvc
yield return describe.Transient<IActionResultHelper, ActionResultHelper>();
yield return describe.Transient<IActionResultFactory, ActionResultFactory>();
yield return describe.Transient<IParameterDescriptorFactory, DefaultParameterDescriptorFactory>();
yield return describe.Transient<IControllerAssemblyProvider, AppDomainControllerAssemblyProvider>();
yield return describe.Transient<IControllerAssemblyProvider, DefaultControllerAssemblyProvider>();
yield return describe.Transient<IActionDiscoveryConventions, DefaultActionDiscoveryConventions>();
yield return describe.Instance<IMvcRazorHost>(new MvcRazorHost(typeof(RazorView).FullName));

View File

@ -0,0 +1,65 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Microsoft.Net.Runtime;
using Moq;
using Xunit;
namespace Microsoft.AspNet.Mvc.Core
{
public class DefaultControllerAssemblyProviderTests
{
[Fact]
public void CandidateAssemblies_IgnoresMvcAssemblies()
{
// Arrange
var manager = new Mock<ILibraryManager>();
manager.Setup(f => f.GetReferencingLibraries(It.IsAny<string>()))
.Returns(new[]
{
CreateLibraryInfo("Microsoft.AspNet.Mvc.Core"),
CreateLibraryInfo("Microsoft.AspNet.Mvc"),
CreateLibraryInfo("Microsoft.AspNet.Mvc.ModelBinding"),
CreateLibraryInfo("SomeRandomAssembly"),
})
.Verifiable();
var provider = new DefaultControllerAssemblyProvider(manager.Object);
// Act
var candidates = provider.GetCandiateLibraries();
// Assert
Assert.Equal(new[] { "SomeRandomAssembly" }, candidates.Select(a => a.Name));
}
[Fact]
public void CandidateAssemblies_ReturnsLibrariesReferencingAnyMvcAssembly()
{
// Arrange
var manager = new Mock<ILibraryManager>();
manager.Setup(f => f.GetReferencingLibraries(It.IsAny<string>()))
.Returns(Enumerable.Empty<ILibraryInformation>());
manager.Setup(f => f.GetReferencingLibraries("Microsoft.AspNet.Mvc.Core"))
.Returns(new[] { CreateLibraryInfo("Foo") });
manager.Setup(f => f.GetReferencingLibraries("Microsoft.AspNet.Mvc.ModelBinding"))
.Returns(new[] { CreateLibraryInfo("Bar") });
manager.Setup(f => f.GetReferencingLibraries("Microsoft.AspNet.Mvc"))
.Returns(new[] { CreateLibraryInfo("Baz") });
var provider = new DefaultControllerAssemblyProvider(manager.Object);
// Act
var candidates = provider.GetCandiateLibraries();
// Assert
Assert.Equal(new[] { "Baz", "Foo", "Bar" }, candidates.Select(a => a.Name));
}
private static ILibraryInformation CreateLibraryInfo(string name)
{
var info = new Mock<ILibraryInformation>();
info.SetupGet(b => b.Name).Returns(name);
return info.Object;
}
}
}