Использование Ninject для создания типизированных наборов репозиториев - PullRequest
0 голосов
/ 11 декабря 2011

Я хочу избегать каторов, которые занимают большое количество интерфейсов репозитория.

Вместо этого я хочу иметь один параметр ctor: типизированный набор репозиториев.

Под типом я подразумеваю класс с кучей свойств для каждого репозитория, который использует включающий класс. Как то так:

public sealed class MyRepositorySet
{
    public IUserRepository UserRepository { get; set; }

    public IOtherRepository OtherRepository { get; set; }
}

Я хочу использовать Ninject для автоматического создания такого набора репозиториев, заполнить каждое из свойств, создав экземпляр каждого репозитория, а затем создать зависимый класс с инициализированным набором репозитория.

Я знаю, что это можно сделать, но я не могу найти пример. Не думаю, что знаю, как правильно называется типизированный набор репозиториев.

Ответы [ 2 ]

2 голосов
/ 11 декабря 2011

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

0 голосов
/ 12 декабря 2011

Я действительно ответил на свой вопрос.У Ninject было все необходимое для работы, но не из коробки.После вызова Bind () возвращается экземпляр IBindingToSyntax.У него есть метод с именем WithPropertyValue.Этот метод принимает значение или обратный вызов.Обратный вызов имеет экземпляр Ninject.Activation.IContext, который имеет IKernel, который, наконец, имеет метод Get.Итак ... Я могу посмотреть на тип свойства, названного в WithPropertyValue, захватить свойство, определить тип и затем получить обратный вызов Получить экземпляр для типа свойства.Фу.

Вот класс расширения, который я написал, чтобы помочь заполнить мои типизированные наборы репозиториев:

using System;
using System.Linq.Expressions;
using System.Reflection;
using Ninject;
using Ninject.Activation;
using Ninject.Planning.Targets;
using Ninject.Syntax;

namespace NinjectExtensions
{
    /// <summary>
    /// Provides extension methods for the BindingWithSyntax class.
    /// </summary>
    public static class BindingWithSyntaxExtensions
    {
        /// <summary>
        /// Indicates that the specified property should be injected with the bound type of the property.
        /// </summary>
        /// <typeparam name="TBinding">The type of the object to set the property for.</typeparam>
        /// <param name="instance">Used to add additional information to a binding.</param>
        /// <param name="name">The name of the property.</param>
        /// <returns>The instance that was passed in.</returns>
        public static IBindingWithSyntax<TBinding> WithPropertyValue<TBinding>(this IBindingWithSyntax<TBinding> instance, string name)
        {
            if (instance == null)
            {
                throw new ArgumentNullException("instance");
            }
            PropertyInfo propertyInfo = typeof(TBinding).GetProperty(name);
            if (propertyInfo == null)
            {
                throw new ArgumentException("There was not a public property with the given name.", "name");
            }
            Func<IContext, object> callback = context => context.Kernel.Get(propertyInfo.PropertyType);
            return instance.WithPropertyValue(name, callback);
        }

        /// <summary>
        /// Indicates that the specified property should be injected with the bound type of the property.
        /// </summary>
        /// <typeparam name="TBinding">The type of the object to set the property for.</typeparam>
        /// <typeparam name="T">The type of the property.</typeparam>
        /// <param name="instance">Used to add additional information to a binding.</param>
        /// <param name="propertyGetter">An expression yielding the property to set the value to.</param>
        /// <returns>The instance that was passed in.</returns>
        public static IBindingWithSyntax<TBinding> WithPropertyValue<TBinding, T>(this IBindingWithSyntax<TBinding> instance, Expression<Func<TBinding, T>> propertyGetter)
        {
            if (instance == null)
            {
                throw new ArgumentNullException("instance");
            }
            if (propertyGetter == null)
            {
                throw new ArgumentNullException("propertyGetter");
            }
            PropertyInfo propertyInfo = getPropertyInfo(typeof(TBinding), propertyGetter);
            Func<IContext, object> callback = context => context.Kernel.Get<T>();
            return instance.WithPropertyValue(propertyInfo.Name, callback);
        }

        /// <summary>
        /// Indicates that the specified property should be injected with the given value.
        /// </summary>
        /// <typeparam name="TBinding">The type of the object to set the property for.</typeparam>
        /// <typeparam name="T">The type of the property.</typeparam>
        /// <param name="instance">Used to add additional information to a binding.</param>
        /// <param name="propertyGetter">An expression yielding the property to set the value to.</param>
        /// <param name="value">The value to set the property to.</param>
        /// <returns>The instance that was passed in.</returns>
        public static IBindingWithSyntax<TBinding> WithPropertyValue<TBinding, T>(this IBindingWithSyntax<TBinding> instance, Expression<Func<TBinding, T>> propertyGetter, T value)
        {
            if (instance == null)
            {
                throw new ArgumentNullException("instance");
            }
            if (propertyGetter == null)
            {
                throw new ArgumentNullException("propertyGetter");
            }
            PropertyInfo propertyInfo = getPropertyInfo(typeof(TBinding), propertyGetter);
            return instance.WithPropertyValue(propertyInfo.Name, value);
        }

        /// <summary>
        /// Indicates that the specified property should be injected with the value returned by the callback.
        /// </summary>
        /// <typeparam name="TBinding">The type of the object to set the property for.</typeparam>
        /// <typeparam name="T">The type of the property.</typeparam>
        /// <param name="instance">Used to add additional information to a binding.</param>
        /// <param name="propertyGetter">An expression yielding the property to set the value to.</param>
        /// <param name="callback">A function to call to retrieve the value to set the property to.</param>
        /// <returns>The instance that was passed in.</returns>
        public static IBindingWithSyntax<TBinding> WithPropertyValue<TBinding, T>(this IBindingWithSyntax<TBinding> instance, Expression<Func<TBinding, T>> propertyGetter, Func<IContext, T> callback)
        {
            if (instance == null)
            {
                throw new ArgumentNullException("instance");
            }
            if (propertyGetter == null)
            {
                throw new ArgumentNullException("propertyGetter");
            }
            if (callback == null)
            {
                throw new ArgumentNullException("callback");
            }
            PropertyInfo propertyInfo = getPropertyInfo(typeof(TBinding), propertyGetter);
            Func<IContext, object> baseCallback = context => callback(context);
            return instance.WithPropertyValue(propertyInfo.Name, baseCallback);
        }

        /// <summary>
        /// Indicates that the specified property should be injected with the value returned by the callback.
        /// </summary>
        /// <typeparam name="TBinding">The type of the object to set the property for.</typeparam>
        /// <typeparam name="T">The type of the property.</typeparam>
        /// <param name="instance">Used to add additional information to a binding.</param>
        /// <param name="propertyGetter">An expression yielding the property to set the value to.</param>
        /// <param name="callback">A function to call to retrieve the value to set the property to.</param>
        /// <returns>The instance that was passed in.</returns>
        public static IBindingWithSyntax<TBinding> WithPropertyValue<TBinding, T>(this IBindingWithSyntax<TBinding> instance, Expression<Func<TBinding, T>> propertyGetter, Func<IContext, ITarget, T> callback)
        {
            if (instance == null)
            {
                throw new ArgumentNullException("instance");
            }
            if (propertyGetter == null)
            {
                throw new ArgumentNullException("propertyGetter");
            }
            if (callback == null)
            {
                throw new ArgumentNullException("callback");
            }
            PropertyInfo propertyInfo = getPropertyInfo(typeof(TBinding), propertyGetter);
            Func<IContext, ITarget, object> baseCallback = (context, target) => callback(context, target);
            return instance.WithPropertyValue(propertyInfo.Name, baseCallback);
        }

        private static PropertyInfo getPropertyInfo<T>(Type bindingType, Expression<T> expression)
        {
            if (expression.Body.NodeType != ExpressionType.MemberAccess)
            {
                throw new ArgumentException("The expression did access a property.", "propertyGetter");
            }
            MemberExpression memberExpression = (MemberExpression)expression.Body;
            if (memberExpression.Member.MemberType != MemberTypes.Property)
            {
                throw new ArgumentException("The expression did not access a property.", "propertyGetter");
            }
            PropertyInfo propertyInfo = (PropertyInfo)memberExpression.Member;
            if (!propertyInfo.DeclaringType.IsAssignableFrom(bindingType))
            {
                throw new ArgumentException("The expression did not access a property in the specified type.", "propertyGetter");
            }
            return propertyInfo;
        }

        /// <summary>
        /// Injects every property with the bound type of the property.
        /// </summary>
        /// <typeparam name="TBinding">The type of the object to set the property for.</typeparam>
        /// <param name="instance">Used to add additional information to a binding.</param>
        /// <returns>The instance that was passed in.</returns>
        public static IBindingWithSyntax<TBinding> SetAllProperties<TBinding>(this IBindingWithSyntax<TBinding> instance)
        {
            if (instance == null)
            {
                throw new ArgumentNullException("instance");
            }
            IBindingWithSyntax<TBinding> result = instance;
            foreach (PropertyInfo property in typeof(TBinding).GetProperties())
            {
                PropertyInfo local = property;
                result = result.WithPropertyValue(local.Name, context => context.Kernel.Get(local.PropertyType));
            }
            return result;
        }
    }
}

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

Bind<IMyRepository>().To<MyRepository>();
Bind<MyRespositorySet>().ToSelf()
                        .WithPropertyValue(set => set.MyRepository);

Я также потратил некоторое время на создание модуля Module, чтобы избавить меня от необходимости создавать отдельный класс Module для каждого написанного мной модульного теста.Но это довольно большой кусок кода, поэтому я его опущу.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...