2016-12-01 1 views
2

私は作業しているlinqpadスクリプトを持っていますが、問題はそのコレクションをメモリに呼び出す.AsEnumerable()を使用していることです。これらのうち何千ものものがあるかもしれないので、私はできるだけすべてを延期したいと思います。レコードにすべてのリレーションシップがあることを保証します。

私は単に私が関数にnew long[] { 2, 4 }を渡す場合は必ず、その後、 IDが2と4の両方を持っている任意の経験が返されるようにするのチェックを実行しようとしています。

以前、私は.Contains()を使用していたが、これは経験を持ってのいずれか 2または

4.それはIQueryable<Experience>を返すのではなくなるようにこのコードを書くためのより良い方法はありますが返されますa List<Experience>ですから、文字列の連結を実行するためにすべての結果をメモリにロードする必要はありませんか?

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

「Experience」クラスの宣言を共有できますか? –

+0

@ThomasAyoub完全に分離されたコードで質問を更新しました。実行すると、1つの結果が返されます。 2番目のパラメータを '{2、4}'から '{2} 'に変更すると、期待通りに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

@octaviocclが指摘しているように、 '.All'を使用するように更新されました。 –

+0

あなたのソリューションによる 'Tags'はナビゲーションプロパティなので、' Contains'の代わりに 'Any(x => x.TagId == id)'を使うべきですが、その深いレベルでは動作しません。なぜ私のソリューションで 'let'を使うのですか?私の心が私を失敗させないなら、EFはそのレベルのプリミティブな値で動作する必要があるからです。 – octavioccl

+0

@octaviocclああ、そうです。私はそれを反映する答えを更新しました。ほとんどの場合、LINQで拡張メソッドを使用する方法と、それらを連鎖可能にする方法を示します。 –

関連する問題