Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/asp.net-mvc/17.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# 从asp.net mvc中的参数解析依赖项_C#_Asp.net Mvc_Dependency Injection_Autofac - Fatal编程技术网

C# 从asp.net mvc中的参数解析依赖项

C# 从asp.net mvc中的参数解析依赖项,c#,asp.net-mvc,dependency-injection,autofac,C#,Asp.net Mvc,Dependency Injection,Autofac,以下是接口和类(例如): 其消费对象为: // MVC ActionResult to encapsulate the response for a report public class ReportResult : ActionResult { public ReportResult(IReportGenerator rg) { } } 这就是我注册这些的方式: containerBuilder cb; cb.RegisterType<PersonReportGener

以下是接口和类(例如):

其消费对象为:

// MVC ActionResult to encapsulate the response for a report
public class ReportResult : ActionResult
{
  public ReportResult(IReportGenerator rg)
  {
  }
}
这就是我注册这些的方式:

containerBuilder cb;

cb.RegisterType<PersonReportGenerator >()
  .As<IPersonReportGenerator >();

cb.RegisterType<CarReportGenerator>()
  .As<ICarReportGenerator>();
containerBuilder-cb;
cb.RegisterType()
.As();
cb.RegisterType()
.As();
现在在我的行动结果。。而且很难看:

public ActionResult GetReport(int personId)
{
  var report = 
    ((AutoFacDependencyResolver)DependencyResolver.Current)
    .ApplicationContainer.Resolve<PersonReportGenerator>(
      new NamedParameter("personId", personId)
    );

  return new ReportResult(report);
}
public ActionResult GetReport(int personId)
{
风险值报告=
((AutoFacDependencyResolver)DependencyResolver.Current)
.ApplicationContainer.Resolve(
新名称参数(“personId”,personId)
);
返回新的ReportResult(报告);
}

使用MVC/Autofac是否有更干净的方法来解决此类依赖关系?

是。不要在
操作结果中解决它。不要在任何地方明确地解决它

在控制器的构造函数中将其声明为参数,如下所示:

public class MyController : Controller
{
    private readonly IPersonReportGenerator _personReportGenerator;

    public MyController(IPersonReportGenerator personReportGenerator)
    {
        _personReportGenerator = personReportGenerator;
    }
}

public ActionResult GetReport(int personId)
{
    var report = _personReportGenerator.Builder();
    return new ReportResult(report);
}
(假设“Builder”是返回报告的方法。)

如果设置Autofac或任何其他DI容器来创建控制器,则当它创建控制器来处理请求时,它还将解析控制器的依赖关系,包括
IPersonReportGenerator

结果是,您的控制器不依赖于容器,甚至不知道它。它只取决于它需要依赖的接口,而容器提供了它


如果您还没有将容器用作控制器工厂。关键是容器创建了所有内容。如果容器创建了控制器,那么它可以同时确定需要传递给构造函数的内容,并创建这些类。(如果这些类也有依赖项,它也会创建它们,依此类推。)只要容器创建第一个对象,它就会创建所有对象(只要这些依赖项都已注册,就像您在
IPersonReportGenerator
PersonReportGenerator
中所做的那样)

如果您无法更改此接口:

public interface IPersonReportGenerator : IReportGenerator

public IReportGenerator
{
    string Build();
}
这就产生了一个问题。根据定义,接口定义了如何与类交互。挑战在于您需要向它传递一个
personID
,但接口不允许这样做。因此,实际上接口并没有真正定义类需要做什么

如果您无法更改该接口(其他东西已经依赖于它),那么您可以像我们使用遗留代码一样,将其包装到我们需要的接口中。这并不能解决问题。它只是围绕它创建了一个抽象,这样我们就不必不断地处理这个问题

public interface IPersonReportGeneratorV2 //Or some better name
{
    string GetReport(IPersonDb personDb, int personId);
}

public class LegacyPersonReportWrapper : IPersonReportGeneratorV2
{
    var generator = new PersonReportGenerator(personDb, personId);
    return generator.Build();
}

现在,您的控制器可以依赖于
IPersonReportGeneratorV2
。仍然有一些丑陋,但现在它隐藏在新的界面后面。现在,您可以将包装器注册为接口的实现,并让容器完成其工作。

因为您在设计时不知道personId,所以不希望通过构造函数注入personId。(在Autofac中无法实现这一点;它既丑陋又复杂。)

有一种更好更简单的方法

public interface IReportGenerator
{
    string Build(ReportSetting setting);
}

public class ReportSetting
{
    public int PersonId { get; set; }
    public Guid CarMake { get; set; }
    public Guid CarModel { get; set; }
}

public class PersonReportGenerator : IReportGenerator
{
    private readonly IPersonDb _personDb;

    public PersonReportGenerator(IPersonDb personDb)
    {
        _personDb = personDb;
    }

    public string Build(ReportSetting setting)
    {
        // you can use setting.PersonId
    }
}

public class CarReportGenerator : IReportGenerator
{
    private readonly ICarDb _cardDb;
    public CarReportGenerator(ICarDb cardDb)
    {
        _cardDb = cardDb; 
    }

    public string Build(ReportSetting setting)
    {
        // you can use setting.CarMake and setting.CarModel
    }
}
自动传真注册 您可以使用,并基于名称将所需的依赖项注入控制器

比如说,

cb.RegisterType<PersonReportGenerator>().Named<IReportGenerator>("PersonReport");
cb.RegisterType<CarReportGenerator>().Named<IReportGenerator>("CarReport");

cb.Register(c => new MyController(c.Resolve<IReportGenerator>("PersonReport")))
    .As<Controller>();
cb.RegisterType().Named(“PersonReport”);
cb.RegisterType().Named(“CarReport”);
cb.Register(c=>newmycontroller(c.Resolve(“PersonReport”))
.As();

我无法在控制器的构造函数中解析它,因为personId是对象构造所必需的,并且在调用
GetReport()
方法之前不可用。。。(正如我侧边所述,控制器是由autofac创建的)。请重新检查该问题。将personId设置为GetReport()的参数。它不是依赖项,只是一个方法参数。然后它会破坏接口。。。那么使用接口就没有意义了。每个报告对参数(构造函数或方法)有不同的要求。(CarReportGenerator
不需要
personId
)不要将其视为“破坏”接口-将其视为按照需要的方式制作接口。您总是需要将personId传递给报表生成器,但界面没有指明这一点。现在接口假设实现它的类将找到另一种方法来获取id。但是传递它的逻辑方法是作为方法参数。你读过我的代码吗?您总是需要将personId传递给报表生成器。如果不正确,CarReportGenerator永远不需要personId。我建议将参数从构造函数移动到方法调用,但它被拒绝,因为接口不能更改。如果不能更改IReportGenerator接口,OP可以在运行时创建;除了IReportGenerator之外,它将完全改变体系结构。这接近于我在第二个答案中建议的。如果目的是让一个类为给定的
personId
生成报告,那么公开的接口需要反映这一点。如果没有,那么问题就从那里滚雪球了。查看原始界面,可以合理地得出结论,报告没有参数。它假设实现将采用构造函数中的参数。换句话说,接口没有指明如何使用它的实现。为什么
ReportResult
类需要依赖关系?
cb.RegisterType<PersonReportGenerator>().Named<IReportGenerator>("PersonReport");
cb.RegisterType<CarReportGenerator>().Named<IReportGenerator>("CarReport");

cb.Register(c => new MyController(c.Resolve<IReportGenerator>("PersonReport")))
    .As<Controller>();