简体   繁体   中英

Create Instance of a class from interface which it implements

Hi I am trying to build a generic UoWFactory which creates a UnitOfWork(there will be one default unitofwork and multiple custom implementaitons as well). So far I was able to create factory method which creates default UoW and returns it. I have modify the following method to return specified UoW depending on the parameter passed.

Current implementation

private BaseResult<IUnitOfWork> GetUnitOfWorkByCompiledModel<IUnitOfWork>(DbCompiledModel compiledModel)
{
    return new BaseResult<IUnitOfWork>
    {
        Payload = new DbUnitOfWork(_context, _dbRepository, _mapper, _entityMapper)
    };
}

I wish to have something like this

private BaseResult<TUoW> GetUnitOfWorkByCompiledModel<IUnitOfWork>(DbCompiledModel compiledModel) where TUoW :Class
{
    return new BaseResult<TUoW>
    {
        //Create instance of type TUoW where TUoW can be IUnitOfWork, ICustomUnitOfWork etc
        //DbUnitOfWork implements IUnitOfWork and CustomUnitOfWork implements ICustomUnitOfWork
        //All the TUoW will have constructors with identical parmeters
    };
}

Create an instance of class is straight forward

Activator.CreateInstance (Type type, object[] args);

But if I pass Interface type as a parameter how to create instance of DbUnitOfWork or CustomUnitOfWork. eg:-

GetUnitOfWorkByCompiledModel<IUnitOfWork>(compiledModel);
GetUnitOfWorkByCompiledModel<ICustomUnitOfWork>(compiledModel);

Parameterless constructors

What you want is possible, except for one thing: you can't use non-default constructors with generic type arguments . You can't avoid that.

Part of the issue here is that you can't enforce specific constructor method signatures from an interface, so there is no way to guarantee that all implementation of IUnitOfWork are going to have the same constructor.

The simplest solution here is to step away from using constructors and instead use object initialization:

public interface IUnitOfWork
{
    Foo Foo { get; set; }
}

private BaseResult<TUnitOfWork> GetUnitOfWorkByCompiledModel<TUnitOfWork>(DbCompiledModel compiledModel) where TUnitOfWork : IUnitOfWork, new()
{
    return new BaseResult<TUnitOfWork>
    {
        Payload = new TUnitOfWork()
                  {
                      Foo = myFoo
                  };
    };
}

I think this suits your expectations while being a minimal change.


Resolving interfaces

But if I pass Interface type as a parameter how to create instance of DbUnitOfWork or CustomUnitOfWork. eg

GetUnitOfWorkByCompiledModel<IUnitOfWork>(compiledModel);
GetUnitOfWorkByCompiledModel<ICustomUnitOfWork>(compiledModel);

If you intend to use interface types without concrete types, then the above answer is incomplete.

Regardless of the generic type issue, if you want to resolve an interface into a concrete type, you need to register which concrete type you want to use.

This is most commonly done via a Dependency Injection framework. These framework ask you to register all necessary types, eg a .NET Core example:

services.AddTransient<IUnitOfWork, MyUnitOfWork>();
services.AddTransient<ICustomUnitOfWork, MyCustomUnitOfWork>();

And the framework will then use this registration to automatically fill in constructor parameters for you:

public class Example
{
    public Example(ICustomUnitOfWork uow)
    {
        
    }
}

The good practice approach here requires you to thread this dependency injection through your entire framework so you never need to call any constructor explicitly (and instead have the DI framework do it for you).

It is possible to use a service locator , which is essentially a DI framework that you call at-will. A small example of usage would be:

private BaseResult<TUnitOfWork> GetUnitOfWorkByCompiledModel<TUnitOfWork>(DbCompiledModel compiledModel) where TUnitOfWork : IUnitOfWork, new()
{
    var uow = myServiceLocator.Get<TUnitOfWork>();
    uow.Foo = myFoo;

    return new BaseResult<TUnitOfWork>
    {
        Payload = uow;
    };
}

This creates an instance of whichever concrete type was registered to the interface you're using as the generic parameter.

However, service locators are generally considered to be an anti-pattern , and I would strongly suggest that you avoid favor a cleaner IoC approach than this.

I can't elaborate fully on dependency injection in scope of a single StackOverflow answer. I suggest you look up dependency injection as it does exactly what you're expecting (getting a concrete type by referencing an interface)

This will be work if DbUnitOfWork Class has right name of values

What you want to change

Payload = new DbUnitOfWork(_context, _dbRepository, _mapper, _entityMapper);

Change as

Payload = new DbUnitOfWork() {
    context = _context, 
    dbRepoitory = _dbRepository,
    mapper = _mapper,
    entityMapper = _entityMapper
};

hope this work.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM