Unity 和带有泛型的汽车工厂
我们正在尝试使用 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 操作;>',但没有取得太大成功。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
.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>
时检查它是否已注册,如果未注册,则在生成的苍蝇工厂。换句话说,没有简单的方法可以做到这一点。
我可以建议的另一种选择是以引导程序风格进行多次注册,但您已经知道这个选项。
您还可以像这样定义一个通用工厂:
但是话又说回来,您每次都会经历反射,我确信您不想这样做。
.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:
But then again, you are going through reflection every time, and I'm sure you don't want to do that.
我无法理解你想要实现的目标,但我提出了一个关于我心中最接近的半逻辑事物是什么的建议。
首先释放你的代表。 (或者解释一下它的用途)。这样定义您的 UserDataAccess:
现在您的注册将如下所示:
然后您像这样解析:
UserDataAccess 将被创建,并且正确的封闭类型 Repoistory<> 将被创建。将被注入到构造函数中。反过来,DataContextAdapter 将被注入到 Repoistory<> 的构造函数中。
更新 1
根据原始问题中的信息,链接到您在上面提供的链接,是否有以下原因对您不起作用?
我仍然认为委托和注入工厂的复杂性没有理由。您在不同的代码路径中需要不同的数据上下文,因此只需在容器中注入不同的实例,如下所示。当您不再需要容器时,可以将其丢弃。
该解决方案是否还有任何未满足您的要求的地方?
更新2
针对您上面的评论,我想澄清一下,您不必使用上述方法预先注册或实例化上下文。您可以在使用它们的站点上执行此操作:
要点是您可以控制实例化它们的位置和时间
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:
Now your registration will look something like this:
And then you resolve like this:
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?
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:
The main point to take away is that's you who control where and when instantiate them