在ASP.NET MVC ContactsManager教程中是否有一种很好/正确的方法来解决依赖注入循环问题?

Joh*_*esH 22 asp.net-mvc dependency-injection inversion-of-control unity-container

如果您不知道我在说什么,请查看教程并尝试自己添加依赖注入,或者尝试一下我对问题的解释.

注意:此问题不在ASP.NET原始教程的范围内.本教程仅建议使用的模式是依赖注入友好的.

问题基本上是Controller,ModelStateWrapper和ContactManagerService之间存在依赖循环.

  1. ContactController构造器采用IContactManagerService.
  2. ContactManagerService构造函数采用IContactManagerRepository (不重要)和IValidationDictionary (ModelStateWrapper实现).
  3. ModelStateWrapper构造函数采用ModelStateDictionary (在控制器上是一个名为"ModelState"的属性).

所以依赖循环是这样的:Controller> Service> ModelStateWrapper> Controller

如果您尝试向此添加依赖项注入,它将失败.所以我的问题是; 我该怎么办呢?其他人已经发布了这个问题,但答案很少,不同,所有看起来都有点"黑客".

我目前的解决方案是从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...
}
Run Code Online (Sandbox Code Playgroud)

使用此构造,我可以像这样配置我的Unity容器:

public static void ConfigureUnityContainer()
{
    IUnityContainer container = new UnityContainer();

    // Registrations
    container.RegisterTypeInHttpRequestLifetime<IContactRepository, EntityContactRepository>();
    container.RegisterTypeInHttpRequestLifetime<IContactService, ContactService>();

    ControllerBuilder.Current.SetControllerFactory(new UnityControllerFactory(container));
}
Run Code Online (Sandbox Code Playgroud)

不幸的是,这意味着服务上的"Initialize"方法必须由控制器构造函数手动调用.有没有更好的办法?也许我在我的统一配置中以某种方式包含IValidationDictionary?我应该切换到另一个DI容器吗?我错过了什么吗?

Mar*_*ann 11

作为一般考虑,循环依赖表示设计缺陷 - 我想我可以安全地说这个,因为你不是代码的原作者:)

我不认为Initialize方法是一个很好的解决方案.除非您正在处理加载项方案(您不是),否则Method Injection不是正确的解决方案.你几乎已经想到了这一点,因为你发现你需要手动调用它是不能令人满意的,因为你的DI容器不能.

除非我完全弄错了,否则在调用Action方法之前,ContactController不需要IValidationDictionary实例?

如果这是真的,最简单的解决方案可能是定义IValidationDictionaryFactory接口并使ContactController构造函数获取此接口的实例.

这个接口可以像这样定义:

public interface IValidationDictionaryFactory
{
    IValidationDictionary Create(Controller controller);
}
Run Code Online (Sandbox Code Playgroud)

控制器上需要IValidationDictionary实例的任何Action方法都可以调用Create方法来获取实例.

默认实现看起来像这样:

public class DefaultValidationDictionaryFactory : IValidationDictionaryFactory
{
    public IValidationDictionary Create(Controller controller)
    {
        return controller.ModelState;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • Mark,我不是匿名的downvoter,但我相信这里的问题是如何优雅地将Controller.ModelState放入IContactService实例中进行填充.使用您的解决方案,如果要将IValidationDictionaryFactory实例添加到IContactService实例中,我们仍然需要服务中的控制器实例,以便调用Create方法. (6认同)