如何修改GetAll函数以在获取所有数据时应用分页?

huangapple go评论54阅读模式
英文:

How to modify GetAll function to apply pagination when get all data?

问题

I working on blazor web application with .NET core 7 . I face Issue I can't implement Pagination when get all data from server Names model .

意思是我正在使用.NET Core 7开发Blazor Web应用程序,我遇到了一个问题,无法在从服务器获取Names模型的所有数据时实现分页。

meaning I need to apply pagination when get all data with server side .

这意味着我需要在从服务器端获取所有数据时应用分页。

with code below I can get all data from server name joining with server type without any issue .

使用下面的代码,我可以从服务器获取与服务器类型关联的所有数据,没有任何问题。

How to modify GetAll function to apply pagination when get all data?

如何修改GetAll函数以在获取所有数据时应用分页?

what I try as below :

我尝试如下:

public static class PagedResultExtensions
{
    public static PagedResult<T> GetPaged<T>(this IQueryable<T> query, int page, int pageSize) where T : class
    {
        var result = new PagedResult<T>();
        result.CurrentPage = page;
        result.PageSize = pageSize;
        result.RowCount = query.Count();

        var pageCount = (double)result.RowCount / pageSize;
        result.PageCount = (int)Math.Ceiling(pageCount);

        var skip = (page - 1) * pageSize;
        result.Results = query.Skip(skip).Take(pageSize).ToList();

        return result;
    }
}

public abstract class PagedResultBase
{
    public int CurrentPage { get; set; }
    public int PageCount { get set; }
    public int PageSize { get; set; }
    public int RowCount { get; set; }

    public int FirstRowOnPage
    {
        get { return (CurrentPage - 1) * PageSize + 1; }
    }

    public int LastRowOnPage
    {
        get { return Math.Min(CurrentPage * PageSize, RowCount); }
    }
}

public class PagedResult<T> : PagedResultBase where T : class
{
    public IList<T> Results { get; set; }

    public PagedResult()
    {
        Results = new List<T>();
    }
}

Generic Base repository

public class BaseRepository<TEntity> : IRepository<TEntity> where TEntity : class
{
    internal AppsRepositoryDBContext _context;
    internal DbSet<TEntity> dbSet;

    public BaseRepository(AppsRepositoryDBContext context)
    {
        _context = context;
        this.dbSet = _context.Set<TEntity>();
    }

    public IEnumerable<TEntity> GetAll() => _context.Set<TEntity>().ToList();
}

on service

public class ServerNameService : BaseRepository<ServerNames>, IserverNamesService
{
    private readonly AppsRepositoryDBContext _context;
    public ServerNameService(AppsRepositoryDBContext context) : base(context)
    {
        _context = context;
    }
    public IEnumerable<ServerNames> IserverNamesService.GetAll()
    {
        return _context.ServerNames
           .Join(_context.ServerTypes,
           sn => sn.ServerTypeId,
           st => st.ServerTypeId,
           (sn, st) => new ServerNames
           {
               ServerID = sn.ServerID,
               server_Name = sn.server_Name,
               ServerType = st.ServerType
           }).ToList();
    }
}

on controller server names :

private readonly IserverNamesService _IserverNamesService;
public ServerNamesController(IserverNamesService IserverName)
{
    _IserverNamesService = IserverName;
}
[HttpGet]
public IActionResult GetAll()
{
    return Ok(_IserverNamesService.GetAll());
}

以上是代码示例,用于获取与服务器类型关联的所有数据。如果要在GetAll函数中应用分页,你可以使用PagedResultExtensions中的GetPaged方法。将其用于_IserverNamesService.GetAll()的结果,以实现分页。

英文:

I working on blazor web application with .NET core 7 . I face Issue I can't implement Pagination when get all data from server Names model .

meaning I need to apply pagination when get all data with server side .

with code below I can get all data from server name joining with server type without any issue .

How to modify GetAll function to apply pagination when get all data?

what I try as below :

    public static class PagedResultExtensions
    {
        public static PagedResult&lt;T&gt; GetPaged&lt;T&gt;(this IQueryable&lt;T&gt; query, int page, int pageSize) where T : class
        {
            var result = new PagedResult&lt;T&gt;();
            result.CurrentPage = page;
            result.PageSize = pageSize;
            result.RowCount = query.Count();

            var pageCount = (double)result.RowCount / pageSize;
            result.PageCount = (int)Math.Ceiling(pageCount);

            var skip = (page - 1) * pageSize;
            result.Results = query.Skip(skip).Take(pageSize).ToList();

            return result;
        }
    }
}
 public abstract class PagedResultBase
    {
        public int CurrentPage { get; set; }
        public int PageCount { get; set; }
        public int PageSize { get; set; }
        public int RowCount { get; set; }

        public int FirstRowOnPage
        {
            get { return (CurrentPage - 1) * PageSize + 1; }
        }

        public int LastRowOnPage
        {
            get { return Math.Min(CurrentPage * PageSize, RowCount); }
        }
    }
public class PagedResult&lt;T&gt; : PagedResultBase where T : class
        {
            public IList&lt;T&gt; Results { get; set; }
    
            public PagedResult()
            {
                Results = new List&lt;T&gt;();
            }
        }

Generic Base repository

public class BaseRepository&lt;TEntity&gt; : IRepository&lt;TEntity&gt; where TEntity : class
{
    internal AppsRepositoryDBContext _context;
    internal DbSet&lt;TEntity&gt; dbSet;

    public BaseRepository(AppsRepositoryDBContext context)
    {
        _context = context;
        this.dbSet = _context.Set&lt;TEntity&gt;();
    }


    public IEnumerable&lt;TEntity&gt; GetAll() =&gt; _context.Set&lt;TEntity&gt;().ToList();
  
   
}
public interface IRepository&lt;TEntity&gt; where TEntity : class
{
    IEnumerable&lt;TEntity&gt; GetAll();
}

on service

 public class ServerNameService : BaseRepository&lt;ServerNames&gt;, IserverNamesService
    {
        private readonly AppsRepositoryDBContext _context;
        public ServerNameService(AppsRepositoryDBContext context) : base(context)
        {
            _context = context;
        }
 public IEnumerable&lt;ServerNames&gt; IserverNamesService.GetAll()
        {
            return _context.ServerNames
               .Join(_context.ServerTypes,
               sn =&gt; sn.ServerTypeId,
               st =&gt; st.ServerTypeId,
               (sn, st) =&gt; new ServerNames
               {
                   ServerID = sn.ServerID,
                   server_Name = sn.server_Name,
                   ServerType = st.ServerType
               }).ToList();
        }
      }
public interface IserverNamesService : IRepository&lt;ServerNames&gt;
    {
        IEnumerable&lt;ServerNames&gt; GetAll();
       
    }

on controller server names :

private readonly IserverNamesService _IserverNamesService;
        public  ServerNamesController(IserverNamesService IserverName)
        {
        _IserverNamesService = IserverName;
        }
        [HttpGet]
        public IActionResult GetAll()
        {
            return Ok(_IserverNamesService.GetAll());
            
        }

答案1

得分: 1

以下是翻译好的部分:

"In any data request, making an unrestrained query i.e. GetAll is not a good idea. I know you see it all the time, the classic repository pattern does it. Consider the edge conditions: will your App gracefully handle a list of a million rows. Unlikely, but something goes wrong in an update process...? If you don't code for edge conditions they will bite you!

在任何数据请求中,进行不受限制的查询,即 GetAll,不是一个好主意。我知道你经常看到这种情况,经典的存储库模式也这样做。考虑边缘情况:你的应用程序是否能够优雅地处理包含一百万行的列表。可能性不大,但如果在更新过程中出现问题呢...?如果你不为边缘情况编写代码,它们会让你吃大亏!"

"First you need to define:

首先,你需要定义:

  1. A request to pass into your data pipeline
  2. 一个要传递到数据管道中的请求
  3. A result that you get back.
  4. 一个你获得的结果。"

"If you don't want to page, set the PageSize in the request big enough to handle what you consider to be a maximum number of records.

如果你不想分页,就在请求中将 PageSize 设置得足够大,以处理你认为是最大记录数量。"

"Here's the request.

以下是请求的内容。

public sealed record ListQueryRequest
{
    public int StartIndex { get; init; } = 0;
    public int PageSize { get; init; } = 1000;  //set at some maximum epected count for no paging
    public CancellationToken Cancellation { get; set; } = new();
}
```"

"And the result. Note it contains both the data and status information.

还有结果。请注意,它包含了数据和状态信息。

```csharp
public sealed record ListQueryResult&lt;TRecord&gt;
{
    public IEnumerable&lt;TRecord&gt; Items { get; init;} = Enumerable.Empty&lt;TRecord&gt();  
    public bool Successful { get; init; }
    public string Message { get; init; } = string.Empty;
    public int TotalCount { get; init; }

    private ListQueryResult() { }

    public static ListQueryResult&lt;TRecord&gt; Success(IEnumerable&lt;TRecord&gt; Items, int totalCount, string? message = null)
        =&gt; new ListQueryResult&lt;TRecord&gt; {Successful=true,  Items= Items, TotalCount = totalCount, Message= message ?? string.Empty };

    public static ListQueryResult&lt;TRecord&gt; Failure(string message)
        =&gt; new ListQueryResult&lt;TRecord&gt; { Message = message};
}
```"

"You need to switch to using a DbContextFactory rather than a single reusable context. See this MS Docs article on how to implement it - https://learn.microsoft.com/en-us/ef/core/dbcontext-configuration/#using-a-dbcontext-factory-eg-for-blazor.

你需要切换到使用 DbContextFactory 而不是单个可重用的上下文。参考这篇 MS Docs 文章来实现它 - https://learn.microsoft.com/en-us/ef/core/dbcontext-configuration/#using-a-dbcontext-factory-eg-for-blazor。"

"Your repository pattern code then looks like this:

然后,你的存储库模式代码看起来像这样:

```csharp
public class BaseRepository&lt;TEntity&gt; : IRepository&lt;TEntity&gt; where TEntity : class
{
     private readonly IDbContextFactory&lt;TDbContext&gt; _factory;

    public BaseRepository(IDbContextFactory&lt;TDbContext&gt; factory)
    {
        _factory = factory;
    }

    public async ValueTask&lt;ListQueryResult&lt;TEntity&gt;&gt; GetItemsAsync(ListQueryRequest)
    {
        using var dbContext = _factory.CreateDbContext();

        // switch off tracking for request only querying (no updates)
        dbContext.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;

        // Build your query before you actually execute/materialize it
        IQueryable&lt;TEntity&gt; query = dbContext.Set&lt;TEntity&gt;();

        // Can do sorting or filtering here on the IQueryable 

        // Do a materialization to get the total count.
        var count = query.CountAsync(request.Cancellation);

        // apply paging to restrict the rows returned 
        query = query
            .Skip(request.StartIndex)
            .Take(request.PageSize);

        // Materialize the query into a list which you return as an IEnumerable
        var list await query.ToListAsync();

        // missing error checking code

        return ListQueryResult&lt;TEntity&gt;.Success(list, count);
}

public interface IRepository&lt;TEntity&gt; where TEntity : class
{
    public ValueTask&lt;ListQueryResult&lt;TEntity&gt;&gt; GetItemsAsync(ListQueryRequest);
}
```"

"Your actual code will probably look like this (I don't know your model so there's a certain amount of guesswork)

你的实际代码可能会像这样(我不知道你的模型,所以有一定的猜测)"

" The final bit is how to handle this in a API call. I use a Post, which breaks the traditionalists rule that Posts are for sending data updates.

最后一部分是如何在 API 调用中处理这个问题。我使用了一个 Post 请求,这违反了传统观念,即 Post 用于发送数据更新。"

"My (generic) handler looks like this. Note it uses an HttpClientFactory in a similar way to the DbContextFactory above.

我的(通用的)处理程序看起来像这样。请注意,它以与上面的 DbContextFactory 类似的方式使用了一个 HttpClientFactory"

```csharp
public sealed class ListRequestAPIHandler
    : IListRequestHandler
{
    private IHttpClientFactory _factory;

    public ListRequestAPIHandler(IHttpClientFactory factory)
        =&gt; _factory = factory;

    public async ValueTask&lt;ListQueryResult&lt;TRecord&gt;&gt; ExecuteAsync&lt;TRecord&gt;(ListQueryRequest request)
        where TRecord : class, new()
    {
        ListQueryResult&lt;TRecord&gt;? result = null;

        var entityname = (new TRecord()).GetType().Name;

        var httpClient = _factory.CreateClient();
        var response = await httpClient.PostAsJsonAsync&lt;ListQueryRequest&gt;($&quot;/api/{entityname}/listquery&quot;, request, request.Cancellation);

        if (response.IsSuccessStatusCode)
            result = await response.Content.ReadFromJsonAsync&lt;ListQueryResult&lt;TRecord&gt;&gt;();

        return

<details>
<summary>英文:</summary>

In any data request, making an unrestrained query i.e. `GetAll` is not a good idea. I know you see it all the time, the classic repository pattern does it. Consider the edge conditions: will your App gracefully handle a list of a million rows. Unlikely, but something goes wrong in an update process...?  If you don&#39;t code for edge conditions they will bite you!

First you need to define:

1. A request to pass into your data pipeline 
2. A result that you get back.  

If you don&#39;t want to page, set the PageSize in the request big enough to handle what you consider to be a maximum number of records.

Here&#39;s the request.

```csharp
public sealed record ListQueryRequest
{
    public int StartIndex { get; init; } = 0;
    public int PageSize { get; init; } = 1000;  //set at some maximum epected count for no paging
    public CancellationToken Cancellation { get; set; } = new();
}

And the result. Note it contains both the data and status information.

public sealed record ListQueryResult&lt;TRecord&gt;
{
    public IEnumerable&lt;TRecord&gt; Items { get; init;} = Enumerable.Empty&lt;TRecord&gt;();  
    public bool Successful { get; init; }
    public string Message { get; init; } = string.Empty;
    public int TotalCount { get; init; }

    private ListQueryResult() { }

    public static ListQueryResult&lt;TRecord&gt; Success(IEnumerable&lt;TRecord&gt; Items, int totalCount, string? message = null)
        =&gt; new ListQueryResult&lt;TRecord&gt; {Successful=true,  Items= Items, TotalCount = totalCount, Message= message ?? string.Empty };

    public static ListQueryResult&lt;TRecord&gt; Failure(string message)
        =&gt; new ListQueryResult&lt;TRecord&gt; { Message = message};
}

You need to switch to using a DbContextFactory rather than a single reusable context. See this MS Docs article on how to implement it - https://learn.microsoft.com/en-us/ef/core/dbcontext-configuration/#using-a-dbcontext-factory-eg-for-blazor.

Your repository pattern code then looks like this:

public class BaseRepository&lt;TEntity&gt; : IRepository&lt;TEntity&gt; where TEntity : class
{
     private readonly IDbContextFactory&lt;TDbContext&gt; _factory;

    public BaseRepository(IDbContextFactory&lt;TDbContext&gt; factory)
    {
        _factory = factory;
    }

    public async ValueTask&lt;ListQueryResult&lt;TEntity&gt;&gt; GetItemsAsync(ListQueryRequest)
    {
        using var dbContext = _factory.CreateDbContext();

        // switch off tracking for request only querying (no updates)
        dbContext.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;

        // Build your query before you actually execute/materialize it
        IQueryable&lt;TEntity&gt; query = dbContext.Set&lt;TEntity&gt;();

        // Can do sorting or filtering here on the IQueryable 

        // Do a materialization to get the total count.
        var count = query.CountAsync(request.Cancellation);

        // apply paging to restrict the rows returned 
        query = query
            .Skip(request.StartIndex)
            .Take(request.PageSize);

        // Materialize the query into a list which you return as an IEnumerable
        var list await query.ToListAsync();

        // missing error checking code

        return ListQueryResult&lt;TEntity&gt;.Success(list, count);
}

public interface IRepository&lt;TEntity&gt; where TEntity : class
{
    public ValueTask&lt;ListQueryResult&lt;TEntity&gt;&gt; GetItemsAsync(ListQueryRequest);
}

Your actual code will probably look like this (I don't know your model so there's a certain amount of guesswork)

        using var dbContext = _factory.CreateDbContext();
        dbContext.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;

        IQueryable&lt;TRecord&gt; query = dbContext.ServerNames;
        query = query
            .Skip(request.StartIndex)
            .Take(request.PageSize);

        query = query
               .Join(_context.ServerTypes,
               sn =&gt; sn.ServerTypeId,
               st =&gt; st.ServerTypeId,
               (sn, st) =&gt; new ServerNames
               {
                   ServerID = sn.ServerID,
                   server_Name = sn.server_Name,
                   ServerType = st.ServerType
               });

        // Materialize the query into a list which you return as an IEnumerable
        // You need to do this before the DbContext goes out of scope
        var list await query.ToListAsync();

The final bit is how to handle this in a API call. I use a Post, which breaks the traditionalists rule that Posts are for sending data updates.

My (generic) handler looks like this. Note it uses an HttpClientFactory in a similar way to the DbContextFactory above.

public sealed class ListRequestAPIHandler
   : IListRequestHandler
{
   private IHttpClientFactory _factory;

   public ListRequestAPIHandler(IHttpClientFactory factory)
       =&gt; _factory = factory;

   public async ValueTask&lt;ListQueryResult&lt;TRecord&gt;&gt; ExecuteAsync&lt;TRecord&gt;(ListQueryRequest request)
       where TRecord : class, new()
   {
       ListQueryResult&lt;TRecord&gt;? result = null;

       var entityname = (new TRecord()).GetType().Name;

       var httpClient = _factory.CreateClient();
       var response = await httpClient.PostAsJsonAsync&lt;ListQueryRequest&gt;($&quot;/api/{entityname}/listquery&quot;, request, request.Cancellation);

       if (response.IsSuccessStatusCode)
           result = await response.Content.ReadFromJsonAsync&lt;ListQueryResult&lt;TRecord&gt;&gt;();

       return result ?? ListQueryResult&lt;TRecord&gt;.Failure($&quot;{response.StatusCode} = {response.ReasonPhrase}&quot;); ;
   }
}

And a generic Controller:


    [Route(&quot;/api/[controller]/listquery&quot;)]
    [HttpPost]
    public async Task&lt;ListQueryResult&lt;TRecord&gt;&gt; ListQuery([FromBody] ListQueryRequest query)
        =&gt; await _dataBroker.GetItemsAsync&lt;TRecord&gt;(query);

huangapple
  • 本文由 发表于 2023年2月19日 00:23:37
  • 转载请务必保留本文链接:https://go.coder-hub.com/75494705.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定