我使用自定义JsonConverter和JsonSerializerSettings.TypeNameHandling = TypeNameHandling.Objects
在反序列化期间创建所需的实例。通过解析Autofac IOC容器中的类型来创建实例。一切都很好,除了......
我有几个"核心对象"从服务请求构造函数中的唯一Id(正确地注入到构造函数中)。当反序列化时不应该发生,因为它相当昂贵,并且一旦创建了实例,Ids就会从Json文件中填充。
目前,当从自定义JsonConverter中解析时,我使用_scope.Resolve<T>(new TypedParameter(typeof(IIdService), null));
然后 - 在被调用的构造函数中 - 检查null并相应地采取行动。
当使用IOC时,有些人显然认为多个构造函数比代码气味更糟糕(这让我想知道为什么Autofac提供了关于该主题的几个特征),但在反序列化的背景下,我认为它可以完全合理。
据我所知,Autofac有机制决定在注册期间使用哪个构造函数,但不能在解析时使用。我首选的解决方案是将自定义属性添加到构造函数(例如[CtorForDeserializing]
)并使用它来进行决策。这可能吗?
答案 0 :(得分:2)
Autofac有几个扩展点可用于基于反射的激活,但尚未有详细记录可能对您有所帮助:IConstructorFinder
和IConstructorSelector
。
IConstructorFinder
用于定位类型上的所有可用构造函数。核心示例是DefaultConstructorFinder
,它只定位公共构造函数。如果您想隐藏具有特定属性的构造函数或者开始查找内部/私有构造函数,您可以创建自定义查找程序。这实际上只发生一次,所以你不能在这里做出运行时选择。
IConstructorSelector
用于在解析时选择应该使用哪个构造函数来实例化对象。核心Autofac中有几个,但主要的例子是MostParametersConstructorSelector
,它选择当时具有最多匹配参数的构造函数。构造函数由IConstructorFinder
找到,然后该组构造函数将呈现给IConstructorSelector
以供选择。这是您可以进行更多运行时选择的地方,因为每次解析对象时都会发生这种情况。
有一些扩展方法可以帮助您将查找器/选择器添加到注册中:
builder.RegisterType<MyType>()
.FindConstructorsWith(new MyConstructorFinder())
.UsingConstructor(new MyConstructorSelector());
您不必自定义这两件事,如果需要,您可以单独执行其中一项操作。我只是向您展示扩展程序。
答案 1 :(得分:0)
实际上,Autofac能够决定在注册或解决期间使用哪种构造函数。对于解决方案部分,这里是文档的引用:“ Autofac自动使用您的类的构造函数,其中包含能够从容器中获取的最多参数”(see here)。
考虑以下示例。
public interface ISomeService
{
Guid Id { get; }
}
public class SomeService : ISomeService
{
public Guid Id { get; }
public SomeService()
{
Id = Guid.NewGuid();
}
public SomeService(Guid id)
{
Id = id;
}
}
// Startup.cs:
builder.RegisterType<SomeService>().As<ISomeService>().InstancePerLifetimeScope();
// TestController.cs:
[Route("api/[controller]")]
public class TestController : Controller
{
private readonly IComponentContext _context;
public TestController(IComponentContext context)
{
_context = context;
}
[HttpGet]
public IActionResult Get()
{
var service = _context.Resolve<ISomeService>();
return Ok(service.Id);
}
[HttpGet("{id}")]
public IActionResult Get(Guid id)
{
var service = _context.Resolve<ISomeService>(new NamedParameter("id", id));
return Ok(service.Id);
}
}
// GET http://localhost:5000/api/test/e0198f72-6337-4880-b608-68935122cdea
// each and every response will be the same: e0198f72-6337-4880-b608-68935122cdea
// GET http://localhost:5000/api/test
// this way it responds with some random guid each time endpoint is called
答案 2 :(得分:0)
Travis Illig让我朝着正确的方向前进 - 谢谢!
我最终围绕以下细节实施了解决方案:
实施自定义属性,例如:IConstructorFinder
,将由(也将被实施)IDeserializable<T>
使用。
实现一个空的通用接口,例如:MyClass : IDeserializable<MyClass>
,用于解析服务/组件。
让相关的组件类实现接口(_builder.RegisterType<MyClass>().As<IDeserializable<MyClass>>()
.FindConstructorsWith(MyConstructorFinder);
)并为组件添加额外的注册:
DeserializeCtorAttribute
在MyClass
所需的构造函数中使用已实现的JsonConverter
。
让(MyClass) scope.Resolve(IDeserializable<MyClass>)
通过调用<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
img:hover {
-webkit-transform: scaleX(-1);
transform: scaleX(-1);
}
</style>
</head>
<body
<img src="Image URl" height="auto" width="auto">
</body>
</html>
创建所需的实例;铸造是必需的,但安全。由于注册,将使用所需的构造函数创建实例。