When setting this application up, I wanted to be able to define contracts on my reports so that I could import them into a ReportManager class when the application starts up. However, I didn't want my report objects to be instantiated right away. To resolve this, I made use of one of MEFs most useful features, Metadata. Here is the attribute class that defines my contract.
[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class, AllowMultiple=
false
)]
public
class
ReportExportAttribute : ExportAttribute
{
public
ReportExportAttribute(
string
name)
:
base
(
typeof
(Telerik.Reporting.Report))
{
Name = name;
}
public
string
Name {
get
;
set
; }
}
public
interface
IReportExportMetadata
{
string
Name {
get
; }
}
Simply placing the [ReportExport("")] attribute on any Telerik Report will now make it available for importing by my ReportManager class.
[ReportExport(
"Products Report"
)]
public
partial
class
ProductsReport : Telerik.Reporting.Report
{
public
ProductsReport()
{
/// <summary>
/// Required for telerik Reporting designer support
/// </summary>
InitializeComponent();
}
}
The class that ties everything together is the ReportManager class. When it gets instantiated, it immediatly creates its own MEF container and lazily imports all exported Telerik Reports. Again, the reason I chose to do a lazy import is because I only want to instantiate a report when it is actually needed.
public
class
ReportManager
{
CompositionContainer _container;
[ImportMany(
typeof
(Report), AllowRecomposition=
true
)]
private
Lazy<Report, IReportExportMetadata>[] _reports;
public
ReportManager()
{
AggregateCatalog catalog =
new
AggregateCatalog();
catalog.Catalogs.Add(
new
DirectoryCatalog(
string
.Format(
"{0}/Reports"
, Application.StartupPath)));
_container =
new
CompositionContainer(catalog);
_container.ComposeParts(
this
);
}
public
IEnumerable<
string
> GetReportNames()
{
var names = (from r
in
_reports select r.Metadata.Name).ToList();
return
names;
}
public
Report GetReport(
string
name)
{
return
(from r
in
_reports where r.Metadata.Name == name select r.Value).FirstOrDefault();
}
}
Once everything has been set up, using the report manager is as simple as instantiating it and calling GetReport() to retrieve the report you want to display. Using this technique, you no-longer have to worry about recompiling your whole application when you add new reports to a separate assembly.
Note: You still need to make sure to update the app.config to contain proper connection strings for your reports.
ReportManager _reportManager =
new
ReportManager();
reportViewer1.Report = _reportManager.GetReport(
"Products Report"
);
reportViewer1.RefreshReport();
Click here to download the source code...