Linq returns list or single object
Asked Answered
P

8

7

I have a Linq to Entities query like this one:

var results = from r in entities.MachineRevision
              where r.Machine.IdMachine == pIdMachine
                 && r.Category == (int)pCategory
              select r;

Usually, I use the code below to check if some results are returned:

if (results.Count() > 0)
{
    return new oMachineRevision(results.First().IdMachineRevision);
}

However, I'm getting NotSupportedException in the if condition.

The error message is: Unable to create a constant value of type 'Closure type'. Only primitive types ('such as Int32, String, and Guid') are supported in this context.

Note that pCategory is an Enum type.

Perse answered 30/12, 2008 at 11:53 Comment(0)
C
11

EDIT: Based on your update, the error may be related to an enum in your entity class. See this blog entry for more information and a work-around. I'm leaving my original answer as an improvement on your query syntax.

Try doing the selection of the first entity in the query itself using FirstOrDefault and then check if the result is null.

int compareCategory = (int)pCategory; // just a guess
var result = (from r in entities.MachineRevision
              where r.Machine.IdMachine == pIdMachine
                 && r.Category == compareCategory
              select r).FirstOrDefault();

if (result != null)
{
     return new oMachineRevision(result.IdMachineRevision);
}
Chev answered 30/12, 2008 at 12:9 Comment(3)
Still doesn't work, because we're trying to execute the FirstOrDefault() method on an entity object (that's what is returned by the linq statement) and not an IEnumerable.Perse
Select always returns an IEnumerable, see docs here msdn.microsoft.com/en-us/library/bb548891.aspx. Are you sure that the example is exactly the same as your code?Chev
My mistake, I apologize for that. To simplify the code, I've removed an enum, just like tvanfosson noticed by the error message I was getting. Thanks to all, and sorry for misleading you.Perse
M
2

Why not just use FirstOrDefault() instead, and check for null? I can't see the benefit in querying for the count and then taking the first element.

Mechanical answered 30/12, 2008 at 12:9 Comment(1)
I can see your point. However, if I use FirstOrDefault(), I'm still getting that NotSupportedException, because I'm trying to execute that method on an entity object (that's what is returned by the Linq statement), not on a IEnumerable.Perse
L
2

In the standard implementation of linq, the operators "select" and "where" map to methods that return an IEnumerable or IQueryable. So standard linq methods when used should always return an IEnumerable from your query not a single object.

But linq methods that are candidates for the linq operators are not restricted to methods returning IEnumerables, any method returning anything can be chosen.

In case you have instance methods named "Select" and "Where" that return a single object or extensions methods that are specific to your class and return a single object those will be used instead of the standard linq ones.

My guess is that either a "Select" or "Where" method defined in your class is making linq return a single value instead of a IEnumerable<T>.

Lapidify answered 30/12, 2008 at 12:57 Comment(0)
S
1

I didn't know different anonymous objects would be created depending on the query result. I guess they just wanted results to be of type IEnumerable

How about using a foreach?

var results = from r in entities.MachineRevision
              where r.Machine.IdMachine == pIdMachine
                 && r.Category == pCategory
              select r;

foreach( var r in results )
{
    yield return new oMachineRevision( r.IdMachineRevision );
}
Sculpturesque answered 30/12, 2008 at 12:51 Comment(0)
G
1

This goes for all implicit types too. I must admit I keep forgetting this and that's how I came across this post.

if you have

class ClassA {
               ...

               private string value;

               ...

               public static implicit operator string(ClassA value)
               {
                    return value.ToString();
               } 

              ...
}

you need to explictly cast the class to astring for comparison.

so I usually do this

    var myClassAStr = myClassA.ToString();

    var x = (from a in entites where a.ValToCompare == myClassAStr select a).first();

// do stuff with x
    ...
Glasgow answered 29/11, 2011 at 10:34 Comment(1)
actually I usually use a.ValToCompare.Equals(myClassAstr)Glasgow
S
0

try using

IENumerable<MachineRevision> results = from r in entities.MachineRevision
...

instead.

I think its the var that's causing your issue.

Sanmiguel answered 30/12, 2008 at 11:57 Comment(0)
C
0

Edit:

Read the error message. "Unable to create a constant value of type 'Closure type'. Only primitive types ('such as Int32, String, and Guid') are supported in this context."

One of these comparisions is with a type that is not int, string or guid. I'm guessing the Category.

r.Machine.IdMachine == pIdMachine && r.Category == pCategory

Interestingly, LinqToSql will allow this construction. Don't know why LinqToEntities does not support this.

Consortium answered 30/12, 2008 at 12:14 Comment(0)
E
0

I think you could also select the item you want another, simpler way by using lambda expressions.

var result = entities.MachineRevision
                 .Where(x => x.Machine.IdMachine == pIdMachine)
                 .Where(y => y.Category == (int)pCategory)
                 .FirstOrDefault();

if (result != null)
{
     return new oMachineRevision(result.IdMachineRevision);
}

and then proceeding as you would normally

Eyeglasses answered 16/6, 2009 at 10:20 Comment(1)
Doesn't work, because the ".Where(y => y.Category == (int)pCategory)" produces an error. You need to do the cast of the Enum before the query. var iCategory = (int)pCategory; and then just use the iCategory in the query itself.Hanway

© 2022 - 2024 — McMap. All rights reserved.