آشنایی با Gridify
اندازه‌ی قلم متن
تخمین مدت زمان مطالعه‌ی مطلب: سه دقیقه

Gridify چیست ؟

به طور خلاصه Gridify یک کتابخانه ساده و سریع است که عملیات‌های Filtering , Pagination و Sorting را با استفاده از شرط‌های متنی (string based) امکان پذیر میکند.
به طور مثال فرض کنید که یک API را برای دریافت لیست کاربران با نام UsersList نوشته‌اید. مثال:
 [HttpGet("[action]")]
 public async Task<IActionResult> UsersList()
 {
    var users =  await _dbContext.Users.AsNoTracking().ToListAsync();
    return Ok(users);
 }
طبیعتا بخش FrontEnd نرم افزار شما نیاز دارد این اطلاعات را به کاربر نمایش دهد. به همین جهت در بیشتر مواقع از یک گرید برای نمایش این اطلاعات استفاده میشود.
پس از دریافت اطلاعات از سرور با مشکلات زیر مواجه خواهیم شد.
  1. عدم پشتیبانی از Pagination: چون API، تمامی کاربران را به سمت کلاینت ارسال میکند؛ به همین جهت، هم با مشکل کارآیی (performance) در آینده مواجه میشویم و هم امکان گذاشتن صفحه بندی (Pagination) وجود نخواهد داشت. 
  2. عدم پشتیبانی از Sorting: اگر در گرید نمایش داده شده کاربر بخواهد اطلاعات را Sort کند، چون چنین امکانی هنوز برای API ما تعریف نشده، این عملیات سمت سرور امکان پذیر نیست.
  3. عدم پشتیبانی از Filtering: همیشه نمایش تمامی اطلاعات مفید نیست. در اکثر مواقع ما نیاز داریم تا قسمتی از اطلاعات را با شرطی خاص، برگردانیم. به طور مثال لیست کاربران فعال در سامانه یا لیست کاربران غیرفعال. 
این مشکلات بدون استفاده از هیچ کتابخانه‌ای قابل حل است ولی نه به سادگی؛ به طور مثال یا باید چندین API مختلف با امکانات مختلف بنویسیم، یا یک API را برای پشتیبانی از این موارد تغییر بسیار دهیم. من برای اینکه از بحث دور نشویم، به پیاده سازی نمونه دستی پشتیبانی از این موارد در اینجا نمی‌پردازم، چرا که اگر یکبار تلاشی را برای اینکار انجام داده باشیم، طبیعتا  مشکلات و کد کثیفی که در نهایت تولید شده است، یادآوری خواهد شد. 
برای رفع این مشکلات میتوان از کتابخانه Gridify استفاده کرد. مثال :
 [HttpGet("[action]")]
 public async Task<IActionResult> UsersList(GridifyQuery filter)
 {
    var users =  await _dbContext.Users.AsNoTracking().GridifyAsync(filter);
    return Ok(users);
 }
در مثال بالا با استفاده از کلاس GridifyQuery میتوانیم به کنترل هر سه مشکل Sorting - Pagination - Filtering سمت کلاینت بپردازیم. (در ادامه با این کلاس بیشتر آشنا خواهیم شد).


استفاده از Gridify به API‌ها محدود نمیشود. به طور کلی ما میتوانیم در هر نوع لیستی که امکان استفاده از IQueryable  را به ما میدهد از آن استفاده نماییم. 
فرض کنید در یک برنامه Console Application قصد داریم یک فیلتر را از کاربر دریافت کرده و آن را روی لیست خروجی خود اعمال کنیم. به دلیل اینکه امکان جستجوی متنی در دات نت وجود ندارد، برای انجام اینکار مجبور خواهیم شد که برای تک تک فیلدهایی که قرار است برای فیلترینگ پشتیبانی کنیم، یک query جداگانه بنویسیم؛ ولی این عملیات توسط کتابخانه Gridify امکان پذیر است. به طور مثال فرض کنید قصد داریم در لیست کاربران، کاربرانی را  با نام Ali، پیدا کنیم. 
var result = Users.AsQueryable().ApplyFiltering("name==Ali");
این کد دقیقا معادل کد زیر است.
var result = Users.AsQueryable().Where(q => q.Name == "Ali");
در اینجا با استفاده از کتابخانه Gridify ما توانستیم یک static Linq را به یک dynamic Linq که در runtime مقدار دهی خواهد شد، تغییر دهیم. به همین جهت استفاده از مورد اول در برنامه‌ی Console ما امکان پذیر است. تا اینجا ما با امکانات کلی این کتابخانه آشنا شدیم. در مقالات بعدی سعی میکنم به سایر امکانات این کتابخانه و بیشتر به جزئیات بپردازم. همینطور برای کسب اطلاعات بیشتر میتوانید به لینک زیر مراجعه نمایید.
  • #
    ‫۳ سال و ۱ ماه قبل، دوشنبه ۱۱ مرداد ۱۴۰۰، ساعت ۱۸:۲۲
    کوئری سمت دیتابیس اجرا میشه دیگه؟
    • #
      ‫۳ سال و ۱ ماه قبل، دوشنبه ۱۱ مرداد ۱۴۰۰، ساعت ۲۳:۴۶
      جواب کوتاه بله.
       به دلیل اینکه Gridify از طریق string یک LINQ Expression تولید میکنه, اگر فریم ورک و Provider شما بتواند LINQ رو سمت دیتابیس اجرا کنه (مثل entity framework), query سمت دیتابیس اجرا میشه.

  • #
    ‫۱ سال و ۴ ماه قبل، دوشنبه ۲۸ فروردین ۱۴۰۲، ساعت ۰۲:۴۹
    فرض کنید مدل زیر رو داریم
    public class Order : AggregateRoot<int>
    {
        private DateTime _orderDate;
        public Address Address { get; private set; }
        public int? GetBuyerId => _buyerId;
        public int? _buyerId;
        public OrderStatus OrderStatus { get; private set; }
        public  int _orderStatusId;
        private string _description;
        private bool _isDraft;
    
    
        private readonly List<OrderItem> _orderItems;
        public IReadOnlyCollection<OrderItem> OrderItems => _orderItems;
    
        protected Order()
        {
            _orderItems = new List<OrderItem>();
            _isDraft = false;
        }
        public Order(string userId, string userName, Address address,
            int? buyerId = null) : this()
        {
            _buyerId = buyerId;
            _orderStatusId = OrderStatus.Submitted.Id;
            _orderDate = DateTime.UtcNow;
            Address = address;
            //AddOrderStartedDomainEvent(userId, userName);
        }
    }

    public class OrderStatus : Enumeration
    {
        public static OrderStatus Submitted = new OrderStatus(1, nameof(Submitted).ToLowerInvariant());
        public static OrderStatus AwaitingValidation = new OrderStatus(2, nameof(AwaitingValidation).ToLowerInvariant());
        public static OrderStatus StockConfirmed = new OrderStatus(3, nameof(StockConfirmed).ToLowerInvariant());
        public static OrderStatus Paid = new OrderStatus(4, nameof(Paid).ToLowerInvariant());
        public static OrderStatus Shipped = new OrderStatus(5, nameof(Shipped).ToLowerInvariant());
        public static OrderStatus Cancelled = new OrderStatus(6, nameof(Cancelled).ToLowerInvariant());
        public OrderStatus(int id, string name)
            : base(id, name)
        {
        }
    }
    حال چگونه میتوانیم از Order بر اساس OrderStatus.Id فیلترینگ انجام بدیم ؟
    var query = _orderQueryRepository.GetAll(x => x._buyerId == 52).AsNoTracking();
    var s = await query.GridifyAsync(request.queryFilter);
    return s.Adapt<Paging<OrderQuery>>();
    {
      "buyerid": 0,
      "queryFilter": {
        "page": 1,
        "pageSize": 5,
        "orderBy": "id",
        "filter": "Order_OrderStatus_Id==1"
      }
    }
    خروجی
     "message": "Property 'Order_OrderStatus_Id' not found.",

  • #
    ‫۱ سال و ۴ ماه قبل، دوشنبه ۲۸ فروردین ۱۴۰۲، ساعت ۱۵:۱۲
    هنگام استفاده از این کتابخانه با swagger، اگر فیلتر خالی باشه درست کار می‌کنه، اما اگر چیزی برای فیلتر ثبت شده باشه، هیچ موردی رو درنظر نمی‌گیره و فقط 20 آیتم صفحه اول رو نشون میده. این مواردی هست که پر کردم. 

    • #
      ‫۱ سال و ۴ ماه قبل، دوشنبه ۲۸ فروردین ۱۴۰۲، ساعت ۱۵:۳۶
      سلام با توجه به اینکه تمام فیچرهای این library قبلا تست شده احتمالا جایی در کد اشتباهی انجام دادید بهتر است کد مرتبط رو به این endpoint رو به اشتراک بگذارید تا راهنماییتون کنم.  
      • #
        ‫۱ سال و ۴ ماه قبل، دوشنبه ۲۸ فروردین ۱۴۰۲، ساعت ۱۶:۱۹
        سلام؛ روی یک پروژه خام webapi هم تست کردم این مشکل وجود داشت:

         
        [ApiController]
        [Route("[controller]")]
        public class WeatherForecastController : ControllerBase
        {
            private static readonly string[] Summaries = new[]
            {
                "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
            };
        
            private readonly ILogger<WeatherForecastController> _logger;
        
            public WeatherForecastController(ILogger<WeatherForecastController> logger)
            {
                _logger = logger;
            }
        
            [HttpGet(Name = "GetWeatherForecast")]
            public IActionResult Get([FromQuery] GridifyQuery filter)
            {
                var weatherForecasts = Enumerable.Range(1, 5).Select(index => new WeatherForecast
                    {
                        Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
                        TemperatureC = Random.Shared.Next(-20, 55),
                        Summary = Summaries[Random.Shared.Next(Summaries.Length)]
                    })
                    .ToArray();
        
                return Ok(weatherForecasts.AsQueryable().ApplyFilteringOrderingPaging(filter));
            }
        }
        • #
          ‫۱ سال و ۴ ماه قبل، دوشنبه ۲۸ فروردین ۱۴۰۲، ساعت ۱۷:۰۷
          اسم پارامتر کنترل رو تغییر بدید مشکل حل میشه. مثلا query بگذارید یا هر چیز دیگه‌ای غیر از filter
          public IActionResult Get([FromQuery] GridifyQuery query)
  • #
    ‫۱ سال و ۴ ماه قبل، دوشنبه ۲۸ فروردین ۱۴۰۲، ساعت ۱۷:۱۶
    نکته تکمیلی: این پست مرتبط با Gridify ورژن 1 است, در حال حاضر آخرین نسخه گریدیفای ورژن 2 میباشد که در این ورژن operator‌ها تغییر کرده اند. به طور مثال بجای `==` از `=` استفاده میشود. برای اطلاعات بیشتر لطفا داکیومنتیشن پروژه را مطالعه فرمایید.