Search code examples
c#dependency-injectionautofacxmlserializer

How to inject dependencies with XmlSerializer and Autofac?


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?


Solution

  • 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);
        }
    }