Как стать автором
Обновить

Комментарии 6

Уже на этом этапе в голове начинает звенеть тревожный звоночек: а не слишком ли много зависимостей у нас внедряется в класс Worker?

По сути у вас все те же зависимости внедряются через фабрику, и вроде без разницы что мы имеем ctor over injection, и одна зависимость которая нам дает возможность получить все те же зависимостей через нее? Не будет правильнее в этом случае воспользоваться фасадом и предоставить клиенту ограниченный набор методов а не сами объекты репозиториев?

А так статья полезная, один вопрос, что если очень хочется воспользоваться интерцепторами, но не хочется тянуть зависимости от самого нинджекта в другие проекты? Знаю что можно определить свой атрибут который будет работать подобно [Inject], есть ли что то подобное для [Intercept]
Не будет правильнее в этом случае воспользоваться фасадом и предоставить клиенту ограниченный набор методов а не сами объекты репозиториев?


У такого класса-фасада возникнет такая же проблема: несколько интерфейсов репозиториев в конструкторе класса. Однако сама по себе мысль спрятать за фасадом только нужные определенному участку бизнес-логики методы доступа к данным кажется мне очень правильной.

Знаю что можно определить свой атрибут который будет работать подобно [Inject], есть ли что то подобное для [Intercept]


Можно, но придется приложить немного усилий:

Тут код
Пишем свою PlanningStrategy:

    public class CustomPlanningStrategy<TAttribute, TInterceptor> :
        NinjectComponent, IPlanningStrategy
        where TAttribute : Attribute
        where TInterceptor : IInterceptor
    {
        private readonly IAdviceFactory _adviceFactory;
        private readonly IAdviceRegistry _adviceRegistry;

        public CustomPlanningStrategy(
            IAdviceFactory adviceFactory, IAdviceRegistry adviceRegistry)
        {
            _adviceFactory = adviceFactory;
            _adviceRegistry = adviceRegistry;
        }

        public void Execute(IPlan plan)
        {
            var methods = GetCandidateMethods(plan.Type);
            foreach (var method in methods)
            {
                var attributes = method.GetCustomAttributes(typeof (TAttribute), true) as TAttribute[];

                if (attributes != null && attributes.Length == 0)
                    continue;

                var advice = _adviceFactory.Create(method);

                advice.Callback = request => request.Kernel.Get<TInterceptor>();
                _adviceRegistry.Register(advice);

                if (!plan.Has<ProxyDirective>())
                    plan.Add(new ProxyDirective());
            }
        }

        private static IEnumerable<MethodInfo> GetCandidateMethods(Type type)
        {
            var methods = type.GetMethods(
                BindingFlags.Public |
                BindingFlags.NonPublic |
                BindingFlags.Instance
                );

            return methods.Where(ShouldIntercept);
        }

        private static bool ShouldIntercept(MethodInfo methodInfo)
        {
            return methodInfo.DeclaringType != typeof (object) &&
                   !methodInfo.IsPrivate &&
                   !methodInfo.IsFinal;
        }
    }


Добавляем в kernel, указывая тип целевого атрибута и тип Interceptor:

            IKernel kernel = new StandardKernel(new CommonModule());
            kernel.Components.Add<IPlanningStrategy,
                CustomPlanningStrategy<LogExceptionAttribute, ExceptionInterceptor>>();

            Worker worker = kernel.Get<Worker>();


Теперь наш атрибут не зависит от Ninject:

    public class LogExceptionAttribute : Attribute
    {

    }


Встречал мнение, что скрытие используемых зависимой за фабрикой (в широком смысле) снижает читаемость и поддерживаемость кода. Т.е. при написании тестов на такой код неочевидно, какие зависимости необходимо предоставить для корректной проверки логики.

Еще один камень в ваш огород: пример с логгингом, наверное, используется в 99.9% примерах AOP. Было бы прикольнее почитать про другие примеры.

Кстати, а как обстоят дела с отладкой аспектов? Студия позволяет ставить брейкпоинты?
Поздравляю, вы изобрели анти-паттерн Service Locator. Теперь вместо трех зависимостей у IWorker'а вы получили потенциальную зависимость на все объекты, которые предоставляет RepositoryFactory. И, если раньше вас могло насторожить, что у IWorker'а появилось слишком много зависимостей, и он начинает нарушать single responsibility principle, то теперь вы это не заметите так легко.

Мне стоило больших усилий избавиться от этой RepositoryFactory в одном из предыдущих проектов, в котором из-за него все зависимости совершенно размылись по коду.

Это не говоря об упомянутой выше неочевидности зависимостей при написании юнит тестов.
если в приложении 200 классов используют DI, то при попытке получения экземпляра класса, который находится на вершине дерева зависимостей, будет создано 200 экземпляров остальных классов, даже если в текущем сценарии будет использовано 10
а если прописать время жизни объекта то будет всё в порядке. Singleton, PerThread, PerRequest, PerScope обычно DI фреймворки предоставляют стандартный набор для указания времени жизни. По моему это один из смыслов использовать DI. К тому же как вы работаете с EF? создавали контекст для каждого репозитория свой?

Property Injection не пробовали? Конструктор не такой страшный и не знаю как в Ninject, но в AutoFac и Unity циклические зависимости тоже отлавливаются. Но мне больше нравиться когда все зависимости прописаны в конструкторе, напрягает только если есть наследование.
К тому же как вы работаете с EF? создавали контекст для каждого репозитория свой?


Конечно же нет, согласно лучшим рекомендациям от Microsoft: one dbContext per web request. Однако получается, что только Singleton позволяет не создавать экземпляр во время веб-запроса. Singleton удается применить далеко не всегда и не везде,

Property Injection не пробовали? Конструктор не такой страшный и не знаю как в Ninject, но в AutoFac и Unity циклические зависимости тоже отлавливаются. Но мне больше нравиться когда все зависимости прописаны в конструкторе, напрягает только если есть наследование.


Ninject при циклической зависимости кинет исключение. Property Injection не нравится тем, что, во-первых, еще больше скрывает зависимости типа, во-вторых, создает иллюзию необязательности внедрения зависимости. Но все-таки иногда используем, например, на уровне родительского класса.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации