One remark: in code snippets, please take not of the wrong formatting for “<" and ">” (it’s shown as > and < signs). There are other signs too that could be different but my code snippeter is a bit moody today:)
Telerik reporting is a great tool for just that, but if you feel like doing it OOP style, using your existing architecture, and doing it with some kind of IOC, then you have to do some minor adjustments.
Say you want your Invoice report to use you repository, through your constructor:
public InvoiceReport(IRepository repository) { InitializeComponent(); _rep = repository; txtNumber.Value = this.Field(x => x.Number); txtPartnerName.Value = this.Field(x => x.Partner.Name); }
There’s another interesting bit here (that this.Field extension method) but i’ll get to this later on.
First thing you have to do is inherit the default ReportService and override the ReportResolver
public class MyReportService:ReportService { public MyReportService() { ReportResolver = new MyReportResolver(); } }
Now, another piece of the puzzle would be this MyReportResolver, and that would look something like this
public class MyReportResolver:Telerik.Reporting.Service.IReportResolver { public ReportSource Resolve(string report) { var rpt=(IReportDocument)MyIoc.GetInstance(Type.GetType(report)); return new InstanceReportSource{ReportDocument = rpt}; } }
I have it set up so that it finds the exact type provided by the given name. Obviously, you could have it set-up any other way you see fit. Just another small detail and flexibility at your hand:). Now, what you have here is my Service Locator static instance creating an instance of the report and returning it. Pretty simple actually. One last thing you have to do is go to your ReportService.svc file, view markup, and switch the regular reportService for your own
<%@ ServiceHost Service="My.Reporting.MyReportService, My.Web" %>
And ofcourse, don’t forget to register your Reports in your container, but you probably alread know that:).
Now, let’s discuss that method from the first code snippet and some other interesting things related. Now, as you probably know, telerik report bindings are done by binding strings, which basically mean you put a path to your member for binding. Since it’s a string, it will stay the same during some variable refactoring, and you also have to be careful not to mistype it. Now, since we are using dependency injection, and we want to show a single entity report (invoice that is), i did a few additions. Firstly, i defined a ne interface called IEntityReport
public partial class InvoiceReport : Telerik.Reporting.Report, IEntityReport<Invoice> { ...
Then, i defined an extension class as follows:
public static class ReportingExtensions { public static string Field<TReportModel>(this IEntityReport<TReportModel> report, Expression<Func<TReportModel, object>> expression) { return "=Fields." + expression.GetMemberName1(); } public static string GetMemberName1<TMember>(this Expression<Func<TMember>> expression) { return GetMemberName1(expression.Body); } public static string GetMemberName1<T, TMember>(this Expression<Func<T, TMember>> expression) { return GetMemberName1(expression.Body); } private static string GetMemberName1(Expression expression) { var mem = GetMembersOnPath(expression); return String.Join(".", GetMembersOnPath(expression).Select(m => m.Member.Name).Reverse()); } private static IEnumerable<MemberExpression> GetMembersOnPath(Expression expr) { MemberExpression memExpr = expr as MemberExpression; if (memExpr == null) { UnaryExpression ubody = (UnaryExpression)expr; memExpr = ubody.Operand as MemberExpression; } if (memExpr == null) throw new ArgumentException("Invalid property expression", "expr"); while (memExpr != null) { yield return memExpr; memExpr = memExpr.Expression as MemberExpression; } } }
There are a few good methods there, but the main one is on the top called Field. It mimics the string thing so when you do
txtPartnerName.Value = this.Field(x => x.Partner.Name);
It writes it to a property like “=Field.Partner.Name”. And it’s type safe now! Plus you can take care of all of your bindings in the code-behind, which i actually prefer.
And how do you use that Repository in your report? One option is to override the OnNeedDataSource
protected override void OnNeedDataSource(object sender, EventArgs e) { base.OnNeedDataSource(sender, e); var ord=GetParam<int>("InvoiceOrdinal"); if (DesignMode) return; var inv=_rep.Query<Invoice>().FirstOrDefault(x=>x.Ordinal==ord); DataSource = inv; salesOrderDetails1.DataSource = inv.InvoiceItems; //txtCustomerName.Value = this.Field(m => m.Name); }
Notice that in the previous code snippet i’m also setting a Datasource for a child report (the invoice line items).
You can also see in the previous code how to use a given report parameter in the code behind. Well, maybe you’re missing a GetParam method:).
private TOut GetParam<TOut>(string paramtr) { var val = ReportParameters[paramtr].Value; if (val == null) return default(TOut); if (typeof(TOut).IsAssignableFrom(val.GetType())) return (TOut)val; return (TOut)Convert.ChangeType(val, typeof (TOut)); }
So that’s about it i guess, it’s all the code you need for using your IRepository and object model in the report code behind.
Till next time!
Leave a Comment