Using LINQ Objects Wisely, Part 1
I've mentioned before (probably several times) about using LINQ business object references instead of key (or other) values in your BAL or DAL code. Though I have mentioned it before, I decided to come up with a few scenarios. The first scenario is this:
//BAL method
public string GetCustomerFormattedAddress(int customerKey) { }
This is assuming we have a Customer object, and this object has a foreign key relationship to the Address object collection. A customer can more, therefore a history of addresses can be built. However, the method passes in the key value. Using this approach, this often lead to something like this as its body:
var address = from a in this.Context.Addresses
join ca in this.Context.CustomerAddresses
on a.AddressKey equals ca.AddressKey
where ca.CustomerKey == customerKey
&& ca.EndDate == null
select a;
if (address.Count() == 0)
return string.Empty;
else
return "<formatted address>";
This depicts a many-to-many table in between the addresses and customers tables, in case more than one people at the same address exist. In addition, to get the most current address, look for the EndDate equal to null. So, using this approach, the latest address is retrieved, which works. However, often this is called by:
lblShipToAddress.Text = bal.GetCustomerFormattedAddress(this.SelectedCustomer.CustomerKey);
Again, this all works. But it defeats a purpose of LINQ, which is to provide a drill-down mechanism through a database hierarchy. I believe a better approach would be to do this:
public string GetCustomerFormattedAddress(Customer customer)
{
var addressJoin = from ca in customer.CustomerAddresses.Where(ca => ca.EndDate == null);
if (addressJoin.Count() == 0)
return string.Empty;
else
{
Address address = addressJoin.Address;
//FOrmat address
}
}
I believe this is a better approach because:
-
You aren't querying against the data context directly.
-
The customer addresses and addresses in the second approach are lazy loaded.
-
If something changes, you aren't passing a key in directly. For instance, if the address is dependent on a setting for the customer, it's easy to add that for the second approach, but for the first approach, you would have to add another parameter [such as: string GetCustomerFormattedAddress(int, CustomerType)]. You have to "break the interface" on the first one, requiring code changes to other portions of your application. The second approach minimizes future risk.
-
The drill-through approach is nicer because it can be "cleaner" in many situations.
I hope to add on this with a few more examples in the future.