Unity 和带有泛型的汽车工厂

发布于 2024-10-22 00:55:51 字数 1609 浏览 4 评论 0 原文

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

数据访问使用 Repository 的委托

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

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

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

目前我有以下内容,但我不想对每个具体存储库类(如用户、雇主等)重复它。

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 操作;>',但没有取得太大成功。

We are trying to implement repository pattern in using Linq2SQl and the data context is initialized lazily.

Repository class implements 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 uses delegates to Repository

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

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

I like to know how to define the repositories generically in unity container.

Currently I have the following, but I don't want to repeat it for every concrete repository class like User, Employer etc.

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

I am looking for a way to define the types generically, something like

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

I have tried applying typeof operation with on '<>', but didn't had much success.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

离鸿 2024-10-29 00:55:51

.NET 不允许部分封闭类型。 http://www.lostechies .com/blogs/jimmy_bogard/archive/2009/09/01/partially-close-generic-types.aspx 这就是为什么您无法以通用方式实现您想要实现的目标。在运行时,您可以使用像 Func<,> 这样的开放类型。或封闭类型,例如 Func>

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

如果我们能够想出一种良好且快速的方法来在运行时匹配 Func>,则可以编写一个 Unity 扩展来执行解析。然而,似乎不可能在不使用反射的情况下进行此类匹配,并且出于性能原因,您通常会在每次解析类型时尝试避免使用反射。在最坏的情况下,您在第一个解析期间仅使用反射来动态生成 IL,该 IL 将在之后进行对象创建而无需反射。不幸的是,在我们的例子中这是不可能的。

我看到在 Unity 级别解决此问题的唯一方法是每次请求 Func> 时检查它是否已注册,如果未注册,则在生成的苍蝇工厂。

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

我可以建议的另一种选择是以引导程序风格进行多次注册,但您已经知道这个选项。

您还可以像这样定义一个通用工厂:

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

但是话又说回来,您每次都会经历反射,我确信您不想这样做。

.NET does not allow for partially closed types. http://www.lostechies.com/blogs/jimmy_bogard/archive/2009/09/01/partially-closed-generic-types.aspx This is why you can't do what you are attempting to achieve in a generic way. In runtime you either can have open type like Func<,> or closed type like Func<IDataContextAdapter, IRepository<User>>

Simplifying, Unity maintains a dictionary of type mappings where from type is a non-generic type or generic open type or generic closed type. Thus it is impossible to map a type that does not fall into these categories.

If we could come up with a good and fast way to match against Func<IDataContextAdapter, IRepository<T>> at run time, it would be possible to write an extension to Unity that does the resolution. However it does not seem that it would be possible to do such matching without using reflections, and you normally try to avoid using reflection every time you resolve a type for performance reasons. At worst case you use reflection during the first resolution only to generated IL on the fly that would do the object creation without reflection afterwards. Unfortunately in our case this would be impossible.

The only way to resolve this on Unity level that I see is every time Func<IDataContextAdapter, IRepository<Something>> is requested is to check if this is already registered and if not register it with generated on the fly factory.

In other words there are no simple ways of doing exactly this.

An alternative I can suggest is do this multiple registrations in a bootstrapper style, but you are already aware of this option.

You also can define a generic factory like this:

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

But then again, you are going through reflection every time, and I'm sure you don't want to do that.

离旧人 2024-10-29 00:55:51

我无法理解你想要实现的目标,但我提出了一个关于我心中最接近的半逻辑事物是什么的建议。

首先释放你的代表。 (或者解释一下它的用途)。这样定义您的 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

要点是您可以控制实例化它们的位置和时间

I can't understand what you are trying to achieve, but I make a suggestion as to what is the closes semi-logical thing in my mind is.

First loose your delegate. (Or explain what it's for). Define your UserDataAccess this way:

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

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

Now your registration will look something like this:

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

And then you resolve like this:

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

UserDataAccess will be created and the right closed type Repoistory<> will be injected into the constructor. In turn, DataContextAdapter will be injected in the constructor of Repoistory<>.

UPDATE 1

Based on the information in the original question, link to that you provided above, is there a reason why the following won't work for you?

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

I still see no reason for the complexity with delegates and injection factory. You need different data contexts in different code path, so just inject different instances in your container as shown below. You can dispose of a container when you no longer need it.

Is there is anything else from your requirements that left unfulfilled by this solution?

UPDATE 2

In response to yor comment above, I'd like to clarify, that you don't have to register or instantiate contexts upfront with the approach above. You can do it right at the site you are using them:

        // 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

The main point to take away is that's you who control where and when instantiate them

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文