【问题标题】:Unity and Auto Factories with Generics具有泛型的 Unity 和汽车工厂
【发布时间】:2011-07-16 04:21:34
【问题描述】:

我们正在尝试使用 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>();
        }
...
}

数据访问使用存储库的委托

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

我尝试过使用 on '' 应用 typeof 操作,但没有多大成功。

【问题讨论】:

  • 您如何解决您的注册/依赖关系?能举个例子吗?
  • 您还可以解释一下您想通过 a) Unity 和 b) 与代表实现什么目标?是否存在将 IDataContextAdapter 传递给 UserDataAccess 类但创建的存储库不是通过调用 new Repository(_idatacontextadapter) 创建的情况,其中 _idatacontextadapter 是传递给 UserDataAccess 的 IDataContextAdapter? IE。你想通过委托建模什么样的变化?
  • 最后一个问题是,为什么每个表都需要一个存储库?这很不寻常,很少有用。
  • @zespri : DataContextAdapter 在运行时被调用,并且由于我自己的限制,在此之前无法初始化存储库和数据访问类,并且自动工厂旨在解决相同的问题。看看这个问题的背景,stackoverflow.com/questions/5171779/dependency-injection
  • 好的,仍然不需要代表。我已经在下面更新了我的答案,请您回复建议的解决方案无法实现的您想要实现的目标。

标签: generics unity-container


【解决方案1】:

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

简单地说,Unity 维护一个类型映射字典,其中类型是非泛型类型或泛型开放类型或泛型封闭类型。因此,不可能映射不属于这些类别的类型。

如果我们能想出一种在运行时匹配Func&lt;IDataContextAdapter, IRepository&lt;T&gt;&gt; 的好方法和快速方法,就可以编写一个Unity 扩展来进行解析。但是,不使用反射似乎不可能进行这种匹配,并且出于性能原因,您通常会在每次解析类型时尽量避免使用反射。在最坏的情况下,您在第一次解析期间使用反射只是为了动态生成 IL,之后会在没有反射的情况下创建对象。不幸的是,在我们的案例中这是不可能的。

我看到的在 Unity 级别解决此问题的唯一方法是每次请求 Func&lt;IDataContextAdapter, IRepository&lt;Something&gt;&gt; 时检查它是否已经注册,如果没有注册它,则使用生成的动态工厂。

换句话说,没有简单的方法可以做到这一点。

我可以建议的另一种方法是以引导程序样式进行多次注册,但您已经知道此选项。

你也可以像这样定义一个通用工厂:

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

但话说回来,你每次都在经历反思,我敢肯定你不想那样做。

【讨论】:

【解决方案2】:

我无法理解您要达到的目标,但我就我脑海中的关闭半逻辑事物提出了一个建议。

首先松开你的代表。 (或解释它的用途)。以这种方式定义您的 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 将被注入到构造函数中。反过来,DataContextAdapter 会被注入到 Repoistory 的构造函数中。

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

要带走的要点是,您可以控制在何时何地实例化它们

【讨论】:

  • 拥有两个数据上下文只是问题的一部分。委托的实际原因是您不知道何时需要 dataContext。
  • 请您进一步解释一下。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-12-26
  • 1970-01-01
  • 1970-01-01
  • 2020-09-17
相关资源
最近更新 更多