Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/285.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 使用依赖注入注入多个实现_C#_Dependency Injection_Asp.net Core_Asp.net Core Mvc - Fatal编程技术网

C# 使用依赖注入注入多个实现

C# 使用依赖注入注入多个实现,c#,dependency-injection,asp.net-core,asp.net-core-mvc,C#,Dependency Injection,Asp.net Core,Asp.net Core Mvc,我目前正在从事一个ASP.NET核心项目,希望使用内置的依赖项注入(DI)功能 我从一个界面开始: ICar { string Drive(); } 并且想要多次实现ICar接口,就像 public class BMW : ICar { public string Drive(){...}; } public class Jaguar : ICar { public string Drive(){...}; } 并在启动类中添加以下内容 public void Co

我目前正在从事一个ASP.NET核心项目,希望使用内置的依赖项注入(DI)功能

我从一个界面开始:

ICar
{
    string Drive();
}
并且想要多次实现
ICar
接口,就像

public class BMW : ICar
{
    public string Drive(){...};
}

public class Jaguar : ICar
{
    public string Drive(){...};
}
并在
启动
类中添加以下内容

public void ConfigureServices(IServiceCollection services)
{
     // Add framework services.
     services.AddMvc();
     services.AddTransient<ICar, BMW>(); 
     // or 
     services.AddTransient<ICar, Jaguar>();
 }

您正在寻找的功能不容易实现,至少当您在控制器中使用它时是这样的,因为控制器是经过特殊处理的(默认情况下,控制器未注册到
ServiceCollection
,因此未由容器解析/实例化,而是在请求期间由ASP.NET Core实例化,另请参见上的说明和示例)

对于内置的IoC容器,您只能通过factory方法执行此操作,以下是
BmwCarFactory
类的示例:

services.AddScoped<ICar, BmwCar>();
services.AddScoped<BmwCar>();
services.AddScoped<BmwCarFactory>(p => new BmwCarFactory(p.GetRequiredService<BmwCar>())));

但是AutoFac的这第四个选项使IOC容器泄漏到您的服务中,您应该考虑其他方法。

或者,您可以创建基于某些约定解析服务的类和工厂。例如,
ICarFactory

public ICarFactory
{
    ICar Create(string carType);
}

public CarFactory : ICarFactory
{
    public IServiceProvider provider;

    public CarFactory(IServiceProvider provider)
    {
        this.provider = provider;
    }

    public ICar Create(string carType)
    {
        if(type==null)
            throw new ArgumentNullException(nameof(carType));

        var fullQualifedName = $"MyProject.Business.Models.Cars.{carType}Car";
        Type carType = Type.GetType(fullQualifedName);
        if(carType==null)
            throw new InvalidOperationException($"'{carType}' is not a valid car type.");

        ICar car = provider.GetService(carType);
        if(car==null)
            throw new InvalidOperationException($"Can't resolve '{carType.Fullname}'. Make sure it's registered with the IoC container.");

        return car;
    }
}
然后像这样使用它

public class BmwController : Controller
{
    public ICarFactory carFactory;

    public BmwController(ICarFactory carFactory)
    {
        this.carFactory = carFactory;

        // Get the car
        ICar bmw = carFactory.Create("Bmw");
    }
}
##IServiceProvider的替代方案

// alternatively inject IEnumerable<ICar>
public CarFactory : ICarFactory
{
    public IEnumerable<ICar> cars;

    public CarFactory(IEnumerable<ICar> cars)
    {
        this.cars = cars;
    }

    public ICar Create(string carType)
    {
        if(type==null)
            throw new ArgumentNullException(nameof(carType));

        var carName = "${carType}Car";
        var car = cars.Where(c => c.GetType().Name == carName).SingleOrDefault();

        if(car==null)
            throw new InvalidOperationException($"Can't resolve '{carName}.'. Make sure it's registered with the IoC container.");

        return car;
    }
}
//或者注入IEnumerable
公共汽车制造厂:ICarFactory
{
公共汽车;
公共汽车制造厂(IEnumerable CarFactory)
{
这个。汽车=汽车;
}
公共ICar创建(字符串类型)
{
if(type==null)
抛出新ArgumentNullException(nameof(carType));
var carName=“${carType}Car”;
var car=cars.Where(c=>c.GetType().Name==carName.SingleOrDefault();
if(car==null)
抛出新的InvalidOperationException($“无法解析“{carName}.”。请确保它已在IoC容器中注册。”);
返回车;
}
}

我认为这个想法是错误的。如果你有需要具体car实现的BMWController,注入ICar毫无意义——DI的魔力——你说“给我ICar的实现”,而不管它是什么。我会准备一个单独的接口:
接口IBmwCar:ICar{…}
并将其注入
BMWController
。另一个想法是创建factory.f.e
类CarFactory:ICarFactory{ICar GimmeCar()}
您已经将控制器类型映射到具体实现。如果我为我想要实现的所有品牌使用类似于IBmwCar:ICar的东西,这不是界面溢出吗?我只有Drive-as方法,不会有任何其他方法。好吧,我将创建IBMW,IJaguar…具有与ICar相同的签名,并使用类似于t的语句我实现的每个品牌的his:services.AddTransient();显然,我可以使用工厂模式,但没有解决方案来处理DI吗?DI的一个问题是,您可以依赖IEnumerable并获取所有已注册的实现,如果没有注册任何实现,您将得到一个空列表,而不是解决它的失败。因此,如果ICar有一些标识符属性,您可以在列表中找到所需的内容。还使用
IEnumerable
添加了@JoeAudette注释的替代项,以避免对容器的依赖性这正是我想要的,谢谢!我以前在项目中使用过Unity,我想知道是否有可能在ASP.NET Core的nativ DI中使用Unity。我得到了答案谢谢!为了更好地理解第1点,我还有一个问题。一个接口应该只有一个实现吗?取决于。它可以有多个实现,但您在注册时只能选择一个。例如,
IUserRepository
具有
SqliteUserRepository
sqlserveruserrepository
并且您只注册其中一个。如果您注册了多个注册,您无法根据上下文轻松地注入它们,或者您使用
IEnumerable
解析所有注册。因此,如果您想使用其他选项通过
IBmwCar
w/o,那么是的,它应该只有一个实现感谢您提供的详细答案。当我们通常依赖DI来解决基于注册的服务。只是想知道,除了我们对容器的依赖性增加之外,这种情况是否还有其他副作用。
public ICarFactory
{
    ICar Create(string carType);
}

public CarFactory : ICarFactory
{
    public IServiceProvider provider;

    public CarFactory(IServiceProvider provider)
    {
        this.provider = provider;
    }

    public ICar Create(string carType)
    {
        if(type==null)
            throw new ArgumentNullException(nameof(carType));

        var fullQualifedName = $"MyProject.Business.Models.Cars.{carType}Car";
        Type carType = Type.GetType(fullQualifedName);
        if(carType==null)
            throw new InvalidOperationException($"'{carType}' is not a valid car type.");

        ICar car = provider.GetService(carType);
        if(car==null)
            throw new InvalidOperationException($"Can't resolve '{carType.Fullname}'. Make sure it's registered with the IoC container.");

        return car;
    }
}
public class BmwController : Controller
{
    public ICarFactory carFactory;

    public BmwController(ICarFactory carFactory)
    {
        this.carFactory = carFactory;

        // Get the car
        ICar bmw = carFactory.Create("Bmw");
    }
}
// alternatively inject IEnumerable<ICar>
public CarFactory : ICarFactory
{
    public IEnumerable<ICar> cars;

    public CarFactory(IEnumerable<ICar> cars)
    {
        this.cars = cars;
    }

    public ICar Create(string carType)
    {
        if(type==null)
            throw new ArgumentNullException(nameof(carType));

        var carName = "${carType}Car";
        var car = cars.Where(c => c.GetType().Name == carName).SingleOrDefault();

        if(car==null)
            throw new InvalidOperationException($"Can't resolve '{carName}.'. Make sure it's registered with the IoC container.");

        return car;
    }
}