I have a Document
class that has a list of Objects
. To create and add an Object
, you pass in a numeric object type id to Document.CreateObject()
. Then depending on the type id, it will create the appropriate object instance.
If the id is 0, it will create an instance of Object1, if it is 1, it will create an instance of Object2.
How can I set this up with a DI container that allows me to specify the implementation for IObject1 and IObject2?
This is what I have so far, but don't know how to implement the commented out bit.
namespace ConsoleApp9
{
public interface IObjectFactory
{
IObject Create(IDocument document, int type);
}
public class ObjectFactory : IObjectFactory
{
private readonly Func<IDocument, Type, IObject> _factory;
public ObjectFactory(Func<IDocument, Type, IObject> factory)
{
_factory = factory;
}
public IObject Create(IDocument document, int type)
{
if (type == 0) return _factory(document, typeof(IObject1));
if (type == 1) return _factory(document, typeof(IObject2));
throw new NotSupportedException(nameof(type));
}
}
public interface IDocument
{
void CreateObject(int type);
}
public class Document : IDocument
{
private readonly IObjectFactory _objectFactory;
public List<IObject> Objects { get; } = new();
public Document(IObjectFactory objectFactory)
{
_objectFactory = objectFactory;
}
public void CreateObject(int type)
{
var obj = _objectFactory.Create(this, type);
Objects.Add(obj);
}
}
public interface IObject
{
IDocument Document { get; }
}
public interface IObject1 : IObject { }
public class Object1 : IObject1
{
public IDocument Document { get; }
public Object1(IDocument document)
{
Document = document;
}
}
public interface IObject2 : IObject { }
public class Object2 : IObject2
{
public IDocument Document { get; }
public Object2(IDocument document)
{
Document = document;
}
}
internal class Program
{
static void Main(string[] args)
{
var services = new ServiceCollection();
services.AddTransient<IObject1, Object1>();
services.AddTransient<IObject2, Object2>();
services.AddSingleton(typeof(IObjectFactory), (x) =>
{
return new ObjectFactory((IDocument document, Type type) =>
{
// type will be either typeof(IObject1) or typeof(IObject2)
// I want to resolve by this type and also pass the constructor arg (document)
// x.GetRequiredService(type, document);
// I don't want to do this.
return new Object1(document);
});
});
services.AddTransient<IDocument, Document>();
var serviceProvider = services.BuildServiceProvider();
var document = serviceProvider.GetRequiredService<IDocument>();
document.CreateObject(0);
document.CreateObject(1);
}
}
}
As mentioned in the comment. I want to resolve the object to create by using the type returned from the factory.
I'm probably going about this the complete wrong way!
First and foremost, as per your current design, The
IObject
instances can only be created by passing theIDocument
object in the constructor, so instantiatingIObject
instance types explicitly using thenew
keyword inside theCreate
method is a good approach, and this way, we can avoid circular dependency.In case, you don't like to create instances explicitly using the
new
keyword, then don't pass theIDocument
object in the constructor, instead create an object using the service provider, and then configure theIDocument
object by calling an initialization method.