统一按需解析接口属性

本文关键字:接口 属性 | 更新日期: 2023-09-27 18:18:02

我有一个接口IRDFReport和一个实现它的基类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);

统一按需解析接口属性

您所做的被称为服务定位,并且被认为是一个反模式。

我将建议一个不同的方法来获得你的依赖。请注意,我将提供一些泛型代码作为示例。我也只讲IReport。其他依赖项也可以类似地处理。

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

一种方法是使用抽象工厂。

下面是一些例子:

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

你可以创建这个工厂的实现,并把它注入到BaseReport的构造函数中。这将使BaseReport能够根据需要请求IReport的依赖项。

另一个解决方案是使用。net Lazy类。该类允许您在第一次尝试使用它时创建依赖项。Unity有对Lazy类的原生支持(参见此参考)。

下面是一个如何使用它的例子:

你可以像这样把Lazy<IReport>注入到你的BaseReport类中:

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根目录(你使用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对象。