How to pass a service from .net core di container

2019-08-09 21:40发布

问题:

I'm facing a scenario where I need to inject a service from asp.net core DI container to the constructor of an object created using automapper.

I don't know if this is the best practice but let me explain what I'm trying to accomplish.

I've an asp.net core mvc controller that receives a model parameter, just a POCO, that model needs to be converted into a ViewModel class that contains some business logic, data access etc, in that class object I want to get some info from the injected service, that is the part where I'm having problems, cant figure out how to inject the service from controller to the final ViewModel.

My code at this moment looks something like this.

NewGameModel.cs

namespace MyProject.Shared.Models
{
    public class NewGameModel
    {
        public List<PlayerModel> Players { get; set; }

        public bool IsValid => Players.Any();

        public NewGameModel()
        {
            Players = new List<PlayerModel>();
        }
    }
}

NewGameViewModel.cs

namespace MyProject.Core.ViewModels
{
    public class NewGameViewModel
    {
        private Guid Token = Guid.NewGuid();
        private DateTime DateTimeStarted = DateTime.Now;
        private readonly IConfiguration _configuration;

        public List<PlayerModel> Players { get; set; }

        public NewGameViewModel(IConfiguration config)
        {
            _configuration = config;
        }

        public string DoSomething()
        {
            //Do something using _configuration

            //Business Logic, Data Access etc
        }
    }
}

MapperProfile.cs

namespace MyProject.Service
{
    public class MapperProfile : Profile
    {
        public MapperProfile()
        {
            CreateMap<NewGameModel, NewGameViewModel>();
        }
    }
}

ASP.NET Core project - Startup.cs

namespace MyProject.Service
{
    public class Startup
    {
        public IConfiguration Configuration { get; }

        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc();
            services.AddSingleton(Configuration);

            var autoMapperConfig = new MapperConfiguration(cfg =>
            {
                cfg.AddProfile(new MapperProfile());
            });

            var mapper = autoMapperConfig.CreateMapper();

            services.AddSingleton(mapper);
        }

        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseMvc();
        }
    }
}

ASP.NET Core project - GameController.cs

namespace MyProject.Service.Controllers
{
    [Route("api/[controller]")]
    public class GameController : Controller
    {
        private readonly IConfiguration _configuration;
        private readonly IMapper _mapper;

        public GameController(IConfiguration config, IMapper mapper)
        {
            _configuration = config;
            _mapper = mapper;
        }

        [HttpPost]
        public IActionResult CreateNewGame([FromBody]NewGameModel model)
        {
            if (!model.IsValid) return BadRequest();

            //Throws error because no constructor parameter was passed    
            //How to pass the IConfiguration to the destination NewGameViewModel object?

            var viewModel = _mapper.Map<NewGameModel, NewGameViewModel>(model);

            var result = viewModel.DoSomething();

            return CreatedAtRoute("GetGame", new { token = result.GameToken }, result);
        }
    }
}

I will appreciate your help

回答1:

Update the profile to take the configuration as an injected dependency and use ConstructUsing when creating the mapping.

public class MapperProfile : Profile {
    public MapperProfile(IConfiguration config) {
        CreateMap<NewGameModel, NewGameViewModel>()
          .ConstructUsing(_ => new NewGameViewModel(config));
    }
}


回答2:

For those coming later, the method above is not preferred, as it has state inside profiles. Instead, use the AutoMapper.Extensions.Microsoft.DependencyInjection package and inside your startup:

services.AddAutoMapper();

Then in your profile, just tell AutoMapper that you want your destination object constructed with the container:

public class MapperProfile : Profile
{
    public MapperProfile()
    {
        CreateMap<NewGameModel, NewGameViewModel>()
            .ConstructUsingServiceLocator();
    }
}

Your controller can then depend in IMapper and AutoMapper will use the DI container to construct the view model, and your configuration to ASP.NET Core will just be the one line to services.AddAutoMapper();