I am using the VS Report Viewer control and stored procedures to print forms from a web application (in PDF and streamed to the client). These documents have between 1 to 200 pages, roughly, and process the same amount of rows. Not much.
Any data retrieval code needs to know about business rules. For instance, there are accounts, and accounts have addresses, and when I print an invoice, the billing address has to be on it. There are business rules for how to obtain the billing address, and I would like to code these rules only once, and not in the database if possible. What's so nice about Linq is that it allows me to extend the stored procedure results with my own properties. This is very elegant, and these properties are accessible in the report as if they were database columns.
For instance, I added a property that returns a barcode image, or another one that returns the billing address. The business logic that knows how to get the billing address is in the Account partial Linq class. So the billing address property of the stored procedure results is instantiating an account from a datacontext to help with this task.
You see where this is going - it does not scale well. I find myself with 1,000s of database hits for a 15-page report, connection pool issues, etc, in short, I need a different approach.
The trivial answer is to move the business logic into the stored procedure so that Linq does not have to do look-ups. I can join to the address table, using the key for the billing address type, and join the address table again with other types to fall back on if there is no billing address defined.
Is there a better approach?
Here is some code to illustrate this. The essential question is, I can make my stored procedure return the billing address line 3, but is there a way to keep this in the persistence layer instead?
public partial class sp_rpt_invoicedetailResult
{
private bool gotBillingAdress = false;
private tb_address BillToAddress;
private myDataContext _db;
private myDataContext db {
get {
if (_db == null) _db = new myDataContext();
return _db;
}
}
public string BillToAddressLine3 {
get {
if (!gotBillingAdress)
GetBillToAddress();
return
BillToAddress != null ?
BillToAddress.city + ", " + BillToAddress.tb_state.code + " " +
BillToAddress.zip :
string.Empty;
}
}
private void GetBillToAddress() {
tb_account act = db.tb_accounts.
SingleOrDefault(a => a.id == this.billto_account_id);
BillToAddress = act.BilltoAddress; //tb_account knows it!
gotBillingAdress = true;
}
}