2

I created a project that uses Web API. I want to have the controller a constructor that accepts one argument for contsructor injection. In startup, I added this:

Startup.cs:

public class Startup
{
    // Other codes ommitted

    public void ConfigureServices(IServiceCollection services)
    {
        // Other codes ommitted

        // Add application services.
        services.AddTransient<IStudentDataAccess, StudentDataAccess>();
    }
}

In controller, I have this constructor:

ValuesController:

[Route("api/[controller]")]
public class ValuesController : Controller
{
    private IStudentDataAccess _studentDataAccess;

    public ValuesController(IStudentDataAccess studentDataAccess)
    {
        _studentDataAccess = studentDataAccess;
    }

    // GET: api/values
    [HttpGet]
    public IEnumerable<string> Get()
    {
        IStudentLogic studentLogic = new StudentLogic(_studentDataAccess);
        return new string[] { "value1", "value2" };
    }
}

But the constructor isn't called. I saw that for the previous ASP.NET versions, you can do this. How do I do this in the new ASP.NET? Or how do I pass a parameter when ASP.NET resolves IStudentDataAccess?

UPDATE:

Ok, I think the problem is my StudentDataAccess class has a non-default constructor that accepts an IDbContext. If I remove the constructor, it works. But I need to be able to pass an IDbContext in StudentDataAccess' constructor. I tried adding this in ConfigureServices:

services.AddTransient<IDbContext, TestDbContext>();

But the constructor isn't getting hit by the breakpoint. Here is the declaration of StudentDataAccess:

public class StudentDataAccess : IStudentDataAccess
{
    private IDbContext _context;

    public StudentDataAccess(IDbContext context)
    {
        _context = context;
    }
}

IDbContext is just an interface with no member.

10
  • 2
    It should work. Is your controller derived from Microsoft.AspNet.Mvc.Controller? Do you have more than one constructor? Do you have a minimal, complete and verifiable example? Commented Mar 5, 2016 at 15:42
  • @Leonardo Herrera: It doesn't unfortunately. I can't copy everything here but what I did is the defaut Web API template, created a new constructor with one parameter, put a breakpoint ont it, and then ran it. It doesn't hit the breakpoint. Commented Mar 5, 2016 at 16:21
  • You can create an MCVE. Show your controller declaration. Commented Mar 5, 2016 at 16:30
  • @mason: Added as suggested. Commented Mar 5, 2016 at 17:10
  • If you put a breakpoint in the 1st line of your get method, does that get hit? Commented Mar 5, 2016 at 19:57

2 Answers 2

3

Something is not right with what you are showing.

To add your own transient service to your controller:

  • Define your interface
  • Define your class implementing your interface
  • Add services.AddTransient<IYourInterface, YourClass>(); to ConfigureServices in Startup.cs
  • Add a constructor to your controller that receives your service as a parameter. Make sure you have only one constructor.

My minimal working example:

Startup.cs:

using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;

namespace WebApplication5
{
    public class Startup
    {
        public Startup(IHostingEnvironment env)
        {
        }

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc();
            services.AddTransient<IStudentDataAccess, StudentDataAccess>();
        }

        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
            app.UseIISPlatformHandler();
            app.UseMvc();
        }
        public static void Main(string[] args) => WebApplication.Run<Startup>(args);
    }
}

ValuesController.cs:

using Microsoft.AspNet.Mvc;
using System.Collections.Generic;

namespace WebApplication5.Controllers
{
    [Route("api/[controller]")]
    public class ValuesController : Controller
    {
        private IStudentDataAccess _studentDataAccess;

        public ValuesController(IStudentDataAccess studentDataAccess)
        {
            _studentDataAccess = studentDataAccess;
        }

        [HttpGet]
        public IEnumerable<string> Get()
        {
            return new string[] { "value", _studentDataAccess.Hello() };
        }
    }
}

StudentDataAccess.cs:

namespace WebApplication5
{
    public interface IStudentDataAccess
    {
        string Hello();
    }

    public class StudentDataAccess : IStudentDataAccess
    {
        public string Hello()
        {
            return "Hello from Service";
        }
    }
}

And the result:

["value","Hello from Service"]
Sign up to request clarification or add additional context in comments.

6 Comments

I tried out your example and it does work. In my project, the Interface and the concrete class is in both a different assembly (but StudentDataAccess surely implements IStudentDataAccess). When this is the setup, the constructor isn't hit. But when I move them to the same assembly (just after Startup.cs), it works. Any idea why this is? I have the same code, just different assembly.
Ok, I think the problem is my StudentDataAccess class has a non-default constructor that accepts an IDbContext. If I remove the constructor, it works. But I need to be able to pass an IDbContext in StudentDataAccess' constructor. Can you tell me how?
I created a new project and it works even with StudentDataAccess no default constructor. I need to track down what is happening on my original problem, but as you said, it just works.
I think you need to use configuration options for your service. That's probably another question.
Can we have two constructors?
|
1

Try putting FromServicesAttribute, for example:

public ValuesController([FromServices]IStudentDataAccess studentDataAccess)
{
    _studentDataAccess = studentDataAccess;
}

this worked for me :)

1 Comment

The FromServices attribute is not needed in the constructor. It is used in actions.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.