2

I have a class named SomeRule that can be serialized in a XML format. The class uses an ISomeService that I would like to be injected via autofac.

[Serializable]
public class SomeRule 
{
    [XmlAttribute("Attribute1")]
    public string Attribute1 {get;set;}

    [XmlAttribute("Attribute2")]
    public string Attribute2 { get; set; }
    
    private readonly ISomeService m_someService;
    
    private SomeRule() { }

    public SomeRule(ISomeService someService)
    {
        m_someService = someService;
    }
    
    public void DoSomething()
    {
        m_someService.DoStuff(Attribute1);
    }
}

public interface ISomeService {
    
    void DoStuff(string param);
}

public class SomeServiceImpl : ISomeService
{
    public void DoStuff(string param) => //    Do something with the stuff.
}

Now, my program receives an XML string that I would like to deserialize but also, at the same time, have autofac inject the dependency for me.

void Main()
{
    string serializedRule =
        "<?xml version=\"1.0\" encoding=\"utf-16\"?>" + 
        "<SomeRule xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " +
            "xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" " + 
            "Attribute1=\"Huuuuuge\" " +
            "Attribute2=\"Cofveve\" />";
        
    XmlSerializer xmlSerializer = new XmlSerializer(typeof(SomeRule));
        
    var stringBuilder = new StringBuilder(serializedRule);
    var newRule = xmlSerializer.Deserialize(
        new StringReader(stringBuilder.ToString())) as SomeRule;

    // ISomeService was not injected yet. Is it possible?
}

I can make this work by calling the autofac container, get the registered implementation of the ISomeService interface and assign it to a public property of the SomeRule instance. I am looking for a better solution, one that would not require the class to have a public property.

Is there a way to automatically inject dependencies with XmlSerializer?

Steven
  • 166,672
  • 24
  • 332
  • 435
Kzryzstof
  • 7,688
  • 10
  • 61
  • 108

1 Answers1

6

From a DI standpoint, having data-centric objects with constructors that accepts service dependencies is rather problematic, and should be prevented.

When practicing DI, we try to centralize the composition of our object graphs of application components (i.e. the classes that contain behavior and have dependencies of their own) to a centralized place in the application called the Composition Root.

A data-centric object that includes constructor dependencies, however, complicates this practice, since it either forces composition out of the Composition Root, or forces the addition of factory abstractions for the creation of these objects.

Instead, you should use one of following two alternatives:

  1. Separate data and behavior. This means moving SomeRule's DoSomething method to a new class, that takes SomeRule as an argument in its public method(s). The constructor dependency will move to this new class as well.
  2. Remove the constructor dependency of SomeRule and instead inject it into DoSomething using method injection.

Option 1 might look like this:

// SomeRule only contains data. Much simpler
[Serializable]
public class SomeRule 
{
    [XmlAttribute("Attribute1")]
    public string Attribute1 {get;set;}

    [XmlAttribute("Attribute2")]
    public string Attribute2 { get; set; }
}

// Moved behavior to new class. This class can be injected
// into consumers, as usual.
public class SomeRuleHandler : IRuleHandler<SomeRule>
{
    private readonly ISomeService m_service;

    // There's now just one constructor left
    public SomeRuleHandler(ISomeService service)
    { 
        m_service = service ?? throw new ArgumentNullException("service");
    }

    public void DoSomething(SomeRule rule)
    {
        m_service.DoStuff(rule.Attribute1);
    }
}

With option 2, the result will be the following:

[Serializable]
public class SomeRule 
{
    [XmlAttribute("Attribute1")]
    public string Attribute1 { get; set; }

    [XmlAttribute("Attribute2")]
    public string Attribute2 { get; set; }

    // No more constructors. The dependency is supplied in the method,
    // but *not* stored.
    public void DoSomething(ISomeService service)
    {
        service.DoStuff(Attribute1);
    }
}
Steven
  • 166,672
  • 24
  • 332
  • 435
  • public SomeRule(ISomeService someService) - should be SomeRuleHandler? – Leszek P Jun 20 '18 at 10:54
  • @LeszekRepie: You are correct. Thanks. I updated my answer. – Steven Jun 20 '18 at 11:00
  • 1
    Option 1 looks definitely better :) I will try that right away. – Kzryzstof Jun 20 '18 at 11:56
  • @Steven, nice pattern. Thanks. However, how would you do this (inject dependencies) if you are implementing IXmlSerializable (custom serialization) in `SomeRule` class and need to inject (or use some service) in ReadXml method of `SomeRule`? – ANewGuyInTown Jul 13 '22 at 03:10
  • @ANewGuyInTown: I would say you should prevent this situation all together by redesigning that code. How to do this, however, depends on the details of your situation. If in doubt, please post a new question here on SO with all the relevant details. – Steven Jul 13 '22 at 07:59
  • I created a similar question already with my scenario. https://stackoverflow.com/questions/72892642/how-do-i-make-xmlserializer-to-ignore-ixmlserializer-implementation – ANewGuyInTown Jul 13 '22 at 12:54