LINQ options.load с проблемой

Я пишу систему ASP.net на основе тегов. Используя следующую схему БД:

Topic <many-many> TagTopicMap <many-many> Tag

По сути, это подход 3NF (toxi), который я нашел из следующего: http://www.pui.ch/phred/archives/2005/04/tags-database-schemas.html

Вот фрагмент кода, который у меня есть:

DataLoadOptions options = new DataLoadOptions();
        options.LoadWith<Topic>(t => t.TagTopicMaps);
        options.LoadWith<TagTopicMap>(tt => tt.Tag);
        var db = new lcDbDataContext();
        db.LoadOptions = options;
        db.Log = w;

        var x = from topic in db.Topics
                orderby topic.dateAdded descending
                select topic;

        ViewData["TopicList"] = x.Take(10);

Когда я выполняю это, результат в порядке, но он выдает 11 одиночных SQL-запросов, один для получения списка 10 лучших тем:

    SELECT TOP (10) [t0].[Id], [t0].[title], [t0].[dateAdded]
FROM [dbo].[Topics] AS [t0] ORDER BY [t0].[dateAdded] DESC
-- Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 3.5.30729.1 

И 10 других для получения подробной информации о тегах по отдельности.

Я попытался включить и выключить два оператора loadwith и обнаружил, что происходит следующее:

loadwith<topic> : no difference for on or off.
loadwith<tagtopicmap>: 11 Queries when on, much more when off.

Короче говоря, ТОЛЬКО вторая опция loadwith работает должным образом. Первый не имеет никакого эффекта!

Я также попытался сделать набор результатов ToList(). Но возникает еще большая проблема: для части сведений о тегах он извлекает только те УНИКАЛЬНЫЕ элементы, все эти повторяющиеся теги (конечно, этот же тег может появляться в нескольких темах!) Отбрасываются запросом.

И последнее, ниже приведен код, который я использовал в aspx для извлечения данных, в случае создания результата tolist() я меняю (IQueryable) на (IList):

<% foreach (var t in (IQueryable)ViewData["TopicList"])
       {
           var topic = (Topic)t;

    %>
    <li>
        <%=topic.title %> || 
        <% foreach (var tt in (topic.TagTopicMaps))
           { %>
                <%=tt.Tag.Name%>, 
                <%} %>
    </li>
    <%
        }
    %>

person xandy    schedule 19.05.2009    source источник


Ответы (3)


Короткий ответ: у LinqToSql есть несколько подобных особенностей, и иногда вам приходится использовать обходные пути...

Параметр Linq2Sql LoadWith просто вызывает внутреннее соединение между таблицами базы данных, поэтому вы можете вызвать аналогичное поведение, переписав оператор Linq во что-то вроде (пожалуйста, простите любые опечатки, я привык писать Linq в синтаксисе VB...):

var x = from topic in db.Topics
        join topicMap in topic.TagTopicMaps
        orderby topic.dateAdded descending
        group topicMap by topicMap.topic into tags = Group;

Этот синтаксис может быть ужасно неправильным, но основная идея заключается в том, что вы заставляете Linq2Sql оценивать соединение между Topics и TagTopicMaps, а затем использовать группировку (или «групповое соединение», «let» и т. д.) для сохранения иерархии объектов в набор результатов.

person Mark    schedule 23.05.2009
comment
Это не всегда ВНУТРЕННЕЕ СОЕДИНЕНИЕ. Если все поля соединения допускают значение NULL, тогда это будет ЛЕВОЕ СОЕДИНЕНИЕ -- сомнительно, что это недокументированная неправильная функция, которую генерирует ВНУТРЕННЕЕ СОЕДИНЕНИЕ, даже когда поля соединения содержат некоторые (но не все) столбцы с нулевым значением. - person ; 13.01.2011

Установите для EnabledDefferedLoad в вашем классе datacontext значение false.

person vik sehdave    schedule 29.11.2009

Проблема в вашем случае - Take(10). Вот из уст коня:

https://connect.microsoft.com/VisualStudio/feedback/details/473333/linq-to-sql-loadoptions-ignored-when-using-take-in-the-query

Предлагаемый обходной путь — добавить Skip(0). У меня это не сработало, но Skip(1) сработал. Бесполезно, как это может быть, по крайней мере, я знаю, в чем моя проблема.

person DenNukem    schedule 06.02.2012