views:

54

answers:

1

I have an entity framework 3.5 project where I'm using TPH inheritence. Two of my concrete types are in their own DAO class and contain a selector that projects the Entity Class into a DTO. However, both of these concrete classes have similar relations with another table which I use a let statement to identity clearly. My question is, can I somehow refactor this let statement because as I create more inherited concrete classes I feel I'm violating DRY.

StudentDAO.cs:

var myDTO = from x in context.Students
let address = (from a in x.addresses where a.active == true select a).FirstOrDefault()
select new StudentDTO { id = x.id, studentname = x.studentname, address = a.address};

ProfessorDAO.cs:

var myDTO = from x in context.Professors
let address = (from a in x.addresses where a.active == true select a).FirstOrDefault()
select new ProfessorDTO { id = x.id, professorname = x.studentname, address = a.address};

so is there a way I can refactor the address out of both of these queries?

+2  A: 

Short answer: I'm afraid there isn't much you could easily do. The problem is that you need to parameterize the query. The query however needs to be represented as an expression tree (so that it can be translated to SQL or whatever EF uses). Unfortunately, C# don't provide any elegant ways for composing expression trees.

Long answer: Some time ago, I wrote a C# article that explains how to compose queries (using some tricks) - you can take expression tree and splice it into a parameterized query. So, in principle you could use the techniques from the article to create a method that takes the changing parts of the expression tree and then compose the query.

I'll use the types used in LINQ to SQL, because I'm more familiar with it, but I believe the principle should be the same:

IQueryable<R> GetInfo<T>(IQueryable<T> source, // e.g. context.Students
  Expression<Func<T, IQueryable<Address>>> getAddr, // x => x.adresses
  Expression<Func<T, Address, R>> getRes // (x, a) => new StudentDTO { ... }
  return
    from x in source.AsExpandable()
    let address = (from a in getAddr(x).Expand() where a.active == true 
                   select a).FirstOrDefault() 
    select getRes(x, a).Expand();
}

// The use would look like this
var res = GetInfo(context.Professors, x => x.addresses, (x, a) => new 
  ProfessorDTO { id = x.id, professorname = x.studentname, address = a.address }); 

So a summary is that C# and LINQ do not provide any built-in support that would make it easier. However, with some effort you can write query that is parameterized by some parts of the expression tree. In your case, it makes the code a little shorter and less repetitive, but it makes it a lot more complex (and you also need to use library like the one I referenced that provides AsExpandable and Expand).

In this case I'm afraid the it's not worth the effort. But it would be nice if C# v(Next)^x provided some more elegant support for things like this :-)

Tomas Petricek