193

I am getting this error for the query below

Unable to create a constant value of type API.Models.PersonProtocol. Only primitive types or enumeration types are supported in this context

ppCombined below is an IEnumerable object of PersonProtocolType, which is constructed by concat of 2 PersonProtocol lists.

Why is this failing? Can't we use LINQ JOIN clause inside of SELECT of a JOIN?

var persons = db.Favorites
    .Where(x => x.userId == userId)
    .Join(db.Person, x => x.personId, y => y.personId, (x, y) =>
        new PersonDTO
        {
            personId = y.personId,
            addressId = y.addressId,                   
            favoriteId = x.favoriteId,
            personProtocol = (ICollection<PersonProtocol>) ppCombined
                .Where(a => a.personId == x.personId)
                .Select( b => new PersonProtocol()
                 {
                     personProtocolId = b.personProtocolId,
                     activateDt = b.activateDt,
                     personId = b.personId
                 })
        });
user2515186
  • 2,117
  • 3
  • 15
  • 7
  • Related: [LINQ, Unable to create a constant value of type XXX. Only primitive types or enumeration types are supported in this context](http://stackoverflow.com/q/13405568/456814). –  Oct 13 '15 at 11:52
  • Related: [Unable to create a constant value - only primitive types](http://stackoverflow.com/q/10862491/456814). –  Oct 13 '15 at 11:53

6 Answers6

275

This cannot work because ppCombined is a collection of objects in memory and you cannot join a set of data in the database with another set of data that is in memory. You can try instead to extract the filtered items personProtocol of the ppCombined collection in memory after you have retrieved the other properties from the database:

var persons = db.Favorites
    .Where(f => f.userId == userId)
    .Join(db.Person, f => f.personId, p => p.personId, (f, p) =>
        new // anonymous object
        {
            personId = p.personId,
            addressId = p.addressId,   
            favoriteId = f.favoriteId,
        })
    .AsEnumerable() // database query ends here, the rest is a query in memory
    .Select(x =>
        new PersonDTO
        {
            personId = x.personId,
            addressId = x.addressId,   
            favoriteId = x.favoriteId,
            personProtocol = ppCombined
                .Where(p => p.personId == x.personId)
                .Select(p => new PersonProtocol
                {
                    personProtocolId = p.personProtocolId,
                    activateDt = p.activateDt,
                    personId = p.personId
                })
                .ToList()
        });
Slauma
  • 175,098
  • 59
  • 401
  • 420
  • 20
    The key part for me was adding .AsEnumerable() // database query ends here, the rest is a query in memory – Sameer Alibhai Jun 03 '15 at 17:55
  • 3
    @Slauma So if I'm concerned about performance I should avoid doing this as it would load all data in memory first and then query it. Should I write raw sql for this scenarios? – Saber Dec 28 '15 at 11:30
  • It seems like @Arvand has a great point. If you have a large number of records before the filter, this could take a huge bite out of the available memory resources. – spadelives Nov 25 '16 at 01:53
  • 6
    @Slauma "This cannot work because ppCombined is a collection of objects in memory and you cannot join a set of data in the database with another set of data that is in memory." Where can I find documentation about things like this? I’m really lacking knowledge of EF’s limits, and when I try to restrict a query’s result set like this, this incompetence makes itself very apparent and slows me down. – Nomenator Apr 05 '17 at 21:31
  • 1
    Good information. I am adding this exception to my list of least intuitive exception messages ever. It only makes sense AFTER you understand why it is happening. – DVK Mar 06 '18 at 18:22
4

In my case, I was able to resolve the issue by doing the following:

I changed my code from this:

var r2 = db.Instances.Where(x => x.Player1 == inputViewModel.InstanceList.FirstOrDefault().Player2 && x.Player2 == inputViewModel.InstanceList.FirstOrDefault().Player1).ToList();

To this:

var p1 = inputViewModel.InstanceList.FirstOrDefault().Player1;
var p2 = inputViewModel.InstanceList.FirstOrDefault().Player2;
var r1 = db.Instances.Where(x => x.Player1 == p1 && x.Player2 == p2).ToList();
Colin
  • 1,758
  • 1
  • 19
  • 24
  • This does not work for me. As `p1` and `p2` are both in the memory whether they are declared anonymously or by a variable name. – Rahat Zaman Apr 30 '18 at 15:31
  • 2
    The variable type isn't the issue. In my case the error was caused because was doing a .FirstOrDefault() inside of the Where clause. – Colin Apr 30 '18 at 16:12
  • This was the solution for me! Thanku so much @Colin :) – ajzbrun Oct 15 '21 at 17:32
2

Don't know if anyone searches for this. I had the same problem. A select on the query and then doing the where (or join) and using the select variable solved the problem for me. (problem was in the collection "Reintegraties" for me)

query.Select(zv => new
            {
                zv,
                rId = zv.this.Reintegraties.FirstOrDefault().Id
            })
            .Where(x => !db.Taken.Any(t => t.HoortBijEntiteitId == x.rId
                                             && t.HoortBijEntiteitType == EntiteitType.Reintegratie
                                             && t.Type == TaakType))
            .Select(x => x.zv);

hope this helps anyone.

William-H-M
  • 1,050
  • 1
  • 13
  • 19
Roelant
  • 65
  • 2
  • 8
  • 6
    `zv.this.Reintegraties.FirstOrDefault().Id` potential NullReferenceException –  Nov 24 '16 at 06:12
2

I had this issue and what I did and solved the problem was that I used AsEnumerable() just before my Join clause. here is my query:

List<AccountViewModel> selectedAccounts;

 using (ctx = SmallContext.GetInstance()) {
                var data = ctx.Transactions.
                    Include(x => x.Source).
                    Include(x => x.Relation).
                    AsEnumerable().
                    Join(selectedAccounts, x => x.Source.Id, y => y.Id, (x, y) => x).
                    GroupBy(x => new { Id = x.Relation.Id, Name = x.Relation.Name }).
                    ToList();
            }

I was wondering why this issue happens, and now I think It is because after you make a query via LINQ, the result will be in memory and not loaded into objects, I don't know what that state is but they are in in some transitional state I think. Then when you use AsEnumerable() or ToList(), etc, you are placing them into physical memory objects and the issue is resolving.

ebrahim.mr
  • 699
  • 5
  • 10
1

It's worth adding, since the OP's code sample doesn't provide enough context to prove otherwise, but I received this error as well on the following code:

public RetailSale GetByRefersToRetailSaleId(Int32 refersToRetailSaleId)
{
    return GetQueryable()
        .FirstOrDefault(x => x.RefersToRetailSaleId.Equals(refersToRetailSaleId));
}

Apparently, I cannot use Int32.Equals in this context to compare an Int32 with a primitive int; I had to (safely) change to this:

public RetailSale GetByRefersToRetailSaleId(Int32 refersToRetailSaleId)
{
    return GetQueryable()
      .FirstOrDefault(x => x.RefersToRetailSaleId == refersToRetailSaleId);
}
James Perih
  • 1,360
  • 1
  • 17
  • 19
0

Just add AsEnumerable() andToList() , so it looks like this

db.Favorites
    .Where(x => x.userId == userId)
    .Join(db.Person, x => x.personId, y => y.personId, (x, y).ToList().AsEnumerable()

ToList().AsEnumerable()
khaled saleh
  • 470
  • 7
  • 18