2016-12-01 60 views
2

我有一個linqpad腳本我正在處理,它的工作原理,但問題是它使用.AsEnumerable()將集合調用到內存中。有一天可能會有成千上萬個,所以我想盡可能地推遲一切事情。確保記錄具有所有關係,而不是任何

我想簡單地進行檢查,以確保如果我通過new long[] { 2, 4 }的功能,那麼任何經驗標識2和4將被退回。

以前,我只使用了.Contains()但這將返回經驗要麼 2或4

有沒有更好的方式來寫這個代碼,以便它會返回一個IQueryable<Experience>而不是一個List<Experience>所以我不必將所有結果加載到內存中以執行字符串concat?

void Main() 
{ 
    var AllExperiences = new List<_Experience>(); 
    AllExperiences.Add(new _Experience { Id = 1, Name = "Experience 1" }); 
    AllExperiences.Add(new _Experience { Id = 2, Name = "Experience 2" }); 

    AllExperienceTags.Add(new _ExperienceTag { ExperienceId = 1, TagId = 2 }); 
    AllExperienceTags.Add(new _ExperienceTag { ExperienceId = 1, TagId = 4 }); 
    AllExperienceTags.Add(new _ExperienceTag { ExperienceId = 2, TagId = 2 }); 

    var experiences = FilterBySelectedTags(AllExperiences, new[] { 2, 4 }.ToList()); 

    experiences.Dump(); 
} 

public List<_ExperienceTag> AllExperienceTags = new List<UserQuery._ExperienceTag>(); 

// Define other methods and classes here 
public List<_Experience> FilterBySelectedTags(List<_Experience> experiences, List<int> selectedTagIds) 
{ 
    var filteredExperiencesTags = AllExperienceTags.Where(x => selectedTagIds.Contains(x.TagId)); 

    var obj = filteredExperiencesTags.OrderBy(x => x.TagId).GroupBy(x => x.ExperienceId).AsEnumerable().Select(x => new 
    { 
     ExperienceId = x.Key, 
     ExpTags = string.Join(", ", x.Select(y => y.TagId)) 
    }); 

    var filteredTags = obj.Where(x => x.ExpTags == string.Join(", ", selectedTagIds)); 

    // make sure all the selected tags are found in the experience, not just any 
    return experiences.Where(x => filteredTags.Select(y => y.ExperienceId).Contains(x.Id)).ToList(); 
} 

public class _Experience 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
} 

public class _ExperienceTag 
{ 
    public int ExperienceId { get; set; } 
    public int TagId { get; set; } 
} 
+0

你能分享「體驗」類的聲明嗎? –

+0

@ThomasAyoub用完全隔離的代碼更新了問題。如果你運行它,它會返回一個結果。如果將第二個參數從「{2,4}」更改爲「{2}」,則會按預期返回兩個結果。 – user1477388

回答

3

Experience實體應該有一個導航屬性ExperienceTags

public virtual ICollection<ExperienceTag> ExperienceTags{get;set;} 

如果是這樣的情況下,這應該工作:

var query= from e in Experiences 
      let experienceTagIds=e.ExperiencesTags.Select(et=>et.TagId) 
      where selectedTagIds.All(x=>experienceTagIds.Contains(x)) 
      select e; 
+0

奇怪的是,我用'.All()'以不同的方式嘗試了這一點,但它沒有奏效。感謝分享這個解決方案! – user1477388

+0

不客氣:) – octavioccl

1

假設經驗,上有一個導航屬性標籤,那麼你可以這樣做:

void Main() 
{ 
    var experiences = Experiences.FilterBySelectedTags(new long[] { 2, 4 }); 

    experiences.Dump(); 
} 
public static class ExperienceExtensions { 
    public static IQueryable<Experience> FilterBySelectedTags(this IQueryable<Experience> experiences, IEnumerable<long> selectedTagIds) 
    { 
    return experiences.Where(e=>selectedTagIds.All(id=>e.Tags.Any(t=>t.TagId==id))); 
    } 
} 
+0

更新後的答案使用'.All'作爲@octavioccl指出的。 –

+0

根據你的解決方案'Tags'是一個導航屬性,你應該使用'Any(x => x.TagId == id)'而不是'Contains',但我認爲這不會在這個深層次上工作,那就是爲什麼我在我的解決方案中使用'let',因爲如果我的思維不會使我失敗,EF需要在該級別使用原始值。 – octavioccl

+0

@octavioccl嗯,是的,你是對的。我已經更新了答案以反映這一點。主要是爲了展示如何在LINQ中使用擴展方法以及如何使它們可鏈接。 –

相關問題