views:

59

answers:

1

Using

  • Visual Studio 2010
  • .Net Framework 4
  • C#
  • Linq to Entities

Issue

I would like to be able to apply Object Oriented Principles like DRY and SOLID to some Linq Projections. With compiled queries or passed parameters I can apply these to the rest of Linq successfully so far, just not in the projections.

Please let me know if this isn't possible, and I must choose one of my alternate solutions (described below), if it is possible then how, or if I am missing something and there is another alternative implementation that will satisfy the goal.

Details

At a high level I would like to be able to dynamically control the type used in a Linq Projection, either with a standard Linq Query or a CompiledQuery. I am using Linq to Entities in my examples and actual code, however the issue should be applicable to the core Linq.

Below are simplistic examples that are not dynamic and do not solve the issue. They are fixed to always use the FooUser for each type. What I would like to be able to do is dynamically control the type of user created in the projection all of which would be based on a common IUser interface. This would be or could be similar to how I can control what type the query filters on.

Alternate Solutions

I am trying to conform to DRY, SOLID, and also trying to avoid using an enum to deal which is a typical code smell. However in all my attempts and research I seem to have to fall to one of the following solutions.

  1. Implement a query for each type which are all the same except for the type they filter on and the type used int he projection. While this violates DRY and OCP, I can encapsulate this within a single class and keep them close together as complied queries. This will require the class to change if I add a new type or if how you query for the data changes.

  2. Implement a enum that has the types, and use a more generalized User class that has its type as a property. However this will cause me to have to use the enum in several locations and introduce long case statements to handle them, which I would like to avoid.

I would love not to have to choose between different evils, and have an implementation that can conform to all SOLID principles and DRY. However if I must I think I will end up with the first or a version of it.

Examples

Standard Simple Linq Query

using (MyEntities context = new MyEntities())
{
    var results = from u in context.Users
                  where u.UserType == type
                  select new FooUser
                  {
                      Id = u.UserID,
                      Name = u.UserName,
                      Location = u.UserLocation
                  };
}

Compiled Version of the Above Query

private static readonly Func<MyEntities, int, IQueryable<FooUser>> query = CompiledQuery.Compile<MyEntities, int, IQueryable<FooUser>>(
    (context, type) => from u in context.Users
                       where u.UserType == type
                       select new FooUser
                       {
                           Id = u.UserID,
                           Name = u.UserName,
                           Location = u.UserLocation
                       });-
A: 

I found a way to do this with a standard method call. I haven't figured out how to do it with a compiled query, it doesn't look likely.

I was not aware of the Constructor Constraint I on the where statement for a generic. This can suit my needs. I would love to do this with a compiled query, but can live happy with this solution.

public IQueryable<IUser> FooMethod<T>(int type) where T : IUser, new()
{
    using (MyEntities context = new MyEntities())
    {
        var results = from u in context.users
                      where u.usertype == type
                      select new T
                      {
                          id = u.UserId,
                          name = u.UserName,
                          location = u.Userlocation
                      };
        return results; 
    }
}

I chose to post an answer instead of deleting the question for two reasons, one in case others are looking for something similar it could be helpful. Then of course I could be way off base and its is always fun to have people shoot holes in things and see what better stuff we can come up with.

Rodney Foley