Can you apply OOP to Linq Projections?
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.
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.
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
});-
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.
精彩评论