简体   繁体   English

使用列表属性过滤具有 LINQ 的数据库集

[英]Using a list property to filter a dbset with LINQ

Say I have an event that has a list of staff tasks:假设我有一个包含员工任务列表的事件:

public class Event()
{
  public Guid? StaffId { get; set; }
}

public class StaffTask()
{
  public Guid StaffId { get; set; }
  public Guid TaskId { get; set; }
}

How would I do something like this where I get all the events for a list of staff members?在获取工作人员列表的所有事件的情况下,我将如何做这样的事情?

var staffTasks = new List<StaffTasks>() 
{ 
  new StaffTask () { StaffId = "guid1", TaskId = "guid2" },
  new StaffTask () { StaffId = "guid3", TaskId = "guid4" }
};

queryable = _db.Events.AsQueryable()
  .Where(event => 
      staffTasks.Any(st => st.StaffId == event.StaffId)
  );

I currently get this error when running the above:运行上述内容时,我目前收到此错误:

The LINQ expression 'DbSet<Event>()
    .Where(e => __staffTasks
        .Any(or => (Nullable<Guid>)or.StaffId == e.StaffId))' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'.

The goal would be to have this return only the second and third event here目标是让这里只返回第二个和第三个事件

var events = new List<Event>() {
  new Event() { StaffId = null },
  new Event() { StaffId = "guid1" },
  new Event() { StaffId = "guid2" },
  new Event() { StaffId = "guid20" },
  new Event() { StaffId = null }
}

Try adding additional condition in where clause to check StaffId in event class is null or not,尝试在 where 子句中添加附加条件以检查事件 class 是否为StaffId中的 StaffId,

Usage:用法:

queryable = _db.Events.AsQueryable()
         .Where(event =>  event.StaffId.HasValue && 
        staffTasks.Any(st => st.StaffId.HasValue  && st.StaffId == event.StaffId));

or with null coalescing operator??或与null 合并运算符?

queryable = _db.Events.AsQueryable()
        .Where(event => 
        staffTasks.Any(st => (st.StaffId ?? Guid.Empty) == event?.StaffId));

Try Online在线试用

this seemed to get the job done, though i'm still not sure why @Prasad's answer didn't work这似乎完成了工作,尽管我仍然不确定为什么@Prasad 的回答不起作用

var staffTasks = new List<StaffTasks>() 
{ 
  new StaffTask () { StaffId = "guid1", TaskId = "guid2" },
  new StaffTask () { StaffId = "guid3", TaskId = "guid4" }
};

var staff = staffTasks.Select(st => st.StaffId).ToList();

queryable = _db.Events.AsQueryable()
  .Where(event => staffTasks.Contains(event.StaffId ?? Guid.Empty));

i'm still not sure why @Prasad's answer didn't work我仍然不确定为什么@Prasad 的回答不起作用

EF wants to take the C# you provide and make an SQL out of it. EF 希望取出您提供的 C# 并从中制作 SQL。 It knows how to do some things, but not everything它知道如何做一些事情,但不是所有事情

When you have a pattern of "collection in c#, search in column for value within the collection" EF will want to make an SQL like WHERE id IN(value1,value2..) but critically it won't go digging and running complex projections to get that list of values当您具有“ Z240AA2CEC4B29C56F3BEE520A8DCEE7EE中的“收集”模式时,在集合中的列中搜索WHERE id IN(value1,value2..)获取该值列表

Any will work, but (as far as I know) only on collections that are just the type of the value being searched. Any都可以,但(据我所知)仅在 collections 上,这只是正在搜索的值的类型。 This means projecting your StaffTasks to a simple Guid?这意味着将您的 StaffTasks 投影到一个简单的 Guid? collection would also work as an Any:集合也可以作为 Any:

var staff = staffTasks.Select(st => (Guid?)st.StaffId).ToArray();

_db.Events
  .Where(event => staff.Any(st => event.StaffId == st ));

EF can translate this to an IN like it can Contains, but the reason it's probably just better to remember "don't use Any, use Contains" is because Contains is much better at causing a compile error if you do something EF won't tolerate. EF 可以将其转换为 IN,就像它可以包含一样,但记住“不要使用任何,使用包含”可能更好的原因是,如果您执行 EF 不会执行的操作,包含会更好地导致编译错误容忍。

This wouldn't compile (note I've used staffTasks.Contains):这不会编译(注意我使用了 staffTasks.Contains):

_db.Events
  .Where(event => staffTasks.Contains(event.StaffId));

So Contains automatically guides you towards using a list of primitives whereas Any makes you think in LINQ "I'll just pull the prop I want in the lambda" mode and write:所以 Contains 会自动引导您使用原语列表,而 Any 让您在 LINQ 中思考“我将在 lambda 模式下拉出我想要的道具”并写道:

_db.Events
  .Where(event => staffTasks.Any(st => event.StaffId == st.SomeProp));

This would compile in c# but won't translate to EF because EF would have to run the projection to get the values it wants to put in the IN.这将在 c# 中编译,但不会转换为 EF,因为 EF 必须运行投影以获取它想要放入 IN 中的值。 It also tries to get away with doing a nullable<T> == T here (StaffTask and Event have different types for StaffId), which is legal C# in a locally evaluated Any, but another thing that EF doesn't translate它还尝试在此处执行nullable<T> == T (StaffTask 和 Event 对 StaffId 有不同的类型),这在本地评估的 Any 中是合法的 C#,但 EF 不翻译的另一件事

-- --

So ends up, your answer became translated as COALESCE(event.StaffId, '00000000-0000-0000-0000-000000000000') IN ('guid1', 'guid3') (I guess that 3's a typo because your sample data is guid2) which is fine所以最后,你的答案被翻译为COALESCE(event.StaffId, '00000000-0000-0000-0000-000000000000') IN ('guid1', 'guid3') (我猜 3 是一个错字,因为你的样本数据是 guid2 ) 这很好

If you'd type-matched the list so it was full of Guid?如果您对列表进行类型匹配,那么它就充满了Guid? you could have dropped the ??Guid.Empty and it would have translated as event.StaffId IN ('guid1', 'guid3') which is also fine (it would discard the null StaffId on events) and actually possibly faster as the COALESCE could preclude the use of an index你可以放弃??Guid.Empty并且它会被翻译为event.StaffId IN ('guid1', 'guid3')这也很好(它会丢弃 null StaffId 事件)并且实际上可能比 COALESCE 更快可以排除使用索引

And if you'd used a list of Guid?如果您使用了Guid? With Any it also would have worked..与任何它也将工作..

..but generally if you use Contains you will have these things work out first time more often because the way Contains demands you supply things is more often in line with how EF needs to receive things ..但一般来说,如果你使用 Contains,你会更频繁地第一次解决这些事情,因为 Contains 要求你提供东西的方式更经常符合 EF 需要接收东西的方式

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM