views:

144

answers:

1

I have a LINQ to Entities query (using EF 4) that has some pretty complicated set-based filtering going on. The code compiles just fine, but when I try to run it, I get the following error:

Unable to create a constant value of type 'ITextEntity'. Only primitive types ('such as Int32, String, and Guid') are supported in this context.

Now for the code. First of all, I have an interface that looks like this:

public interface ITextEntity
{
    //...snip...
    EntityCollection<Product> Product { get; set; }
}

I have a nested collection (actually a List<IEnumerable<ITextEntity>>) which is a set of sets. As you can see from the ITextEntity interface, the ITextEntity objects in the nested sets each contain another set, a set of Product entities. This is because they themselves are entities, with 1:N or N:N relationships back to Product.

I have a set, Products, that I want to compare against these nested sets in a semi-inclusive fashion. Here's the code:

List<IEnumerable<ITextEntity>> InclusiveFilters;
IQueryable<Product> Products;

//...snip...

Products = Products.Where(prod => 
    InclusiveFilters.All(filter => 
        filter.Any(iTextEnt => 
            iTextEnt.Product.Contains(prod)
        )
    )
);

So what I'm trying to do is this:

  1. For each of several types that inherit from ITextEntity, you have a set S of objects of that type.
  2. Each object O has a set O.P of products.
  3. For each Product prod in Products,
    for each set S,
    for at least one O in S,
    O.P must contain prod. If not, remove prod from Products.

As you can see, this is pretty complicated.

I have the feeling that this is caused by LINQ not being able to work with the ITextEntity type, rather than a problem with my set operations. However, the complexity of the above is making this difficult to work with, and difficult to find a non-LINQ alternative. It's going to get pretty ugly if I can't use LINQ.

I found an MSDN page and a Stack Overflow thread discussing similar exceptions, but neither was much help. Another SO thread aims a finger at my use of the Contains method, but because of the complexity here, I haven't had much luck trying to replace it with the BuildOrExpression method. I doubt BuildOrExpression will work anyway, since this is EF 4 and Contains is supposed to be supported.

So I'm rather stuck here. Can anyone advise?

EDIT: I'm using partials to add this interface to some of my entities, like this:

public partial class Brand : ITextEntity { }
public partial class PatternType : ITextEntity { }
public partial class Performance : ITextEntity { }
//..etc...
+1  A: 

You're implicitly casting to ITextEntity. But ITextEntity is not part of your entity data model, so the EF doesn't know how to translate its members into SQL. Contains is supported, but only with primitive or entity types. Also, you use IEnumerable, which also prevents translation to SQL; you need IQueryable to be convertable to SQL.

So if you remove the interface reference and IEnumerable, you should be able to execute the query on the DB server. Otherwise you have to go into L2O (with, e.g., AsEnumerable()).

Craig Stuntz
So to remove the interface, you're thinking, for example, something like `filter.Any(iTextEnt => iTextEnt.Product.Select(p => p.ID).Contains(prod.ID))`? That one works in L2O, but not in L2E. I understand what you mean by plugging in a primitive type rather than an interface, but I'm not quite sure how to get it done in a way L2E will be able to translate.
Justin Morgan
The problem is `InclusiveFilters`. Since it has `ITextEntity` in its type, it can't be used in L2E (mostly, certainly not when you get this error). All references to this type, direct or indirect, must be removed from the query.
Craig Stuntz
Update: I think I figured it out, though I haven't yet verified that it's getting processed to the DB server. If so, that will be outstanding. Thanks for your help.
Justin Morgan