I have an interface IRDFReport
and a base class BaseReport
which implements that. The View
and Report
properties are heavy objects and should be resolving only when the report is actually being requested. I used two simple string suffix for finding named mappings of View
and Report
properties of the objects.
I'd like to use Unity to resolve heavy objects on demand while being able to resolve all reports to have a list of them. Is this kind of resolving in get
methods the best I can do for this?
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
which implements this interface:
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
///
}
And in my report manager I register them like this:
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);
What you are doing is called Service Location and is considered an anti-pattern.
I am going to suggest a different way to obtain your dependency. Please note that I am going to provide some generic code just as an example. And also I am going to speak about IReport
only. The other dependency can be treated similarly.
What I am suggesting is to use Constructor Injection. Your BaseReport
has a dependency on IReport
, but it want to be able to obtain it (and to have it constructed) only later on demand.
One way to do this is to use an abstract factory.
Here is some example:
public interface IReportFactory
{
IReport Create(); //this can also take parameters
}
You can then create an implementation of this factory and inject it to the constructor of BaseReport
. This will enable BaseReport
to request the IReport
dependency on demand.
Another solution is to use the .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).
Here is an example on how you would use it:
You can inject Lazy<IReport>
into your BaseReport
class like this:
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; }
}
}
In the Composition Root (the place where you use the DI container), do the following:
UnityContainer container = new UnityContainer();
container.RegisterType<IReport, Report>("name");
container.RegisterType<BaseReport>(
new InjectionConstructor(
new ResolvedParameter<Lazy<IReport>>("name")));
It is enough to just register IReport
, and then Unity can resolve Lazy<IReport>
without any problem, and it knows to make it work in such a way that only when the Lazy
object value is accessed that it goes ahead and creates the Report
object.