![](/img/trans.png)
[英]Autofac type registered on a keyed enum with a constructor not resolving
[英]Unable to resolve keyed generic parameter of registered type using Autofac
这个问题是对我先前的问题的扩展或替换,无法解决,但KeyFilterAttribute无法正常工作的AutoFac Keyed服务
因此,我有一个通用的UnitOfWork模式。 在我之前的问题中,IUnitOfWork接口是通用接口。 但是,我意识到IUnitOfWork的通用类型参数TContext没有用。 因此,我决定删除通用参数的IUnitOfWork。
但是,在我之前的问题中提到的问题仍然存在-将错误类型的DbContext分配给UnitOfWork的_dbContext属性。 使用此代码,我无法在服务依赖项上明确指定上下文,因为它不再是通用接口。 因此,唯一的解决方案是依靠Autofac键控服务。 有人可以帮我这个问题。
以下是我修改后的代码的新代码段:
非通用IUnitOfWork接口:
public interface IUnitOfWork
通用UnitOfWork类:
public sealed class UnitOfWork<TContext> : IDisposable, IUnitOfWork where TContext : IDbContext
{
private static readonly ILog Log = LogManager.GetLogger(typeof(UnitOfWork<TContext>));
private readonly IDbContext _dbContext;
private Dictionary<string, IRepository> _repositories;
private IDbTransaction Transaction { get; set; }
public UnitOfWork(IDbContext context)
{
_dbContext = context;
}
}
Autofac注册:
builder.RegisterType<CommentsService>().As<ICommentsService().WithAttributeFiltering();
builder.RegisterType<ReconciliationDbContext>().As<IDbContext>();
builder.RegisterType<GenevaDataDbContext>().As<IDbContext>();
builder.RegisterType<OpenStaarsDbContext>().As<IDbContext>();
builder.RegisterType<UnitOfWork<ReconciliationDbContext>>().Keyed<IUnitOfWork>(ContextKey.Recon).InstancePerLifetimeScope();
builder.RegisterType<UnitOfWork<OpenStaarsDbContext>>().Keyed<IUnitOfWork>(ContextKey.OpenStaars).InstancePerLifetimeScope();
CommentsService类:
public class CommentsService : ICommentsService
{
private readonly IUnitOfWork _reconciliationUoW;
public CommentsService([KeyFilter(ContextKey.Recon)]IUnitOfWork reconciliationUoW)
{
_reconciliationUoW = reconciliationUoW;
}
}
如果不打算使IDbContext
通用,则将必须一直使用键控服务。 在设计DI中的深层次内容时,需要考虑一些事项。
注意:这是Autofac网站上的常见问题解答,但我将尝试在此处拆开一些东西以提供帮助。
概念1:最后获胜
能够使用同一接口注册不同类型的目的是出于两个目的:
IEnumerable<IDbContext>
),或者 您的注册在这里...
builder.RegisterType<ReconciliationDbContext>().As<IDbContext>();
builder.RegisterType<GenevaDataDbContext>().As<IDbContext>();
builder.RegisterType<OpenStaarsDbContext>().As<IDbContext>();
...说两件事:
IEnumerable<IDbContext>
我希望实例化这三件事并交还给我。 IDbContext
我想要一个ReconciliationDbContext
...不,等等,我真的想要GenevaDataDbContext
...不,我的意思是我想要一个OpenStaarsDbContext
。 是。 每当我解析一个IDbContext
我都想要OpenStaarsDbContext
。 概念2:接口使用者不了解底层实现
接口与实现的重点在于您不知道底层实现是什么。 这就是李斯科夫替代原则。 如果UnitOfWork<ReconciliationDbContext>
必须具有ReconciliationDbContext
且只能使用该类型...则将ReconciliationDbContext
放入该类的构造函数中,不要使用接口。 句号 如果您必须具有特定类型并且不能将所有IDbContext
类型都IDbContext
相同,则存在设计问题。
概念3:认真地说,接口使用者不了解底层实现
在尝试弄清楚如何在堆栈上进行此工作的过程中,您会想到:“嘿,我可以将参数传递给工作单元类,并一直沿解析过程传递堆叠并设置某种“上下文” ...” 这也是一个常见问题解答。 您不能这样做,因为同样,您不应该知道或不在乎解析堆栈的整个链是什么。 如果需要,您无需使用DI或“反相控制”-您也可以采用旧方法重新设置所有内容。
-
再次,这是Autofac网站上的常见问题解答 ,我建议您通读以更深入地了解为什么您要的东西会有点困难,以及为什么在某些情况下有意地困难。
但是,如果要使用键控服务,则必须从字面上一直进行下去。
CommentService
很好,但是您还需要类似...的东西
public class ReconciliationUnitOfWork : UnitOfWork<ReconciliationDbContext>
{
public ReconciliationUnitOfWork([KeyFilter(ContextKey.Recon)]IDbContext context)
{ /* ... */ }
}
然后,您必须更新您的注册才能启用过滤功能,例如...
builder.RegisterType<ReconciliationDbContext>()
.Keyed<IDbContext>(ContextKey.Recon);
builder.RegisterType<ReconciliationUnitOfWork>()
.Keyed<IUnitOfWork>(ContextKey.Recon)
.InstancePerLifetimeScope();
是的,那很痛苦。 您可以通过不默认在所有地方使用相同的界面来使其更容易一些。
public class ReconciliationUnitOfWork : UnitOfWork<ReconciliationDbContext>
{
public ReconciliationUnitOfWork(ReconciliationDbContext context)
: base(context)
{ /* ... */ }
}
然后,您没有数据库上下文的关键内容,但是您必须这样做...
builder.RegisterType<ReconciliationDbContext>()
.AsSelf()
.As<IDbContext>();
builder.RegisterType<ReconciliationUnitOfWork>()
.Keyed<IUnitOfWork>(ContextKey.Recon)
.InstancePerLifetimeScope();
好的,现在您仍然可以根据需要解析所有IDbContext
对象,但是您也可以将ReconciliationDbContext
解析为一种具体类型,以在那里支持新的ReconciliationUnitOfWork
。
如何使它更通用? 重新添加泛型,因为您说您已从其他问题中删除了泛型。 UnitOfWork<TContext>
应该具有类型为TContext
的构造函数参数,而不是IDbContext
。 您可以添加一个约束,例如:
public class UnitOfWork<TContext> where TContext : IDbContext
现在可以确保获得IDbContext
实现,但这将是一个强大的类型,这正是您所需要的。
然后,您不必烦恼注册上下文和类型的每种组合,就可以使其变得更加通用。
builder.RegisterGeneric(typeof(UnitOfWork<>))
.AsSelf()
.InstancePerLifetimeScope();
然后, CommentsService
应该采用确切的工作单元类型,因为它也不能互换使用任何旧的工作单元。
public class CommentsService : ICommentsService
{
public CommentsService(UnitOfWork<ReconciliationDbContext> reconciliationUoW)
{ /* ... */ }
}
现在,您实际上正在使用类型系统,并且没有违反Liskov替换原理。 您的生活会更轻松,您会得到想要的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.