接口属性的Unity按需解析

Unity On-Demand Resolve of Interfce Properties

我有一个接口 IRDFReport 和一个实现它的基础 class BaseReportViewReport 属性是重对象,应该仅在实际请求报告时才解析。我使用了两个简单的字符串后缀来查找对象的 ViewReport 属性的命名映射。

我想使用 Unity 按需解析重对象,同时能够解析所有报告以获得它们的列表。这种在 get 方法中的解析是我能做的最好的吗?

public interface IRDFReport
{
    UserControl View { get; }
    string ReportName { get; }
    string Name { get; set; }
    Task<bool> GenerateReport(SynchronizationContext context);
    DevExpress.XtraReports.IReport Report { get; set; }
}

BaseReport实现了这个接口:

public class BaseReport : IRDFReport
{

    public DevX.IReport Report
    {
        get
        {
            return ReportManager.myContainer.Resolve<IReport>(ReportName + ReportManager.Report_Suffix) as XtraReport;
        }
    }

    public UserControl View
    {
        get
        {
            return ReportManager.myContainer.Resolve<UserControl>(ReportName + ReportManager.View_Suffix);
        }
    }

    ///
    /// other members
    ///
}       

在我的报告管理器中,我这样注册它们:

public const string View_Suffix = ".View";
public const string Report_Suffix = ".XtraReport";

Reports = new List<IRDFReport>();

myContainer.RegisterType<IReport, BalanceSheetXtraReport>(nameof(BalanceSheetReport) + Report_Suffix, new ContainerControlledLifetimeManager());
myContainer.RegisterType<UserControl, BalanceSheetView>(nameof(BalanceSheetReport) + View_Suffix, new ContainerControlledLifetimeManager());

  ///
  /// registering other reports inherited from BaseReport
  ///

myContainer.RegisterTypes(
    AllClasses.FromLoadedAssemblies()
    .Where(type => typeof(IRDFReport).IsAssignableFrom(type)),
    WithMappings.FromAllInterfaces,
    WithName.TypeName);

var reports = myContainer.ResolveAll<IRDFReport>().Where(x => !string.IsNullOrEmpty(x.Name)).ToList();
Reports.AddRange(reports);

您正在做的是所谓的服务位置和 is considered an anti-pattern

我将建议一种不同的方式来获得你的依赖。请注意,我将提供一些通用代码作为示例。而且我将只谈论 IReport。其他依赖可以类似处理。

我的建议是使用构造函数注入。您的 BaseReport 依赖于 IReport,但它希望仅在以后按需获取它(并构建它)。

一种方法是使用 abstract factory.

这里是一些例子:

public interface IReportFactory
{
    IReport Create(); //this can also take parameters
}

然后您可以创建该工厂的实现并将其注入 BaseReport 的构造函数。这将使 BaseReport 请求 IReport 按需依赖。

另一个解决方案是使用 .NET Lazy class. This class allows you to create the dependency on the first time your try to use it. Unity has native support for the Lazy class (see this reference).

这是一个关于如何使用它的例子:

您可以像这样将 Lazy<IReport> 注入到您的 BaseReport class 中:

public class BaseReport
{
    private readonly Lazy<IReport> m_LazyReport;

    public BaseReport(Lazy<IReport> lazy_report)
    {
        m_LazyReport = lazy_report;
    }

    public IReport Report
    {
        get { return m_LazyReport.Value; }
    }
}

Composition Root(使用DI容器的地方),执行以下操作:

UnityContainer container = new UnityContainer();

container.RegisterType<IReport, Report>("name");

container.RegisterType<BaseReport>(
    new InjectionConstructor(
        new ResolvedParameter<Lazy<IReport>>("name")));

只要注册IReport就够了,然后Unity就可以毫无问题地解析Lazy<IReport>,它知道如何让它工作,只有当Lazy 访问对象值,它会继续并创建 Report 对象。