So I've been spending some time lately learning to use one of the powerful new features of .NET 4.0, the Managed Extensibility Framework (MEF). Using MEF, I thought it would be neat to create an application that allows you to dynamically load Telerik Reports from an assembly without having to recompile your application. Here is how I set it up.

Contracts

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 ReportManager

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();
    }
}

 

The Application

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...


Related Posts

Comments

Comments are disabled in preview mode.