Linq To Sql - вернуть результат таблицы и количество - PullRequest
0 голосов
/ 22 декабря 2011

Я очень плохо знаком с linq to sql и нуждаюсь в небольшой помощи.

В основном я создаю доску объявлений на C #. У меня есть 3 таблицы базы данных - основная информация выглядит следующим образом.

ФОРУМЫ Идентификационный код на форуме Имя

НИТИ ThreadId Идентификационный код на форуме заглавие * 1007 Идентификатор_пользователя *

ПОСТЫ сообщения дан ThreadId текст Идентификатор пользователя дата

По сути, я хочу вернуть все, что мне нужно, в одном запросе. Я хочу отобразить страницу THREADS (для определенного ФОРУМА), а также отобразить количество POSTS в этой строке THREAD и время, когда последний POST был для этой THREAD.

В настоящий момент я возвращаю все НИТИ, а затем перебираю каждый набор результатов и делаю отдельные вызовы таблицы POST для подсчета POST для потока и последнего сообщения в этой теме, но, очевидно, это вызовет проблемы сроки попадания в базу данных по мере увеличения доски объявлений.

My Linq To SQL на данный момент:

    public IList<Thread> ListAll(int forumid)
    {
        var threads =
            from t in db.Threads
            where t.forumid == forumid
            select t;
        return threads.ToList();
    }

В основном мне нужно получить количество сообщений в каждой теме и дату последнего сообщения в каждой теме.

Любая помощь будет наиболее ценится:)

EDIT

Привет, ребята. Спасибо за вашу помощь до сих пор. В основном я почти там. Тем не менее, я оставил важную часть моего первоначального вопроса в том факте, что мне нужно получить имя пользователя человека, который сделал последний POST. Поэтому мне нужно объединить p.userid с u.userid в таблице USERS. Пока у меня есть следующее, но мне просто нужно изменить это, чтобы соединить таблицу POST с таблицей USER:

    public IList<ThreadWithPostInfo> ListAll(int forumid)
    {
        var threads = (from t in db.Threads
                                   where t.forumid == forumid
                                   join p in db.Posts on t.threadid equals p.threadid into j
                       select new ThreadWithPostInfo() { thread = t, noReplies = j.Count(), lastUpdate = j.Max(post => post.date) }).ToList();
        return threads;
    }

UPDATE:

    public IList<ThreadWithPostInfo> ListAll(int forumid)
    {
        var threads = (from t in db.Threads
                       from u in db.Users
                       where t.forumid == forumid && t.hide == "No" && t.userid == u.userid
                                   join p in db.Posts on t.threadid equals p.threadid into j
                       select new ThreadWithPostInfo() { thread = t, deactivated = u.deactivated, lastPostersName = j.OrderByDescending(post => post.date).FirstOrDefault().User.username, noReplies = j.Count(), lastUpdate = j.Max(post => post.date) }).ToList();
        return threads;
    }

Я наконец понял эту часть благодаря всем вам, ребята :). Моя единственная проблема сейчас - это метод поиска результатов. На данный момент это так:

    public IList<Thread> SearchThreads(string text, int forumid)
    {
        var searchResults = (from t in db.Threads
                            from p in db.Posts
                            where (t.title.Contains(text) || p.text.Contains(text)) && t.hide == "No"
                            && p.threadid == t.threadid
                            && t.forumid == forumid
                            select t).Distinct();
        return searchResults.ToList();
    }

Обратите внимание, что мне нужно получить выражение where в новый код linq:

        where (t.title.Contains(text) || p.text.Contains(text)) && t.hide == "No"

, поэтому включение этого пункта в новый метод linq. Любая помощь с благодарностью получена:)

РЕШЕНИЕ:

Я нашел решение, но не знаю, является ли оно лучшим или наиболее эффективным. Может быть, вы, ребята, можете сказать мне, потому что я все еще думаю о linq. Джеймс, я думаю, твой ответ был самым близким и привел меня туда, где я хотел быть - спасибо:)

   public IList<ThreadWithPostInfo> SearchThreads(string text, int forumid)
    {
        var searchResults = (from t in db.Threads
                             from p in db.Posts
                             where (t.title.Contains(text) || p.text.Contains(text)) && t.hide == "No"
                             && p.threadid == t.threadid
                             && t.forumid == forumid
                             select t).Distinct();

        //return searchResults.ToList();           

        var threads = (from t in searchResults 
                            join p in db.Posts on t.threadid equals p.threadid into j
                       select new ThreadWithPostInfo() { thread = t,  lastPostersName = j.OrderByDescending(post => post.date).FirstOrDefault().User.username, noReplies = j.Count(), lastUpdate = j.Max(post => post.date) }).ToList();
        return threads;
    }

Ответы [ 4 ]

1 голос
/ 22 декабря 2011

Может быть слишком много вызовов базы данных за сеанс ....

Вызов базы данных. запросить или написать, это удаленный вызов, и мы хотим максимально уменьшить количество удаленных вызовов. Это предупреждение появляется, когда профилировщик замечает, что за один сеанс выполняется слишком много обращений к базе данных. Обычно это указывает на потенциальную оптимизацию использования сеанса.

Существует несколько причин, по которым это может быть:

  • Большое количество запросов в результате выбора N + 1
  • Вызов базы данных в цикле
  • Обновление (или вставка / удаление) большого количества объектов
  • Большое количество (разных) запросов, которые мы выполняем для выполнения нашей задачи

По первой причине вы можете увидеть предложения для выбора N + 1. Выбор N + 1 - это шаблон доступа к данным, при котором доступ к базе данных осуществляется неоптимальным образом. Взгляните на этот пример кода:

    // SELECT * FROM Posts
var postsQuery = from post in blogDataContext.Posts
                 select post;

foreach (Post post in postsQuery)
{   
    //lazy loading of comments list causes:    
    // SELECT * FROM Comments where PostId = @p0   
    foreach (Comment comment in post.Comments)
    {       
        //print comment...   
    }
}

В этом примере мы видим, что мы загружаем список постов (первый выбор), а затем пересекаем граф объектов. Однако мы лениво обращаемся к коллекции, заставляя Linq to Sql переходить в базу данных и возвращать результаты по одной строке за раз. Это невероятно неэффективно, и Linq to Sql Profiler будет генерировать предупреждение всякий раз, когда он сталкивается с таким случаем.

Решение для этого примера простое. Принудительная загрузка коллекции с помощью класса DataLoadOptions, чтобы указать, какие части объектной модели мы хотим загрузить заранее.

var loadOptions = new DataLoadOptions();
loadOptions.LoadWith<Post>(p => p.Comments);
blogDataContext.LoadOptions = loadOptions;

// SELECT * FROM Posts JOIN Comments ...
var postsQuery = (from post in blogDataContext.Posts
                 select post);

foreach (Post post in postsQuery)
{   
    // no lazy loading of comments list causes    
    foreach (Comment comment in post.Comments)
    {       
        //print comment...   
    }
}

Далее об обновлении большого количества объектов обсуждается в разделе «Использование инструкции оператора», и его можно достичь с помощью проекта PLinqO , который представляет собой набор расширений поверх Linq к Sql. Как здорово было бы хранить элементы в кеше как группу. Ну, угадай что! PLINQO это круто! При хранении элементов в кэше просто скажите PLINQO, что результат запроса должен принадлежать группе, и укажите имя. Недействительный кеш - вот где действительно проявляется классность группировки. Никакой связи кеша и действий, выполняемых с этим кешем, когда они находятся в группе. Проверьте этот пример:

public ActionResult MyTasks(int userId)
{
    // will be separate cache for each user id, group all with name MyTasks
    var tasks = db.Task
        .ByAssignedId(userId)
        .ByStatus(Status.InProgress)
        .FromCache(CacheManager.GetProfile().WithGroup("MyTasks"));

    return View(tasks);
}

public ActionResult UpdateTask(Task task)
{
    db.Task.Attach(task, true);
    db.SubmitChanges();

    // since we made an update to the tasks table, we expire the MyTasks cache
    CacheManager.InvalidateGroup("MyTasks");
}

PLinqO поддерживает понятие пакетирования запросов с использованием функции, называемой фьючерсами, которая позволяет вам принимать несколько разных запросов и отправлять их в базу данных за один удаленный вызов. Это может значительно сократить количество выполняемых удаленных вызовов и значительно повысить производительность вашего приложения.

cmiiw ^ _ ^

1 голос
/ 22 декабря 2011
   public IList<Thread> ListAll(int forumid)
    {
        var threads =
            from t in db.Threads
            where t.forumid == forumid
            select new 
                    {
                      Thread = t,
                      Count = t.Post.Count,
                      Latest = t.Post.OrderByDescending(p=>p.Date).Select(p=>p.Date).FirstOrDefault()
                    }
    }

Должно быть что-то подобное

0 голосов
/ 22 декабря 2011

Я думаю, что вы действительно ищете это:

        var threadsWithPostStats = from t in db.Threads
                                   where t.forumid == forumid
                                   join p in db.Posts on t.threadid equals p.threadid into j
                                   select new { Thread = t, PostCount = j.Count(), LatestPost = j.Max(post => post.date) };

За ваш комментарий и обновленный вопрос я добавляю следующее заявление:

       var threadsWithPostsUsers = from t in db.Threads
                                   where t.forumid == forumid
                                   join p in db.Posts on t.threadid equals p.threadid into threadPosts
                                   let latestPostDate = threadPosts.Max(post => post.date)
                                   join post in db.Posts on new { ThreadID = t.threadid, PostDate = latestPostDate } equals new { ThreadID = post.threadid, PostDate = post.date} into latestThreadPosts
                                   let latestThreadPost = latestThreadPosts.First()
                                   join u in db.Users on latestThreadPost.userid equals u.userid
                                   select new { Thread = t, LatestPost = latestThreadPost, User = u  };
0 голосов
/ 22 декабря 2011

Не мешало бы познакомиться с group by в LINQ и агрегатами (Max, Min, Count).

Примерно так:

var forums = (from t in db.Threads 
      group t by t.forumid into g
      select new { forumid = g.Key, MaxDate = g.Max(d => d.ForumCreateDate) }).ToList();

Также ознакомьтесь с этой статьей длякак считать элементы в запросе LINQ с группой по:
LINQ to SQL с использованием GROUP BY и COUNT (DISTINCT)

Агрегаты LINQ: Агрегат LINQ с субагрегатами

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...