Sunday, October 16, 2011

How to easily turn your L2S objects into interfaces




I know it’s been a while since I posted an actual post, but I think this one will be fun. So let’s get to it…
In one of our recent code reviews, we found several methods that looked almost identical. All of these methods were just for lookup tables that populated a dropdown. So…this is what we did:
We had a repository with something like GetDepartmentList and GetStatusList that looked something like this:
public class SampleRepository : ISampleRepository
    {
        public IList<LookupDepartment> GetDepartmentList()
        {
            using (var dc = new LookupSampleDataContext())
                return dc.LookupDepartments
                                 .Where(x => !x.IsDeleted)
                                 .OrderBy(x=>x.Name)
                                 .ToList();
        }

        public IList<LookupStatus> GetStatusList()
        {
            using (var dc = new LookupSampleDataContext())
                return dc.LookupStatus
                                 .Where(x => !x.IsDeleted)
                                 .OrderBy(x=>x.Status)
                                 .ToList();
        }
    }
Note: we do not return L2S objects typically, but to make this example simple, I’m going to pretend that we do.
The first thing we did was changed the type returned by both methods to return ILookupObject instead of the L2S objects…like this:
public class SampleRepository : ISampleRepository
    {
        public IList<ILookupObject> GetDepartmentList()
        {
            using (var dc = new LookupSampleDataContext())
                return dc.LookupDepartments
                                 .Where(x => !x.IsDeleted)
                                 .OrderBy(x=>x.Name)
                                 .Cast<ILookupObject>()
                                 .ToList();
        }

        public IList<ILookupObject> GetStatusList()
        {
            using (var dc = new LookupSampleDataContext())
                return dc.LookupStatus
                                 .Where(x => !x.IsDeleted)
                                 .OrderBy(x=>x.Status)
                                 .Cast<ILookupObject>()
                                 .ToList();
        }
    }
Obviously this cast isn’t going to work so we need to finagle our dbml. It’s easy to extend the L2S objects without modifying the dbml. I actually prefer never to touch the dbml itself unless absolutely necessary because I like to Ctrl+A, del, drag everything back over whenever I want :)
So to extend the classes, we just need to do this:
public partial class LookupDepartment : ILookupObject
    {
        public string DisplayName { get { return Name; } }
    }

    public partial class LookupStatus : ILookupObject
    {
        public string DisplayName { get { return Status;} }
    }
Done.
Now we need to revisit our SampleRepository and extract out all the redundant code so we have something like this:
private static IList<ILookupObject> GetList<T>(Expression<Func<T, object>> orderby) where T : class, ILookupObject
        {
            using (var dc = new LookupSampleDataContext())
                return dc.GetTable<T>()
                                 .Where(x => !x.IsDeleted)
                                 .OrderBy(orderby)
                                 .Cast<ILookupObject>()
                                 .ToList();
        }
All we’ve done is made a generic GetList method that takes in the orderby. You could easily make this not dependent on the datacontext too, but to keep this post simple, we won’t do that now. As you can see, this looks almost exactly like our original method with the exception of dc.GetTable<T>(), which is built-in to the datacontext class. All we need to do is pass it the type so it knows from which table to pull data. It’s actually a pet peeve of mine for a lookup type dropdown to not be ordered, so I’m forcing the orderby method and not giving another option.
The repository looks like this now:
public class RefactoredSampleRepository : ISampleRepository
    {
        private static IList<ILookupObject> GetList<T>(Expression<Func<T, object>> orderby) where T : class, ILookupObject
        {
            using (var dc = new LookupSampleDataContext())
                return dc.GetTable<T>()
                         .Where(x => !x.IsDeleted)
                         .OrderBy(orderby)
                         .Cast<ILookupObject>()
                         .ToList();
        }

        public IList<ILookupObject> GetDepartmentList()
        {
            return GetList<LookupDepartment>(x=>x.Name);
        }

        public IList<ILookupObject> GetStatusList()
        {
            return GetList<LookupStatus>(x=>x.Status);
        }
    }
Nice and pretty…I don’t like the orderby expression here, but I think it’s the best option for now. There is another way, but it jacks up L2S for inserts and updates. So, if you KNOW your not going to ever update or insert a new record for a lookup, then you could do this:
public partial class LookupStatus : ILookupObject
    {
        [Column(Name="Status")]
        public string DisplayName { get; set; }
    }
What this does is maps the DisplayName to the Status column so you could just do .OrderBy(x=>x.DisplayName) instead of passing in the orderby. The reason you have to map the column is because you’ll get a “SQL has no supported translation…” error. You could also just rename the property Status to DisplayName in the dbml file itself.
Also, it’s not recommended that you use multiple datacontexts according to l2sprof, but to make this simple, I did.
So now we can easily bind this to a dropdown via another helper method like this:
public static class extensions
    {
        public static void BindDropDownTo(this DropDownList ddl, IList<ILookupObject> data)
        {
            ddl.DataSource = data;
            ddl.DataTextField = "DisplayName";
            ddl.DataValueField = "ID";
            ddl.DataBind();
        }
    }
So in the UI, we just do this: (if using webforms)
ddlStatus.BindDropDownTo(rep.GetStatusList());
If you’re using MVC, you could create an htmlhelper to do something similar.
Alright, I think that’s it. Please post if you have better methods or have any questions.
Thanks for reading!
Shout it

kick it on DotNetKicks.com

blog comments powered by Disqus
Related Posts Plugin for WordPress, Blogger...