views:

435

answers:

6

Hi,

I have a [Flags] enum like this:

[Flags]
public enum Status
{
  None = 0,
  Active = 1,
  Inactive = 2,
  Unknown = 4
}

A Status enum may contain two values such as:

Status s = Status.Active | Status.Unknown;

Now I need to create a linq query (LINQ to ADO.NET Entities) and ask for records whose status is s above, that is Active or Unknown;

var result = from r in db.Records
             select r
             where (r.Status & (byte)s) == r.Status

Of course I get an error, because LINQ to Entities only knows to handle primitive types in the Where clause.

The error 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.

Is there a workable way? I may have a status Enum with 10 possible values and to query for 5 of the statuses. How do I construct the query using Flags enum in an elegant way?

Thanks.

Update

This seems to be a Linq to Entities problem. I think in LINQ to SQL it works (not sure, didn't tested).

+3  A: 

the solution is to cast Status to byte in a variable and use that in the query:

    byte bs = (byte)s;

    var result = from r in db.Records
                 where (r.Status & bs) == r.Status
                 select r;

<< Problem with casting enums in LINQ to Entities >>

najmeddine
I already said that this solution throws an exception"Unable to create a constant value of type 'Closure type'. Only primitive types ('such as Int32, String, and Guid') are supported in this context."
Vasi
sixlettervariables
+1  A: 

I don't know EF, but could inserting additional casts work?

var result = from r in db.Records
             where ((byte)r.Status & (byte)s) == (byte)r.Status
             select r
erikkallen
r.Status is byte already, and I added the cast in te question also.This is not the problem, the problem is that it will not run. It compiles but doesn't run. See the error in the question.
Vasi
A: 

Try it like this:

byte status = (byte)(Status.Active | Status.Unknown);

var result = from r in db.Records
             select r
             where (r.Status & status) != 0
LukeH
I think the evaluation in Where is not translatable to esql or somethong like that. That's why I get the exception:"Unable to create a constant value of type 'Closure type'. Only primitive types ('such as Int32, String, and Guid') are supported in this context."
Vasi
@Vasi: Yes, but do you get that error with my version?
LukeH
I've got the same error.
Vasi
+1  A: 
var result = from r in db.Records
             where r.Status == s
             select r
iburlakov
A: 

I am unsure if a bitwise AND-operation will work, but try casting s to an int:

        int i = (int)s;
        var result = from r in db.Records
             select r
             where (r.Status & i) == r.Status

Which database engine are you using? Possibly the engine does not support bitwise operations.

Reference: http://www.matthidinger.com/archive/2008/02/26/entity-framework-comparison-frustration-explained.aspx

Yannick M.
Using LINQ to Entities over a MS SQL Server.
Vasi
It is NOT a cast problem.
Vasi
No, it is a problem that it cannot translate the result of the enum to a native SQL type. Thus using an integer comparison might work.
Yannick M.
I think it cannot translate the bit comparison into eSql. I used the cast to byte in my code before posting this question, but I missed it when I wrote the short example here. So it is not a cast problem, I think it is a bit comparison problem, not being supported bu EF.
Vasi
what is the datatype of the Status column?
Yannick M.
Still, please try a cast to `int`. This is really all about finding a workaround for EF LINQ->SQL translator deficiency... you have to try the corner cases.
Pavel Minaev
A: 

The folloiwng works for me in C#

    public const StatusTypes ActiveAlert = StatusTypes.Accepted | StatusTypes.Delivered;

        int flags = (int)ActiveAlert;

        try
        {
            var query = from p in model.AlertsHistory
                        where (p.UserId == clientId
                        && (p.Status.HasValue && (p.Status.Value & flags) != 0))
                        select p;
            var aList = query.ToList();

            return (aList);


        }
        catch (Exception exc)
        {
            log.Error("Exception getting Alerts History for user.", exc);
            throw;
        }
Ann