Как использовать перечисления флагов в запросах Linq to Entities?

У меня есть такое перечисление [Flags]:

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

Перечисление Status может содержать два значения, например:

Status s = Status.Active | Status.Unknown;

Теперь мне нужно создать запрос linq (LINQ to ADO.NET Entities) и запросить записи, статус которых указан выше, то есть Active или Unknown;

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

Конечно, я получаю сообщение об ошибке, потому что LINQ to Entities знает, как обрабатывать примитивные типы только в предложении Where.

Ошибка:

Невозможно создать постоянное значение типа «Тип закрытия». В этом контексте поддерживаются только примитивные типы (такие как Int32, String и Guid).

Есть действенный способ? Я могу иметь статус Enum с 10 возможными значениями и запрашивать 5 статусов. Как элегантно построить запрос с использованием перечисления флагов?

Спасибо.

Обновить

Кажется, это проблема Linq to Entities. Я думаю, что в LINQ to SQL это работает (не уверен, не тестировал).


person Vasile Tomoiaga    schedule 15.09.2009    source источник
comment
Ваш запрос (если он работает) вернет только те записи, статус которых одновременно Active и Unknown. Это то, что вы хотите?   -  person LukeH    schedule 15.09.2009
comment
Нет, это было ИЛИ, а не И. Итак, 'Status s = Status.Active | Статус неизвестен;' верно. Спасибо!   -  person Vasile Tomoiaga    schedule 15.09.2009
comment
@Vasi: Эта часть правильная, но Status.Active|Status.Unknown эквивалентно 1|4, то есть 5. Таким образом, ваше предложение where фактически говорит where (r.Status & 5) == r.Status, что то же самое, что и where r.Status == 5, что то же самое, что сказать (на английском языке), где r.Status - это одновременно Active и Unknown!   -  person LukeH    schedule 15.09.2009
comment
Вы можете преобразовать свои сущности в перечислимые. См .: stackoverflow.com/a/13954549/616274   -  person Eric Bole-Feysot    schedule 20.12.2012
comment
Начиная с EF6.1 HasFlag поддерживается в LINQ-to-Entities. См. data.uservoice. ru / forum / и entityframework.codeplex.com/workitem/1497   -  person Shay    schedule 17.02.2015


Ответы (7)


Просто используйте HasFlag()

var result = from r in db.Records
         where r.Status.HasFlag(s)
         select r
person Eat at Joes    schedule 31.12.2014

Взгляните на следующий хороший пост, он показывает вам следующие альтернативы:

  • Установлен хотя бы один из выбранных флагов
  • Точно такие же флаги установлены
  • По крайней мере, те флаги, которые вы выбрали, и многое другое

https://timdams.wordpress.com/2011/02/14/using-enum-flags-to-write-filters-in-linq/#comment-30

person Emile    schedule 11.05.2011

В БД Flags перечисление должно быть целым. После этого вы можете попробовать вот так:

Status s = Status.Active | Status.Unknown;

var result = from r in db.Records
where (s & r.Status) == r.Status
select r
person Gökhan CANGÜL    schedule 23.10.2015

Я не знаю EF, но может ли работать добавление дополнительных приведений?

var result = from r in db.Records
             where ((byte)r.Status & (byte)s) == (byte)r.Status
             select r
person erikkallen    schedule 15.09.2009
comment
r.Status уже является байтом, и я также добавил вопрос о приведении. Проблема не в этом, проблема в том, что он не запускается. Он компилируется, но не запускается. Смотрите ошибку в вопросе. - person Vasile Tomoiaga; 15.09.2009

Попробуйте вот так:

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

var result = from r in db.Records
             select r
             where (r.Status & status) != 0
person LukeH    schedule 15.09.2009
comment
Я думаю, что оценка в Where не может быть переведена на esql или что-то в этом роде. Вот почему я получаю исключение: невозможно создать постоянное значение типа Closure type. В этом контексте поддерживаются только примитивные типы (такие как Int32, String и Guid). - person Vasile Tomoiaga; 15.09.2009

Я не уверен, что побитовая операция И будет работать, но попробуйте преобразовать s в int:

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

Какой движок базы данных вы используете? Возможно, движок не поддерживает поразрядные операции.

Ссылка: http://www.matthidinger.com/archive/2008/02/26/entity-framework-comparison-frustration-explained.aspx

person Yannick Motton    schedule 15.09.2009
comment
Использование LINQ to Entities через MS SQL Server. - person Vasile Tomoiaga; 15.09.2009
comment
Нет, проблема в том, что он не может преобразовать результат перечисления в собственный тип SQL. Таким образом, использование целочисленного сравнения может работать. - person Yannick Motton; 15.09.2009
comment
Я думаю, что это не может перевести сравнение битов в eSql. Я использовал преобразование в байты в своем коде перед тем, как опубликовать этот вопрос, но я пропустил его, когда писал здесь короткий пример. Так что это не проблема с приведением, я думаю, что это проблема небольшого сравнения, не поддерживаемая EF. - person Vasile Tomoiaga; 15.09.2009
comment
какой тип данных столбца состояния? - person Yannick Motton; 15.09.2009
comment
Тем не менее, попробуйте выполнить приведение к int. На самом деле все сводится к поиску обходного пути для устранения недостатка в EF LINQ- ›SQL-переводчике ... вам нужно попробовать крайние случаи. - person Pavel Minaev; 17.09.2009

Для меня это работает на С #

    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;
        }
person Community    schedule 17.09.2009