Как 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();
    }
}

Во время сериализации я занимаюсь хранением Data и MoreData (и типа объекта, но не затрудняю ситуацию на данный момент). Теперь, чтобы десериализовать, могу ли я назвать что-то вроде

var obj = JsonConvert.DeserializeObject<SomeFoo>(jsonText);

Как я могу сообщить JsonConvert о моем контейнере DI?

(Примечание. Обход должен состоять в том, чтобы всегда иметь конструкторы по умолчанию в моих классах и вызывать службу Locator там, чтобы получить какие-либо зависимости, которые мне нужны. Я просто ищу еще одно чистое решение, не перебирая мои классы такими конструкторы).

Ответ 1

Я согласен с разделом проблем, поставленных Стивеном, и ответ Марк Семанн разместил здесь. Однако, если вы все еще хотите пойти по этому пути, вот решение, которое может помочь:

Наследовать a 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;
    }
}

Затем убедитесь, что вы извлекли этот экземпляр конвертера через ваш контейнер DI. В приведенном ниже коде десериализуются и выполнение DI на вашем объекте:

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

var instance = JsonConvert.DeserializeObject<SerializedObject>(json, settings);

Ниже приведен полный рабочий пример.

Ответ 2

Вы не должны позволять 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();
    }
}

Поскольку данные и поведение разделены, SomeFoo можно сериализовать без каких-либо проблем, а SomeFooHandler можно просто ввести. SomeFoo стал Объектом параметров.

Ответ 3

Если ваша цель - использовать введенную зависимость для изменения данных, вы можете создать собственный Converter.

С этим вы должны быть в состоянии внедрить вашу зависимость. Аналогично коду ниже:

 var settings = new JsonSerializerSettings
            {
                Converters = { new FooConverter<T>(injectedDependency) }
            };
 return JsonConvert.DeserializeObject<Dto>(json, settings);

Существует множество примеров того, как создавать собственные Converters, поэтому вы можете обратиться к ним.