我正在尝试提炼这个(以及更多):
builder.RegisterType<ZipCodeRepository<ZipCode>>.AsImplementedInterfaces();
builder.RegisterType<MemberRepository<Member>>.AsImplementedInterfaces();
builder.RegisterType<CreditRepository<Credit>>.AsImplementedInterfaces();
builder.RegisterType<DebitRepository<Debit>>.AsImplementedInterfaces();
builder.RegisterType<StateRepository<State>>.AsImplementedInterfaces();
builder.RegisterType<CityRepository<City>>.AsImplementedInterfaces();
...变成这样:
Type[] inheritedTypes;
Type baseType;
baseType = typeof(Repository<Entity>);
inheritedTypes = baseType.Assembly.GetTypes().Where(type =>
{
return type.BaseType.Name == baseType.Name;
}).ToArray();
builder.RegisterTypes(inheritedTypes).AsImplementedInterfaces();
LINQ 查询有效,返回所有(且仅)继承自
Repository<Entity>
的存储库,但我仍然收到 Autofac 解析错误:
无法解析参数
IZipCodeRepository<ZipCode> ZipCodeRepository
这是我尝试解析存储库的位置:
public class GetZipCodeByCodeQueryHandler : Handler
{
public GetZipCodeByCodeQueryHandler(string unitOfWork, IZipCodeRepository<ZipCode> zipCodeRepository) : base(unitOfWork)
{
_zipCodeRepository = zipCodeRepository;
}
}
我的目标是自动注册这些,这样我就不必为我在应用程序中创建的每个新实体创建注册(我才刚刚开始,它已经变得过于乏味)。
我尝试了这个答案的变体,这可能可以解决我的情况(如果有的话,也可以松散地解决):
builder
.RegisterAssemblyTypes(baseType.Assembly)
.Where(type => type.IsClosedTypeOf(typeof(Repository<>)))
.As(type => GetIRepositoryType(type)).AsImplementedInterfaces();
...但这没有用。我尝试了
Repository<>
和 IRepository<>
。
我也尝试过这个答案。没有运气。同样的错误。
如何在单个表达式中自动注册我的存储库,而不必手动维护不断增长的列表?
--开始编辑--
真正奇怪的是
t
从这个列表中出来的是一个空列表:
var t = new List<Type>();
builder
.RegisterAssemblyTypes(AppDomain.CurrentDomain.GetAssemblies())
.Where(type =>
{
t.Add(type);
return type.IsClosedTypeOf(typeof(Repository<>));
})
.As(type => this.GetIRepositoryType(type)).AsImplementedInterfaces();
假设我正确地阅读了这篇文章(我认为是这样),Autofac 根本不会尝试评估任何类型。这是怎么算的?
--编辑结束--
这是一个完全可重现的示例:
using System;
using System.Linq;
using Autofac;
public static class Program
{
public static void Main(string[] Args)
{
Succeeds();
Fails();
}
private static void Succeeds()
{
IContainer container;
ContainerBuilder builder;
builder = new ContainerBuilder();
builder.RegisterType<ZipCodeRepository<ZipCode>>.AsImplementedInterfaces();
container = builder.Build;
container.Resolve<IZipCodeRepository<ZipCode>>();
}
private static void Fails()
{
Type baseType;
Type[] allTypes;
IContainer container;
ContainerBuilder builder;
baseType = typeof(Repository<Entity>);
allTypes = baseType.Assembly.GetTypes().ToArray();
Console.WriteLine(allTypes.Contains(typeof(ZipCodeRepository<ZipCode>)));
builder = new ContainerBuilder();
builder.RegisterTypes(allTypes).AsImplementedInterfaces();
container = builder.Build;
container.Resolve<IZipCodeRepository<ZipCode>>();
}
}
public interface IRepository<T> where T : Entity
{
}
public abstract class Repository<T> : IRepository<T> where T : Entity
{
}
public abstract class Entity
{
public int Id { get; set; }
}
public interface IZipCodeRepository<T> : IRepository<ZipCode> where T : ZipCode
{
}
public class ZipCodeRepository<T> : Repository<ZipCode>, IZipCodeRepository<ZipCode> where T : ZipCode
{
}
public class ZipCode : Entity
{
}
public class UnitTest1
{
public interface IRepository<T> where T : Entity
{
}
public abstract class Repository<T> : IRepository<T> where T : Entity
{
}
public abstract class Entity
{
public int Id { get; set; }
}
public interface IZipCodeRepository<T> : IRepository<ZipCode> where T : ZipCode
{
}
public class ZipCodeRepository<T> : Repository<ZipCode>, IZipCodeRepository<ZipCode> where T : ZipCode
{
}
public class ZipCode : Entity
{
}
[Fact]
public void Succeeds()
{
var builder = new ContainerBuilder();
builder.RegisterType<ZipCodeRepository<ZipCode>>().AsImplementedInterfaces();
var container = builder.Build();
container.Resolve<IZipCodeRepository<ZipCode>>();
}
[Fact]
public void Fails()
{
var baseType = typeof(Repository<Entity>);
var allTypes = baseType.Assembly.GetTypes().ToArray();
Assert.Contains(typeof(ZipCodeRepository<ZipCode>), allTypes);
var builder = new ContainerBuilder();
builder.RegisterTypes(allTypes).AsImplementedInterfaces();
var container = builder.Build();
container.Resolve<IZipCodeRepository<ZipCode>>();
}
}
此重现的问题是 Fails
单元测试实际上在解析调用之前失败 - 它在这里失败:
// This fails
Assert.Contains(typeof(ZipCodeRepository<ZipCode>), allTypes);
原因是你声明的是一个开放通用。 ZipCodeRepository<T>
没有填写的是
T
。
// This passes - the open generic IS there.
Assert.Contains(typeof(ZipCodeRepository<>), allTypes);
这很重要,因为RegisterAssemblyTypes
builder.RegisterGeneric
-文档此处)和使用开放泛型处理程序集扫描(此处文档)有不同的注册。 如果我们暂时更新失败的单元测试只是为了注册有问题的一种类型(让我们暂时跳过程序集扫描),我们有:
[Fact]
public void Fails()
{
var baseType = typeof(Repository<Entity>);
var allTypes = baseType.Assembly.GetTypes().ToArray();
Assert.Contains(typeof(ZipCodeRepository<>), allTypes);
var builder = new ContainerBuilder();
builder.RegisterGeneric(typeof(ZipCodeRepository<>)).AsImplementedInterfaces();
var container = builder.Build();
container.Resolve<IZipCodeRepository<ZipCode>>();
}
到目前为止一切顺利,但现在我们遇到了解决异常:
The service 'IRepository
1[[ZipCode, TestRepro, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]' 不是开放的泛型类型定义。`也就是说,
IRepository<ZipCode>
不是泛型类型定义。我们来看看类和接口:
public interface IZipCodeRepository<T> : IRepository<ZipCode> where T : ZipCode
{
}
public class ZipCodeRepository<T> : Repository<ZipCode>, IZipCodeRepository<ZipCode> where T : ZipCode
{
}
嗯,其中大部分内容的 <ZipCode>
部分看起来像是拼写错误。我们希望类型参数是带有约束的泛型,而不是填充。让我们解决这个问题:
public interface IZipCodeRepository<T> : IRepository<T> where T : ZipCode
{
}
public class ZipCodeRepository<T> : Repository<T>, IZipCodeRepository<T> where T : ZipCode
{
}
现在当我们运行测试时,它通过了。所以问题是通用声明不正确。
让我们尝试立即通过程序集扫描更新测试:
[Fact]
public void DoesNotFailAnymore()
{
var baseType = typeof(Repository<Entity>);
var allTypes = baseType.Assembly.GetTypes().ToArray();
Assert.Contains(typeof(ZipCodeRepository<>), allTypes);
var builder = new ContainerBuilder();
builder.RegisterAssemblyOpenGenericTypes(baseType.Assembly)
.Where(t => t.GetInterfaces().Any(i => i.Name == typeof(IRepository<>).Name))
.AsImplementedInterfaces();
var container = builder.Build();
container.Resolve<IZipCodeRepository<ZipCode>>();
}
这有效。请注意,存在一些名称比较奇怪的情况,其中开放泛型上的 GetInterfaces()
返回类型,而
type(IRepository<>)
是类型,但是虽然类型相同,但它们并不显示为equal 在 LINQ 查询中,所以我做了一个快捷方式并使用了名称。您可以按照自己的喜好更新过滤器。 所以,综上,原代码存在的问题: