如何有效处理Where和OrderBy子句

发布于 2024-10-24 16:51:27 字数 436 浏览 2 评论 0原文

我的业务层将所有必需的信息传递给 UI 层。据我所知,一般来说,最佳实践是将获取的数据发送到 UI 层,并避免传递像 ObjectQuery 这样的查询。我对这种方法的问题是:

如果我要创建一个灵活的业务层,那么我应该允许 UI 以任何需要的方式对数据进行排序。从数据库中获取排序后的数据,然后在 UI 中对它们进行排序对我来说是一种不好的做法,所以唯一的方法就是以某种方式

那么我的选择是什么?有没有办法让它像这样:

public void OrderByMethod(params ...) { .... }

所以我可以这样称呼它:

OrderByMethod(MyEntity.Property1, MyEntity.Property2 descending....);

谢谢, 戈兰

My business layer passes all the required information to UI layer. From what I have read, in general, best practice is to send fetched data to UI layer, and to avoid passing queries like ObjectQuery. My problem with this approach is next:

If I am to make a flexible business layer, then I should allow UI to sort the data anyway it requires. Fetching sorted data from database, and then resorting them in UI is kind of bad practice for me, so the only way is to somehow

So what are my options? Is there a way to make it like this:

public void OrderByMethod(params ...) { .... }

so I can call it like this:

OrderByMethod(MyEntity.Property1, MyEntity.Property2 descending....);

Thanks,
Goran

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

枕梦 2024-10-31 16:51:28

如果您不直接向 UI 公开 IQueryable (您可能不会这样做,因为您在 UI 和数据访问层之间使用业务层),您可以使用传递“列表信息”的自定义对象。它可以看起来像:

public class ListOptions<T>
{
    // Paging
    public int Page { get; set; }
    public int PageSize { get; set; }
    // Sorting
    public IList<SortOptions<T>> SortOptions { get; set; }
}

public class SortOptions<T>
{
    public Expression<Func<T, object>> SortProperty { get; set; }
    public bool IsDescending { get; set; }
}

您将使用它作为返回数据的业务方法的参数,并且在业务方法内部您将使用自定义扩展来与 EF 或存储库提供的 IQueryable 一起使用:

public static class QueryableExtensions
{
    public static IQueryable<T> ApplyListOptions<T>(this IQueryable<T> query, ListOptions<T> options)
    {
        if (options != null && options.SortOptions.Count > 0)
        {
            IOrderedQueryable<T> orderedQuery = query.ApplyOrderBy(options.SortOptions[0]);

            for (int i = 1; i < options.SortOptions.Count; i++)
            {
                orderedQuery = orderedQuery.ApplyThenBy(options.SortOptions[i]);
            }

            query = orderedQuery.ApplyPaging(options.Page, options.PageSize);
        }

        return query;
    }

    public static IOrderedQueryable<T> ApplyOrderBy<T>(this IQueryable<T> query, SortOptions<T> sortOption)
    {
        if (sortOption.IsDescending)
        {
            return query.OrderByDescending(sortOption.SortProperty);
        }

        return query.OrderBy(sortOption.SortProperty);
    }

    public static IOrderedQueryable<T> ApplyThenBy<T>(this IOrderedQueryable<T> query, SortOptions<T> sortOption)
    {
        if (sortOption.IsDescending)
        {
            return query.ThenByDescending(sortOption.SortProperty);
        }

        return query.ThenBy(sortOption.SortProperty);
    }

    public static IQueryable<T> ApplyPaging<T>(this IOrderedQueryable<T> query, int page, int pageSize)
    {
        if (pageSize > 0)
        {
            return query.Skip((page - 1)*pageSize).Take(pageSize);
        }

        return query;
    }
}

因此您的处理方法可以如下所示:

public IEnumerable<User> GetUsers(ListOptions<User> listOptions)
{
    return _context.Users.ApplyListOptioins(listOptions).AsEnumerable();
}

您将调用该方法,如下所示:

var options = new ListOptions<User>
    {
        Page = 2,
        PageSize = 3,
        SortOptions = new List<SortOptions<User>>
            {
                new SortOptions<User> 
                    { 
                        IsDescending = false, 
                        SortProperty = u => u.LastName 
                    },
                new SortOptions<User> 
                    { 
                        IsDescending = true, 
                        SortProperty = u => u.FirstName 
                    }
            }
    };


var data = usersService.GetUsers(options);        

If you do not expose IQueryable directly to UI (you probably don't because you are using business layer between UI and data access layer) you can use custom object passing "list information". It can look like:

public class ListOptions<T>
{
    // Paging
    public int Page { get; set; }
    public int PageSize { get; set; }
    // Sorting
    public IList<SortOptions<T>> SortOptions { get; set; }
}

public class SortOptions<T>
{
    public Expression<Func<T, object>> SortProperty { get; set; }
    public bool IsDescending { get; set; }
}

You will use this as parameter of your business method returning data and inside business method you will use custom extensions to work with IQueryable provided by EF or repository:

public static class QueryableExtensions
{
    public static IQueryable<T> ApplyListOptions<T>(this IQueryable<T> query, ListOptions<T> options)
    {
        if (options != null && options.SortOptions.Count > 0)
        {
            IOrderedQueryable<T> orderedQuery = query.ApplyOrderBy(options.SortOptions[0]);

            for (int i = 1; i < options.SortOptions.Count; i++)
            {
                orderedQuery = orderedQuery.ApplyThenBy(options.SortOptions[i]);
            }

            query = orderedQuery.ApplyPaging(options.Page, options.PageSize);
        }

        return query;
    }

    public static IOrderedQueryable<T> ApplyOrderBy<T>(this IQueryable<T> query, SortOptions<T> sortOption)
    {
        if (sortOption.IsDescending)
        {
            return query.OrderByDescending(sortOption.SortProperty);
        }

        return query.OrderBy(sortOption.SortProperty);
    }

    public static IOrderedQueryable<T> ApplyThenBy<T>(this IOrderedQueryable<T> query, SortOptions<T> sortOption)
    {
        if (sortOption.IsDescending)
        {
            return query.ThenByDescending(sortOption.SortProperty);
        }

        return query.ThenBy(sortOption.SortProperty);
    }

    public static IQueryable<T> ApplyPaging<T>(this IOrderedQueryable<T> query, int page, int pageSize)
    {
        if (pageSize > 0)
        {
            return query.Skip((page - 1)*pageSize).Take(pageSize);
        }

        return query;
    }
}

So your processing method can look like:

public IEnumerable<User> GetUsers(ListOptions<User> listOptions)
{
    return _context.Users.ApplyListOptioins(listOptions).AsEnumerable();
}

And you will call the method like:

var options = new ListOptions<User>
    {
        Page = 2,
        PageSize = 3,
        SortOptions = new List<SortOptions<User>>
            {
                new SortOptions<User> 
                    { 
                        IsDescending = false, 
                        SortProperty = u => u.LastName 
                    },
                new SortOptions<User> 
                    { 
                        IsDescending = true, 
                        SortProperty = u => u.FirstName 
                    }
            }
    };


var data = usersService.GetUsers(options);        
殤城〤 2024-10-31 16:51:28

数据如何从一层传递到另一层?听起来好像您正在使用简单的存储库模式,对吗?如果是这样,存储库方法通常返回什么?

对于数据层和业务层(假设没有硬分离,例如 Web 服务层)来说,以 IEnumerableIQueryable的形式传递数据通常是一种很好的做法。 T>。这样,当数据到达 UI 时,UI 代码很容易根据需要继续操作数据(排序、分页等)。

事实上,如果实际数据在到达 UI 之前没有被枚举,那么在此之前它甚至不应该发出数据库请求。这允许将整个结果集的 IEnumerable 逻辑地传递给 UI,然后让 UI 确定它只需要记录的第一“页”(通过调用 >.Take() 并仅抓取前 10 个,例如)。这样,您就可以在整个应用程序的许多不同位置使用一个非常简单的存储库方法,从而减少向数据层注入的业务和显示逻辑。

How is the data being passed from one layer to another? It sort of sounds like you're using a simple repository pattern, is that correct? If so, what do the repository methods generally return?

It's often good practice for the data layer and business layer (assuming no hard separations, such as a web service layer) to pass data along in the form of an IEnumerable<T> or IQueryable<T>. That way when the data gets to the UI it's very easy for the UI code to continue to manipulate the data as needed (sorting, paging, etc.).

In fact, if the actual data isn't enumerated until it gets to the UI, then it shouldn't even make the database request until that time. This allows for such things as passing an IEnumerable<T> of an entire result set logically to the UI and then having the UI determine that it needs only the first "page" of records (by calling .Take() and grabbing only the first 10, for example). That way you can use a single very simple repository method in lots of different places throughout the application, bleeding less business and display logic into the data layer.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文