3

I'm using the following code:

using(MainEntities mainContext = new MainEntities())
{
    return (from member in mainContext.aspnet_Membership
            where adminGroupUserIDs.Contains(member.UserId)
            select new
            {
                FullName = member.FirstName + " " + member.LastName,
                UserName = (from user in mainContext.aspnet_Users
                            where user.UserId == member.UserId
                            select user.UserName)
            }).ToList(); 
}

where adminGroupUserIDs is an IQueryable<GUID> that is formed from a query to a different instance of MainEntities.

With this query LINQ complains that:

The specified LINQ expression contains references to queries that are associated with different contexts.

Any ideas why?

seebiscuit
  • 4,905
  • 5
  • 31
  • 47
  • I'm not sure what is going on in the background, but maybe you need to create another instance of MainEntities to use in the inner expression (`from user in mainContext.aspnet_Users ...`) - see http://stackoverflow.com/questions/7332920/the-specified-linq-expression-contains-references-to-queries-that-are-associated – PzYon Oct 22 '14 at 21:53
  • @Pzyon: Quite the opposite. If he did, then he would definitely get that error. – Allon Guralnek Oct 23 '14 at 08:03

2 Answers2

4

I can't be certain from the code you show here, but I'm pretty sure that adminGroupUserIDs is the result of another query that hasn't been retrieved yet, and was created with a different instance of MainEntities. You can't mix queries from different contexts, not even different instances of the same context class. Try changing it to the following:

var loadedAdminGroupUserIDs = adminGroupUserID.ToArray();

using(MainEntities mainContext = new MainEntities())
{
    return (from member in mainContext.aspnet_Membership
            where loadedAdminGroupUserIDs.Contains(member.UserId)
            select new
            {
                FullName = member.FirstName + " " + member.LastName,
                UserName = (from user in mainContext.aspnet_Users
                            where user.UserId == member.UserId
                            select user.UserName)
            }).ToList(); 
}
Allon Guralnek
  • 15,813
  • 6
  • 60
  • 93
  • Excellent point Allon. I totally overlooked `adminGroupUserIDs`! Originally the query I posted built the query using the repositories in the Repository Pattern fashion, i.e. using `MembershipRepository.All` and `UsersRepository.All`. But then I got the error I posted above and switched to a `using` block. But I think you nailed it, because `adminGroupUserIDs` is and IQueryable being built by a third repository! – seebiscuit Oct 23 '14 at 13:42
  • The problem with your fix is that `loadeAdminGroupUserIDs` is now in-memory, and I'm bound to get a `Unable to create a constant value of type` error, since the query is in the DB. At the end I had to go with a join, which is probably closer to native SQL. But still I was sure that I could've implemented your solution. What was I missing? – seebiscuit Oct 23 '14 at 13:46
  • Another point. I tripped myself up because I wanted to build my query from my repositories, each of which represents a table. With this pattern, each Repo will instantiate a `MainEntities` context. Gleaning from this case, can I infer that the Repo pattern is only useful when performing queries on only ONE table? Am I implementing the pattern incorrectly? This may be another SO question. – seebiscuit Oct 23 '14 at 13:52
  • Assuming `adminGroupUserIDs` is an `IQueryable` (or ``), my suggestion above would cause Entity Framework to convert the `.Contains(...)` into an `IN` expression, so that if `adminGroupUserIDs` contained three IDs, 5, 18 and 42, an in-memory array of these three values would be created by the first line, then EF would recognize this and create the following SQL: `WHERE UserId IN (5, 18, 42)`, which would also work without using a single data context. But if you want all of this to happen inside the database, then you have to use a single data context (with or without a join). – Allon Guralnek Oct 23 '14 at 14:05
  • I'm sorry, Allon, I missed a material point. `adminGroupUserIDs` is a GUID :(. Which explains why the `.Contains` failed. Thanks for the insight. Will edit my question. – seebiscuit Oct 23 '14 at 14:15
  • @Seabiscuit: Entity Framework (4.0 and above) supports `.Contains()` on most SQL primitives, including GUIDs. So it should work even on an `Guid[]` or `IEnumerable` (but not `IQueryable`). – Allon Guralnek Oct 24 '14 at 15:09
0

Try adding .FirstOrDefault() to your subquery.

using(MainEntities mainContext = new MainEntities())
{
    return (from member in mainContext.aspnet_Membership
            where adminGroupUserIDs.Contains(member.UserId)
            select new
            {
                FullName = member.FirstName + " " + member.LastName,
                UserName = (from user in mainContext.aspnet_Users
                            where user.UserId == member.UserId
                            select user.UserName).FirstOrDefault()
            }).ToList(); 
}
serdar
  • 1,564
  • 1
  • 20
  • 30
  • Yes, this is another mistake in the query, good catch! Although I don't see how it would cause the above error. – Allon Guralnek Oct 23 '14 at 08:00
  • 1
    Good catch. Unfortunately, the problem was exactly what @AllonGuralnek suggested, I included 3! different `MainEntity` contexts in my query (2 in the query itself, and `adminGroupUserIDs` was an IQueryable from a third `MainEntities` context). That's sort of the reality of using the Repository Pattern. I required 3 different repositories, each of which instantiated a `MainEntities` context. – seebiscuit Oct 23 '14 at 13:49