Json.NET如何在反序列化期间执行依赖注入?

zaf*_*s.m 17 c# json dependency-injection ninject json.net

当我有一个没有默认构造函数的类,即使用依赖注入来传递其依赖项时,可以Newtonsoft.Json创建这样的对象吗?

例如:

public class SomeFoo
{
    private readonly IFooDependency _dependency;

    public SomeFoo(IFooDependency dependency){
        if(dependency == null)
            throw new ArgumentNullException("dependency");

        _dependency = dependency;
    }

    public string Data { get; set; }
    public int MoreData { get; set; }

    public void DoFoo(){
        Data = _dependency.GetFooData();
        MoreData = _dependency.GetMoreFooDate();
    }
}
Run Code Online (Sandbox Code Playgroud)

在序列化过程中,我只关心存储Data和MoreData(以及对象的类型,但暂时不要让事情复杂化).现在,为了反序列化,我可以调用类似的东西

var obj = JsonConvert.DeserializeObject<SomeFoo>(jsonText);
Run Code Online (Sandbox Code Playgroud)

如何让JsonConvert了解我的DI容器?

(注意:解决方法是在我的类中始终使用默认构造函数,并在那里调用Service Locator以获取我需要的任何依赖项.我只是在寻找一些更简洁的解决方案而不用这些构造函数来规划我的类).

Ste*_*ven 11

您不应该JsonConvert对DI容器有任何了解.您遇到的问题是由应用程序设计中的缺陷引起的.这里的缺陷是你混合数据和行为.

如果您将数据与行为分开,您的问题(以及许多其他问题)将会消失.您可以通过创建两个类来完成此操作:一个用于数据,另一个用于行为:

public class SomeFoo
{
    public string Data { get; set; }
    public int MoreData { get; set; }
}

public class SomeFooHandler
{
    private readonly IFooDependency _dependency;

    public SomeFooHandler(IFooDependency dependency) {
        _dependency = dependency;
    }

    public void Handle(SomeFoo foo) {
        foo.Data = _dependency.GetFooData();
        foo.MoreData = _dependency.GetMoreFooDate();
    }
}
Run Code Online (Sandbox Code Playgroud)

由于现在数据和行为是分开的,SomeFoo因此可以顺利SomeFooHandler地进行序列化,并且可以简单地注入.SomeFoo已成为参数对象.

  • 啊! 呃,我简直不敢相信我没有意识到这一点.我正在创建一个任务执行运行程序,我有任务接受他们的依赖**with*with options.我会把它们分开,这样就更干净了.感谢您的提醒. (2认同)
  • 只有当行为不需要序列化时,这个论点才成立。在这种情况下,该点无效。 (2认同)

cvb*_*ros 9

我同意Steven发布的关注点的分离,以及Mark Seemann在发布的答案.但是,如果你仍然想这样,这里有一个可能有用的解决方案:

继承一个CustomCreationConverter<T>:

internal class NinjectCustomConverter<T> : CustomCreationConverter<T> where T : class
{
    private readonly IResolutionRoot _serviceLocator;

    public NinjectCustomConverter(IResolutionRoot serviceLocator)
    {
        _serviceLocator = serviceLocator;
    }

    public override T Create(Type objectType)
    {
        return _serviceLocator.Get(objectType) as T;
    }
}
Run Code Online (Sandbox Code Playgroud)

然后确保通过DI容器检索此转换器实例.下面的代码将反序列化并对您的对象执行DI:

var ninjectConverter = kernel.Get<NinjectCustomConverter<SerializedObject>>();
var settings = new JsonSerializerSettings();
settings.Converters.Add(ninjectConverter);

var instance = JsonConvert.DeserializeObject<SerializedObject>(json, settings);
Run Code Online (Sandbox Code Playgroud)

这是一个完整的工作示例.