views:

1090

answers:

3

I am using a similar approach to others in keeping my LINQ objects in my LINQ data provider and returning an IQueryable to allow filtering etc. This works fine for filtering a simple object by it's ID or other property, but I am having a problem with a join table object that is composed of other child objects

    //CoreDBDataContext db = coreDB;
public IQueryable<DTO.Position> GetPositions()    {
     return from p in coreDB.Positions
         select new DTO.Position
       {
       DTO.User = new DTO.User(p.User.id,p.User.username, p.User.firstName,p.User.lastName,p.User.email,p.User.isActive),
       DTO.Role = new DTO.Role(p.Role.id, p.Role.name, p.Role.isActive),
       DTO.OrgUnit = new DTO.OrgUnit(p.OrgUnit.id,p.OrgUnit.name,p.OrgUnit.isActive)
       };

The coreDB.Positions is my Linq Position Object and I am returning a DTO Position which is composed of a User, OrgUnit and Role (the underlying table is a join table with UserID, RoleID, and OrgUnitID)

The problem I am having is that when I try to add a filter on the Iqueryable I get a SQL Error saying that there is no translation available for my DTO.User object

public static IQueryable<Position> WithUserID(this IQueryable<Position> query, int userID)
 {
  return query.Where(p => p.User.ID == userID);
 }

I am at a complete loss as to how to go about resolving this, as all of my Google results seem to be with people working directly with the generated LINQ objects

Any thought as to how to make this work, or am I doing something completely wrong here?

Thanks

+1  A: 

Linq2SQL does only understand the designer-generated objects. Well, that's not entirely true, but close enough.

So when you write Linq queries agains Linq2SQL objects, the query will be converted to valid SQL when the query is actually executed, not when it's written. Since your DTO objects are not Linq2SQL objects, Linq2SQL will not know how to create the proper SQL.

If you want to keep your separation this way, you have to find a way to execute your queries with only Linq2SQL objects involved and only map the result to your DTOs.

Maybe you could rewrite your query method to:

Update: Parameter must be of type Expression<>, and there's no need to return an IQueryable<>. Thanks to Freddy for pointing out.

public IEnumerable<DTO.Position> FindPositions(Expression<Func<Position, bool>> criteria)
{
    return from p in coreDB.Positions
           where criteria.Invoke(p)
           select new DTO.Position
                      {
                          User = new DTO.User(p.User.id, p.User.username, p.User.firstName, p.User.lastName,
                                       p.User.email, p.User.isActive),
                          Role = new DTO.Role(p.Role.id, p.Role.name, p.Role.isActive),
                          OrgUnit = new DTO.OrgUnit(p.OrgUnit.id, p.OrgUnit.name, p.OrgUnit.isActive)
                      };
}
Thomas Eyde
almost, wrap Func in Expr<>
eglasius
but then, why make the result IQueryable :)
eglasius
btw, I have used both approaches, see my answer :)
eglasius
Freddy, you are spot on. My bad, I was too hasty copying and pasting :-(
Thomas Eyde
+1  A: 

I have been able to work successfully with a similar approach:

var courses = from c in Map(origCourses)
where !expiredStatuses.Contains(c.Status)
select c;

Where Map has:

    select new UserCourseListItem
    {
        CourseID = c.CourseID,
        CourseName = cm.CourseName,
        CourseType = c.CourseType.Value
        ...

How about trying it with that type of initialization (instead of constructors).

Ps. this is part of a working application, and the expiredStatuses is even related to a complex expression.

Update 1: This is similar compared to the mentioned scenario, because:

  • Map is returning an IQueryable, which is a POCO object.
  • After calling the Map method that returns an IQueryable with the POCO object I am applying a filter against it.
eglasius
I have tried a similar mapping function, but I am trying to keep my data repos class as slim as possible and then use extension methods to add the filters to implementation of my data repository. we have a test repos that just uses list<obj>, and a sql repos for our actual production code
Devon
I understand, but I meant that it might be due to the use of the constructor (just a guess). Notice that my map method returns an IQueryable<UserCourseListItem>, which is why I think the filter situation is an equivalent.
eglasius
So will this fetch all data from the Data Store and then filter the mapped objects?
Devon
And by fetch you mean have an IQueryable that can get all data if you don't filter it, yes. When you filter it, linq2sql will actually consider that on the query, so you only get the filtered data :)
eglasius
A: 

I ended up not using filters for my complex queries. Instead, I added methods to the repository for the more complex query requirements. I feel this will make the system easier to understand and hence maintain.

Devon