Given the code example at the end of this question, how would you configure a Microsoft DI container to resolve the types?
For example, I would like to be able to have something like :
var services = new ServiceCollection();
services.AddTransient<IHeader, Header>();
services.AddTransient<IParagraph, Paragraph>();
services.AddTransient<IFooter, Footer>();
services.AddTransient<IDocument, Document>();
services.AddTransient<IUndoRedo, UndoRedo>();
where I could for example swap out the implementation of IParagraph
for something else, without having to make the change in lots of places, like this :
services.AddTransient<IParagraph, ParagraphVersion2>();
Here is the code I am trying to convert to use DI :
public enum ObjectType
{
Header,
Footer,
Paragraph
}
public interface IObject
{
IUndoRedo UndoRedo { get; }
}
public interface IHeader : IObject { }
public class Header : IHeader
{
public IUndoRedo UndoRedo { get; }
public Header(IUndoRedo undoRedo)
{
UndoRedo = undoRedo;
}
}
public interface IParagraph : IObject { }
public class Paragraph : IParagraph
{
public IUndoRedo UndoRedo { get; }
public Paragraph(IUndoRedo undoRedo)
{
UndoRedo = undoRedo;
}
}
public interface IFooter : IObject { }
public class Footer : IFooter
{
public IUndoRedo UndoRedo { get; }
public Footer(IUndoRedo undoRedo)
{
UndoRedo = undoRedo;
}
}
public interface IDocument
{
IUndoRedo UndoRedo { get; }
List<IObject> Objects { get; }
}
public interface IUndoRedo { }
public class UndoRedo : IUndoRedo { }
public class Document : IDocument
{
public IUndoRedo UndoRedo { get; }
public List<IObject> Objects { get; } = new();
public Document(IUndoRedo undoRedo)
{
UndoRedo = undoRedo;
}
}
public class ObjectFactory
{
public IObject Create(ObjectType objectType, IUndoRedo undoRedo)
{
switch (objectType)
{
case ObjectType.Header:
return new Header(undoRedo);
case ObjectType.Footer:
return new Footer(undoRedo);
case ObjectType.Paragraph:
return new Paragraph(undoRedo);
default:
throw new NotSupportedException(nameof(objectType));
}
}
}
internal class Program
{
static void Main(string[] args)
{
var factory = new ObjectFactory();
// each new document gets a unique undoredo that is shared by its objects
{
var undoRedo1 = new UndoRedo();
var document1 = new Document(undoRedo1);
document1.Objects.Add(factory.Create(ObjectType.Header, undoRedo1));
document1.Objects.Add(factory.Create(ObjectType.Footer, undoRedo1));
document1.Objects.Add(factory.Create(ObjectType.Paragraph, undoRedo1));
document1.Objects.Add(factory.Create(ObjectType.Paragraph, undoRedo1));
}
// each new document gets a unique undoredo that is shared by its objects
{
var undoRedo2 = new UndoRedo();
var document2 = new Document(undoRedo2);
document2.Objects.Add(factory.Create(ObjectType.Header, undoRedo2));
document2.Objects.Add(factory.Create(ObjectType.Footer, undoRedo2));
document2.Objects.Add(factory.Create(ObjectType.Paragraph, undoRedo2));
}
}
}
I can't quite figure out how to pass the constructor arguments. There should be one instance of IUndoRedo
per document, and each object within the document should be passed the documents IUndoRedo
instance.
The only way I have managed to do it, is to remove the constructor arguments and use properties instead, that are set after construction. But I don't want to do this, as it means you can accidently create partially valid objects if you forget to set the required properties.