解决asp.net mvc中参数的依赖关系

时间:2016-04-22 20:00:40

标签: c# asp.net-mvc dependency-injection autofac

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

public IReportGenerator
{
    string Build();
}

public interface IPersonReportGenerator : IReportGenerator

public class PersonReportGenerator : IPersonReportGenerator 
{
  private readonly int _personId;
  private readonly IPersonDb _personDb;

  public PersonReportGenerator(IPersonDb personDb, int personId)
  {
    _personDb = personDb;
    _personId = personId;
  }

  public string Build()
  {
    // get data with _personId;
    return "Person Report";
  }
}

public interface ICarReportGenerator : IReportGenerator

public class CarReportGenerator : ICarReportGenerator 
{
  private readonly guid _make;
  private readonly guid _model;
  private readonly ICarDb _cardDb;

  public CarReportGenerator (ICarDb cardDb, guid make, guid model)
  {
    _cardDb = cardDb;
    _make = make;
    _model = model;
  }

  public string Builder()
  {
    // get data with _make and _model;
    return "Car Report";
  }
}

消耗的是:

// 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>();

现在我的行​​动结果......而且很难看:

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

  return new ReportResult(report);
}

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

3 个答案:

答案 0 :(得分:0)

如果您无法更改此界面:

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。还有一些丑陋,但现在它隐藏在新界面背后。现在,您可以将包装器注册为接口的实现,并让容器完成其工作。

答案 1 :(得分:0)

由于您在设计时不了解 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
    }
}

Autofac注册

您可以使用Named and Keyed Services,并根据名称将所需的依赖项注入控制器。

例如,

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

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

答案 2 :(得分:-1)

是。不要在ActionResult中解决它。不要明确地在任何地方解决它。

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

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);
}

(假设&#34; Builder&#34;是返回报告的方法。)

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

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

如果您还没有将容器用作控制器工厂here's the documentation for Autofac。关键是容器创建所有内容。如果容器创建控制器,那么它同时可以确定它需要传递给构造函数的内容并创建这些类。 (如果这些类也有依赖关系,它也会创建它们,依此类推。)只要容器创建第一个对象,它就会创建所有这些对象(只要注册了依赖项,就像使用{{1}一样}和IPersonReportGenerator。)