Unity On-Demand Resolve Interfce Properties
У меня есть интерфейс IRDFReport
и базовый класс BaseReport
который реализует это. View
а также Report
свойства являются тяжелыми объектами и должны разрешаться только тогда, когда отчет действительно запрашивается. Я использовал два простых суффикса строки для поиска именованных отображений View
а также Report
свойства объектов.
Я хотел бы использовать 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);
1 ответ
То, что вы делаете, называется Service Location и считается анти-паттерном.
Я собираюсь предложить другой способ получить вашу зависимость. Обратите внимание, что в качестве примера я приведу общий код. А также я буду говорить о IReport
только. Другая зависимость может рассматриваться аналогично.
Я предлагаю использовать Constructor Injection. Ваш 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; }
}
}
В корне композиции (место, где вы используете контейнер 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
объект.