Linq к объектам Объединение коллекций и фильтрация - PullRequest
1 голос
/ 01 марта 2012

У меня проблемы с фильтрацией / объединением списков объектов в памяти с Linq для объектов, и для того, что обычно было бы тривиальным запросом SQL ...

Сценарий:
Наличие службы json (.asmx) позволяет клиенту выполнять вызов БД через оболочку sproc Entity Framework (функция импорта). В этом случае [веб-метод] «GetArticles (int [] TagIds)»
Сейчас я пытаюсь кэшировать эти данные, чтобы сэкономить на вызовах БД и применить фильтры к кешированной коллекции.

Данные:

  • Многие ко многим отношения с 2 сущностями: статьи, теги
  • Статьи могут иметь много тегов, и теги могут быть связаны со многими статьями (так что дополнительная таблица ссылок 'ArticlesTags')

Параметр фильтра веб-службы будет содержать массив тегов, которые могут иметь возвращаемые статьи.

Так что мой базовый 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 (параметры табличных значений), поэтому я не могу передать фильтр списка тегов в предпочтительном формате. Я нашел пример, где два набора результатов могут быть возвращены из одного вызова sproc, который может обойти это. Итак, когда у меня есть две коллекции (Articles и ArticlesTags), которые я намереваюсь кэшировать, как их объединить и затем отфильтровать на основе возможного параметра фильтра TagId с linq-to-objects?

Ответы [ 2 ]

1 голос
/ 01 марта 2012

Итак, у вас есть таблица Article, таблица Tags и таблица ArticleTags, которые связывают идентификатор статей с идентификатором тегов.Название статьи является легкой частью.Затем вы просматриваете ArticleTags и получаете все TagID, которые соответствуют этому идентификатору статей, затем вы проходите через Tag, чтобы получить имя, связанное с этим идентификатором тега.Превращает его в один аккуратный словарь с TagName для ключа, и значением является IEnumerable для TagNames.(вы можете вытянуть всю Статью и весь тег для словаря

вместо Dictionary , просто не указывать селектор элементов.
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)
)
1 голос
/ 01 марта 2012

Вот один из способов «переписать» ваш SQL-запрос в LINQ:

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