Startup: Call ConfigureServices if exists
This commit is contained in:
parent
6826eb183c
commit
53eea70c06
|
|
@ -20,8 +20,9 @@ using System.Collections.Generic;
|
|||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Microsoft.AspNet.Builder;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.Framework.DependencyInjection;
|
||||
using Microsoft.Framework.DependencyInjection.Fallback;
|
||||
using Microsoft.Framework.OptionsModel;
|
||||
|
||||
namespace Microsoft.AspNet.Hosting.Startup
|
||||
{
|
||||
|
|
@ -38,6 +39,66 @@ namespace Microsoft.AspNet.Hosting.Startup
|
|||
_next = next;
|
||||
}
|
||||
|
||||
private MethodInfo FindMethod(Type startupType, string methodName, string environmentName, Type returnType = null, bool required = true)
|
||||
{
|
||||
var methodNameWithEnv = methodName + environmentName;
|
||||
var methodInfo = startupType.GetTypeInfo().GetDeclaredMethod(methodNameWithEnv)
|
||||
?? startupType.GetTypeInfo().GetDeclaredMethod(methodName);
|
||||
if (methodInfo == null)
|
||||
{
|
||||
if (required)
|
||||
{
|
||||
throw new Exception(string.Format("TODO: {0} or {1} method not found",
|
||||
methodNameWithEnv,
|
||||
methodName));
|
||||
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
if (returnType != null && methodInfo.ReturnType != returnType)
|
||||
{
|
||||
throw new Exception(string.Format("TODO: {0} method does not return " + returnType.Name,
|
||||
methodInfo.Name));
|
||||
}
|
||||
|
||||
return methodInfo;
|
||||
}
|
||||
|
||||
private void Invoke(MethodInfo methodInfo, object instance, IApplicationBuilder builder, IServiceCollection services = null)
|
||||
{
|
||||
var parameterInfos = methodInfo.GetParameters();
|
||||
var parameters = new object[parameterInfos.Length];
|
||||
for (var index = 0; index != parameterInfos.Length; ++index)
|
||||
{
|
||||
var parameterInfo = parameterInfos[index];
|
||||
if (parameterInfo.ParameterType == typeof(IApplicationBuilder))
|
||||
{
|
||||
parameters[index] = builder;
|
||||
}
|
||||
else if (services != null && parameterInfo.ParameterType == typeof(IServiceCollection))
|
||||
{
|
||||
parameters[index] = services;
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
parameters[index] = _services.GetService(parameterInfo.ParameterType);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
throw new Exception(string.Format(
|
||||
"TODO: Unable to resolve service for {0} method {1} {2}",
|
||||
methodInfo.Name,
|
||||
parameterInfo.Name,
|
||||
parameterInfo.ParameterType.FullName));
|
||||
}
|
||||
}
|
||||
}
|
||||
methodInfo.Invoke(instance, parameters);
|
||||
}
|
||||
|
||||
public Action<IApplicationBuilder> LoadStartup(
|
||||
string applicationName,
|
||||
string environmentName,
|
||||
|
|
@ -81,65 +142,34 @@ namespace Microsoft.AspNet.Hosting.Startup
|
|||
|
||||
if (type == null)
|
||||
{
|
||||
throw new Exception(String.Format("TODO: {0} or {1} class not found in assembly {2}",
|
||||
throw new Exception(String.Format("TODO: {0} or {1} class not found in assembly {2}",
|
||||
startupName1,
|
||||
startupName2,
|
||||
applicationName));
|
||||
}
|
||||
|
||||
var configureMethod1 = "Configure" + environmentName;
|
||||
var configureMethod2 = "Configure";
|
||||
var methodInfo = type.GetTypeInfo().GetDeclaredMethod(configureMethod1);
|
||||
if (methodInfo == null)
|
||||
{
|
||||
methodInfo = type.GetTypeInfo().GetDeclaredMethod(configureMethod2);
|
||||
}
|
||||
if (methodInfo == null)
|
||||
{
|
||||
throw new Exception(string.Format("TODO: {0} or {1} method not found",
|
||||
configureMethod1,
|
||||
configureMethod2));
|
||||
}
|
||||
|
||||
if (methodInfo.ReturnType != typeof(void))
|
||||
{
|
||||
throw new Exception(string.Format("TODO: {0} method isn't void-returning.",
|
||||
methodInfo.Name));
|
||||
}
|
||||
var configureMethod = FindMethod(type, "Configure", environmentName, typeof(void), required: true);
|
||||
// TODO: accept IServiceProvider method as well?
|
||||
var servicesMethod = FindMethod(type, "ConfigureServices", environmentName, typeof(void), required: false);
|
||||
|
||||
object instance = null;
|
||||
if (!methodInfo.IsStatic)
|
||||
if (!configureMethod.IsStatic || (servicesMethod != null && !servicesMethod.IsStatic))
|
||||
{
|
||||
instance = ActivatorUtilities.GetServiceOrCreateInstance(_services, type);
|
||||
}
|
||||
|
||||
return builder =>
|
||||
{
|
||||
var parameterInfos = methodInfo.GetParameters();
|
||||
var parameters = new object[parameterInfos.Length];
|
||||
for (var index = 0; index != parameterInfos.Length; ++index)
|
||||
if (servicesMethod != null)
|
||||
{
|
||||
var parameterInfo = parameterInfos[index];
|
||||
if (parameterInfo.ParameterType == typeof(IApplicationBuilder))
|
||||
var services = new ServiceCollection();
|
||||
services.Add(OptionsServices.GetDefaultServices());
|
||||
Invoke(servicesMethod, instance, builder, services);
|
||||
if (builder != null)
|
||||
{
|
||||
parameters[index] = builder;
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
parameters[index] = _services.GetService(parameterInfo.ParameterType);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception(string.Format(
|
||||
"TODO: Unable to resolve service for startup method {0} {1}",
|
||||
parameterInfo.Name,
|
||||
parameterInfo.ParameterType.FullName));
|
||||
}
|
||||
builder.ApplicationServices = services.BuildServiceProvider(builder.ApplicationServices);
|
||||
}
|
||||
}
|
||||
methodInfo.Invoke(instance, parameters);
|
||||
Invoke(configureMethod, instance, builder);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
"Microsoft.Framework.ConfigurationModel": "1.0.0-*",
|
||||
"Microsoft.Framework.DependencyInjection": "1.0.0-*",
|
||||
"Microsoft.Framework.Logging": "1.0.0-*",
|
||||
"Microsoft.Framework.OptionsModel": "1.0.0-*",
|
||||
"Microsoft.Framework.Runtime.Interfaces": { "version": "1.0.0-*", "type": "build" },
|
||||
"Newtonsoft.Json": "6.0.4"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -0,0 +1,25 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc.
|
||||
// All Rights Reserved
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
|
||||
// WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF
|
||||
// TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR
|
||||
// NON-INFRINGEMENT.
|
||||
// See the Apache 2 License for the specific language governing
|
||||
// permissions and limitations under the License.
|
||||
|
||||
namespace Microsoft.AspNet.Hosting.Fakes
|
||||
{
|
||||
public class FakeOptions
|
||||
{
|
||||
public bool Configured { get; set; }
|
||||
public string Environment { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
using Microsoft.AspNet.Builder;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.Framework.DependencyInjection;
|
||||
|
||||
namespace Microsoft.AspNet.Hosting.Fakes
|
||||
{
|
||||
|
|
@ -26,6 +27,39 @@ namespace Microsoft.AspNet.Hosting.Fakes
|
|||
{
|
||||
}
|
||||
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
services.ConfigureOptions<FakeOptions>(o => o.Configured = true);
|
||||
}
|
||||
|
||||
public void ConfigureServicesDev(IServiceCollection services)
|
||||
{
|
||||
services.ConfigureOptions<FakeOptions>(o =>
|
||||
{
|
||||
o.Configured = true;
|
||||
o.Environment = "Dev";
|
||||
});
|
||||
}
|
||||
|
||||
public void ConfigureServicesRetail(IServiceCollection services)
|
||||
{
|
||||
services.ConfigureOptions<FakeOptions>(o =>
|
||||
{
|
||||
o.Configured = true;
|
||||
o.Environment = "Retail";
|
||||
});
|
||||
}
|
||||
|
||||
public static void ConfigureServicesStatic(IServiceCollection services)
|
||||
{
|
||||
services.ConfigureOptions<FakeOptions>(o =>
|
||||
{
|
||||
o.Configured = true;
|
||||
o.Environment = "Static";
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public void Configure(IApplicationBuilder builder)
|
||||
{
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,34 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc.
|
||||
// All Rights Reserved
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
|
||||
// WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF
|
||||
// TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR
|
||||
// NON-INFRINGEMENT.
|
||||
// See the Apache 2 License for the specific language governing
|
||||
// permissions and limitations under the License.
|
||||
|
||||
using Microsoft.AspNet.Builder;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.Framework.DependencyInjection;
|
||||
|
||||
namespace Microsoft.AspNet.Hosting.Fakes
|
||||
{
|
||||
public class StartupNoServices
|
||||
{
|
||||
public StartupNoServices()
|
||||
{
|
||||
}
|
||||
|
||||
public void Configure(IApplicationBuilder builder)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -21,6 +21,9 @@ using Microsoft.AspNet.Hosting.Startup;
|
|||
using Microsoft.Framework.DependencyInjection;
|
||||
using Microsoft.Framework.DependencyInjection.Fallback;
|
||||
using Xunit;
|
||||
using Microsoft.Framework.OptionsModel;
|
||||
using Microsoft.AspNet.Builder;
|
||||
using System;
|
||||
|
||||
namespace Microsoft.AspNet.Hosting
|
||||
{
|
||||
|
|
@ -46,6 +49,49 @@ namespace Microsoft.AspNet.Hosting
|
|||
Assert.Equal(2, _configurationMethodCalledList.Count);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(null)]
|
||||
[InlineData("Dev")]
|
||||
[InlineData("Retail")]
|
||||
[InlineData("Static")]
|
||||
public void StartupClassAddsConfigureServicesToApplicationServices(string environment)
|
||||
{
|
||||
var serviceCollection = new ServiceCollection();
|
||||
serviceCollection.Add(HostingServices.GetDefaultServices());
|
||||
var services = serviceCollection.BuildServiceProvider();
|
||||
var manager = services.GetService<IStartupManager>();
|
||||
|
||||
var startup = manager.LoadStartup("Microsoft.AspNet.Hosting.Tests", environment ?? "");
|
||||
|
||||
var app = new ApplicationBuilder(services);
|
||||
|
||||
startup.Invoke(app);
|
||||
|
||||
var options = app.ApplicationServices.GetService<IOptionsAccessor<FakeOptions>>().Options;
|
||||
Assert.NotNull(options);
|
||||
Assert.True(options.Configured);
|
||||
Assert.Equal(environment, options.Environment);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StartupClassDoesNotRegisterOptionsWithNoConfigureServices()
|
||||
{
|
||||
var serviceCollection = new ServiceCollection();
|
||||
serviceCollection.Add(HostingServices.GetDefaultServices());
|
||||
serviceCollection.AddInstance<IFakeStartupCallback>(this);
|
||||
var services = serviceCollection.BuildServiceProvider();
|
||||
var manager = services.GetService<IStartupManager>();
|
||||
|
||||
var startup = manager.LoadStartup("Microsoft.AspNet.Hosting.Tests", "NoServices");
|
||||
|
||||
var app = new ApplicationBuilder(services);
|
||||
|
||||
startup.Invoke(app);
|
||||
|
||||
var ex = Assert.Throws<Exception>(() => app.ApplicationServices.GetService<IOptionsAccessor<FakeOptions>>());
|
||||
Assert.True(ex.Message.Contains("No service for type 'Microsoft.Framework.OptionsModel.IOptionsAccessor"));
|
||||
}
|
||||
|
||||
public void ConfigurationMethodCalled(object instance)
|
||||
{
|
||||
_configurationMethodCalledList.Add(instance);
|
||||
|
|
|
|||
Loading…
Reference in New Issue