LINQ to SQL, Lambda, Generics and Readability

In my previous (and perhaps still ongoing) article series about LINQ to SQL Performance Optimization I demonstrated code that builds up a LINQ to SQL hierarchy using parts that had been loaded unconnected.

The code to hook up the hierarchy looked a bit messy, so I decided to try to clean it up. What I came up with was much cleaner. But is it more readable? I’m not so sure, my point with this post is this, be careful with what clever methods you introduce, because in the end, they might not be more readable than doing it the hard way. And let’s face it, readability is more important than brevity… at least sometimes…

The brief version

Note that with the supporting methods, this method is more verbose – but the supporting methods could be re-used over and over again…

The actual method that performs the assembling;

        public static List<Supplier> AssembleHierarchyTight(List<Supplier> suppliers, List<Product> products, List<Category> categories, List<Order_Detail> orderDetails, List<Order> orders, List<Shipper> shippers, List<Customer> customers)
        {
            LoadFrom(suppliers, supplier => supplier.Products, (supplier, product) => supplier.SupplierID == product.ProductID, products);
            LoadFrom(products, supplier => supplier.Category, (supplier, category) => supplier.CategoryID == category.CategoryID, categories);
            LoadFrom(products, supplier => supplier.Order_Details, (supplier, orderDetail) => supplier.ProductID == orderDetail.ProductID, orderDetails);
            LoadFrom(orderDetails, detail => detail.Order, (detail, order) => detail.OrderID == order.OrderID, orders);
            LoadFrom(orders, order => order.Shipper, (order, shipper) => order.ShipVia == shipper.ShipperID, shippers);
            LoadFrom(orders, order => order.Customer, (order, customer) => order.CustomerID == customer.CustomerID, customers);           

            return suppliers;
        }

The support methods;

        public static void LoadFrom<TDestination, TTarget>(
            IEnumerable<TDestination> destinations,
            Expression<Func<TDestination, EntitySet<TTarget>>> expression,
            Func<TDestination, TTarget, bool> comparator,
            IEnumerable<TTarget> sources)
            where TTarget : class
        {
            PropertyInfo property = TypeHelper.FindProperty(expression);
            List<TTarget> sourceList = sources.ToList();
            foreach (TDestination destination in destinations)
            {
                List<TTarget> filtered =
                    sourceList
                        .Where(source => comparator(destination, source))
                        .ToList();

                EntitySet<TTarget> target = (EntitySet<TTarget>)property.GetValue(destination, _emptyIndex);
                target.SetSource(filtered);
            }
        }

        public static void LoadFrom<TDestination, TTarget>(
            IEnumerable<TDestination> destinations,
            Expression<Func<TDestination, TTarget>> expression,
            Func<TDestination, TTarget, bool> comparator,
            IEnumerable<TTarget> sources)
            where TTarget : class
        {
            PropertyInfo property = TypeHelper.FindProperty(expression);
            List<TTarget> sourceList = sources.ToList();
            foreach (TDestination destination in destinations)
            {
                TTarget filtered =
                    sourceList
                        .SingleOrDefault(source => comparator(destination, source));

                property.SetValue(destination, filtered, _emptyIndex);
            }

The verbose version

This method doesn’t require any support methods;

        public static List<Supplier> AssembleHierarchyVerbose(List<Supplier> suppliers, List<Product> products, List<Category> categories, List<Order_Detail> orderDetails, List<Order> orders, List<Shipper> shippers, List<Customer> customers)
        {
            suppliers.ForEach(
                supplier => supplier.Products.SetSource(products.Where(product => product.SupplierID == supplier.SupplierID)));

            products.ForEach(
                product =>
                {
                    product.Category = categories.Single(category => category.CategoryID == product.CategoryID);
                    product.Order_Details.SetSource(
                        orderDetails.Where(detail => detail.ProductID == product.ProductID));
                });

            orderDetails.ForEach(
                detail =>
                {
                    detail.Order = orders.Single(order => order.OrderID == detail.OrderID);
                });

            orders.ForEach(
                order =>
                {
                    order.Shipper = shippers.Single(shipper => shipper.ShipperID == order.ShipVia);
                    order.Customer = customers.Single(customer => customer.CustomerID == order.CustomerID);
                });

            return suppliers;
        }

About mfagerlund
Writes code in my sleep - and sometimes it even compiles!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: