Единство и автозаводы с дженериками - PullRequest
0 голосов
/ 16 марта 2011

Мы пытаемся реализовать шаблон репозитория с использованием Linq2SQl, и контекст данных инициализируется лениво.

Класс репозитория реализует IRepository

public class Repository<T> : IRepository<T> where T : class
{
        private readonly Table<T> _table;

        public Repository(IDataContextAdapter dataContextAdapter)
        {
            _table = dataContextAdapter.Context.GetTable<T>();
        }
...
}

Доступ к данным использует делегаты для репозитория

public class UserDataAccess : IUserDataAccess
{
    private readonly IRepository<User> _userRepository;

    public UserDataAccess(IDataContextAdapter dataContextAdapter, 
        Func<IDataContextAdapter, IRepository<User>> userRepository)
    {
        _userRepository = userRepository(dataContextAdapter);
    }
 }  

Мне нравится знать, как определять репозитории в общем случае в контейнере единиц.

В настоящее время у меня есть следующее, но я не хочу повторять его для каждого конкретного класса репозитория, такого как Пользователь, Работодатель и т. Д.

UnityContainer.RegisterType<Func<IDataContextAdapter, IRepository<User>>>(
    new InjectionFactory(c => 
                    new Func<IDataContextAdapter, IRepository<User>>(
                        context => new Repository<User>(context))
        )
 );

Я ищу способ определитьТипы в общем, что-то вроде

  UnityContainer.RegisterType<Func<IDataContextAdapter, IRepository<T>>>(
        new InjectionFactory(c => 
                        new Func<IDataContextAdapter, IRepository<T>>(
                            context => new Repository<T>(context))
            )
     );

Я пытался применить операцию typeof с '<>', но без особого успеха.

Ответы [ 2 ]

0 голосов
/ 18 марта 2011

.NET не допускает частично закрытых типов. http://www.lostechies.com/blogs/jimmy_bogard/archive/2009/09/01/partially-closed-generic-types.aspx Вот почему вы не можете сделать то, что пытаетесь достичь, в общем виде. Во время выполнения вы можете иметь открытый тип, например Func <,> или закрытый тип, например Func<IDataContextAdapter, IRepository<User>>

Упрощенно, Unity поддерживает словарь сопоставлений типов, где from type - это неуниверсальный тип или универсальный открытый тип или универсальный закрытый тип. Таким образом, невозможно отобразить тип, который не попадает в эти категории.

Если бы мы могли найти хороший и быстрый способ сопоставления с Func<IDataContextAdapter, IRepository<T>> во время выполнения, можно было бы написать расширение для Unity, которое выполняет разрешение. Однако не представляется возможным, чтобы такое сопоставление было возможно без использования отражений, и вы обычно стараетесь избегать использования отражений каждый раз, когда разрешаете тип по соображениям производительности. В худшем случае вы используете отражение во время первого разрешения только для генерирования IL на лету, который впоследствии создал бы объект без отражения. К сожалению, в нашем случае это было бы невозможно.

Единственный способ решить эту проблему на уровне Unity, который я вижу, это каждый раз, когда запрашивается Func<IDataContextAdapter, IRepository<Something>>, это проверить, зарегистрировано ли это уже, и если нет, зарегистрировать его сгенерированным на фабрике мух.

Другими словами, не существует простых способов сделать именно это.

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

Вы также можете определить общую фабрику следующим образом:

public class GenericDataServiceFactory<TResult, TModel>
{
    private UnityContainer container;
    private Func<IDataContextAdapter, IRepository<TModel>> _repositoryFactory;

    public GenericDataServiceFactory(Func<IDataContextAdapter, IRepository<TModel>> repositoryFactory)
    {
        _repositoryFactory = repositoryFactory;
    }

    public TResult CreateUserDataAccess(IDataContextAdapter dataContextAdapter)
    {
        Type targerType = typeof (TResult);
        // Via reflection find constructor like this
        // TResult(IDataContextAdapter dataContextAdapter, Func<IDataContextAdapter,IRepository<Something>> repositoryFactory)
        // Invoke it with dataContextAdapter and _repositoryFactory and return the result

        return newObject;
    }
}

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

0 голосов
/ 16 марта 2011

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

Сначала освободите своего делегата.(Или объясните, для чего это).Определите свой UserDataAccess следующим образом:

public class UserDataAccess : IUserDataAccess
{
    private readonly IRepository<User> _userRepository;

    public UserDataAccess(IRepository<User> userRepository)
    {
        _userRepository = userRepository;
    }
} 

Теперь ваша регистрация будет выглядеть примерно так:

        DataContextAdapter context = new DataContextAdapter();
        UnityContainer unityContainer = new UnityContainer();
        unityContainer.RegisterType(typeof (IRepository<>), typeof (Repository<>));
        unityContainer.RegisterType<IUserDataAccess, UserDataAccess>();
        unityContainer.RegisterInstance<IDataContextAdapter>(context);

И тогда вы решите так:

        IUserDataAccess test = unityContainer.Resolve<IUserDataAccess>();

UserDataAccessбудет создан и правильный конструктор закрытого типа <> будет введен в конструктор.В свою очередь, DataContextAdapter будет добавлен в конструктор хранилища <>.

ОБНОВЛЕНИЕ 1

На основе информации в исходном вопросе, ссылка на которую вы предоставили выше, есть ли причина, по которой следующее не будет работать для вас?

        unityContainer.RegisterType(typeof (IRepository<>), typeof (Repository<>));
        unityContainer.RegisterType<IUserDataAccess, UserDataAccess>();

        DataContextAdapter context1 = new DataContextAdapter();
        DataContextAdapter context2 = new DataContextAdapter();

        IUnityContainer container1 = unityContainer.CreateChildContainer();
        IUnityContainer container2 = unityContainer.CreateChildContainer();

        container1.RegisterInstance<IDataContextAdapter>(context1);
        container2.RegisterInstance<IDataContextAdapter>(context2);

        IUserDataAccess test1 = container1.Resolve<IUserDataAccess>();
        IUserDataAccess test2 = container2.Resolve<IUserDataAccess>();

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

Есть ли что-то еще из ваших требований, которое осталось невыполненным этим решением?

ОБНОВЛЕНИЕ 2

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

        // When you need context, obtain it
        DataContextAdapter context1 = new DataContextAdapter();

        //Register it for the local scope
        IUnityContainer container1 = unityContainer.CreateChildContainer();
        container1.RegisterInstance<IDataContextAdapter>(context1);

        // Resolve the dependency
        IUserDataAccess test1 = container1.Resolve<IUserDataAccess>();

        // Use your interface
          ......

        // Release and dispose your context
        container1.Dispose

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

...