We'll go over how to add a global search function to an ASP.NET Core MVC application in this article. This feature will provide search results with photographs and have a drop-down filter to search through all categories, products, and particular categories or goods. To ensure full end-to-end operation, we'll also develop detailed views for the search results.
Step 1: Configure Models and Database
We will first establish our models and database context. We'll use two models in this example: Product and Category.
Models
In the Models folder, create the Product and Category models.
namespace GlobalSearchInAspNetCoreMVC.Models
{
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public string? Description { get; set; }
public string ImageUrl { get; set; }
public int CategoryId { get; set; }
public Category Category { get; set; }
}
}
namespace GlobalSearchInAspNetCoreMVC.Models
{
public class Category
{
public int Id { get; set; }
public string? Name { get; set; }
}
}
namespace GlobalSearchInAspNetCoreMVC.Models
{
public class SearchResultItem
{
public int Id { get; set; }
public string? Name { get; set; }
public string? Description { get; set; }
public string? ImageUrl { get; set; }
}
}
Database Context
Create the ApplicationDbContext in the Data folder.
using GlobalSearchInAspNetCoreMVC.Models;
using Microsoft.EntityFrameworkCore;
namespace GlobalSearchInAspNetCoreMVC.Data
{
public class ApplicationDbContext : DbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
public DbSet<Product> Products { get; set; }
public DbSet<Category> Categories { get; set; }
}
}
Step 2. Create and Seed the Database
Configure the database connection string in appsettings.json.
{
"ConnectionStrings": {
"DefaultConnection": "Data Source=SQLLiteDatabase.db"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*"
}
Update the Startup. cs or Program. cs to use the ApplicationDbContext.
using GlobalSearchInAspNetCoreMVC.AppService;
using GlobalSearchInAspNetCoreMVC.Data;
using GlobalSearchInAspNetCoreMVC.IAppServices;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddRazorPages();
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlite(builder.Configuration.GetConnectionString("DefaultConnection")));
builder.Services.AddScoped<ISearchService, SearchService>();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
endpoints.MapRazorPages();
});
app.Run();
Create a migration and update the database.
add-migration GlobalSearch
update-database
Seed the Database
Create a DataSeeder class to seed the database with initial data.
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Linq;
namespace GlobalSearchInAspNetCoreMVC.Data
{
public static class DataSeeder
{
public static void Seed(IServiceProvider serviceProvider)
{
using (var context = new ApplicationDbContext(
serviceProvider.GetRequiredService<DbContextOptions<ApplicationDbContext>>()))
{
if (!context.Products.Any())
{
context.Products.AddRange(
new Product { Name = "Product 1", Description = "Description 1", ImageUrl = "/images/product1.jpg" },
new Product { Name = "Product 2", Description = "Description 2", ImageUrl = "/images/product2.jpg" }
);
context.SaveChanges();
}
if (!context.Categories.Any())
{
context.Categories.AddRange(
new Category { Name = "Category 1" },
new Category { Name = "Category 2" }
);
context.SaveChanges();
}
}
}
}
}
Or save the data using a database query.
-- Insert dummy data into Categories table
INSERT INTO Categories (Name)
VALUES
('Electronics'),
('Books'),
('Clothing'),
('Home & Kitchen'),
('Sports & Outdoors');
-- Insert dummy data into Products table
INSERT INTO Products (Name, Description, ImageUrl, CategoryId)
VALUES
('Smartphone', 'Latest model smartphone with advanced features', 'https://via.placeholder.com/150/0000FF/808080?text=Smartphone', 1),
('Laptop', 'High performance laptop suitable for gaming and work', 'https://via.placeholder.com/150/FF0000/FFFFFF?text=Laptop', 1),
('Headphones', 'Noise-cancelling over-ear headphones', 'https://via.placeholder.com/150/FFFF00/000000?text=Headphones', 1),
('Science Fiction Book', 'A gripping science fiction novel', 'https://via.placeholder.com/150/00FF00/000000?text=Science+Fiction+Book', 2),
('Cookbook', 'Delicious recipes for home cooks', 'https://via.placeholder.com/150/FF69B4/000000?text=Cookbook', 2),
('T-shirt', 'Comfortable cotton T-shirt', 'https://via.placeholder.com/150/800080/FFFFFF?text=T-shirt', 3),
('Jeans', 'Stylish denim jeans', 'https://via.placeholder.com/150/FFA500/FFFFFF?text=Jeans', 3),
('Blender', 'High-speed blender for smoothies and soups', 'https://via.placeholder.com/150/000080/FFFFFF?text=Blender', 4),
('Coffee Maker', 'Programmable coffee maker with timer', 'https://via.placeholder.com/150/FFC0CB/000000?text=Coffee+Maker', 4),
('Yoga Mat', 'Non-slip yoga mat for all types of exercise', 'https://via.placeholder.com/150/008080/FFFFFF?text=Yoga+Mat', 5),
('Dumbbells', 'Set of adjustable dumbbells', 'https://via.placeholder.com/150/000000/FFFFFF?text=Dumbbells', 5);
Call the seeder in the Program. cs.
public static void Main(string[] args)
{
var host = CreateHostBuilder(args).Build();
using (var scope = host.Services.CreateScope())
{
var services = scope.ServiceProvider;
var context = services.GetRequiredService<ApplicationDbContext>();
context.Database.Migrate();
DataSeeder.Seed(services);
}
host.Run();
}
Step 3. Implement the Search Service
Create a search service to handle the search logic.
ISearchService.cs
using GlobalSearchInAspNetCoreMVC.Models;
namespace GlobalSearchInAspNetCoreMVC.IAppServices
{
public interface ISearchService
{
Task<IEnumerable<object>> SearchAsync(string searchTerm,string filter);
Task<SearchResultItem> GetItemByIdAsync(int id);
}
}
SearchService.cs
using GlobalSearchInAspNetCoreMVC.Data;
using GlobalSearchInAspNetCoreMVC.IAppServices;
using GlobalSearchInAspNetCoreMVC.Models;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace GlobalSearchInAspNetCoreMVC.AppService
{
public class SearchService : ISearchService
{
private readonly ApplicationDbContext _context;
public SearchService(ApplicationDbContext context)
{
_context = context;
}
public async Task<IEnumerable<object>> SearchAsync(string searchTerm, string filter)
{
List<object> results = new List<object>();
if (filter == "all" || filter == "products")
{
var products = await _context.Products
.Where(p => p.Name.Contains(searchTerm) || p.Description.Contains(searchTerm))
.Select(p => new { p.Id, p.Name, p.Description, p.ImageUrl })
.ToListAsync();
results.AddRange(products);
}
if (filter == "all" || filter == "categories")
{
var categories = await _context.Categories
.Where(c => c.Name.Contains(searchTerm))
.Select(c => new { c.Id, c.Name })
.ToListAsync();
results.AddRange(categories);
}
return results;
}
public async Task<SearchResultItem> GetItemByIdAsync(int id)
{
var product = await _context.Products.FindAsync(id);
if (product != null)
{
return new SearchResultItem
{
Id = product.Id,
Name = product.Name,
Description = product.Description,
ImageUrl = product.ImageUrl
};
}
var category = await _context.Categories.FindAsync(id);
if (category != null)
{
return new SearchResultItem
{
Id = category.Id,
Name = category.Name,
Description = "Category"
};
}
return null;
}
}
}
Step 4. Create the HomeController
Create the HomeController to handle the search and detail views.
HomeController.cs
using GlobalSearchInAspNetCoreMVC.IAppServices;
using Microsoft.AspNetCore.Mvc;
namespace GlobalSearchInAspNetCoreMVC.Controllers
{
public class HomeController : Controller
{
private readonly ISearchService _searchService;
public HomeController(ISearchService searchService)
{
_searchService = searchService;
}
public IActionResult Index()
{
return View();
}
public async Task<IActionResult> Details(int id)
{
var item = await _searchService.GetItemByIdAsync(id);
if (item == null)
{
return NotFound();
}
return View(item);
}
}
}
Step 5. Create the Views
Views/Home/Index.cshtml
<div class="text-center">
<h1 class="display-4">Search</h1>
<div class="form-group">
<select id="search-filter" class="form-control">
<option value="all">All</option>
<option value="products">Products</option>
<option value="categories">Categories</option>
</select>
</div>
<input type="text" id="search-input" placeholder="Search..." class="form-control" />
<div id="search-results" class="dropdown-menu" style="display: block; position: absolute; width: 100%;"></div>
</div>
@section Scripts {
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script>
$(document).ready(function () {
function performSearch() {
let query = $('#search-input').val();
let filter = $('#search-filter').val();
if (query.length >= 3) {
$.ajax({
url: '/api/search',
type: 'GET',
data: { query: query, filter: filter },
success: function (data) {
let results = $('#search-results');
results.empty();
if (data.length > 0) {
data.forEach(item => {
if (item.imageUrl) { // Check if item has an image
results.append(`
<a class="dropdown-item" href="/Home/Details/${item.id}">
<img src="${item.imageUrl}" alt="${item.name}" style="width:50px; height:50px;"/>
${item.name || item.description}
</a>
`);
} else {
results.append(`<a class="dropdown-item" href="/Home/Details/${item.id}">${item.name || item.description}</a>`);
}
});
} else {
results.append('<a class="dropdown-item">No results found</a>');
}
}
});
} else {
$('#search-results').empty();
}
}
$('#search-input').on('input', performSearch);
$('#search-filter').on('change', performSearch);
});
</script>
}
Views/Home/Details.cshtml
@model SearchResultItem
@{
ViewData["Title"] = "Details";
}
<div class="text-center">
<h1 class="display-4">Details</h1>
@if (Model != null)
{
<div class="card" style="width: 18rem;">
@if (!string.IsNullOrEmpty(Model.ImageUrl))
{
<img class="card-img-top" src="@Model.ImageUrl" alt="Card image cap">
}
<div class="card-body">
<h5 class="card-title">@Model.Name</h5>
<p class="card-text">@Model.Description</p>
</div>
</div>
}
else
{
<p>Item not found.</p>
}
</div>
Step 6. Create the Search Controller
using GlobalSearchInAspNetCoreMVC.IAppServices;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace GlobalSearchInAspNetCoreMVC.Controllers
{
[ApiController]
[Route("api/[controller]")]
public class SearchController : ControllerBase
{
private readonly ISearchService _searchService;
public SearchController(ISearchService searchService)
{
_searchService = searchService;
}
[HttpGet]
public async Task<IActionResult> Get(string query, string filter)
{
if (string.IsNullOrEmpty(query))
{
return BadRequest("Query cannot be empty");
}
var results = await _searchService.SearchAsync(query, filter);
return Ok(results);
}
}
}
Step 7. Run and Test
Now that everything is set up, run your application. You should be able to search for products and categories using the dropdown filter, view the search results with images, and click on each result to see detailed information.
Step 8. Output
Conclusion
We've added a global search function to an ASP.NET Core MVC application in this article. Creating models, establishing the database context, populating the database with initial information, putting the search service into operation, and generating the required views and controllers were all addressed. This complete implementation ought to offer a thorough grasp of how to create a search feature in ASP.NET Core MVC applications.