我正在使用Unity 3作为DI框架的ASP.NET MVC4应用程序。 我以模块化方式设计了该体系结构,创建了几个卫星库项目(外观,管理器,数据库适配器等),所有这些项目都静态链接到应用程序(这意味着添加为对Web项目的引用)。 这种架构背后的原因是允许在其他环境中重用(例如,单独的基于REST的后端服务,作为独立的Web项目实现)。
我正在使用依赖注入,它发生在Web项目级别,但也发生在附属DLL中。为了集中DI分辨率,我使用了一个简单的界面
public interface IIocInstaller
{
void Setup(IUnityContainer container);
}
每个库项目都有一个实现该接口的类。使用反射,Web应用程序扫描所有已加载的程序集以查找实现该接口的类,并调用Setup方法。
启动应用时,一切正常。但是,当Web服务器由于不活动而卸载时,下次请求时会抛出以下异常:
[InvalidOperationException: The type ICustomerFacade does not have an accessible constructor.]
Microsoft.Practices.ObjectBuilder2.DynamicMethodConstructorStrategy.ThrowForNullExistingObject(IBuilderContext context) +178
lambda_method(Closure , IBuilderContext ) +25
Microsoft.Practices.ObjectBuilder2.<>c__DisplayClass1.<GetBuildMethod>b__0(IBuilderContext context) +35
Microsoft.Practices.ObjectBuilder2.DynamicMethodBuildPlan.BuildUp(IBuilderContext context) +10
Microsoft.Practices.ObjectBuilder2.BuildPlanStrategy.PreBuildUp(IBuilderContext context) +196
Microsoft.Practices.ObjectBuilder2.StrategyChain.ExecuteBuildUp(IBuilderContext context) +193
Microsoft.Practices.ObjectBuilder2.BuilderContext.NewBuildUp(NamedTypeBuildKey newBuildKey) +113
Microsoft.Practices.Unity.ObjectBuilder.NamedTypeDependencyResolverPolicy.Resolve(IBuilderContext context) +48
lambda_method(Closure , IBuilderContext ) +111
Microsoft.Practices.ObjectBuilder2.<>c__DisplayClass1.<GetBuildMethod>b__0(IBuilderContext context) +35
Microsoft.Practices.ObjectBuilder2.DynamicMethodBuildPlan.BuildUp(IBuilderContext context) +10
Microsoft.Practices.ObjectBuilder2.BuildPlanStrategy.PreBuildUp(IBuilderContext context) +196
Microsoft.Practices.ObjectBuilder2.StrategyChain.ExecuteBuildUp(IBuilderContext context) +193
Microsoft.Practices.Unity.UnityContainer.DoBuildUp(Type t, Object existing, String name, IEnumerable`1 resolverOverrides) +165
[ResolutionFailedException: Resolution of the dependency failed, type = "Eden.SMS.UI.Web.Controllers.CustomersController", name = "(none)".
Exception occurred while: while resolving.
Exception is: InvalidOperationException - The type ICustomerFacade does not have an accessible constructor.
-----------------------------------------------
At the time of the exception, the container was:
Resolving Eden.SMS.UI.Web.Controllers.CustomersController,(none)
Resolving parameter "customerFacade" of constructor Eden.SMS.UI.Web.Controllers.CustomersController(Eden.SMS.Service.Facades.Interface.ICustomerFacade customerFacade)
Resolving Eden.SMS.Service.Facades.Interface.ICustomerFacade,(none)
]
Microsoft.Practices.Unity.UnityContainer.DoBuildUp(Type t, Object existing, String name, IEnumerable`1 resolverOverrides) +329
Microsoft.Practices.Unity.UnityContainer.Resolve(Type t, String name, ResolverOverride[] resolverOverrides) +15
Microsoft.Practices.Unity.UnityContainerExtensions.Resolve(IUnityContainer container, Type t, ResolverOverride[] overrides) +18
Unity.Mvc4.UnityDependencyResolver.GetService(Type serviceType) +67
System.Web.Mvc.DefaultControllerActivator.Create(RequestContext requestContext, Type controllerType) +41
[InvalidOperationException: An error occurred when trying to create a controller of type 'Eden.SMS.UI.Web.Controllers.CustomersController'. Make sure that the controller has a parameterless public constructor.]
System.Web.Mvc.DefaultControllerActivator.Create(RequestContext requestContext, Type controllerType) +178
System.Web.Mvc.DefaultControllerFactory.GetControllerInstance(RequestContext requestContext, Type controllerType) +77
System.Web.Mvc.DefaultControllerFactory.CreateController(RequestContext requestContext, String controllerName) +66
System.Web.Mvc.MvcHandler.ProcessRequestInit(HttpContextBase httpContext, IController& controller, IControllerFactory& factory) +191
System.Web.Mvc.MvcHandler.BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, Object state) +50
System.Web.Mvc.MvcHandler.BeginProcessRequest(HttpContext httpContext, AsyncCallback callback, Object state) +48
System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, Object extraData) +16
System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +301
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +155
所有与统一相关的代码都在Application_Start中执行。 ICustomerFacade接口注入一个控制器,它本身也可以注入其他实例,当然还是使用DI。 错误消息警告控制器有一个无参数构造函数,当然没有实现,因为我需要一个带注入参数的 - 我也试图指定要使用的构造函数(通过使用InjectionConstructor),但没有运气。
知道抛出异常的原因吗?
更新
看起来问题是由于在卫星项目中调用初始化程序失败。
这是我用来扫描查找实现IIocInstaller接口的实例的所有程序集的代码
public class AssemblyInjector
{
/// <summary>
/// Initialize IoC in all assemblies implementing the IocExportAssembly attribute
/// </summary>
/// <param name="container"></param>
/// <param name="assemblyPrefix">The prefix of the assembly names to load</param>
public static void RegisterAssemblyTypes(IUnityContainer container, string assemblyPrefix)
{
var bootstrapers = EnumerateIocBootstraperTypes(assemblyPrefix);
foreach ( Type type in bootstrapers )
{
var instance = (IIocInstaller) Activator.CreateInstance(type);
instance.Setup(container);
}
}
/// <summary>
/// Given a list of assemblies, find all types exposing the
/// <see cref="IocExportAssemblyAttribute"/> attribute
/// </summary>
/// <param name="assemblyPrefix">The prefix of the assembly names to load</param>
/// <returns>list of types exposing the <see cref="IocExportAssemblyAttribute"/> attribute</returns>
private static IEnumerable<Type> EnumerateIocBootstraperTypes(string assemblyPrefix)
{
var assemblies = EnumerateIocAssemblies(assemblyPrefix);
var iocInterface = typeof(IIocInstaller);
var bootstrapers = assemblies.SelectMany(s => s.GetTypes())
.Where(iocInterface.IsAssignableFrom);
return bootstrapers;
}
/// <summary>
/// Enumerate and return all assemblies whose name starts by "Eden.SMS"
/// </summary>
/// <returns><see cref="IEnumerable{T}"/>list of assemblies</returns>
/// <param name="assemblyPrefix">The prefix of the assembly names to load</param>
private static IEnumerable<Assembly> EnumerateIocAssemblies(string assemblyPrefix)
{
return from assembly in AppDomain.CurrentDomain.GetAssemblies()
where assembly.GetName().Name.StartsWith(assemblyPrefix)
select assembly;
}
}
,这在Unity引导程序中使用
/// <summary>
/// Dependency injection bootstrapper
/// </summary>
public static class Bootstrapper
{
private const string ASSEMBLY_PREFIX = "Eden.SMS";
public static IUnityContainer Initialise()
{
var container = BuildUnityContainer();
DependencyResolver.SetResolver(new UnityDependencyResolver(container));
return container;
}
private static IUnityContainer BuildUnityContainer()
{
var container = new UnityContainer();
AssemblyInjector.RegisterAssemblyTypes(container, ASSEMBLY_PREFIX);
//RegisterSatelliteTypes(container);
// Register local types
RegisterTypes(container);
foreach ( var registration in container.Registrations )
{
Debug.WriteLine(string.Format("Registered {0} as {1}", registration.RegisteredType, registration.MappedToType));
}
return container;
}
private static void RegisterSatelliteTypes(UnityContainer container)
{
new Eden.SMS.Data.Adapters.Mssql.Bootstrapper().Setup(container);
new Eden.SMS.Data.Managers.Bootstrapper().Setup(container);
new Eden.SMS.Service.Bootstrapper().Setup(container);
}
private static void RegisterTypes(IUnityContainer container)
{
container.RegisterType<IController, CustomersController>(new InjectionConstructor(typeof(ICustomerFacade)));
container.RegisterType<IController, AuthenticationController>(new InjectionConstructor(typeof(IAuthenticationFacade)));
}
}
反过来,引导程序从Global.asax中的Application_Start()调用。
如果在引导程序中我评论此行
AssemblyInjector.RegisterAssemblyTypes(container, ASSEMBLY_PREFIX);
并取消注释
RegisterSatelliteTypes(container);
它按预期工作(在Web服务器卸载应用程序后没有抛出异常)。 该更改禁用动态查找实现IIocInstaller的类,通过直接调用每个附属程序集来替换它。
此解决方法修复了该错误,但我希望坚持原始的“动态”计划。所以我想弄清楚为什么会发生这种情况以及如何解决它。有什么建议吗?
答案 0 :(得分:3)
我相信你的问题是this other post describes关于.Net Framework将DLL加载到AppDomain的方式:
.NET Framework将加载程序集推迟到当前 AppDomain直到他们需要。例如,如果你打电话给 第三方库只来自SomeMethod(),第三方DLL 通常在第一次运行SomeMethod()之前不会加载。
AppDomain.GetAssemblies()为您提供已经存在的所有程序集 已加载到当前的AppDomain中。
好消息是有方法BuildManager.GetReferencedAssemblies()
返回从Web.config和其他地方引用的所有程序集的列表,并将这些程序集加载到当前的AppDomain中。
这是this issue与您相似的原因,使用BuildManager.GetReferencedAssemblies()
代替AppDomain.CurrentDomain.GetAssemblies()
解决了这个问题。
希望它有所帮助!