Move the HostFactoryResolver to Extensions (dotnet/extensions#925)

\n\nCommit migrated from 9cd9cea398
This commit is contained in:
Chris Ross 2019-01-09 13:38:12 -08:00 committed by GitHub
parent ee6763d146
commit 4de3b5e105
25 changed files with 523 additions and 0 deletions

View File

@ -0,0 +1,112 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Reflection;
namespace Microsoft.Extensions.Hosting
{
internal class HostFactoryResolver
{
public static readonly string BuildWebHost = nameof(BuildWebHost);
public static readonly string CreateWebHostBuilder = nameof(CreateWebHostBuilder);
public static readonly string CreateHostBuilder = nameof(CreateHostBuilder);
public static Func<string[], TWebHost> ResolveWebHostFactory<TWebHost>(Assembly assembly)
{
return ResolveFactory<TWebHost>(assembly, BuildWebHost);
}
public static Func<string[], TWebHostBuilder> ResolveWebHostBuilderFactory<TWebHostBuilder>(Assembly assembly)
{
return ResolveFactory<TWebHostBuilder>(assembly, CreateWebHostBuilder);
}
public static Func<string[], THostBuilder> ResolveHostBuilderFactory<THostBuilder>(Assembly assembly)
{
return ResolveFactory<THostBuilder>(assembly, CreateHostBuilder);
}
private static Func<string[], T> ResolveFactory<T>(Assembly assembly, string name)
{
var programType = assembly?.EntryPoint?.DeclaringType;
if (programType == null)
{
return null;
}
var factory = programType.GetTypeInfo().GetDeclaredMethod(name);
if (!IsFactory<T>(factory))
{
return null;
}
return args => (T)factory.Invoke(null, new object[] { args });
}
// TReturn Factory(string[] args);
private static bool IsFactory<TReturn>(MethodInfo factory)
{
return factory != null
&& typeof(TReturn).IsAssignableFrom(factory.ReturnType)
&& factory.GetParameters().Length == 1
&& typeof(string[]).Equals(factory.GetParameters()[0].ParameterType);
}
// Used by EF tooling without any Hosting references. Looses some return type safety checks.
public static Func<string[], IServiceProvider> ResolveServiceProviderFactory(Assembly assembly)
{
// Prefer the older patterns by default for back compat.
var webHostFactory = ResolveWebHostFactory<object>(assembly);
if (webHostFactory != null)
{
return args =>
{
var webHost = webHostFactory(args);
return GetServiceProvider(webHost);
};
}
var webHostBuilderFactory = ResolveWebHostBuilderFactory<object>(assembly);
if (webHostBuilderFactory != null)
{
return args =>
{
var webHostBuilder = webHostBuilderFactory(args);
var webHost = Build(webHostBuilder);
return GetServiceProvider(webHost);
};
}
var hostBuilderFactory = ResolveHostBuilderFactory<object>(assembly);
if (hostBuilderFactory != null)
{
return args =>
{
var hostBuilder = hostBuilderFactory(args);
var host = Build(hostBuilder);
return GetServiceProvider(host);
};
}
return null;
}
private static object Build(object builder)
{
var buildMethod = builder.GetType().GetMethod("Build");
return buildMethod?.Invoke(builder, Array.Empty<object>());
}
private static IServiceProvider GetServiceProvider(object host)
{
if (host == null)
{
return null;
}
var hostType = host.GetType();
var servicesProperty = hostType.GetTypeInfo().GetDeclaredProperty("Services");
return (IServiceProvider)servicesProperty.GetValue(host);
}
}
}

View File

@ -0,0 +1,116 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using MockHostTypes;
using System;
using Xunit;
namespace Microsoft.Extensions.Hosting.Tests
{
public class HostFactoryResolverTests
{
[Fact]
public void BuildWebHostPattern_CanFindWebHost()
{
var factory = HostFactoryResolver.ResolveWebHostFactory<IWebHost>(typeof(BuildWebHostPatternTestSite.Program).Assembly);
Assert.NotNull(factory);
Assert.IsAssignableFrom<IWebHost>(factory(Array.Empty<string>()));
}
[Fact]
public void BuildWebHostPattern_CanFindServiceProvider()
{
var factory = HostFactoryResolver.ResolveServiceProviderFactory(typeof(BuildWebHostPatternTestSite.Program).Assembly);
Assert.NotNull(factory);
Assert.IsAssignableFrom<IServiceProvider>(factory(Array.Empty<string>()));
}
[Fact]
public void BuildWebHostPattern__Invalid_CantFindWebHost()
{
var factory = HostFactoryResolver.ResolveWebHostFactory<IWebHost>(typeof(BuildWebHostInvalidSignature.Program).Assembly);
Assert.Null(factory);
}
[Fact]
public void BuildWebHostPattern__Invalid_CantFindServiceProvider()
{
var factory = HostFactoryResolver.ResolveServiceProviderFactory(typeof(BuildWebHostInvalidSignature.Program).Assembly);
Assert.Null(factory);
}
[Fact]
public void CreateWebHostBuilderPattern_CanFindWebHostBuilder()
{
var factory = HostFactoryResolver.ResolveWebHostBuilderFactory<IWebHostBuilder>(typeof(CreateWebHostBuilderPatternTestSite.Program).Assembly);
Assert.NotNull(factory);
Assert.IsAssignableFrom<IWebHostBuilder>(factory(Array.Empty<string>()));
}
[Fact]
public void CreateWebHostBuilderPattern_CanFindServiceProvider()
{
var factory = HostFactoryResolver.ResolveServiceProviderFactory(typeof(CreateWebHostBuilderPatternTestSite.Program).Assembly);
Assert.NotNull(factory);
Assert.IsAssignableFrom<IServiceProvider>(factory(Array.Empty<string>()));
}
[Fact]
public void CreateWebHostBuilderPattern__Invalid_CantFindWebHostBuilder()
{
var factory = HostFactoryResolver.ResolveWebHostBuilderFactory<IWebHostBuilder>(typeof(CreateWebHostBuilderInvalidSignature.Program).Assembly);
Assert.Null(factory);
}
[Fact]
public void CreateWebHostBuilderPattern__InvalidReturnType_CanFindServiceProvider()
{
var factory = HostFactoryResolver.ResolveServiceProviderFactory(typeof(CreateWebHostBuilderInvalidSignature.Program).Assembly);
Assert.NotNull(factory);
Assert.Null(factory(Array.Empty<string>()));
}
[Fact]
public void CreateHostBuilderPattern_CanFindHostBuilder()
{
var factory = HostFactoryResolver.ResolveHostBuilderFactory<IHostBuilder>(typeof(CreateHostBuilderPatternTestSite.Program).Assembly);
Assert.NotNull(factory);
Assert.IsAssignableFrom<IHostBuilder>(factory(Array.Empty<string>()));
}
[Fact]
public void CreateHostBuilderPattern_CanFindServiceProvider()
{
var factory = HostFactoryResolver.ResolveServiceProviderFactory(typeof(CreateHostBuilderPatternTestSite.Program).Assembly);
Assert.NotNull(factory);
Assert.IsAssignableFrom<IServiceProvider>(factory(Array.Empty<string>()));
}
[Fact]
public void CreateHostBuilderPattern__Invalid_CantFindHostBuilder()
{
var factory = HostFactoryResolver.ResolveHostBuilderFactory<IHostBuilder>(typeof(CreateHostBuilderInvalidSignature.Program).Assembly);
Assert.Null(factory);
}
[Fact]
public void CreateHostBuilderPattern__Invalid_CantFindServiceProvider()
{
var factory = HostFactoryResolver.ResolveServiceProviderFactory(typeof(CreateHostBuilderInvalidSignature.Program).Assembly);
Assert.Null(factory);
}
}
}

View File

@ -13,6 +13,16 @@
" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\testassets\BuildWebHostInvalidSignature\BuildWebHostInvalidSignature.csproj" />
<ProjectReference Include="..\testassets\BuildWebHostPatternTestSite\BuildWebHostPatternTestSite.csproj" />
<ProjectReference Include="..\testassets\CreateHostBuilderInvalidSignature\CreateHostBuilderInvalidSignature.csproj" />
<ProjectReference Include="..\testassets\CreateHostBuilderPatternTestSite\CreateHostBuilderPatternTestSite.csproj" />
<ProjectReference Include="..\testassets\CreateWebHostBuilderInvalidSignature\CreateWebHostBuilderInvalidSignature.csproj" />
<ProjectReference Include="..\testassets\CreateWebHostBuilderPatternTestSite\CreateWebHostBuilderPatternTestSite.csproj" />
<ProjectReference Include="..\testassets\MockHostTypes\MockHostTypes.csproj" />
</ItemGroup>
<ItemGroup>
<Reference Include="System.Threading.Tasks.Extensions" />
<Reference Include="System.Security.Cryptography.Cng" />

View File

@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netcoreapp3.0;net472</TargetFrameworks>
<OutputType>Exe</OutputType>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\MockHostTypes\MockHostTypes.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,17 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using MockHostTypes;
namespace BuildWebHostInvalidSignature
{
public class Program
{
static void Main(string[] args)
{
}
// Missing string[] args
public static IWebHost BuildWebHost() => null;
}
}

View File

@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netcoreapp3.0;net472</TargetFrameworks>
<OutputType>Exe</OutputType>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\MockHostTypes\MockHostTypes.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,16 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using MockHostTypes;
namespace BuildWebHostPatternTestSite
{
public class Program
{
static void Main(string[] args)
{
}
public static IWebHost BuildWebHost(string[] args) => new WebHost();
}
}

View File

@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netcoreapp3.0;net472</TargetFrameworks>
<OutputType>Exe</OutputType>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\MockHostTypes\MockHostTypes.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,18 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using MockHostTypes;
namespace CreateHostBuilderInvalidSignature
{
public class Program
{
public static void Main(string[] args)
{
var webHost = CreateHostBuilder(null, args).Build();
}
// Extra parameter
private static IHostBuilder CreateHostBuilder(object extraParam, string[] args) => null;
}
}

View File

@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netcoreapp3.0;net472</TargetFrameworks>
<OutputType>Exe</OutputType>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\MockHostTypes\MockHostTypes.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,19 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using MockHostTypes;
namespace CreateHostBuilderPatternTestSite
{
public class Program
{
public static void Main(string[] args)
{
var webHost = CreateHostBuilder(args).Build();
}
// Do not change the signature of this method. It's used for tests.
private static HostBuilder CreateHostBuilder(string[] args) =>
new HostBuilder();
}
}

View File

@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netcoreapp3.0;net472</TargetFrameworks>
<OutputType>Exe</OutputType>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\MockHostTypes\MockHostTypes.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,17 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using MockHostTypes;
namespace CreateWebHostBuilderInvalidSignature
{
public class Program
{
static void Main(string[] args)
{
}
// Wrong return type
public static IWebHost CreateWebHostBuilder(string[] args) => new WebHost();
}
}

View File

@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netcoreapp3.0;net472</TargetFrameworks>
<OutputType>Exe</OutputType>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\MockHostTypes\MockHostTypes.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,19 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using MockHostTypes;
namespace CreateWebHostBuilderPatternTestSite
{
public class Program
{
public static void Main(string[] args)
{
var webHost = CreateWebHostBuilder(args).Build();
}
// Do not change the signature of this method. It's used for tests.
private static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
new WebHostBuilder();
}
}

View File

@ -0,0 +1,12 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
namespace MockHostTypes
{
public class Host : IHost
{
public IServiceProvider Services { get; } = new ServiceProvider();
}
}

View File

@ -0,0 +1,10 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace MockHostTypes
{
public class HostBuilder : IHostBuilder
{
public IHost Build() => new Host();
}
}

View File

@ -0,0 +1,12 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
namespace MockHostTypes
{
public interface IHost
{
IServiceProvider Services { get; }
}
}

View File

@ -0,0 +1,10 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace MockHostTypes
{
public interface IHostBuilder
{
IHost Build();
}
}

View File

@ -0,0 +1,12 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
namespace MockHostTypes
{
public interface IWebHost
{
IServiceProvider Services { get; }
}
}

View File

@ -0,0 +1,10 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace MockHostTypes
{
public interface IWebHostBuilder
{
IWebHost Build();
}
}

View File

@ -0,0 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netcoreapp3.0;net472</TargetFrameworks>
</PropertyGroup>
</Project>

View File

@ -0,0 +1,12 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
namespace MockHostTypes
{
public class ServiceProvider : IServiceProvider
{
public object GetService(Type serviceType) => null;
}
}

View File

@ -0,0 +1,12 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
namespace MockHostTypes
{
public class WebHost : IWebHost
{
public IServiceProvider Services { get; } = new ServiceProvider();
}
}

View File

@ -0,0 +1,10 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace MockHostTypes
{
public class WebHostBuilder : IWebHostBuilder
{
public IWebHost Build() => new WebHost();
}
}