统一和汽车工厂与泛型

时间:2011-03-15 22:17:30

标签: generics unity-container

我们正在尝试使用Linq2SQl实现存储库模式,并且数据上下文被懒惰地初始化。

Repository类实现了IRepository

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

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

Data Access使用委托给存储库

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

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

我想知道如何在统一容器中统一定义存储库。

目前我有以下内容,但我不想为User,Employer等每个具体的存储库类重复它。

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))
            )
     );

我尝试在'&lt;&gt;'上应用typeof操作,但没有取得多大成功。

2 个答案:

答案 0 :(得分:0)

我无法理解你想要达到的目标,但我提出了一个建议,即我脑子里关闭的半逻辑事物是什么。

首先放弃你的代表。 (或解释它的用途)。以这种方式定义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并且右侧关闭类型Repoistory&lt;&gt;将被注入构造函数中。反过来,DataContextAdapter将被注入Repoistory&lt;&gt;的构造函数中。

更新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

要点的重点是你控制实例化它们的地点和时间

答案 1 :(得分:0)

.NET不允许部分关闭的类型。 http://www.lostechies.com/blogs/jimmy_bogard/archive/2009/09/01/partially-closed-generic-types.aspx这就是为什么你不能以通用的方式做你想要实现的目标。在运行时,您可以使用像Func&lt;,&gt;这样的开放类型。或关闭类型,如Func<IDataContextAdapter, IRepository<User>>

简化,Unity维护类型映射的字典,其中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;
    }
}

但话说回来,你每次都会经历反思,我相信你不想这样做。