Подтвердить что ты не робот

Есть ли хороший/правильный способ решения проблемы цикла инъекций зависимостей в учебнике ASP.NET MVC ContactsManager?

Если вы не знаете, о чем я говорю, пройдите через учебник и попробуйте добавить зависимую инъекцию самостоятельно или попробуйте свою удачу с моим объяснением проблемы.

Примечание.. Эта проблема не входит в рамки исходного руководства по ASP.NET. В учебном пособии только предполагается, что используемые шаблоны являются дружественными к инъекциям.

Проблема в основном состоит в том, что существует цикл зависимостей между контроллером, ModelStateWrapper и ContactManagerService.

  • Конструктор ContactController принимает IContactManagerService.
  • Конструктор ContactManagerService принимает IContactManagerRepository (не важно) и документ IValidationDictionary (который реализует ModelStateWrapper).
  • Конструктор ModelStateWrapper принимает ModelStateDictionary (который является свойством "ModelState" на контроллере).

Итак, цикл зависимостей выглядит так: Controller > Service > ModelStateWrapper > Controller

Если вы попытаетесь добавить инъекцию зависимостей к этому, он потерпит неудачу. Поэтому мой вопрос: что мне с этим делать? Другие задали этот вопрос, но ответов мало, разные, и все они кажутся "хаки-иш".

Мое текущее решение заключается в том, чтобы удалить IModelStateWrapper из конструктора IService и добавить метод 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));
}

К сожалению, это означает, что метод "Инициализировать" в службе должен вызываться вручную с помощью конструктора контроллера. Есть ли способ лучше? Может быть, где-то я включаю IValidationDictionary в мою конфигурацию единства? Должен ли я переключиться на другой контейнер DI? Я что-то пропустил?

4b9b3361

Ответ 1

Как общее соображение, круговые зависимости указывают на недостаток дизайна - я думаю, что могу с уверенностью сказать это, поскольку вы не являетесь оригинальным автором кода:)

Я бы не считал метод Initialize хорошим решением. Если вы не имеете дело с сценарием надстройки (которого вы не являетесь), метод Injection не является правильным решением. Вы уже почти поняли это, так как вы считаете неудовлетворительным, что вам нужно вручную вызывать его, потому что ваш контейнер DI не может.

Если я не ошибаюсь, ContactController не нуждается в экземпляре IValidationDictionary до того, как будут вызваны его действия.

Если это так, самым простым решением, вероятно, будет определение интерфейса IValidationDictionaryFactory и создание конструктора ContactController для экземпляра этого интерфейса.

Этот интерфейс можно определить следующим образом:

public interface IValidationDictionaryFactory
{
    IValidationDictionary Create(Controller controller);
}

Любой метод Action на контроллере, который нуждается в экземпляре IValidationDictionary, может затем вызвать метод Create для получения экземпляра.

Реализация по умолчанию будет выглядеть примерно так:

public class DefaultValidationDictionaryFactory : IValidationDictionaryFactory
{
    public IValidationDictionary Create(Controller controller)
    {
        return controller.ModelState;
    }
}

Ответ 3

Каждый контроллер имеет виртуальный метод Инициализировать, чтобы делать такие вещи.

Я думаю, что нет лучшего способа, потому что IValidationDictionary - это уровень абстракции между вашим текущим запросом/контроллером/modelstate и IContactService. Моделирование контроллеров инъекций в службу, а затем инжектирование службы в контроллер просто невозможно с помощью встраивания конструктора. Первый должен быть первым.

Может быть, есть способ использования инъекции свойств? Но я думаю, что это тоже будет сложно.