European ASP.NET MVC 4 and MVC 5 Hosting

BLOG about ASP.NET MVC 3, ASP.NET MVC 4, and ASP.NET MVC 5 Hosting and Its Technology - Dedicated to European Windows Hosting Customer

ASP.NET MVC Hosting - HostForLIFE.eu :: Full-Stack Web Development in ASP.NET Core 8 MVC

clock July 26, 2024 08:55 by author Peter

Complete-Stack Development
The process of designing, creating, and implementing a web application's client-side (or front-end) and server-side (or back-end) components is known as full-stack web development. Developers handle all aspects of this comprehensive method, including database management, server configuration, and user interface design and interaction.

Components of a Full-Stack Web UI
Front-End Development
HTML, CSS, and JavaScript
Front-end development involves creating the visual elements and user experience of a website or web application. HTML (HyperText Markup Language), CSS (Cascading Style Sheets), and JavaScript form the core technologies used in front-end development. HTML provides the structure, CSS dictates the presentation and layout, and JavaScript adds interactivity and dynamic behavior to the user interface.

Front-End Frameworks and Libraries

To streamline development and enhance functionality, front-end developers often leverage frameworks and libraries such as React, Angular, Vue.js, and Bootstrap. These tools provide pre-built components, responsive design capabilities, and state management features, enabling developers to build interactive and visually appealing user interfaces more efficiently.

Back-End Development
Server-Side Programming Languages
Back-end development focuses on the server-side logic and functionality of a web application. Developers use programming languages like JavaScript (Node.js), Python (Django, Flask), Ruby (Ruby on Rails), Java (Spring Boot), and C# (ASP.NET Core) to implement server-side operations, data processing, and communication with databases.

Databases and Data Storage
Back-end developers work with databases to store, retrieve, and manipulate data required by the web application. Common types of databases include relational databases like MySQL, PostgreSQL, and SQL Server, as well as NoSQL databases like MongoDB and Firebase Firestore. Developers also utilize Object-Relational Mapping (ORM) libraries to simplify database interactions and manage data models.

APIs and Web Services
Application Programming Interfaces (APIs) play a crucial role in full-stack development by facilitating communication between the front-end and back-end components of a web application. Developers design and implement RESTful APIs or GraphQL endpoints to define how data is exchanged between the client and server. Web services, such as authentication services, payment gateways, and third-party integrations, are also integrated into the back-end architecture to extend the functionality of the web application.

DevOps and Deployment
Continuous Integration and Deployment (CI/CD)

DevOps practices are essential for streamlining the development, testing, and deployment processes of a full-stack web application. Developers utilize CI/CD pipelines to automate build, test, and deployment tasks, ensuring rapid and reliable delivery of updates and new features to production environments.

Cloud Platforms and Hosting Services
Full-stack developers deploy web applications on cloud platforms like Amazon Web Services (AWS), Microsoft Azure, and Google Cloud Platform (GCP), or deploy them to managed hosting services such as Heroku, Netlify, or Vercel. These platforms provide scalable infrastructure, serverless computing, and management tools that simplify deployment, monitoring, and maintenance tasks.

Introduction to ASP.NET Core 8 MVC

ASP.NET Core 8 MVC (Model-View-Controller) is a powerful framework for building modern, scalable, and feature-rich web applications. With its robust architecture, flexible programming model, and extensive tooling support, ASP.NET Core 8 MVC empowers developers to create full-stack web applications that meet the demands of today's digital landscape.

Components of ASP.NET Core 8 MVC

Model
In ASP.NET Core 8 MVC, the model represents the data and business logic of the application. Developers define model classes to encapsulate data entities, define relationships, and implement business rules. Models interact with the database through Entity Framework Core or other data access libraries, enabling seamless data manipulation and persistence.

View

Views in ASP.NET Core 8 MVC are responsible for presenting the user interface and rendering dynamic content to the client. Developers create view templates using Razor syntax, which combines HTML markup with C# code to generate dynamic web pages. Views leverage model data to display information to users and incorporate client-side scripting languages like JavaScript for enhanced interactivity.

Controller
Controllers act as intermediaries between the model and view components in ASP.NET Core 8 MVC. They handle user requests, execute business logic, and orchestrate data retrieval and manipulation operations. Controllers receive input from the user through the browser, process it, and return an appropriate response, such as rendering a view or redirecting to another page.

Full Stack Development in ASP.NET Core 8 MVC

Front-End Development
HTML, CSS, and JavaScript

Front-end development in ASP.NET Core 8 MVC involves creating responsive and visually appealing user interfaces using HTML, CSS, and JavaScript. Developers leverage HTML for structure, CSS for styling, and JavaScript for client-side interactivity and dynamic behavior.

Razor Pages and Views
ASP.NET Core 8 MVC utilizes Razor Pages and Views to generate dynamic web content. Developers use Razor syntax within HTML markup to incorporate server-side logic, access model data, and render dynamic content. Razor Pages provide a streamlined approach to building web pages, while Views offer more flexibility and customization options.

Back-End Development
C# Programming Language
Back-end development in ASP.NET Core 8 MVC revolves around the C# programming language, which serves as the primary language for implementing server-side logic, business rules, and data access operations. C# offers a robust and type-safe development environment, enabling developers to build scalable and maintainable back-end components.

Entity Framework Core

Entity Framework Core is a powerful Object-Relational Mapping (ORM) framework that simplifies data access and manipulation in ASP.NET Core 8 MVC applications. Developers use Entity Framework Core to define data models, perform database migrations, execute LINQ queries, and interact with the underlying database seamlessly.

ASP.NET Core Identity

ASP.NET Core Identity provides robust authentication and authorization features for securing ASP.NET Core 8 MVC applications. Developers leverage ASP.NET Core Identity to implement user authentication, role-based access control, password hashing, and account management functionalities, ensuring the security and integrity of web applications.

Deployment and DevOps
Continuous Integration and Deployment (CI/CD)
DevOps practices play a crucial role in the deployment and maintenance of ASP.NET Core 8 MVC applications. Developers implement CI/CD pipelines to automate build, test, and deployment processes, ensuring rapid and reliable delivery of updates to production environments. Tools like Azure DevOps, GitHub Actions, and Jenkins streamline the CI/CD workflow, enabling efficient collaboration and deployment.

Nuget Packages for this application
Microsoft.AspNetCore.Mvc
Microsoft.EntityFrameworkCore
Microsoft.AspNetCore.Identity.EntityFrameworkCore
Microsoft.Extensions.Logging
Microsoft.Extensions.Configuration
Microsoft.AspNetCore.Authentication.Cookies
Microsoft.Extensions.DependencyInjection
Newtonsoft.Json
Microsoft.AspNetCore.StaticFiles
Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore

SQLite Database
We will use SQL Lite database for this application

Nuget Package for SQL Lite Database
Microsoft.EntityFrameworkCore.Sqlite

Student Model
namespace FullStackDevelopmentUsingAspnetCore8MVC.Models
{
    public class Student
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string RollNo { get; set; }
        public string Section { get; set; }
        public string Program { get; set; }
    }
}


Application Database Context

using FullStackDevelopmentUsingAspnetCore8MVC.Models;
using Microsoft.EntityFrameworkCore;

namespace FullStackDevelopmentUsingAspnetCore8MVC.ApplicationDbContext
{
    public class AppDbContext : DbContext
    {
        public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }

        public DbSet<Student> Students { get; set; }
    }
}


Application Setting File

// appsettings.json
{
  "ConnectionStrings": {
    "DefaultConnection": "Data Source=FullStackDevlopment.db"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*"
}


Program.cs Class
using FullStackDevelopmentUsingAspnetCore8MVC.ApplicationDbContext;
using Microsoft.EntityFrameworkCore;

var builder = WebApplication.CreateBuilder(args);
var configuration = builder.Configuration;

// Add services to the container.
builder.Services.AddDbContext<AppDbContext>(options =>
    options.UseSqlite(configuration.GetConnectionString("DefaultConnection")));
builder.Services.AddRazorPages();

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.UseAuthorization();

app.UseHttpsRedirection();

app.UseStaticFiles();

app.UseRouting();

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");


app.MapRazorPages();

app.Run();

Student Controller
using FullStackDevelopmentUsingAspnetCore8MVC.ApplicationDbContext;
using FullStackDevelopmentUsingAspnetCore8MVC.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;

namespace FullStackDevelopmentUsingAspnetCore8MVC.Controllers
{
    public class StudentsController : Controller
    {
        private readonly AppDbContext _context;

        public StudentsController(AppDbContext context)
        {
            _context = context;
        }

        // GET: Students
        public async Task<IActionResult> Index()
        {
            return View(await _context.Students.ToListAsync());
        }

        // GET: Students/Details/5
        public async Task<IActionResult> Details(int? id)
        {
            if(id == null)
            {
                return NotFound();
            }

            var student = await _context.Students
                .FirstOrDefaultAsync(m => m.Id == id);
            if(student == null)
            {
                return NotFound();
            }

            return View(student);
        }

        // GET: Students/Create
        public IActionResult Create()
        {
            return View();
        }

        // POST: Students/Create
        // To protect from overposting attacks, enable the specific properties you want to bind to.
        // For more details, see http://go.microsoft.com/fwlink/?LinkId=317598.
        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> Create([Bind("Id,Name,RollNo,Section,Program")] Student student)
        {
            if(ModelState.IsValid)
            {
                _context.Add(student);
                await _context.SaveChangesAsync();
                return RedirectToAction(nameof(Index));
            }
            return View(student);
        }

        // GET: Students/Edit/5
        public async Task<IActionResult> Edit(int? id)
        {
            if(id == null)
            {
                return NotFound();
            }

            var student = await _context.Students.FindAsync(id);
            if(student == null)
            {
                return NotFound();
            }
            return View(student);
        }

        // POST: Students/Edit/5
        // To protect from overposting attacks, enable the specific properties you want to bind to.
        // For more details, see http://go.microsoft.com/fwlink/?LinkId=317598.
        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> Edit(int id, [Bind("Id,Name,RollNo,Section,Program")] Student student)
        {
            if(id != student.Id)
            {
                return NotFound();
            }

            if(ModelState.IsValid)
            {
                try
                {
                    _context.Update(student);
                    await _context.SaveChangesAsync();
                }
                catch(DbUpdateConcurrencyException)
                {
                    if(!StudentExists(student.Id))
                    {
                        return NotFound();
                    }
                    else
                    {
                        throw;
                    }
                }
                return RedirectToAction(nameof(Index));
            }
            return View(student);
        }

        // GET: Students/Delete/5
        public async Task<IActionResult> Delete(int? id)
        {
            if(id == null)
            {
                return NotFound();
            }

            var student = await _context.Students
                .FirstOrDefaultAsync(m => m.Id == id);
            if(student == null)
            {
                return NotFound();
            }

            return View(student);
        }

        // POST: Students/Delete/5
        [HttpPost, ActionName("Delete")]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> DeleteConfirmed(int id)
        {
            var student = await _context.Students.FindAsync(id);
            if(student != null)
            {
                _context.Students.Remove(student);
            }

            await _context.SaveChangesAsync();
            return RedirectToAction(nameof(Index));
        }

        private bool StudentExists(int id)
        {
            return _context.Students.Any(e => e.Id == id);
        }
    }
}


Front-End Development Using CSHTML Pages
INDEX Page For showing Details
@model IEnumerable<FullStackDevelopmentUsingAspnetCore8MVC.Models.Student>

@{
    ViewData["Title"] = "Index";
    Layout = "~/Pages/Shared/_Layout.cshtml";
}

<h1>Index</h1>

<p>
    <a asp-action="Create">Create New</a>
</p>
<table class="table">
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.Name)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.RollNo)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Section)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Program)
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
@foreach (var item in Model) {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.Name)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.RollNo)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Section)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Program)
            </td>
            <td>
                <a asp-action="Edit" asp-route-id="@item.Id">Edit</a> |
                <a asp-action="Details" asp-route-id="@item.Id">Details</a> |
                <a asp-action="Delete" asp-route-id="@item.Id">Delete</a>
            </td>
        </tr>
}
    </tbody>
</table>


CREATE Page for creating student
@model FullStackDevelopmentUsingAspnetCore8MVC.Models.Student

@{
    ViewData["Title"] = "Create";
    Layout = "~/Pages/Shared/_Layout.cshtml";
}

<h1>Create</h1>

<h4>Student</h4>
<hr />
<div class="row">
    <div class="col-md-4">
        <form asp-action="Create">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="Name" class="control-label"></label>
                <input asp-for="Name" class="form-control" />
                <span asp-validation-for="Name" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="RollNo" class="control-label"></label>
                <input asp-for="RollNo" class="form-control" />
                <span asp-validation-for="RollNo" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Section" class="control-label"></label>
                <input asp-for="Section" class="form-control" />
                <span asp-validation-for="Section" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Program" class="control-label"></label>
                <input asp-for="Program" class="form-control" />
                <span asp-validation-for="Program" class="text-danger"></span>
            </div>
            <div class="form-group">
                <input type="submit" value="Create" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div>

<div>
    <a asp-action="Index">Back to List</a>
</div>

@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}


DELETE Page for Deleting Student
@model FullStackDevelopmentUsingAspnetCore8MVC.Models.Student

@{
    ViewData["Title"] = "Delete";
    Layout = "~/Pages/Shared/_Layout.cshtml";
}

<h1>Delete</h1>

<h3>Are you sure you want to delete this?</h3>
<div>
    <h4>Student</h4>
    <hr />
    <dl class="row">
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.Name)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Name)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.RollNo)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.RollNo)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.Section)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Section)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.Program)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Program)
        </dd>
    </dl>

    <form asp-action="Delete">
        <input type="hidden" asp-for="Id" />
        <input type="submit" value="Delete" class="btn btn-danger" /> |
        <a asp-action="Index">Back to List</a>
    </form>
</div>


DETAILS Page for showing individual Student Details
@model FullStackDevelopmentUsingAspnetCore8MVC.Models.Student

@{
    ViewData["Title"] = "Details";
    Layout = "~/Pages/Shared/_Layout.cshtml";
}

<h1>Details</h1>

<div>
    <h4>Student</h4>
    <hr />
    <dl class="row">
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.Name)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Name)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.RollNo)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.RollNo)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.Section)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Section)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.Program)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Program)
        </dd>
    </dl>
</div>
<div>
    <a asp-action="Edit" asp-route-id="@Model?.Id">Edit</a> |
    <a asp-action="Index">Back to List</a>
</div>


Edit Page for Editing the Student Details
@model FullStackDevelopmentUsingAspnetCore8MVC.Models.Student

@{
    ViewData["Title"] = "Edit";
    Layout = "~/Pages/Shared/_Layout.cshtml";
}

<h1>Edit</h1>

<h4>Student</h4>
<hr />
<div class="row">
    <div class="col-md-4">
        <form asp-action="Edit">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <input type="hidden" asp-for="Id" />
            <div class="form-group">
                <label asp-for="Name" class="control-label"></label>
                <input asp-for="Name" class="form-control" />
                <span asp-validation-for="Name" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="RollNo" class="control-label"></label>
                <input asp-for="RollNo" class="form-control" />
                <span asp-validation-for="RollNo" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Section" class="control-label"></label>
                <input asp-for="Section" class="form-control" />
                <span asp-validation-for="Section" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Program" class="control-label"></label>
                <input asp-for="Program" class="form-control" />
                <span asp-validation-for="Program" class="text-danger"></span>
            </div>
            <div class="form-group">
                <input type="submit" value="Save" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div>

<div>
    <a asp-action="Index">Back to List</a>
</div>

@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}


Conclusion
ASP.NET Core 8 MVC empowers developers to build full-stack web applications with unparalleled flexibility, scalability, and performance. By mastering the components of ASP.NET Core 8 MVC, including models, views, controllers, front-end and back-end development tools, and deployment practices, developers can create robust and feature-rich web applications that meet the evolving needs of users and businesses.



ASP.NET MVC 6 Hosting - HostForLIFE.eu :: Exception Handling in MVC

clock July 18, 2024 10:07 by author Peter

The practice of reacting to unusual circumstances that call for extra processing is known as exception handling. In any program, addressing exceptions is crucial. There are two methods we can deal with exceptions in ASP.NET.

  • Try-catch-finally block at method level
  • Using Application_Error

Exception Handling in MVC
In ASP.NET MVC we have a larger list of ways to handle exceptions such as,

  • Try-catch-finally
  • Overriding OnException method
  • Using the [HandleError] attribute on actions and controllers
  • Setting a global exception-handling filter
  • Handling Application_Error event
  • Extending HandleErrorAttribute

To begin with, create a new empty ASP.NET MVC application. Add a controller as "ExceptionTestingController". Add a view for Index. Add another view at "Views\Shared" as "Error. cshtml".

For testing purposes, we would cause some kind of exception.

try-catch-finally

Change the Index as in the following.
public ActionResult Index()
{
    int a = 1;
    int b = 0;
    int c = 0;
    try
    {
        c = a / b; // This line would cause an exception.
    }
    catch (Exception ex)
    {
        return View("Error");
    }
    finally
    {
        // Optional code to execute after try block (e.g., cleanup)
    }
    return View();
}


Here we are generating an exception at "c = a / b;" inside the try block. In the catch, we are returning a different view "Error". If you run the application you will get an Error page in the browser. In an actual project, we can also use the catch block to log the error information.

Overriding OnException method

For this, remove the try block from the preceding code. We need to overwrite OnException as in the following.
public ActionResult Index()
{
    int a = 1;
    int b = 0;
    int c = 0;
    c = a / b; // This line would cause an exception.
    return View();
}

protected override void OnException(ExceptionContext filterContext)
{
    string action = filterContext.RouteData.Values["action"].ToString();
    Exception e = filterContext.Exception;
    filterContext.ExceptionHandled = true;
    filterContext.Result = new ViewResult()
    {
        ViewName = "Error"
    };
}


OnException is a void method that takes an argument as an object of ExceptionContext that has all the information about the exception that can be used to log. We need to set ExceptionHandled = true for the ExceptionContext object. We can handle the exception generated from all the actions from a specific controller. We can get the action name from ExceptionContext as in.
string action = filterContext.RouteData.Values["action"].ToString();

There is a problem with this approach that we cannot reuse the exception handling logic across multiple controllers. That is where global error handling is useful.

Using HandleError Attribute

This is a simple way to handle exceptions in MVC. Remove the OnException implementation from the previous code. Use the following procedure to do that.

Step 1. Add <customErrors mode="On" ></customErrors> in web.config under <system.web>.
Step 2. Decorate the action with [HandleError] as,
[HandleError]
public ActionResult Index()
{
    int a = 1;
    int b = 0;
    int c = 0;
    c = a / b; // This line will cause an exception.
    return View();
}

We can handle a different exception with a different view with [HandleError] as in the following.
[HandleError]
[HandleError(ExceptionType = typeof(DivideByZeroException), View = "Error1")]
[HandleError(ExceptionType = typeof(ArgumentOutOfRangeException), View = "Error2")]
public ActionResult Index()
{
    int a = 1;
    int b = 0;
    int c = 0;
    c = a / b; // This would cause an exception.
    return View();
}

Here we should place a specific HandleError at the bottom if we reverse the order of the HandleError attribute then the Error. cshtml will always be displayed.

In the same way, we can decorate our controller with HandleError. This would handle the exception generated from all the actions from a specific controller.

Limitations

  • Does not support logging exceptions.
  • Doesn't catch HTTP exceptions other than 500.
  • Doesn't catch exceptions that are raised outside controllers.
  • Returns an error view even for exceptions raised in AJAX calls.

Setting a global exception-handling filter
For this, we need to ensure that HandleErrorAttribute is added in RegisterGlobalFilters of the FilterConfig file of App_start and registered in Application_Start. No need to decorate our action and controller with [HandleError].

Extending HandleErrorAttribute

We can also create our own Exception Handler by inheriting from HandleErrorAttribute as in the following.
public class MyExceptionHandler : HandleErrorAttribute
{
    public override void OnException(ExceptionContext filterContext)
    {
        if (filterContext.ExceptionHandled || filterContext.HttpContext.IsCustomErrorEnabled)
        {
            return;
        }

        Exception e = filterContext.Exception;
        filterContext.ExceptionHandled = true;
        filterContext.Result = new ViewResult()
        {
            ViewName = "Error2"
        };
    }
}


We need to set ExceptionHandled = true for the ExceptionContext object. We can handle the exception generated from all the actions from a specific controller. We can also get the action name from ExceptionContext as in the following.
string action = filterContext.RouteData.Values["action"].ToString();

Now we can decorate our action/controller with [MyExceptionHandler] to use this.
[MyExceptionHandler]
public ActionResult Index()
{
    int a = 1;
    int b = 0;
    int c = 0;

    // This operation would cause an exception (division by zero).
    c = a / b;

    return View();
}



ASP.NET MVC 6 Hosting - HostForLIFE.eu :: Using Detailed Views for Global Search in ASP.NET Core MVC

clock July 11, 2024 08:06 by author Peter

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.

 



ASP.NET MVC 6 Hosting - HostForLIFE.eu :: Using Gmail Server to Send Email in ASP.NET Core MVC

clock July 2, 2024 07:11 by author Peter

This tutorial will show you how to use the Gmail server in an ASP.NET Core MVC application to send emails. For handling the email functionality, we will make use of the MailKit and MimeKit libraries.

Step 1: Establish an MVC ASP.NET Core project

  • Make a new project in Visual Studio by opening it.
  • Click "Next" after selecting "ASP.NET Core Web Application".
  • Click "Create" after giving your project a name.
  • Make sure ".NET Core" and "ASP.NET Core 3.1 or later" are selected when you choose "Web Application (Model-View-Controller)". Press "Create."

Step 2 Install the necessary NuGet packages
To facilitate emailing, install the MailKit and MimeKit packages.

  • Navigating to Tools > NuGet Package Manager > Package Manager Console will launch the NuGet Package Manager Console.
  • Execute the subsequent commands.
Install-Package MailKit
Install-Package MimeKit


Step 3. Configure SMTP Settings
Add your Gmail SMTP settings in the appsettings.json file.
{
  "SmtpSettings": {
    "Server": "smtp.gmail.com",
    "Port": 587,
    "SenderName": "Your Name",
    "SenderEmail": "[email protected]",
    "Username": "[email protected]",
    "Password": "your-app-specific-password"
  }
}


To generate an app-specific password, follow these steps.
Go to your Google Account
  • Visit myaccount.google.com.
Security Settings
  • Navigate to the "Security" section.
App Passwords
  • Under "Signing in to Google," find "App passwords."
  • You might need to sign in again.
  • Select "App passwords."
Generate an app password
  • Select the app (e.g., Mail) and the device (e.g., Windows Computer) from the dropdown.
  • Click "Generate."
  • Google will provide a 16-character password.
Update Your Configuration
  • Use this 16-character password in your appsettings.json instead of your normal Google account password.
Step 4. Create the Email Service
Create a service to handle the email-sending logic. Add a new class EmailService.cs to your project.
using MimeKit;
using MailKit.Net.Smtp;
using Microsoft.Extensions.Configuration;
using System.Threading.Tasks;
namespace EmailSendingInAspNetCoreMVC.Services
{
    public class EmailService
    {
        private readonly IConfiguration _configuration;
        public EmailService(IConfiguration configuration)
        {
            _configuration = configuration;
        }
        public async Task SendEmailAsync(string toEmail, string subject, string message)
        {
            var emailMessage = new MimeMessage();
            emailMessage.From.Add(new MailboxAddress(_configuration["SmtpSettings:SenderName"], _configuration["SmtpSettings:SenderEmail"]));
            emailMessage.To.Add(new MailboxAddress("", toEmail));
            emailMessage.Subject = subject;
            emailMessage.Body = new TextPart("plain") { Text = message };
            using (var client = new SmtpClient())
            {
                await client.ConnectAsync(_configuration["SmtpSettings:Server"], int.Parse(_configuration["SmtpSettings:Port"]), MailKit.Security.SecureSocketOptions.StartTls);
                await client.AuthenticateAsync(_configuration["SmtpSettings:Username"], _configuration["SmtpSettings:Password"]);
                await client.SendAsync(emailMessage);
                await client.DisconnectAsync(true);
            }
        }
    }
}


Step 5. Register for the Email Service

Register the EmailService in the Startup.cs file.
using EmailSendingInAspNetCoreMVC.Services;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddRazorPages();
builder.Services.AddControllersWithViews();
builder.Services.AddSingleton<EmailService>();
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.MapRazorPages();
app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");
app.Run();


Step 6. Create the EmailViewModel
Add a new class EmailViewModel.cs to your Models folder.
namespace EmailSendingInAspNetCoreMVC.ViewModels
{
    public class EmailViewModel
    {
        public string? ToEmail { get; set; }
        public string? Subject { get; set; }
        public string? Message { get; set; }
    }
}


Step 7. Create the Email Controller
Add a new controller EmailController.cs to handle the email sending requests.
using EmailSendingInAspNetCoreMVC.Sevices;
using EmailSendingInAspNetCoreMVC.ViewModels;
using Microsoft.AspNetCore.Mvc;
namespace EmailSendingInAspNetCoreMVC.Controllers
{
    public class EmailController : Controller
    {
        private readonly EmailService _emailService;

        public EmailController(EmailService emailService)
        {
            _emailService = emailService;
        }
        [HttpGet]
        public IActionResult SendEmail()
        {
            return View();
        }
        [HttpPost]
        public async Task<IActionResult> SendEmail(EmailViewModel model)
        {
            if (ModelState.IsValid)
            {
                await _emailService.SendEmailAsync(model.ToEmail, model.Subject, model.Message);
                ViewBag.Message = "Email sent successfully!";
            }
            return View(model);
        }
    }
}


Step 8. Create the Email View

Create a new view SendEmail.cshtml in the Views/Email folder.
@model EmailViewModel
@{
    ViewData["Title"] = "Send Email";
    Layout = "~/Views/Shared/_Layout.cshtml";
}
<form asp-action="SendEmail" method="post">
    <div>
        <label asp-for="ToEmail"></label>
        <input asp-for="ToEmail" type="email" required />
        <span asp-validation-for="ToEmail"></span>
    </div>
    <div>
        <label asp-for="Subject"></label>
        <input asp-for="Subject" type="text" required />
        <span asp-validation-for="Subject"></span>
    </div>
    <div>
        <label asp-for="Message"></label>
        <textarea asp-for="Message" required></textarea>
        <span asp-validation-for="Message"></span>
    </div>
    <div>
        <input type="submit" value="Send Email" />
    </div>
</form>
@if (ViewBag.Message != null)
{
    <p>@ViewBag.Message</p>
}

Step 9. Add Validation Scripts
Ensure that validation scripts are included in your _Layout.cshtml.
<script src="~/lib/jquery/dist/jquery.min.js"></script>
<script src="~/lib/jquery-validation/dist/jquery.validate.min.js"></script>
<script src="~/lib/jquery-validation-unobtrusive/dist/jquery.validate.unobtrusive.min.js"></script>


Step 10. Run the Application
Build and run your application.
Navigate to /Email/SendEmail.
Fill out the form and send an email.

Conclusion
By following this guide, you will successfully integrate email-sending functionality into your ASP.NET Core MVC application using the Gmail SMTP server. Utilizing `MailKit` and `MimeKit` libraries ensures secure and efficient handling of emails. Setting up an `EmailService`, creating an `EmailViewModel`, and configuring an `EmailController` with a corresponding view will provide a complete solution for sending emails. Generating an application-specific password for Gmail ensures compatibility with accounts that have two-factor authentication enabled. This setup enhances your application's functionality and enables effective communication with users.



About HostForLIFE.eu

HostForLIFE.eu is European Windows Hosting Provider which focuses on Windows Platform only. We deliver on-demand hosting solutions including Shared hosting, Reseller Hosting, Cloud Hosting, Dedicated Servers, and IT as a Service for companies of all sizes.

We have offered the latest Windows 2016 Hosting, ASP.NET Core 2.2.1 Hosting, ASP.NET MVC 6 Hosting and SQL 2017 Hosting.


Tag cloud

Sign in