Как заставить этот Entity Framework SQL Query работать правильно - PullRequest
0 голосов
/ 12 февраля 2020

В моей настройке у меня есть следующий запрос.

Класс:

public class NewsList
{
    public Guid NewsGuid { get; set; }
    public string Heading { get; set; }
    public string FileName { get; set; }
}

Контроллер:

List<NewsList> newsList = (from n in db.News
            join ni in db.NewsImages
            on n.NewsGuid equals ni.NewsGuid
            where (ni.FileOrder == 10 || ni.FileName == null) && n.NewsDate.Year == newsDate.Year && n.NewsDate.Month == newsDate.Month && n.NewsDate.Day == newsDate.Day
            orderby n.NewsDate descending
            select new NewsList
            {
                NewsGuid = n.NewsGuid,
                Heading = n.Heading,
                FileName = ni.FileName
            }).Take(10).ToList<NewsList>();

У меня есть две проблемы с этим.

1) Здесь я объединяю две таблицы: News и NewsImages и их отношение 1-N. Одна новость не может иметь ничего, одно или несколько изображений. С этим присоединением я не получаю новости без изображения, хотя я указал ni.FileName может быть нулевым. Он возвращает только новости, у которых есть хотя бы запись в таблице NewsImages. Я просто хочу, чтобы обычный тип левого соединения здесь. Как мне этого добиться?

2) Строка с утверждением WHERE выглядит ужасно, что мне нужно проверять год, месяц и день отдельно. NewsDate является полем DateTime в базе данных, а newsDate является форматом только даты (гггг-ММ-дд). Я просто хочу выбрать все новости на указанную дату c (строка запроса). Без EF я могу добиться этого с помощью стандартного T- SQL, например:

CAST(NewsDate AS date)='" + newsDate + "'"

Как лучше очистить линию WHERE?

Ответы [ 2 ]

3 голосов
/ 12 февраля 2020

Предполагая, что это EF Core, вы можете просто сравнить даты, используя n.NewsDate.Date == newsDate.Date. Если это предыдущая версия, вам нужно будет импортировать DbFunctions , и вы сможете использовать DbFunctions.TruncateTime(n.NewsDate) == DbFunctions.TruncateTime(newsDate)

Для выполнения левого внешнего соединения вам нужно будет сказать EF вернуть пустой список, используя DefaultIfEmpty().

List<NewsList> newsList = 
            (from n in db.News.Where(mn => DbFunctions.TruncateTime(mn.NewsDate) == DbFunctions.TruncateTime(newsDate))
                    join ni in db.NewsImages.Where(mni => mni.FileOrder == 10 || mni.FileName == null)
                        on n.NewsGuid equals ni.NewsGuid into nni
                    from sublist in nni.DefaultIfEmpty()
                    orderby n.NewsDate descending
                    select new NewsList
                    {
                        NewsGuid = n.NewsGuid,
                        Heading = n.Heading,
                        FileName = sublist.FileName
                    }).Take(10).ToList<NewsList>()
1 голос
/ 12 февраля 2020

Во-первых, здесь помогут свойства навигации. EF может отобразить отношения и составить SQL, вместо того, чтобы пытаться преобразовать LINQ в SQL.

Если отношения между News и NewsImage отображаются там, где News содержит:

Внутри класса News:

public virtual ICollection<NewsImage> NewsImages { get; set; } = new List<NewsImage>();

Внутри класса NewsImage:

public virtual News News { get; set; }

Если ваш NewsImage также содержит свойство NewsGuid, то это может быть связано к свойству навигации по новостям:

[ForeignKey("News")]
public Guid NewsGuid { get; set; }

Наконец, отображение. Это можно сделать с помощью определения EntityTypeMapping, которое EF может загрузить, чтобы понять взаимосвязь между сущностями, или с помощью события OnModelCreating и его modelBuilder в DbContext. С modelBuilder это будет выглядеть примерно так:

public override OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<News>().HasMany(x => x.NewsImages).WithRequired(x => x.News);
}

Запрос контроллера будет выглядеть примерно так:

newsDate = newsDate.Date;
var cutOff = newsDate.AddDays(1);
var news = db.News
    .Where(n => n.NewsDate >= newsDate && n.NewsDate < cutOff);

Это даст вам новости за указанную дату. Появляется вторая часть, в которой вы хотите отфильтровать только изображения для любых статей (если таковые имеются), которые соответствуют критериям, но по-прежнему отображать элемент списка новостей, если нет изображений.

var newsListItems = db.News
    .Where(n => n.NewsDate >= newsDate && n.NewsDate < cutOff)
    .SelectMany(n => n.NewsImages
        .Where(ni => ni.FileOrder == 10)
        .Select(ni => new NewsList
        {
            NewsGuid = n.NewsGuid,
            Heading = n.Heading,
            FileName = ni.FileName
        })).OrderByDescending(n => n.NewsDate)
       .Take(10).ToList();

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

Чтобы включить Новости без изображений:

var newsListItems = db.News
    .Where(n => n.NewsDate >= newsDate && n.NewsDate < cutOff)
    .SelectMany(n => n.NewsImages
        .Where(ni => ni.FileOrder == 10)
        .Select(ni => new NewsList
        {
            NewsGuid = n.NewsGuid,
            Heading = n.Heading,
            FileName = ni.FileName
        }))
     .Union( db.News
          .Where(n => n.NewsDate >= newsDate && n.NewsDate < cutOff 
              && !n.NewsImages.Any(ni => ni.FileOrder == 10)
          .Select(n => new NewsList
          {
              NewsGuild = n.NewsGuid,
              Heading = n.Heading
          }))
     .OrderByDescending(n => n.NewsDate)
     .Take(10).ToList();

... И я считаю, что это должно вернуть вам то, что вы ожидаете ... Список новостей с или без изображений для день, упорядоченный по дате / времени. Отказ от ответственности, что это написано из памяти и может иметь некоторые ошибки в Linq, но это должно быть довольно близко.

  • Редактировать: я не могу не думать, что это может быть сделано без Union, хотя ...:)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...