【问题标题】:How get this Entity Framework SQL Query to run properly如何让这个实体框架 SQL 查询正常运行
【发布时间】:2020-02-12 11:01:21
【问题描述】:

在我的设置中,我有以下查询。

类:

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 可以为空,我也不会得到没有图像的新闻。它只返回 News,它在 NewsImages 表中至少有一条记录。 我只想要一个常规类型的 LEFT JOIN 这里。我如何做到这一点?

2) 带有 WHERE 语句的行看起来很糟糕,我需要分别检查年、月和日。 NewsDate 是数据库上的 DateTime 字段,newsDate 是仅日期格式 (yyyy-MM-dd)。我只想从特定日期(查询字符串)中选择所有新闻。 如果没有 EF,我可以像这样使用标准 T-SQL 实现这一点:

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

如何更好地清理 WHERE 线?

【问题讨论】:

  • n.NewsDate.Date == newsDate.Date 在 where 子句中是否适合您的需要?
  • @JDDavis 我试过了。它确实引发了错误“LINQ to Entities 中不支持指定类型成员 'Date'。仅支持初始化程序、实体成员和实体导航属性。”
  • 这是什么版本的EF?核心还是 6?
  • 其带有 EF 6 的普通 ASP.NET MVC 项目。运行时版本:v4.0.30319 和版本 6.0.0.0

标签: c# asp.net-mvc entity-framework


【解决方案1】:

假设这是 EF Core,您应该能够直接使用 n.NewsDate.Date == newsDate.Date 简单地比较日期。如果这是以前的版本,你需要导入DbFunctions,你就可以使用DbFunctions.TruncateTime(n.NewsDate) == DbFunctions.TruncateTime(newsDate)

要执行left outer join,您需要告诉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>()

【讨论】:

  • 从 nni.DefaultIfEmpty() 的子列表中抛出错误
  • 现在 nni.DefaultIfEmpty() ok.. 但是下一行抛出错误.. 它说名称 ni 在当前上下文中不存在。它会为 ni.FileOrder 和 ni.FileName 引发该错误
  • @user2160310 现在怎么样?
  • DbFunctions.TruncateTime 已消失,并且在所选日期不返回任何记录。我已经添加了这一行:from n in db.News.Where(nd => DbFunctions.TruncateTime(nd.NewsDate) == DbFunctions.TruncateTime(newsDate) 并且它现在正在工作。谢谢!
【解决方案2】:

首先,导航属性在这里会有所帮助。 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 属性,那么这可能与 News 导航属性相关:

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

最后是映射。这可以使用 EF 可以加载的 EntityTypeMapping 定义来理解实体之间的关系,或者通过 OnModelCreating 事件和它在 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 的情况下完成... :)

【讨论】:

  • 我在 DbContext 中有以下几行: public virtual DbSet News { get;放; } 公共虚拟 DbSet NewsImages { 获取;放;但是,n.NewsImages 会抛出一个错误,说 News 不包含 NewsImages 的定义
  • 是的,这将涉及在实体中设置导航属性。我将在答案中对此进行扩展。
  • 我是 EF 的新手,这让很多人感到困惑。与此相比,我的常规 T-SQL 看起来更加美观和紧凑。
猜你喜欢
  • 1970-01-01
  • 2021-03-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多