KiddoDeveloper
KiddoDeveloper

Reputation: 628

Dependency injection in .NET Core

I am trying to create an instance of a class in a controller. But, I am not able to do it.

Controller code:

    [HttpPost]
    [Route("connection")]
    public async Task<IActionResult> DoSomething(string firstName, string lastName)
    {
        return new OkObjectResult(new MyClass().ShowData(firstName, lastName));
    }

Class code:

public interface ISomeInterface
{
    void SomeMethod();
}

public class MyClass
{
    private readonly ISomeInterface _someInterface;

    public MyClass(ISomeInterface someInterface)
    {
        _someInterface = someInterface;
    }

    public Task<bool> ShowData(string firstName, string lastName)
    {
        // some logic
        if (firstName == "Ram")
        {
            ClassOne obj1 = new ClassOne();
            obj1.DoPlus();
        } else if (firstName == "Sam")
        {
            ClassTwo obj1 = new ClassTwo();
            obj1.DoPlus();
        }
        return Task.FromResult(true);
    }
}

Another interface

public interface IClassOneTwo
{ 
  void DoPlus();
}

public class ClassOne : IClassOneTwo
{ 
  public void DoPlus()
  { }
}

public class ClassTwo : IClassOneTwo
{ 
  public void DoPlus()
  { }
}

How I can inject ISomeInterface into the controller? Also, How I can decouple instances of ClassOne and ClassTwo? I want to register IClassOneTwo interface and based on IF/ELSE condition, I want to get an instance of ClassOne OR ClassTwo.

Upvotes: 1

Views: 103

Answers (1)

Andy
Andy

Reputation: 13607

For completeness sake, you should abstract MyClass behind an interface.

For example:

public interface IMyClass
{
    Task<bool> ShowData(string firstName, string lastName);
}

And have MyClass implement it:

public class MyClass : IMyClass
{
    private readonly ISomeInterface _someInterface;
    public MyClass(ISomeInterface someInterface)
    {
        _someInterface = someInterface;
    }
    public Task<bool> ShowData(string firstName, string lastName)
    {
        // some logic

        return Task.FromResult(true);
    }
}

Then in your Startup.cs class, in the ConfigureServices method, you should register it:

services.AddTransient<IMyClass, MyClass>();

You should also register ISomeInterface:

services.AddTransient<ISomeInterface, SomeOtherClass>();

Then, in your controller, you should inject the services you want via the constructor:

public sealed class MyController : ControllerBase
    
    private readonly IMyClass _myClass;

    public MyController(IMyClass myClass)
    {
        _myClass = myClass;
    }

    [HttpPost, Route("connection")]
    public async Task<IActionResult> DoSomething(
        [FromQuery]string firstName, [FromQuery]string lastName)
    {
        return Ok(await _myClass.ShowData(firstName, lastName));
    }
}

Edited To Add...

The OP is asking how to make MyClass more-or-less a utility that can perform an operation using ISomeInterface. So, you still want to register ISomeInterface to the pipeline:

services.AddTransient<ISomeInterface, SomeOtherClass>();

You can switch MyClass back to how you have it.

Inject ISomeInterface in to your controller as shown above, then just pass it to MyClass:

public sealed class MyController : ControllerBase
    
    private readonly ISomeInterface _someOtherClass;

    public MyController(ISomeInterface someOtherClass)
    {
        _someOtherClass = someOtherClass;
    }

    [HttpPost, Route("connection")]
    public async Task<IActionResult> DoSomething(
        [FromQuery]string firstName, [FromQuery]string lastName)
    {
        return Ok(await new MyClass(_someOtherClass)
                .ShowData(firstName, lastName));
    }
}

Edited to Add (again)...

So I think we got to the bottom of what OP wants. They want to inject two services that share the same interface. There are many ways to do this. I'll stick to one that I have used in the past.

First thing, define a delegate:

public delegate IClassOneTwo MyServiceResolver(string key);

Then in your Startup.cs, in the ConfigureServices method, register:

services.AddTransient<ClassOne>();
services.AddTransient<ClassTwo>();

Keep in mind we don't use the interface when registering. That is important.

Now we create another service that can resolve which one you want:

services.AddTransient<MyServiceResolver>(serviceProvider => key =>
{
    switch (key)
    {
        case "Ram":
            return serviceProvider.GetService<ClassOne>();
        case "Sam":
            return serviceProvider.GetService<ClassTwo>();
        default:
            return null; // or throw an exception
    }
});

Then, in whatever service you decide to inject in to (for simplicity's sake, I will use the controller):

public sealed class MyController : ControllerBase
    
    private readonly MyServiceResolver _serviceResolver;

    public MyController(MyServiceResolver serviceResolver)
    {
        _serviceResolver = serviceResolver;
    }

    [HttpPost, Route("connection")]
    public IActionResult DoSomething(
        [FromQuery]string firstName, [FromQuery]string lastName)
    {
        _serviceResolver(firstName).DoPlus();

        return Ok();
    }
}

Now, your service will be resolved at runtime.

Upvotes: 4

Related Questions