基于运行时输入的依赖注入

时间:2016-01-19 20:51:47

标签: c# asp.net dependency-injection inversion-of-control solid-principles

CCI正在编写一个外观来获取来自不同来源的数据,对其进行规范化和格式化。我是新手使用asp.net 5并给出依赖注入,但我遇到了问题。我想知道如何根据运行时输入解决依赖关系。基于我想要实例化正确的存储库的路由。例如,如果我通过丰田我想要实例化一个ToyotaRepository,如果我通过福特我想要实例化一个FordRepository。这些存储库还具有每个存储库唯一的依赖项。所有存储库共享相同的ICarRepository接口,并且依赖于相同的接口但具体实现不同。我想过使用工厂来创建存储库,但是每个存储库的依赖关系必须注入工厂,这感觉不对。随着存储库数量的增加,需要注入的依赖项数量也随之增加。目前我只是在工厂中新建了存储库及其依赖项,这也感觉不对,不是非常SOLID。也许我的架构存在问题?

[Route("api/v1/[controller]")]
public class CarsController : Controller
{
    private IDataFormatter<Product> _formatter;
    private ILogger _logger;
    private ICarRepositoryFactory _repositoryFactory;

    public CarssController(ILogger<CarsController> logger, IProductRepositoryFactory repositoryFactory, IDataFormatter<Car> formatter)
    {
        _logger     = logger;
        _repositoryFactory = repositoryFactory;
        _formatter  = formatter;
    }

    [HttpGet("{carType}")]
    public async Task<IEnumerable<Car>> GetCars(string carType)
    {
        var repository = _repositoryFactory.Create(carType);
        var cars = await repository.GetAll();
        foreach(var car in cars)
        {
            _formatter.Format(car);
        }
        return cars;
    }
}

public class CarRepositoryFacotry : ICarRepositoryFactory
{
    private Dictionary<string, Func<ICarRepository>> _carRepositories = new Dictionary<string, Func<ICarRepository>>();
    private ILogger<ICarRepository> _logger;
    private IOptions<WebOptions> _webOptions;
    private IOptions<DisplayInfoOptions> _displayOptions;

    public CarRepositoryFacotry(ILogger<ICarRepository> logger, IOptions<WebOptions> webOptions, IOptions<DisplayInfoOptions> displayInfoOptions)
    {
        _logger = logger;
        _webOptions = webOptions;
        _displayInfoOptions = displayInfoOptions;

        _carRepositories.Add("toyota", () => new ToyotaRepository(_logger, new DisplayInfoRepository(_displayInfoOptions), new ToyotaMapper(), _options));
        _carRepositories.Add("ford", () => new FordRepository(_logger, new DisplayInfoRepository(_displayInfoOptions), new FordMapper(), _options));
    }
    public ICarRepository Create(string carType)
    {
        Func<ICarRepository> repo;
        _carRepositories.TryGetValue(carType, out repo);
        return repo.Invoke();
    }
}

我目前正在使用asp.net 5中的内置依赖框架,但我愿意使用autofac,如果它让事情变得更有效。任何帮助或评论都会有很大的帮助。

3 个答案:

答案 0 :(得分:1)

使用工厂注入所有存储库是可行的方法(并且比临时&#34;新的&#34;依赖性更好)

示例

public interface IVehicleRepository
{
    bool CanHandle(string vendor); // example how to deal with choosing appropriate repository

    IEnumerable<Vehicle> GetAll();
}

public class VehicleFactory
{
    private readonly IVehicleRepository[] repositories;

    public VehicleFactory(IVehicleRepository[] repositories)
    {
        this.repositories = repositories;
    }

    public IVehicleRepository Create(string vendor) {
        return repositories.Single(r => r.CanHandle(vendor));
    }
}

<强>用法:

[Route("api/v1/[controller]")]
public class CarController : Controller
{
    private readonly VehicleFactory factory;

    public CarController(VehicleFactory factory)
    {
        this.factory = factory;
    }

    [HttpGet("{vehicleType}")]
    public IEnumerable<Vehicle> GetVehicles(string vehicleType)
    {
        var repository = factory.Create(vehicleType);
        var vehicles = repository.GetAll();
        foreach (var vehicle in vehicles)
        {
            // Process(vehicle)
        }
        return vehicles;
    }
}

答案 1 :(得分:0)

我是这样看的:

  1. 您的CarsController将ICarRepository作为构造函数参数 并与之合作
  2. 你必须怀好并注册自己的 IControllerFactory将分析路由参数并创建 带有具体存储库的Controller的具体实例
  3. Google的第一个链接。可能不是最好的,但很好。

    http://www.codeproject.com/Articles/560798/ASP-NET-MVC-Controller-Dependency-Injection-for-Be

答案 2 :(得分:0)

我的团队使用 Castle Windsor,这是一个 IoC 容器,可以轻松解决我们的所有依赖项。 (应该类似于 Autofac,但我在企业应用中更常看到 Castle Windsor)

在你的情况下,你可以

1.像这样注册 FordRepository

public class RepositoriesInstaller : IWindsorInstaller
{
    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
        container.Register(
            Classes.FromThisAssembly(),
            Component.For<DisplayInfoRepository>().ImplementedBy<DisplayInfoRepository>
              .DependsOn(Dependency.OnValue("_displayInfoOptions", displayInfoOptionsObject)),
                                           // whatever property name you have in DisplayInfoRepository

            Component.For<ICarRepository>().ImplementedBy<FordRepository>().Named("Ford")
              .DependsOn(Dependency.OnComponent(typeof(Logger), nameof("Logger")))
              .DependsOn(Dependency.OnComponent(typeof(DisplayInfoRepository), nameof(DisplayInfoRepository)))
              .DependsOn(Dependency.OnComponent(typeof(FordMapper), nameof(FordMapper)))
              .DependsOn(Dependency.OnValue("_option", optionObject)),
                                        // what ever property name you have in FordRepository
        );
    }
}

2.启动容器:

// application starts...
var container = new WindsorContainer();
container.Install(FromAssembly.This());

// clean up, application exits
container.Dispose();

3.根据字符串获取您的汽车存储库 像这样

var carRepo = container.Resolve<ICarRepository>("Ford");

如果有任何问题,请告诉我!不胜感激!