Model Binding using FromQuery in ASP.NET Core MVC

Model Binding using FromQuery in ASP.NET Core MVC

In this article, I will discuss How to Use FromQuery Attribute to Perform Model Binding in an ASP.NET Core MVC Application with Examples. Please read our previous article discussing Model Binding using FromForm in ASP.NET Core MVC. FromQuery Attribute is one of the Model Binding Attributes used in ASP.NET Core Applications.

What is FromQuery Attribute in ASP.NET Core MVC?

The FromQuery Attribute in ASP.NET Core MVC specifies that a parameter in a controller’s action method should be bound from the query string of the incoming HTTP request. Query strings are part of a URL that comes after the ? and contain key-value pairs (e.g., ?key1=value1&key2=value2) separated by & characters. When using the FromQuery attribute, ASP.NET Core automatically binds these values to the corresponding parameters in the action method.

To use the FromQuery attribute, we need to annotate a parameter in the action method with the [FromQuery] attribute. This tells the framework to look for the parameter’s value in the request’s query string. That means ASP.NET Core Framework automatically binds data from the query string to that action method parameter based on its name. If you go to the definition of FromQueryAttribute, you will see the following signature.

Model Binding using FromQuery in ASP.NET Core MVC

As you can see, this class also has two Properties. They are as follows:

  • Name Property: The Name property allows us to specify a different name for the query string parameter if it doesn’t match the parameter name in the action method.
  • BindingSource Property: The BindingSource property specifies the source from which the value should be bound, which in this case is the query string (i.e., BindingSource.Query). This property is automatically set to Query when you use the FromQuery attribute. We don’t need to set BindingSource manually, as it is handled by the framework when using FromQuery.

Example to Understand FromQuery Attribute in ASP.NET Core MVC Application:

Let us create one real-time example to demonstrate how to build a robust and scalable ASP.NET Core MVC application that includes filtering, sorting, and pagination functionality for a product listing page using the FromQuery Attribute. At the end of this article, we will create one page, which should look like the image below, where we will perform filtering, sorting, and pagination operations on the product list.

Example to Understand FromQuery Attribute in ASP.NET Core MVC

Let us proceed and implement the example step by step. First, create a new ASP.NET Core Project using the Model View Controller template and provide the project name as FromQueryAttributeDemo.

Product Category Enum:

First, create a class file named ProductCategory.cs within the Models folder, and then copy and paste the following code. The following ProductCategory enum defines a set of product categories, which helps categorize the products in the application. It standardizes the category values across the system, ensuring that categories are consistent and reducing errors that might occur from manual category entries.

namespace FromQueryAttributeDemo.Models
{
    public enum ProductCategory
    {
        Electronics,
        Accessories,
        Computers,
        SmartHome,
        Wearables,
        Cameras,
        Gaming,
        HomeEntertainment,
        Audio
    }
}
Product Model:

Next, create a class file named Product.cs within the Models folder and then copy and paste the following code. The following Product class represents a product entity in the application. It includes properties such as Id, Name, Category (using the ProductCategory enum), Price, and DateAdded. This model is the data structure for storing and manipulating product information throughout the application.

namespace FromQueryAttributeDemo.Models
{
    public class Product
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public ProductCategory Category { get; set; }
        public decimal Price { get; set; }
        public DateTime DateAdded { get; set; }
    }
}
Product Query Parameters:

Next, create a class file named ProductQueryParameters.cs within the Models folder and copy and paste the following code. The ProductQueryParameters class encapsulates the parameters for filtering, sorting, and paginating the product list. It includes SearchTerm, Category, SortBy, SortAscending, PageNumber, and PageSize properties. This class allows for flexible and efficient querying of the product list based on user input. Here, we have also set the Default Page Number and Default Size.

namespace FromQueryAttributeDemo.Models
{
    public class ProductQueryParameters
    {
        public string SearchTerm { get; set; }
        public string Category { get; set; }
        public string SortBy { get; set; }
        public bool SortAscending { get; set; } = true; //Default is True
        public int PageNumber { get; set; } = 1; //Default Page Number
        public int PageSize { get; set; } = 3; //Default Size
    }
}
Product List View Model:

Next, create a class file named ProductListViewModel.cs within the Models folder and copy and paste the following code. The following ProductListViewModel class is used to pass data from the controller action method to the view. It contains the list of products to be displayed, along with pagination information (such as PageNumber, PageSize, TotalPages), filtering options (such as SearchTerm, Category, SortBy, SortAscending), and select lists (such as Categories, SortOptions, PageSizeOptions) for the dropdown controls in the view. This view model ensures that all necessary data is available for rendering the product list and its related controls in the view.

using Microsoft.AspNetCore.Mvc.Rendering;

namespace FromQueryAttributeDemo.Models
{
    public class ProductListViewModel
    {
        public IEnumerable<Product> Products { get; set; }
        public int PageNumber { get; set; }
        public int PageSize { get; set; }
        public int TotalPages { get; set; }
        public string SearchTerm { get; set; }
        public string Category { get; set; }
        public string SortBy { get; set; }
        public bool SortAscending { get; set; }
        public IEnumerable<SelectListItem> Categories { get; set; }
        public IEnumerable<SelectListItem> SortOptions { get; set; }
        public IEnumerable<SelectListItem> PageSizeOptions { get; set; } // Add this property
    }
}
Product Service:

Next, create a class file named ProductService.cs within the Models folder and copy and paste the following code. The following ProductService class handles the business logic for managing products. It provides a collection of products and methods to filter, sort, and paginate them based on the parameters specified in the ProductQueryParameters class. The service ensures these operations are performed efficiently and consistently, making it a central component for product-related data manipulation. The following Service Class code is self-explained, so, please read the comment lines for a better understanding.

namespace FromQueryAttributeDemo.Models
{
    public class ProductService
    {
        // Private field to store the list of products
        private readonly List<Product> _products;

        //Constructor Initializing the _products list with some hardcoded data
        public ProductService()
        {
            _products = new List<Product>
            {
                new Product { Id = 1, Name = "Apple iPhone 13", Category = ProductCategory.Electronics, Price = 999, DateAdded = DateTime.Now.AddDays(-10) },
                new Product { Id = 2, Name = "Samsung Galaxy S21", Category = ProductCategory.Electronics, Price = 899, DateAdded = DateTime.Now.AddDays(-11) },
                new Product { Id = 3, Name = "Sony WH-1000XM4 Headphones", Category = ProductCategory.Accessories, Price = 349, DateAdded = DateTime.Now.AddDays(-12) },
                new Product { Id = 4, Name = "Apple MacBook Pro 16\"", Category = ProductCategory.Computers, Price = 2399, DateAdded = DateTime.Now.AddDays(-13) },
                new Product { Id = 5, Name = "Dell XPS 13 Laptop", Category = ProductCategory.Computers, Price = 1099, DateAdded = DateTime.Now.AddDays(-14) },
                new Product { Id = 6, Name = "Amazon Echo Dot (4th Gen)", Category = ProductCategory.SmartHome, Price = 49, DateAdded = DateTime.Now.AddDays(-15) },
                new Product { Id = 7, Name = "Apple Watch Series 7", Category = ProductCategory.Wearables, Price = 399, DateAdded = DateTime.Now.AddDays(-12) },
                new Product { Id = 8, Name = "Google Nest Thermostat", Category = ProductCategory.SmartHome, Price = 129, DateAdded = DateTime.Now.AddDays(-10) },
                new Product { Id = 9, Name = "Fitbit Charge 5", Category = ProductCategory.Wearables, Price = 179, DateAdded = DateTime.Now.AddDays(-2) },
                new Product { Id = 10, Name = "Bose QuietComfort 35 II", Category = ProductCategory.Accessories, Price = 299, DateAdded = DateTime.Now.AddDays(-11) },
                new Product { Id = 11, Name = "Nikon D3500 DSLR Camera", Category = ProductCategory.Cameras, Price = 499, DateAdded = DateTime.Now.AddDays(-2) },
                new Product { Id = 12, Name = "Sony PlayStation 5", Category = ProductCategory.Gaming, Price = 499, DateAdded = DateTime.Now.AddDays(-11) },
                new Product { Id = 13, Name = "Microsoft Xbox Series X", Category = ProductCategory.Gaming, Price = 499, DateAdded = DateTime.Now.AddDays(-10) },
                new Product { Id = 14, Name = "Apple AirPods Pro", Category = ProductCategory.Accessories, Price = 249, DateAdded = DateTime.Now.AddDays(-8) },
                new Product { Id = 15, Name = "JBL Flip 5 Bluetooth Speaker", Category = ProductCategory.Audio, Price = 119, DateAdded = DateTime.Now.AddDays(-7) },
                new Product { Id = 16, Name = "Canon EOS R6 Mirrorless Camera", Category = ProductCategory.Cameras, Price = 2499, DateAdded = DateTime.Now.AddDays(-6) },
                new Product { Id = 17, Name = "Nintendo Switch", Category = ProductCategory.Gaming, Price = 299, DateAdded = DateTime.Now.AddDays(-5) },
                new Product { Id = 18, Name = "LG 65\" OLED TV", Category = ProductCategory.HomeEntertainment, Price = 1999, DateAdded = DateTime.Now.AddDays(-4) },
                new Product { Id = 19, Name = "Samsung Galaxy Buds Pro", Category = ProductCategory.Accessories, Price = 199, DateAdded = DateTime.Now.AddDays(-3) },
                new Product { Id = 20, Name = "Asus ROG Strix Gaming Laptop", Category = ProductCategory.Computers, Price = 1499, DateAdded = DateTime.Now.AddDays(-1) }
            };
        }

        // Asynchronous method to get filtered, sorted, and paginated products
        public async Task<(IEnumerable<Product> Products, int TotalCount)> GetProductsAsync(ProductQueryParameters queryParameters)
        {
            // Convert the list of products to a queryable to use LINQ methods
            var products = _products.AsQueryable();

            // Filter: check if a search term is provided and filter products based on the term
            if (!string.IsNullOrEmpty(queryParameters.SearchTerm))
            {
                // Case-insensitive search in product names
                products = products.Where(p => p.Name.Contains(queryParameters.SearchTerm, StringComparison.OrdinalIgnoreCase));
            }

            // Filter: check if a category is specified and filter products by category
            if (!string.IsNullOrEmpty(queryParameters.Category))
            {
                // Try parsing the category string to the ProductCategory enum
                if (Enum.TryParse(queryParameters.Category, out ProductCategory category))
                {
                    // Filter products by the parsed category
                    products = products.Where(p => p.Category == category);
                }
            }

            // Get the total count of filtered products
            int totalCount = products.Count();

            // Sorting: check if a sort parameter is specified
            if (!string.IsNullOrEmpty(queryParameters.SortBy))
            {
                // Sort by price if specified
                if (queryParameters.SortBy.Equals("price", StringComparison.OrdinalIgnoreCase))
                {
                    // Apply ascending or descending order based on SortAscending flag
                    products = queryParameters.SortAscending ? products.OrderBy(p => p.Price) : products.OrderByDescending(p => p.Price);
                }
                // Sort by date added if specified
                else if (queryParameters.SortBy.Equals("date", StringComparison.OrdinalIgnoreCase))
                {
                    // Apply ascending or descending order based on SortAscending flag
                    products = queryParameters.SortAscending ? products.OrderBy(p => p.DateAdded) : products.OrderByDescending(p => p.DateAdded);
                }
            }

            // Pagination: calculate the number of items to skip and take based on page number and page size
            products = products.Skip((queryParameters.PageNumber - 1) * queryParameters.PageSize)
                               .Take(queryParameters.PageSize);

            // Return the filtered, sorted, and paginated products along with the total count
            return await Task.FromResult((products.ToList(), totalCount));
        }
    }
}
Products Controller:

Next, create an Empty MVC Controller named ProductsController within the Controllers folder and then copy and paste the following code. The following ProductsController is responsible for handling HTTP requests related to products. It uses the ProductService to retrieve the filtered, sorted, and paginated list of products based on user input, populates the ProductListViewModel, and passes this data to the view. The controller also generates the select lists for filtering and sorting options using the ProductCategory enum, ensuring that the dropdown lists in the view are correctly populated and consistent with the available data. The following Controller Class code is self-explained, so please read the comment lines for a better understanding.

using FromQueryAttributeDemo.Models; 
using Microsoft.AspNetCore.Mvc; 
using Microsoft.AspNetCore.Mvc.Rendering; 

namespace FromQueryAttributeDemo.Controllers
{
    public class ProductsController : Controller
    {
        // Action method to handle the GET request for the Index view, which displays the product list
        public async Task<IActionResult> Index([FromQuery] ProductQueryParameters queryParameters)
        {
            // Create an instance of ProductService to access the list of products and perform operations on them
            var _productService = new ProductService();

            // Call the GetProductsAsync method of ProductService to get the filtered, sorted, and paginated list of products along with the total count
            var (products, totalCount) = await _productService.GetProductsAsync(queryParameters);

            // Generate a list of categories for the dropdown by getting all values of the ProductCategory enum
            var categories = Enum.GetValues(typeof(ProductCategory)) // Get all the values from the ProductCategory enum
                .Cast<ProductCategory>() // Cast them to ProductCategory type
                .Select(category => new SelectListItem { Value = category.ToString(), Text = category.ToString() }) // Convert each category to a SelectListItem for use in the dropdown
                .ToList(); // Convert the IEnumerable to a List

            // Define the sorting options for the Sort By dropdown list
            var sortOptions = new List<SelectListItem>
            {
                new SelectListItem { Value = "price", Text = "Price" }, // Option to sort by price
                new SelectListItem { Value = "date", Text = "Date Added" }, // Option to sort by date added
            };

            // Define the options for the Page Size dropdown list, allowing users to select how many products to display per page
            var pageSizeOptions = new List<SelectListItem>
            {
                new SelectListItem { Value = "3", Text = "3" }, // Option to display 3 products per page
                new SelectListItem { Value = "5", Text = "5" }, // Option to display 5 products per page
                new SelectListItem { Value = "10", Text = "10" }, // Option to display 10 products per page
                new SelectListItem { Value = "20", Text = "20" }, // Option to display 20 products per page
                new SelectListItem { Value = "25", Text = "25" }, // Option to display 25 products per page
                new SelectListItem { Value = "35", Text = "35" }  // Option to display 35 products per page
            };

            // Create a view model object and populate it with the data needed for the view
            var viewModel = new ProductListViewModel
            {
                Products = products, // Assign the list of products to the view model
                PageNumber = queryParameters.PageNumber, // Assign the current page number from the query parameters
                PageSize = queryParameters.PageSize, // Assign the page size (number of products per page) from the query parameters
                TotalPages = (int)Math.Ceiling((double)totalCount / queryParameters.PageSize), // Calculate the total number of pages needed based on the total product count and page size
                SearchTerm = queryParameters.SearchTerm, // Assign the search term entered by the user, if any
                Category = queryParameters.Category, // Assign the selected category for filtering
                SortBy = queryParameters.SortBy, // Assign the selected sorting option (price or date added)
                SortAscending = queryParameters.SortAscending, // Assign whether the sorting is in ascending order
                Categories = categories, // Assign the generated list of categories for the dropdown
                SortOptions = sortOptions, // Assign the generated sorting options for the dropdown
                PageSizeOptions = pageSizeOptions // Assign the generated page size options for the dropdown
            };

            // Return the view along with the populated view model, so the view can display the product list and related controls
            return View(viewModel);
        }
    }
}
Index View:

Next, create a view named Index.cshtml within the Views/Products folder and then copy and paste the following code. The following Index.cshtml view renders the product list on the web page. It provides a user-friendly interface for filtering, sorting, and paginating products. The view uses Bootstrap for responsive design and includes form controls for search, category selection, sorting, and pagination. The view also displays the product data in a table format and uses conditional logic to handle scenarios like empty results. Emojis and other stylistic elements enhance the page’s visual appearance. The following Index View code is self-explained, so please read the comment lines for a better understanding.

@model ProductListViewModel
@{
    ViewData["Title"] = "Products";
}

<div class="container-fluid mt-4">
    <h2 class="text-center mb-4">🛒 Product List</h2>

    <!-- Filter Section -->
    <div class="card mb-4 shadow-sm">
        <div class="card-header bg-primary text-white">
            <h5 class="mb-0">🔍 Filter and Sort Products</h5>
        </div>
        <div class="card-body">
            <form method="get" asp-action="Index" asp-controller="Products" class="row g-3 align-items-end">
                <div class="col-12 col-md-6 col-lg-3">
                    <label asp-for="SearchTerm" class="form-label">Search by Name</label>
                    <input asp-for="SearchTerm" class="form-control" placeholder="Type a product name..." />
                </div>
                <div class="col-12 col-md-6 col-lg-2">
                    <label asp-for="Category" class="form-label">Category</label>
                    <select asp-for="Category" asp-items="Model.Categories" class="form-control">
                        <option value="">All Categories</option>
                    </select>
                </div>
                <div class="col-12 col-md-6 col-lg-2">
                    <label asp-for="SortBy" class="form-label">Sort By</label>
                    <select asp-for="SortBy" asp-items="Model.SortOptions" class="form-control">
                        <option value="">Choose option</option>
                    </select>
                </div>
                <div class="col-12 col-md-6 col-lg-2">
                    <div class="form-check mt-4">
                        <input asp-for="SortAscending" class="form-check-input" />
                        <label asp-for="SortAscending" class="form-check-label">Ascending </label>
                    </div>
                </div>
                <div class="col-12 col-md-6 col-lg-2">
                    <label asp-for="PageSize" class="form-label">Items per Page</label>
                    <select asp-for="PageSize" asp-items="Model.PageSizeOptions" class="form-control" onchange="this.form.submit();"></select>
                </div>
                <div class="col-12 col-md-6 col-lg-1">
                    <button type="submit" class="btn btn-success w-100">Apply 🎯</button>
                </div>
            </form>
        </div>
    </div>

    <!-- Product Data Section -->
    <div class="card shadow-sm">
        <div class="card-header bg-secondary text-white d-flex justify-content-between align-items-center">
            <h5 class="mb-0">📊 Product Data</h5>
            <span class="badge bg-light text-dark">Total: @Model.Products.Count() 📝</span>
        </div>
        <div class="card-body p-0">
            <div class="table-responsive">
                <table class="table table-hover table-bordered mb-0">
                    <thead class="thead-dark">
                        <tr>
                            <th class="text-center">Name 🏷️</th>
                            <th class="text-center">Category 🗂️</th>
                            <th class="text-center">Price 💵</th>
                            <th class="text-center">Date Added 📅</th>
                        </tr>
                    </thead>
                    <tbody>
                        <!-- Check if there are any products to display -->
                        @if (Model.Products.Any())
                        {
                            <!-- Loop through each product in the model's product list -->
                            @foreach (var product in Model.Products)
                            {
                                <tr>
                                    <td class="text-center">@product.Name</td>
                                    <td class="text-center">@product.Category</td>
                                    <td class="text-center">@product.Price.ToString("C")</td>
                                    <td class="text-center">@product.DateAdded.ToShortDateString()</td>
                                </tr>
                            }
                        }
                        else
                        {
                            <tr>
                                <td colspan="4" class="text-center">😞 No products found.</td>
                            </tr>
                        }
                    </tbody>
                </table>
            </div>

            <!-- Pagination -->
            <nav aria-label="Product List Pagination" class="mt-3">
                <ul class="pagination justify-content-center mb-0">
                    <!-- Disable the First and Previous buttons if on the first page -->
                    @if (Model.PageNumber == 1)
                    {
                        <li class="page-item disabled">
                            <span class="page-link">First ⏮️</span>
                        </li>
                        <li class="page-item disabled">
                            <span class="page-link">Previous ⬅️</span>
                        </li>
                    }
                    else
                    {
                        <li class="page-item">
                            <!-- Link to the first page -->
                            <a class="page-link" asp-action="Index" asp-route-SearchTerm="@Model.SearchTerm" asp-route-Category="@Model.Category" asp-route-SortBy="@Model.SortBy" asp-route-SortAscending="@Model.SortAscending" asp-route-PageSize="@Model.PageSize" asp-route-PageNumber="1">First ⏮️</a>
                        </li>
                        <li class="page-item">
                            <!-- Link to the previous page -->
                            <a class="page-link" asp-action="Index" asp-route-SearchTerm="@Model.SearchTerm" asp-route-Category="@Model.Category" asp-route-SortBy="@Model.SortBy" asp-route-SortAscending="@Model.SortAscending" asp-route-PageSize="@Model.PageSize" asp-route-PageNumber="@(Model.PageNumber - 1)">Previous ⬅️</a>
                        </li>
                    }

                    <li class="page-item active">
                        <!-- Display the current page number -->
                        <span class="page-link">@Model.PageNumber</span>
                    </li>

                    <!-- Disable the Next and Last buttons if on the last page -->
                    @if (Model.PageNumber == Model.TotalPages)
                    {
                        <li class="page-item disabled">
                            <span class="page-link">Next ➡️</span>
                        </li>
                        <li class="page-item disabled">
                            <span class="page-link">Last ⏭️</span>
                        </li>
                    }
                    else
                    {
                        <li class="page-item">
                            <!-- Link to the next page -->
                            <a class="page-link" asp-action="Index" asp-route-SearchTerm="@Model.SearchTerm" asp-route-Category="@Model.Category" asp-route-SortBy="@Model.SortBy" asp-route-SortAscending="@Model.SortAscending" asp-route-PageSize="@Model.PageSize" asp-route-PageNumber="@(Model.PageNumber + 1)">Next ➡️</a>
                        </li>
                        <li class="page-item">
                            <!-- Link to the last page -->
                            <a class="page-link" asp-action="Index" asp-route-SearchTerm="@Model.SearchTerm" asp-route-Category="@Model.Category" asp-route-SortBy="@Model.SortBy" asp-route-SortAscending="@Model.SortAscending" asp-route-PageSize="@Model.PageSize" asp-route-PageNumber="@Model.TotalPages">Last ⏭️</a>
                        </li>
                    }
                </ul>
            </nav>
        </div>
    </div>
</div>

@functions {
    // Function to generate route values for pagination links, preserving the current filter and sort options
    private object GetRouteValuesForPage(int pageNumber)
    {
        return new
        {
            SearchTerm = Model.SearchTerm, // Preserve the search term
            Category = Model.Category, // Preserve the selected category
            SortBy = Model.SortBy, // Preserve the selected sort option
            SortAscending = Model.SortAscending, // Preserve the sort order
            PageSize = Model.PageSize, // Preserve the selected page size
            PageNumber = pageNumber // Set the page number to the specified value
        };
    }
}

Now, run the application and test the functionalities, and it should work as expected.

How Does FromQuery Attribute Works in ASP.NET Core MVC?

The FromQuery attribute instructs the ASP.NET Core model binding system to bind a parameter to a value from the query string of the incoming HTTP request. When a request is received, the model binder checks if a query string parameter matches the name of the parameter annotated with [FromQuery]. If a match is found, it will convert the query string value to the parameter type and assign it. If no matching query string parameter is found, the parameter will be set to its default value, assuming it is optional.

When Should We Use FromQuery Attribute in ASP.NET Core?

The FromQuery attribute in ASP.NET Core MVC is useful when handling HTTP GET requests where data is passed through the URL as Query Strings. The following are some of the common scenarios where using the FromQuery attribute makes sense in an ASP.NET Core MVC Web application:

  • Filtering Data: In scenarios where you need to filter the data displayed on a page, such as a product catalog or a list of items, using query parameters is a practical approach. The FromQuery attribute allows the capture of filter criteria like category, price range, or other options directly from the query string in the URL.
  • Pagination: When displaying lists of items that span multiple pages, you can use the FromQuery attribute to manage pagination. Parameters like Page Number and Page Size can be passed through the query string to control which page of data is shown and how many items are displayed per page.
  • Search Parameters: In ASP.NET Core MVC applications with search functionality, you can use the FromQuery attribute to capture search inputs from the URL. For instance, search keywords, date ranges, or other filters can be sent through the query string, making it easy to process the search criteria in your controller.
  • Sorting: When rendering a list of items, you might want to let users sort the list based on different criteria, such as date, name, or price. The FromQuery attribute can accept sorting parameters from the query string, allowing users to control how the data is displayed.
When Not to Use the FromQuery Attribute:

We should avoid using FromQuery Attribute in the following scenarios:

  • Sensitive Data: Avoid using the query string to pass sensitive information like passwords, personal data, or security tokens. Since query strings can be logged in server logs and stored in browser history, they cause a security risk.
  • Very Large Data Sets: When passing data through the query string, be careful of the length of URLs. Browsers and servers have URL length limitations, so if the query string becomes too long, consider using a different method, such as sending data in the body of a POST request or using TempData or session state for temporary storage.

In the next article, I will discuss How to Use FromRoute to Perform Model Binding in ASP.NET Core MVC with Examples. In this article, I explain How to use FromQuery to Perform Model Binding in ASP.NET Core MVC with Examples. I hope you enjoy this FromQuery in ASP.NET Core MVC article.

Leave a Reply

Your email address will not be published. Required fields are marked *