我们正在开发正在成为一个相当大的ASP.NET MVC项目,并且代码气味开始抬头。
每个控制器都有5个或更多依赖项,其中一些依赖项仅用于控制器上的1个操作方法,但显然是为控制器的每个实例创建的。
我正在努力想办法减少90%的电话不必要地创建的对象数量。
以下是我正在考虑的一些想法:
我觉得其他人一定遇到过同样的问题;所以你是如何解决这个问题的,或者你是否只是忍受它,因为它在你眼中并不是那么大的问题?
答案 0 :(得分:3)
我一直在思考这个问题的解决方案,这就是我想出来的:
直接将依赖项注入控制器操作,而不是注入控制器构造函数。这样你只需要注入你需要的东西。
我真的只是把它鞭打了,所以它有些天真并没有在愤怒中测试,但我打算尽快实现这个尝试。建议欢迎!
它当然特定于StructureMap,但您可以轻松使用不同的容器。
在global.asax中:
protected void Application_Start()
{
ControllerBuilder.Current.SetControllerFactory(
new StructureMapControllerFactory());
}
这里是structuremapcontrollerfactory:
public class StructureMapControllerFactory : DefaultControllerFactory
{
protected override IController GetControllerInstance(Type controllerType)
{
try
{
var controller =
ObjectFactory.GetInstance(controllerType) as Controller;
controller.ActionInvoker =
new StructureMapControllerActionInvoker();
return controller;
}
catch (StructureMapException)
{
System.Diagnostics.Debug.WriteLine(ObjectFactory.WhatDoIHave());
throw;
}
}
}
和structuremapcontrolleractioninvoker(可以做得更聪明一点)
public class StructureMapControllerActionInvoker : ControllerActionInvoker
{
protected override object GetParameterValue(
ControllerContext controllerContext,
ParameterDescriptor parameterDescriptor)
{
object parameterValue;
try
{
parameterValue = base.GetParameterValue(
controllerContext, parameterDescriptor);
}
catch (Exception e)
{
parameterValue =
ObjectFactory.TryGetInstance(
parameterDescriptor.ParameterType);
if (parameterValue == null)
throw e;
}
return parameterValue;
}
}
答案 1 :(得分:1)
“服务定位器”的概念已被添加到像Prism这样的作品中。它具有减少开销的优势。
但是,正如你所说,它只是隐藏在地毯下的东西。依赖关系不会消失,你只是让它们不那么明显,这违背了使用DI的目标之一(明确说明你所依赖的东西),所以我要小心不要过度使用它。
也许你可以通过委派一些工作来获得更好的服务。如果您有某种方式想要重新分区控制器,您可能只想创建该类并让控制器通过DI获取它的实例。 它不会降低创建成本,因为依赖关系仍然会在创建时解决,但至少你会按功能隔离这些依赖关系并保持路由方案的简单。
答案 2 :(得分:1)
我会单独考虑依赖和创建依赖对象的问题。依赖只是控制器源代码引用的事实某种类型。这会降低代码复杂性,但是没有运行时成本。另一方面,对象的实例化具有运行时成本。
减少代码依赖性数量的唯一方法是分解控制器。正如你所说,其他任何事情只会使依赖关系变得更漂亮。但是,依赖性(与依赖对象的实例化相反,我将在一秒钟内覆盖)更漂亮可能就足以成为一个解决方案,您不需要分解控制器。因此,我认为IoC是一个不错的解决方案。
Re:创建对象,你写,“......其中一些依赖项仅用于控制器上的一个动作方法,但显然是为控制器的每个实例创建的。”这让我觉得真正的问题,而不是依赖,本身。因为随着项目的扩展,它只会变得更糟。您可以通过更改对象的实例化来解决此问题,以便在需要它之前不会发生。一种方法是使用具有延迟实例化的属性。另一种方法是使用模型绑定器将操作方法的参数用于实例化所需的对象。另一种方法是编写返回所需实例的函数。如果不知道你正在使用的对象的目的,很难说哪种方式最好。
答案 3 :(得分:1)
您的控制器可能变得太“胖”。我建议创建一个位于控制器下方的应用程序层。应用程序层可以封装控制器操作内部的许多业务流程,并且更加可测试。它还可以帮助您组织代码,而不受指定操作方法的限制。
使用ServiceLocation也会有所帮助(是的,我基本上重申了Denis Troller的答案 - 这可能并不好,但我确实投了他的答案)。