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.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using Microsoft.AspNet.Builder;
|
using Microsoft.AspNet.Builder;
|
||||||
using Microsoft.AspNet.Http;
|
|
||||||
using Microsoft.Framework.DependencyInjection;
|
using Microsoft.Framework.DependencyInjection;
|
||||||
|
using Microsoft.Framework.DependencyInjection.Fallback;
|
||||||
|
using Microsoft.Framework.OptionsModel;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Hosting.Startup
|
namespace Microsoft.AspNet.Hosting.Startup
|
||||||
{
|
{
|
||||||
|
|
@ -38,6 +39,66 @@ namespace Microsoft.AspNet.Hosting.Startup
|
||||||
_next = next;
|
_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(
|
public Action<IApplicationBuilder> LoadStartup(
|
||||||
string applicationName,
|
string applicationName,
|
||||||
string environmentName,
|
string environmentName,
|
||||||
|
|
@ -87,59 +148,28 @@ namespace Microsoft.AspNet.Hosting.Startup
|
||||||
applicationName));
|
applicationName));
|
||||||
}
|
}
|
||||||
|
|
||||||
var configureMethod1 = "Configure" + environmentName;
|
var configureMethod = FindMethod(type, "Configure", environmentName, typeof(void), required: true);
|
||||||
var configureMethod2 = "Configure";
|
// TODO: accept IServiceProvider method as well?
|
||||||
var methodInfo = type.GetTypeInfo().GetDeclaredMethod(configureMethod1);
|
var servicesMethod = FindMethod(type, "ConfigureServices", environmentName, typeof(void), required: false);
|
||||||
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));
|
|
||||||
}
|
|
||||||
|
|
||||||
object instance = null;
|
object instance = null;
|
||||||
if (!methodInfo.IsStatic)
|
if (!configureMethod.IsStatic || (servicesMethod != null && !servicesMethod.IsStatic))
|
||||||
{
|
{
|
||||||
instance = ActivatorUtilities.GetServiceOrCreateInstance(_services, type);
|
instance = ActivatorUtilities.GetServiceOrCreateInstance(_services, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
return builder =>
|
return builder =>
|
||||||
{
|
{
|
||||||
var parameterInfos = methodInfo.GetParameters();
|
if (servicesMethod != null)
|
||||||
var parameters = new object[parameterInfos.Length];
|
|
||||||
for (var index = 0; index != parameterInfos.Length; ++index)
|
|
||||||
{
|
{
|
||||||
var parameterInfo = parameterInfos[index];
|
var services = new ServiceCollection();
|
||||||
if (parameterInfo.ParameterType == typeof(IApplicationBuilder))
|
services.Add(OptionsServices.GetDefaultServices());
|
||||||
|
Invoke(servicesMethod, instance, builder, services);
|
||||||
|
if (builder != null)
|
||||||
{
|
{
|
||||||
parameters[index] = builder;
|
builder.ApplicationServices = services.BuildServiceProvider(builder.ApplicationServices);
|
||||||
}
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
methodInfo.Invoke(instance, parameters);
|
Invoke(configureMethod, instance, builder);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
"Microsoft.Framework.ConfigurationModel": "1.0.0-*",
|
"Microsoft.Framework.ConfigurationModel": "1.0.0-*",
|
||||||
"Microsoft.Framework.DependencyInjection": "1.0.0-*",
|
"Microsoft.Framework.DependencyInjection": "1.0.0-*",
|
||||||
"Microsoft.Framework.Logging": "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" },
|
"Microsoft.Framework.Runtime.Interfaces": { "version": "1.0.0-*", "type": "build" },
|
||||||
"Newtonsoft.Json": "6.0.4"
|
"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.Builder;
|
||||||
using Microsoft.AspNet.Http;
|
using Microsoft.AspNet.Http;
|
||||||
|
using Microsoft.Framework.DependencyInjection;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Hosting.Fakes
|
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)
|
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;
|
||||||
using Microsoft.Framework.DependencyInjection.Fallback;
|
using Microsoft.Framework.DependencyInjection.Fallback;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
using Microsoft.Framework.OptionsModel;
|
||||||
|
using Microsoft.AspNet.Builder;
|
||||||
|
using System;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Hosting
|
namespace Microsoft.AspNet.Hosting
|
||||||
{
|
{
|
||||||
|
|
@ -46,6 +49,49 @@ namespace Microsoft.AspNet.Hosting
|
||||||
Assert.Equal(2, _configurationMethodCalledList.Count);
|
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)
|
public void ConfigurationMethodCalled(object instance)
|
||||||
{
|
{
|
||||||
_configurationMethodCalledList.Add(instance);
|
_configurationMethodCalledList.Add(instance);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue