我有一个包含这些表的MS SQL数据库

Item { ItemId | SerialNumber | Type | Height | ... }

Review { ReviewId | Date | ... | ItemId }

其中Review.ItemId是外键,将每个 comments 链接到各自的项目.

For now, with EF Core 6 和 using LINQ (on .NET 6.0) I am fetching N rows (for pagination) of items with some optional search parameters. They are ordred by ItemId. For each item, I also add the last review date that I was getting from the table Review 和 set as a [NotMapped] property of the Item class.

public static List<Item> GetInstruments(Dictionary<string, int> searchParameters, QueryParameters queryParameters)
{
    List<Item> items = new();

    try
    {
        using (var dbContext = new DbContext())
        {
            var dataQuery = dbContext.Items.AsQueryable().AsEnumerable();

            if (searchParameters != null)
            {
                foreach (var searchParameter in searchParameters)
                {
                    if (searchParameter.Key == "SerialNumber")
                    {
                        dataQuery = dataQuery.Where(i => i.SerialNumber == searchParameter.Value);
                    }
                    if (searchParameter.Key == "Type")
                    {
                        dataQuery = dataQuery.Where(i => i.Type == searchParameter.Value);
                    }
                    if (searchParameter.Key == "Height")
                    {
                        dataQuery = dataQuery.Where(i => i.Height == searchParameter.Value);
                    }
                }
            }

            var offset = (queryParameters.PageNumber - 1) * queryParameters.RowsPerPage;
            if (queryParameters.OrderBy == null)
            {
                dataQuery = dataQuery.OrderByDescending(i => i.ItemId);
            }
            else
            {
                var porpertyInfo = typeof(Item).GetProperty(queryParameters.OrderBy);
                if (queryParameters.Ascending == true)
                {
                    dataQuery = dataQuery.AsEnumerable().OrderBy(i => porpertyInfo.GetValue(i, null));
                }
                else
                {
                    dataQuery = dataQuery.AsEnumerable().OrderByDescending(i => porpertyInfo.GetValue(i, null));
                }
            }
            dataQuery = dataQuery
                    .Skip(offset)
                    .Take(queryParameters.RowsPerPage);

            items = dataQuery.ToList();
            foreach (var instrument in items)
            {
                instrument.DateLastReview = dbContext.Reviews
                    .Where(c => c.ItemId == instrument.ItemId)
                    .Max(c => c.Date);
            }
        }
    }
    catch (Exception e)
    {
        ...
    }

    return items;
}

我之所以这样做,是因为我不想获取整个数据库,然后排序,而是排序然后提取(我不知道它是否清楚).希望我做得对.

在使用它之后,我发现按最后Review.Date个排序会更好,但同样,我希望排序然后提取,并且我仍然希望显示没有 comments 的项目(如使用LEFT JOIN或OUTER APPLY).

First I tried to do it in classic SQL 和 it worked when I tested it (btw if there is a better way to do the following in SQL I'll also take it)

SELECT i.ItemId, i.SerialNumber, i.Type, i.Height, r.Date
FROM Item i
OUTER APPLY
(
    SELECT Max(r.Date) as Date
    FROM Review r
    WHERE r.ItemId = i.ItemId
) r
ORDER BY r.Date DESC

But I'm stumped with EF 和 LINQ.

I tried first to just get the Items 和 the Review Date but I obviously doing it wrong. I know it's incomplete to do everything I want but I first wanted to just get the items 和 the LastReviewDate.

var dataQuery = from il in dbContext.Items
                select new
                {
                    il,
                    DateLastReview = dbContext.Reviews
                                     .Where(r => il.ItemId == r.ItemId)
                                     .Max(c => r.Date)
                };

So now I'm here because I don't want to just add a DateLastReview column to the Item table; I will if I don't find any solution or if you think it's better for performance 和 best practice but I didn't want to duplicate a field.

有没有人可以帮我解决这个问题,同时保持对数据进行排序/排序,然后获取数据的 idea .

推荐答案

不要这样做:

dbContext.Items.AsQueryable().AsEnumerable();

这将导致将所有内容从数据库提取到内存中(而不实际获取按相关实体排序所需的信息).我没有您的设置,但是使用"静态"查询可以非常简单地完成所需的排序,例如使用以下实体:

public class Blog
{
    public int Id { get; private set; }
    // ...
    public List<Post> Posts { get; } = new(); // do not forget the relationship setup
}

public class Post
{
    public int Id { get; private set; }
    // ...
    public DateTime PublishedOn { get; set; }
    public Blog Blog { get; set; } = null!; // do not forget the relationship
}

var blogs = ctx.Blogs
    .OrderByDescending(blog => blog.Posts.Max(post => post.PublishedOn))
    .ToList();

这将为我的设置生成以下SQL(Postgres):

SELECT b."Id", b."Name"
FROM "Blogs" AS b
ORDER BY (
   SELECT max(p."PublishedOn")
   FROM "Posts" AS p
   WHERE b."Id" = p."BlogId") DESC

至于动态查询生成,即删除AsEnumerable调用,以及反射内容(var porpertyInfo = typeof(Item).GetProperty(queryParameters.OrderBy);...),您需要使用一些切换用例来硬编码它,或者生成expression tree,或者使用一些第三方库,如System.Linq.Dynamic.CoreLINQKit.

另请参见this answer.

Csharp相关问答推荐

EF Core的GbContent如何 suppress 所有CS 8618(不可为空属性)警告?

C#中的多yield 机制

等待限制选项似乎不适用于频道

在WPF.NET 6中使用C++/WinRT组件(但实际上是任何WinRT组件)

CsWin32如何创建PWSTR的实例,例如GetWindowText

C++/C#HostFXR通过std::tuple传递参数

Azure Redis缓存与Entra ID身份验证

Blazor EventCallback<;MyType<;T>;>;

将现有字段映射到EFCore中的复杂类型

如何在WPF的树视图中显示一个对象的两个或多个属性,其中只有一个是分层项?

我的MRG32k3a算法实现返回的数字超出了预期的[0,1]范围

MudBlazor Textfield已禁用,但其验证工作正常

使用ASP.NET MVC for Lemon Squeezy X-Signature创建散列

为什么我的用户界面对象移动到略低于实际目标?

用于获取字符串的最后12个字符的正则表达式(空格除外)

数据库操作预计影响1行,但实际影响0行; after _dbContext.SaveChanges();

并发表更新.EF核心交易

为什么连接到Google OAuth2后,结果.Credential为空?

如何更改Datagridview行标题

HttpClient,上传文件时实现进度