IEnumerable VS IQueryable

IEnumerable<T> is an interface in the System.Collections.Generic namespace, used for iterating over a collection of objects one at a time.

It operates with deferred execution for in-memory collections, meaning the query is executed after the data is fetched.

Commonly used with in-memory collections like lists or arrays, and in LINQ-to-Objects queries.

Operations like Where, Select, and OrderBy are applied in memory once the collection is loaded.

Not ideal for large datasets, as it loads the entire collection into memory, which may cause performance issues.

 

IEnumerable<T> is commonly used for:

 

Filtering and Transforming Data: Modifying in-memory collections (e.g., filtering products or transforming strings with LINQ).

Sorting and Paging: Sorting and paginating in-memory datasets (e.g., displaying user data with pagination).

Aggregation: Performing calculations like Sum, Count, or Average on small datasets (e.g., calculating total revenue from orders).

Data Transformation: Applying changes to data, such as trimming or formatting (e.g., transforming customer names).

 

 

Example:

using System;

using System.Collections.Generic;

using System.Linq;

 

namespace IEnumerableExample

{

    // Example Product class

    public class Product

    {

        public string Name { get; set; }

        public decimal Price { get; set; }

        public bool InStock { get; set; }

    }

 

    // Example Order class

    public class Order

    {

        public int OrderId { get; set; }

        public decimal Amount { get; set; }

        public DateTime OrderDate { get; set; }

    }

 

    // Example Customer class

    public class Customer

    {

        public string Name { get; set; }

        public string Email { get; set; }

    }

 

    class Program

    {

        static void Main(string[] args)

        {

            // Sample data

            List<Product> products = new List<Product>

            {

                new Product { Name = "Laptop", Price = 1000, InStock = true },

                new Product { Name = "Smartphone", Price = 500, InStock = false },

                new Product { Name = "Tablet", Price = 300, InStock = true },

                new Product { Name = "Headphones", Price = 150, InStock = true }

            };

 

            List<Order> orders = new List<Order>

            {

                new Order { OrderId = 1, Amount = 1500, OrderDate = DateTime.Now.AddDays(-10) },

                new Order { OrderId = 2, Amount = 250, OrderDate = DateTime.Now.AddDays(-5) },

                new Order { OrderId = 3, Amount = 800, OrderDate = DateTime.Now.AddDays(-2) }

            };

 

            List<Customer> customers = new List<Customer>

            {

                new Customer { Name = "John Doe", Email = "john.doe@example.com" },

                new Customer { Name = "Jane Smith", Email = "jane.smith@example.com" },

                new Customer { Name = "Alice Johnson", Email = "alice.johnson@example.com" }

            };

 

            // 1. Filtering and Transforming Data: Filtering products in stock and applying a discount

            var inStockProducts = products

                .Where(p => p.InStock)

                .Select(p => new { p.Name, DiscountedPrice = p.Price * 0.9m })  // Apply 10% discount

                .ToList();

 

            Console.WriteLine("In Stock Products with Discount:");

            foreach (var product in inStockProducts)

            {

                Console.WriteLine($"{product.Name} - Discounted Price: {product.DiscountedPrice:C}");

            }

 

            Console.WriteLine();

 

            // 2. Sorting and Paging: Sorting products by price and paginating (Page 1, 2 items per page)

            int page = 1;

            int pageSize = 2;

            var pagedProducts = products

                .OrderBy(p => p.Price)  // Sort by price

                .Skip((page - 1) * pageSize)

                .Take(pageSize)

                .ToList();

 

            Console.WriteLine($"Paged Products (Page {page}):");

            foreach (var product in pagedProducts)

            {

                Console.WriteLine($"{product.Name} - Price: {product.Price:C}");

            }

 

            Console.WriteLine();

 

            // 3. Aggregation: Calculate the total revenue from orders

            decimal totalRevenue = orders.Sum(o => o.Amount);

            Console.WriteLine($"Total Revenue: {totalRevenue:C}");

 

            Console.WriteLine();

 

            // 4. Data Transformation: Trimming and formatting customer names

            var formattedCustomerNames = customers

                .Select(c => new { FormattedName = c.Name.Trim().ToUpper() })

                .ToList();

 

            Console.WriteLine("Formatted Customer Names:");

            foreach (var customer in formattedCustomerNames)

            {

                Console.WriteLine(customer.FormattedName);

            }

        }

    }

}

 

 

 

 

IQueryable<T> is an interface in the System.Linq namespace, used for querying collections in a flexible way, often with remote data sources like databases or web APIs.

It allows queries to be translated into a query language (e.g., SQL) and executed on the data source, optimizing performance on the source (e.g., SQL Server, MongoDB).

Typically used for querying external data sources, such as databases, and in scenarios like LINQ-to-Entities or LINQ-to-SQL, where queries are constructed to be translated into SQL.

Operations like Where, Select, and OrderBy are applied in the query expression and executed on the data source (e.g., database query).

More efficient for large datasets, as it allows filtering, ordering, and pagination to be performed at the data source level before data is fetched into memory.

 

Summary of Concepts:

Querying Large Data Sources: Efficiently fetches large datasets from external sources like databases by letting the data source handle operations such as filtering, sorting, and pagination.

Remote Data Querying: Ideal for querying remote data sources (e.g., APIs), applying operations server-side to minimize data transfer.

Complex Query Logic: Supports building complex queries, like joins, groupings, and aggregations, which can be translated into optimized SQL queries.

Dynamic Queries: Allows building queries dynamically based on runtime conditions, without needing to load the entire dataset into memory.

Pagination: Efficiently handles large datasets by implementing pagination with Skip and Take to fetch only necessary data.

Optimized for Scalability: Ensures scalability by executing query logic on the database or external system, improving performance and reducing memory usage.

 

 

public async Task<IActionResult> GetFilteredProducts(int page, int pageSize, string? nameFilter, decimal? minPrice, string? sortBy)

{

    IQueryable<Product> query = _dbContext.Products;

 

    // Apply dynamic filters based on user input

    if (!string.IsNullOrEmpty(nameFilter))

    {

        query = query.Where(p => p.Name.Contains(nameFilter));

    }

    if (minPrice.HasValue)

    {

        query = query.Where(p => p.Price >= minPrice.Value);

    }

 

    // Complex query logic: Join with Category table to filter by category

    query = query.Join(_dbContext.Categories, p => p.CategoryId, c => c.Id, (p, c) => new { p, c })

                 .Where(pc => pc.c.IsActive);

 

    // Apply sorting based on user input

    if (sortBy == "Price")

    {

        query = query.OrderBy(pc => pc.p.Price);

    }

    else if (sortBy == "Name")

    {

        query = query.OrderBy(pc => pc.p.Name);

    }

    else

    {

        query = query.OrderBy(pc => pc.p.CreatedDate);  // Default sorting

    }

 

    // Implement pagination: Skip and Take are executed at the database level

    var pagedProducts = query.Skip((page - 1) * pageSize).Take(pageSize);

 

    // Execute query on the database

    var result = await pagedProducts.ToListAsync();

 

    // Count total filtered records for pagination info

    var totalCount = await query.CountAsync();

 

    // Return the paginated and filtered data along with total count

    return Ok(new

    {

        Items = result,

        TotalCount = totalCount

    });

}

 

 

 

IEnumerable<T> is part of the System.Collections.Generic namespace, while IQueryable<T> is part of the System.Linq namespace.

IEnumerable<T> executes queries in-memory after the data is fetched, applying operations like Where and Select on the loaded collection. In contrast, IQueryable<T> executes queries on the data source (e.g., SQL database), translating the query into a query language like SQL for optimization at the source.

IEnumerable<T> is typically used for in-memory collections like lists or arrays, and is commonly used with LINQ-to-Objects. IQueryable<T> is used for querying external data sources like databases and is used in scenarios such as LINQ-to-Entities or LINQ-to-SQL.

With IEnumerable<T>, operations like Where, Select, and OrderBy are applied in-memory once the collection is loaded. For IQueryable<T>, these operations are applied in the query expression and executed on the data source (e.g., as SQL queries).

IEnumerable<T> can be inefficient for large datasets because it loads the entire collection into memory, which may cause performance issues. IQueryable<T> is more efficient for large datasets, as filtering, ordering, and pagination are performed at the data source level before the data is fetched into memory.

Comments

Popular posts from this blog

Multiline to singleline IN C# - CODING

EF Core interview questions for beginners

EF Core interview questions for experienced