ASP.NET MVC ContactsManager 教程中是否有解决依赖注入循环问题的好/正确方法?
如果您不知道我在说什么,请阅读教程 并尝试自己添加依赖注入,或者尝试一下我对问题的解释。
注意:此问题不在 ASP.NET 原始教程的范围内。本教程仅表明所使用的模式是依赖注入友好的。
问题基本上是控制器、ModelStateWrapper 和 ContactManagerService 之间存在依赖循环。
- ContactController 构造函数采用 IContactManagerService。
- ContactManagerService 构造函数采用 IContactManagerRepository (不重要) 和 IValidationDictionary (由 ModelStateWrapper 实现)。
- ModelStateWrapper 构造函数采用 ModelStateDictionary(这是控制器上名为“ModelState”的属性)。
所以依赖循环是这样的:Controller >服务>模型状态包装器> 如果您尝试向控制器
添加依赖注入,它将失败。所以我的问题是;我该怎么办?其他人已经发布了这个问题,但答案很少,各不相同,而且看起来都有点“hack-ish”。
我当前的解决方案是从 IService 构造函数中删除 IModelStateWrapper 并添加一个 Initialize 方法,如下所示:
public class ContactController : Controller
{
private readonly IContactService _contactService;
public ContactController(IContactService contactService)
{
_contactService = contactService;
contactService.Initialize(new ModelStateWrapper(ModelState));
}
//Class implementation...
}
public class ContactService : IContactService
{
private IValidationDictionary _validationDictionary;
private readonly IContactRepository _contactRepository;
public ContactService(IContactRepository contactRepository)
{
_contactRepository = contactRepository;
}
private void Initialize(IValidationDictionary validationDictionary)
{
if(validationDictionary == null)
throw new ArgumentNullException("validationDictionary");
_validationDictionary = validationDictionary;
}
//Class implementation...
}
public class ModelStateWrapper : IValidationDictionary
{
private readonly ModelStateDictionary _modelState;
public ModelStateWrapper(ModelStateDictionary modelState)
{
_modelState = modelState;
}
//Class implementation...
}
使用此构造,我可以像这样配置我的统一容器:
public static void ConfigureUnityContainer()
{
IUnityContainer container = new UnityContainer();
// Registrations
container.RegisterTypeInHttpRequestLifetime<IContactRepository, EntityContactRepository>();
container.RegisterTypeInHttpRequestLifetime<IContactService, ContactService>();
ControllerBuilder.Current.SetControllerFactory(new UnityControllerFactory(container));
}
不幸的是,这意味着必须调用服务上的“Initialize”方法 <由控制器构造函数手动执行。有更好的办法吗?也许我在哪里以某种方式将 IValidationDictionary 包含在我的统一配置中?我应该切换到另一个 DI 容器吗?我错过了什么吗?
If you don't know what I'm talking about either go through the tutorial and try to add dependency Injection yourself or try your luck with my explanation of the problem.
Note: This problem isn't within the scope of the original tutorial on ASP.NET. The tutorial only suggests that the patterns used are dependency injection friendly.
The problem is basically that there is a dependency loop between the Controller, the ModelStateWrapper and the ContactManagerService.
- The ContactController constuctor takes an IContactManagerService.
- The ContactManagerService constructor takes an IContactManagerRepository (not important) and an IValidationDictionary (which ModelStateWrapper implements).
- The ModelStateWrapper constructor takes a ModelStateDictionary (which is a property called "ModelState" on the controller).
So the dependency cycle goes like this: Controller > Service > ModelStateWrapper > Controller
If you try to add dependency injection to this, it will fail. So my question is; what should I do about it? Others have posted this question, but the answers are few, different, and all seem kinda "hack-ish".
My current solution is to remove the IModelStateWrapper from the IService constructor and add an Initialize method instead like this:
public class ContactController : Controller
{
private readonly IContactService _contactService;
public ContactController(IContactService contactService)
{
_contactService = contactService;
contactService.Initialize(new ModelStateWrapper(ModelState));
}
//Class implementation...
}
public class ContactService : IContactService
{
private IValidationDictionary _validationDictionary;
private readonly IContactRepository _contactRepository;
public ContactService(IContactRepository contactRepository)
{
_contactRepository = contactRepository;
}
private void Initialize(IValidationDictionary validationDictionary)
{
if(validationDictionary == null)
throw new ArgumentNullException("validationDictionary");
_validationDictionary = validationDictionary;
}
//Class implementation...
}
public class ModelStateWrapper : IValidationDictionary
{
private readonly ModelStateDictionary _modelState;
public ModelStateWrapper(ModelStateDictionary modelState)
{
_modelState = modelState;
}
//Class implementation...
}
With this construct I can configure my unity container like this:
public static void ConfigureUnityContainer()
{
IUnityContainer container = new UnityContainer();
// Registrations
container.RegisterTypeInHttpRequestLifetime<IContactRepository, EntityContactRepository>();
container.RegisterTypeInHttpRequestLifetime<IContactService, ContactService>();
ControllerBuilder.Current.SetControllerFactory(new UnityControllerFactory(container));
}
Unfortunately this means that the "Initialize" method on the service has to be called manually by the controller constructor. Is there a better way? Maybe where I somehow include the IValidationDictionary in my unity configuration? Should I switch to another DI container? Am I missing something?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
作为一般考虑,循环依赖表明存在设计缺陷 - 我想我可以安全地说这一点,因为您不是代码的原始作者:)
我不认为 Initialize 方法是一个好的解决方案。除非您正在处理加载项场景(您不是),否则方法注入不是正确的解决方案。您几乎已经弄清楚了这一点,因为您发现需要手动调用它并不令人满意,因为您的 DI 容器不能。
除非我完全错误,否则 ContactController 在调用其 Action 方法之前不需要 IValidationDictionary 实例?
如果这是真的,最简单的解决方案可能是定义一个 IValidationDictionaryFactory 接口并让 ContactController 构造函数采用该接口的实例。
该接口可以这样定义:
控制器上需要 IValidationDictionary 实例的任何 Action 方法都可以调用 Create 方法来获取该实例。
默认实现如下所示:
As a general consideration, circular dependencies indicate a design flaw - I think I can safely say this since you are not the original author of the code :)
I wouldn't consider an Initialize method a good solution. Unless you are dealing with an add-in scenario (which you aren't), Method Injection is not the right solution. You have almost already figured that out, since you find it unsatisfactory that you need to manually invoke it because your DI Container can't.
Unless I am entirely mistaken, the ContactController doesn't need the IValidationDictionary instance before its Action methods are being invoked?
If this is true, the easiest solution would probably be to define an IValidationDictionaryFactory interface and make the ContactController constructor take an instance of this interface.
This interface could be defined like this:
Any Action method on the controller that needs an IValidationDictionary instance can then invoke the Create method to get the instance.
The default implementation would look something like this:
稍微改变/改进设计,如下所示: http://forums.asp.net /t/1486130.aspx
How about slightly changing/improving the design to something like this: http://forums.asp.net/t/1486130.aspx
每个控制器都有一个虚拟方法Initialize来执行类似的操作。
我认为没有更好的方法,因为 IValidationDictionary 是当前请求/控制器/模型状态和 IContactService 之间的抽象层。使用构造函数注入将控制器模型状态注入到服务中,然后将服务注入到控制器中是不可能的。一个必须是第一。
可能有一种使用属性注入的方法吗?但我认为这也会很复杂。
Each controller has a virtual method Initialize to do stuff like that.
I think there is no better way because the IValidationDictionary is an abstraction layer between you current request/controller/modelstate and the IContactService. Injecting controllers modelstate into the service and then injecting the service into the controller is simply impossible using constructor injection. One has to be first.
May be there is a way using property injection? But I think this will be complicated too.