在 Entity Framework 查询中使用带有标志的枚举

15

我有一个如下的枚举类型

  [Flags]
    public enum WeekDays
    {

        Monday = 1,
        Tuesday = 2,
        Wednesday = 4,
        Thursday = 8,
        Friday = 16,
        Saturday = 32,
        Sunday = 64,
    }


   WeekDays dw = WeekDays.Friday | WeekDays.Monday | WeekDays.Saturday;

   int dwInt = (int)dw;

   var list = query.Where(f => f.FromDateTime.DayOfWeek == dwInt "??????????? what can i do there????").ToList();

10
你知道自己并没有提出任何问题吗? - usr
8
我写了"??????????", 我能在那儿做什么? - tobias
18
@tobias,请不要把你的问题放在代码内部,特别是当我们需要滚动才能找到它时。请将其放在代码块外面。 - Sampson
@JonathanSampson,感谢您的建议。 - tobias
5个回答

22

从 Entity Framework 6.1 开始,您可以在请求中使用 HasFlag 扩展方法。

例如:

query.Where(f => f.FromDateTime.DayOfWeek.HasFlag(WeekDays.Friday | WeekDays.Monday)).ToList();

请查看https://entityframework.codeplex.com/workitem/1497获取有关功能请求和实现的详细信息。


1
非常好奇如何在扩展标志枚举时进行迁移。 - Ozballer31
备用链接(因为 CodePlex 链接已损坏)https://learn.microsoft.com/de-de/dotnet/api/system.enum.hasflag?view=net-5.0 - Sebastian
1
或者如果您不会说德语:https://learn.microsoft.com/dotnet/api/system.enum.hasflag?view=net-5.0 - agbinfo

14

我猜想您可能不确定在查询中如何筛选出源列表中的日期。

根据您提供的源代码片段,我认为dwInt被用作位掩码(bitmask),而DayOfWeek将有一个位位置“设置”以指示给定的星期几。因此,在Where子句中,您需要对DayOfWeek字段执行逻辑位与(logical bitwise AND)dwInt操作,然后检查结果是否大于0,这意味着您的目标字段中设置了所需星期几的“位”。 我相信下面的代码可以解决问题:

var list = query.Where(f => (f.FromDateTime.DayOfWeek & dwInt) >0).ToList()

如果我误解了你的问题,请原谅。


2
请注意,这无法针对Entity Framework查询工作,因为它无法转换为SQL。您必须调用ToList()并在生成的内存集合上执行Where() - jrummell
在使用EF 5、.NET 4.5时,对我有效,无需使用.ToList()。 - rafasoares
2
“对我来说,使用EF 5可以工作” - 但是你的查询在服务器端和本地端都只完成了一半(EF不理解的部分,如位运算,是在本地完成的),这可能会严重影响性能。而且这种行为在EF Core中是不支持的,它会抛出异常让你知道你必须使用.ToArray()并在本地完成操作 - 这样你就可以决定“没问题”还是“天啊,这太糟糕了,让我们尝试想出不同的解决方案”。 - Jan 'splite' K.

8
[HasFlags]属性非常有趣,因为它只影响.ToString()和(据我所知)Enum.Parse()操作。因此,对于非字符串操作,您的枚举类型是否具有[HasFlags]属性并不重要。在EF中,枚举的工作方式是它们只是转换为基础类型,并被视为以下整数类型之一:int64、int32、int16、byte、sbyte(请注意,EDM不支持无符号整数类型,因此具有无符号基础类型的枚举将无法工作,并且在数据库中,枚举列只是对应于上述类型之一的类型列)。这意味着,除非编译器不允许它(我不认为我知道任何这样的操作),否则基本上对于任何在EF支持的整数值上有效的操作也适用于枚举值。这也意味着,如果编译通过(Sql Server支持按位运算),您想要放置在????处的任何内容都应该有效。

3

解决方案在此处

 public static Dictionary<int, int> map = new Dictionary<int, int>() { { 0, 64 }, { 1, 1 }, { 2, 2 }, { 3, 4 }, { 4, 8 }, { 5, 16 }, { 6, 32 } };

//actually,this comes from the user interface, but we should focus on the database layer
WeekDays[] dw = new WeekDays[] {WeekDays.Saturday,WeekDays.Tuesday,WeekDays.Wednesday };
    
int[] systemDayOfWeekList = new int[daysOfWeek.Length];
    
for (int i = 0; i < daysOfWeek.Length; i++)
{
  systemDayOfWeekList[i] = map.FirstOrDefault(e => e.Value == (int)daysOfWeek[i]).Key;
}
    
query = query.Where(f => dayOfWeekList.Contains(((int)SqlFunctions.DatePart("dw", f.FromDateTime))));

0

通常,我会以一种不在内存中获取结果并在C#中处理它的方式进行操作,我认为这样做没有建设性,而且我的数据库表太大了,一开始就不能这么做。

我的做法是编写一个查询并将 enum 标志作为 SQL 参数传递,SQL "[ColumnName] & @flag=@flag" 与在 DotNet 中使用的 "property.HasFlag(MyEnum.MyFlagValue)" 相同。

public async Task<ICollection<FrequentlyAskedQuestion>> ExecuteAsync(GeoLocation language, ProductGroups productGroup)
{
    var p1 = new SqlParameter("p1", (int)language);
    var flag = new SqlParameter("flag", (int)productGroup);
    using var db= await _contextFactory.CreateDbContextAsync();
    return await db.FrequentlyAskedQuestions
                .FromSqlRaw("select * from [dbo].[GetFrequentlyAskedQuestions](@p1) where ProductGroup & @flag = @flag", p1,flag)                     
                .OrderByDescending(d=>d.AnswerWasHelpfull)
                .AsNoTracking()
                .ToArrayAsync();
}

在这个示例中,我使用了一个UDF函数,它会返回用户语言或默认语言中的表格数据。然后,在需要应用标记的用例中进一步过滤数据。
我不知道EF是否可以为我生成这样的查询,掌控并编写查询语句是可行的。但是当模式更改未得到传播时,单元测试将失败。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接