skip to Main Content

I’m working on web application based on .NET Core 2.0.
I have class ReportModel, which I use for binding parameters in controller method. And I also have some functionality in the same class (method Get() starts doing somethings, based on provided binded parameters), e.g:

    [HttpGet]
    public JsonResult GetReport(ReportModel model)
    {
        return new JsonResult( new { model.Get().ToString() });
    }

Now I’m moving on .NET 5 and want to add IConfiguration via DI to ReportModel.

I’ve expected it to work the follwing way: add constructor for ReportModel with IConfiguration for DI to provide configuration:

    public class ReportModel
    {
        private readonly IConfiguration _configuration;

        public ReportModel(IConfiguration configuration)
        {
            _configuration = configuration;
        }
    ...

But when I try to run GetReport(ReportModel model) method it returns exception:
InvalidOperationException: Could not create an instance of type 'ViewData.Models.Data.ReportModel'. Model bound complex types must not be abstract or value types and must have a parameterless constructor. Record types must have a single primary constructor. Alternatively, give the 'data' parameter a non-null default value.

A little explanation: there are few more classes, which I use this way (binding params to properties and storing functionality inside same class), so I don’t really want to add each method Get() additional parameter for IConfiguration.

How am I supposed to do this properly? Am I missing something? Or It’s just don’t work this way?

I’m sorry if I use any of terminology wrong, would be glad if you point me the wrong stuff. Thanks.

4

Answers


  1. Chosen as BEST ANSWER

    So I listened the advice of @Camilo. My plan is to split ReportModel into ReportParams (DTO class) and ReportProvider (business logics class). Then, add constructor for ReportProvider in order to provide IConfiguration via Dependency Injection by calling ActivatorUtilities.CreateInstance<ReportProvider>()

    Model splitting:

        public class ReportParams
        {
            public double Size { get; set; }
            
            // other properties.
        }
    
        public class ReportProvider
        {
            private readonly IConfiguration _configuration;
    
            public ReportProvider(IConfiguration configuration)
            {
                _configuration = configuration;
            }
    
            public List<string> Get(ReportParams args)
            {
                // ...
            }
        }
    

    ReportController:

        public class ReportController: Controller
        {
            private readonly IServiceProvider _services;
    
            public ReportController(IServiceProvider services)
            {
                _services = services;
            }
    
            public IActionResult Get(ReportParams args)
            {
                ReportProvider model = ActivatorUtilities.CreateInstance<ReportProvider>(_services);
                return JsonResult(model.Get(args));
            }
        }
    

  2. It’s a great question. You’re on the right track.

    In my opinion, the ReportModel simply shouldn’t use dependency injection. The model should just be a property bag that models the incoming parameters.

    Have logic be performed in a controller action (or otherwise handled some other way).

    Login or Signup to reply.
  3. How am I supposed to do this properly? Am I missing something? Or It’s just don’t work this way?

    It doesn’t work that way.

    First of all, models are not supposed to have dependencies injected, they are just a class for getting the input from the request.

    This is also wrong, though: ... GetReport(ReportModel model). This relies on the fact that ASP.NET Core injects a new ReportModel by default – if this behavior changes, your code magically stops working.

    Your code should look like this instead:

    public class ReportsController
    {
        private readonly IReportModelService _reportModelService;
    
        public ReportsController(IReportModelService reportModelService)
        {
            _reportModelService = reportModelService;
        }
    
        [HttpGet]
        public IActionResult GetReport()
        {        
            return new JsonResult(_reportModelService.GetReport());
        }
    }
    

    Along with an appropriate class that builds the report, something like:

    public class ReportModelService: IReportModelService
    {
        private readonly IConfiguration _configuration;
    
        public ReportModelService(IConfiguration configuration)
        {
            _configuration = configuration;
        }
    
        public string GetReport()
        {
            // your logic here
        }
    }
    
    Login or Signup to reply.
  4. default DI container that is used in net core can only inject dependency inside of the constructor, in this case a controller constructor, not inside of an action, since action doesn’t have a constructor. So change your code like this

    public class ReportController
    {
       private readonly ReportModel _model
    
      public ReportController(ReportModel model )
      {
       _model=model;
      }  
    
        [HttpGet]
        public JsonResult GetReport()
        {
            return new JsonResult( new { _model.Get().ToString() });
        }
    }
    

    and don’t forget to add DI to the startup

    services.AddScoped<ReportModel>();
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search