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