In this blog post, we intend to test our Queryable and enumerable extension methods. For this purpose, we created the order and some related entities. Our entities are as follows:

1-Entities

public partial class Order
{
  public int IdOrder { get; set; }
  public DateTime DateTime { get; set; }
  public int Status { get; set; }
  public virtual ICollection<OrderItem> OrderItems { get; set; } = new List<OrderItem>();

  public int UserId { get; set; }
  public User User { get; set; } = null!;
  public Payment Payment { get; set; } = null!;
}
public partial class OrderItem
{
  public int IdOrderItem { get; set; }
  public int OrderId { get; set; }
  public int ProductId { get; set; }
  public int Quantity { get; set; }
  public decimal Price { get; set; }

  public virtual Order Order { get; set; } = null!;
  public virtual Product Product { get; set; } = null!;
}
public partial class Payment
{
  public int IdPayment { get; set; }
  public string BankAccountNumber { get; set; } = string.Empty;
  public DateTime DateTime { get; set; }
  public decimal Value { get; set; }

  public int OrderId { get; set; }
  public virtual Order Order { get; set; } = null!;
}
public partial class Product
{
  public int IdProduct { get; set; }
  public string Name { get; set; } = string.Empty;
  public string Type { get; set; } = string.Empty;
  public decimal Price { get; set; }
  public string Description { get; set; } = string.Empty;
  public virtual ICollection<OrderItem> OrderItems { get; set; } = new List<OrderItem>();

}
public partial class User
{
  public int IdUser { get; set; }
  public string UserName { get; set; } = string.Empty;
  public string FirstName { get; set; } = string.Empty;
  public string LastName { get; set; } = string.Empty;
  public string Email { get; set; } = string.Empty;
}

2-OrderDbContext

After that, we created an entity framework database context and added our entities along with their configurations. We also seeded the in-memory database context with some data.

public partial class OrderDbContext : DbContext
{
.
.
  public virtual DbSet<User> Users { get; set; }
  public virtual DbSet<Order> Orders { get; set; }
  public virtual DbSet<OrderItem> OrderItems { get; set; }
  public virtual DbSet<Payment> Payments { get; set; }
  public virtual DbSet<Product> Products { get; set; }
.
.
}
public static class DataProvider
{
.
.
    public static void Seed(this ModelBuilder modelBuilder)
    {
        var users = GetUsers();
        modelBuilder.Entity<User>().HasData(users);

        var products = GetProducts();
        modelBuilder.Entity<Product>().HasData(products);

        var orders= GetOrders();
        modelBuilder.Entity<Order>().HasData(orders);

        var orderItems = GetOrderItems(products);
        modelBuilder.Entity<OrderItem>().HasData(orderItems);

        var payments=GetPayments(orderItems);
        modelBuilder.Entity<Payment>().HasData(payments);
    }
.
.
}

3-Orders Page

Next, we designed some MVC Razor pages to show the orders and their related entities.

4-Order Detail Page

5-Home and OrderItem Controllers

Finally, we created some controllers to be able to send search string values to the controllers and process them. For the sake of simplicity, we just focused on the essential code sections and removed the rest. To see the complete source code, refer to the related GitHub repository.

public class HomeController : Controller
{
.
.
  [HttpPost]
  public async Task<IActionResult> GetOrders()
  {
    .
    .

    if (!string.IsNullOrEmpty(searchValue))
    {
      queryableOrders = queryableOrders
        .Search(order => new
        {
          order.User.UserName,
          OrderDateTime = order.DateTime,
          order.Status,
          order.Payment.Value,
          PaymentDateTime = order.Payment.DateTime
        },
        searchValue);

      var enumerableOrders = DataProvider.GetEnumerableOrders();
      var retrievedEnumerableOrders = enumerableOrders
        .Search(order => new
        {
          order.User.UserName,
          OrderDateTime = order.DateTime,
          order.Status,
          order.Payment.Value,
          PaymentDateTime = order.Payment.DateTime
        }, searchValue)
        .ToList();
    }
    .
    .
  }
.
.
}
public class OrderItemController : Controller
{
.
.
  [HttpPost]
  public async Task<IActionResult> GetOrderItems()
  {
    .
    .
    if (!string.IsNullOrEmpty(searchValue))
    {
      queryableOrderItems = queryableOrderItems
        .Search(orderItem => new
        {
          orderItem.Product.Name,
          orderItem.Quantity,
          orderItem.Price,
          orderItem.Product.Description
        },
        searchValue);

      var enumerableOrderItemss = DataProvider
          .GetEnumerableOrderItems(parentId);
      var retrievedEnumerableOrderItems = enumerableOrderItemss
          .Search(orderItem => new
          {
              orderItem.Product.Name,
              orderItem.Quantity,
              orderItem.Price,
              orderItem.Product.Description
          }, searchValue)
          .ToList();
    }
    .
    .
  }
.
.
}

Note that, in the search methods in both controllers, if there is any search value, both IQueryable and IEnumerable versions of the search method are executed and return the same result.