Linq to objects加入集合和过滤

时间:2012-03-01 01:34:38

标签: c# linq entity-framework linq-to-objects

我在使用Linq到对象的内存对象列表中进行过滤/加入时遇到问题,并且通常是一个简单的SQL查询...

方案:
有一个json服务(.asmx)允许客户端通过Entity Framework包裹的sproc(函数导入)进行数据库调用。在这种情况下,[Web方法]“GetArticles(int [] TagIds)”
我现在正在寻找缓存此数据以节省数据库调用并将过滤器应用于缓存集合。

数据:

  • 与2个实体的多对多关系:文章,标签
  • 文章可以有很多标签,标签可以与很多文章相关联(所以额外的链接表'ArticlesTags')

Web服务过滤器参数将包含返回文章可以具有的TagId数组。

所以我的基线SQL查询看起来像这样

SELECT DISTINCT a.*
FROM Article a INNER JOIN ArticlesTags at ON a.ArticleId = at.ArticleId
WHERE at.TagId in (0, 1.. 'list of tag ids')

我遇到了许多障碍 -
EF包装的sprocs不允许SQL 2008(表值参数),因此我无法以首选格式传递TagId列表过滤器。 我找到了一个例子,可以从一个sproc调用返回两个结果集,这可能会解决这个问题。 所以,一旦我有两个我打算缓存的集合(文章和文章标签),如何加入并随后根据可能的TagId过滤器参数和linq-to-objects进行过滤?

2 个答案:

答案 0 :(得分:1)

这是在LINQ中“重写”SQL查询的一种方法:

class Article {
    public int ArticleId;
    public string ArticleName;
    // Other fields...
}

class ArticleComparer : IEqualityComparer<Article> {
    public bool Equals(Article x, Article y) {
        return x.ArticleId == y.ArticleId && x.ArticleName == y.ArticleName;
    }
    public int GetHashCode(Article obj) {
        return obj.ArticleId.GetHashCode();
    }
}

class ArticlesTag {
    public int TagId;
    public int ArticleId;
    // Other fields...
}

class Program {

    static void Main(string[] args) {

        // Test data:

        var articles = new[] {
            new Article { ArticleId = 1, ArticleName = "Article A" },
            new Article { ArticleId = 2, ArticleName = "Article B" },
            new Article { ArticleId = 3, ArticleName = "Article C" }
        };

        var article_tags = new[] {
            new ArticlesTag { TagId = 1, ArticleId = 1 },
            new ArticlesTag { TagId = 2, ArticleId = 1 },
            new ArticlesTag { TagId = 3, ArticleId = 1 },
            new ArticlesTag { TagId = 4, ArticleId = 2 },
            new ArticlesTag { TagId = 5, ArticleId = 2 },
            new ArticlesTag { TagId = 6, ArticleId = 3 },
            new ArticlesTag { TagId = 7, ArticleId = 3 }
        };

        var tag_ids = new HashSet<int>(new[] { 2, 3, 6 });

        // JOIN "query":

        var q = (
            from article in articles
            join article_tag in article_tags
                on article.ArticleId equals article_tag.ArticleId
            where tag_ids.Contains(article_tag.TagId)
            select article
        ).Distinct(new ArticleComparer());

        foreach (var article in q)
            Console.WriteLine(
                string.Format(
                    "ArticleId = {0}\tArticleName = {1}",
                    article.ArticleId,
                    article.ArticleName
                )
            );

    }

}

打印:

ArticleId = 1   ArticleName = Article A
ArticleId = 3   ArticleName = Article C

答案 1 :(得分:1)

因此,您有一个Article表,一个Tags表和一个ArticleTags表,它将Article ID与标签ID相关联。文章名称是容易的部分。然后,您浏览ArticleTags并获取与该文章ID匹配的所有TagID,然后您浏览标签以获取与该标签ID相关联的名称。将其转换为一个整齐的字典,其中包含密钥的TagName,值为IEnumerable&lt;字符串&gt; TagNames (您可以将整篇文章和整个标记拉为字典&lt;文章,标记&gt;而不是字典&lt;字符串,字符串&gt;只留下成员选择器。

var articleDictionary = articles.ToDictionary(
       a => a.Name, // article name is key
       a => ArticleTags
             .where(t => t.ArticleID == a.Id)
             .select(t => Tags.Single(tag => tag.id == t.id).TagName // value is an IEnumerable<string> of tag names
)

foreach (var article in articleDictionary)
{
    Console.WriteLine(article.Key);
    foreach (var tag in article.Value)
    { 
        Console.WriteLine("\t" + tag)
    }
}

编辑: 这是你如何拉动整个物体

var articleDictionary = articles.ToDictionary(
       a => a,
       a => ArticleTags
             .where(t => t.ArticleID == a.Id)
             .select(t => Tags.Single(tag => tag.id == t.id)
)