簡體   English   中英

使用列表屬性過濾具有 LINQ 的數據庫集

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

假設我有一個包含員工任務列表的事件:

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

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

在獲取工作人員列表的所有事件的情況下,我將如何做這樣的事情?

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)
  );

運行上述內容時,我目前收到此錯誤:

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'.

目標是讓這里只返回第二個和第三個事件

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 }
}

嘗試在 where 子句中添加附加條件以檢查事件 class 是否為StaffId中的 StaffId,

用法:

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

或與null 合並運算符?

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

在線試用

這似乎完成了工作,盡管我仍然不確定為什么@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));

我仍然不確定為什么@Prasad 的回答不起作用

EF 希望取出您提供的 C# 並從中制作 SQL。 它知道如何做一些事情,但不是所有事情

當您具有“ Z240AA2CEC4B29C56F3BEE520A8DCEE7EE中的“收集”模式時,在集合中的列中搜索WHERE id IN(value1,value2..)獲取該值列表

Any都可以,但(據我所知)僅在 collections 上,這只是正在搜索的值的類型。 這意味着將您的 StaffTasks 投影到一個簡單的 Guid? 集合也可以作為 Any:

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

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

EF 可以將其轉換為 IN,就像它可以包含一樣,但記住“不要使用任何,使用包含”可能更好的原因是,如果您執行 EF 不會執行的操作,包含會更好地導致編譯錯誤容忍。

這不會編譯(注意我使用了 staffTasks.Contains):

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

所以 Contains 會自動引導您使用原語列表,而 Any 讓您在 LINQ 中思考“我將在 lambda 模式下拉出我想要的道具”並寫道:

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

這將在 c# 中編譯,但不會轉換為 EF,因為 EF 必須運行投影以獲取它想要放入 IN 中的值。 它還嘗試在此處執行nullable<T> == T (StaffTask 和 Event 對 StaffId 有不同的類型),這在本地評估的 Any 中是合法的 C#,但 EF 不翻譯的另一件事

--

所以最后,你的答案被翻譯為COALESCE(event.StaffId, '00000000-0000-0000-0000-000000000000') IN ('guid1', 'guid3') (我猜 3 是一個錯字,因為你的樣本數據是 guid2 ) 這很好

如果您對列表進行類型匹配,那么它就充滿了Guid? 你可以放棄??Guid.Empty並且它會被翻譯為event.StaffId IN ('guid1', 'guid3')這也很好(它會丟棄 null StaffId 事件)並且實際上可能比 COALESCE 更快可以排除使用索引

如果您使用了Guid? 與任何它也將工作..

..但一般來說,如果你使用 Contains,你會更頻繁地第一次解決這些事情,因為 Contains 要求你提供東西的方式更經常符合 EF 需要接收東西的方式

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM