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 :: Generic Repository Pattern With MVC

clock January 24, 2025 07:21 by author Peter

In this article, I am going to explain the generic repository pattern. We are going to start the concept and implementation of the repository pattern, so first, let's try to understand what it is.


Why go with the repository pattern?

Enterprise applications such as web-based enterprise applications, order management systems, payroll, accounting applications, etc., are large-scale data flow applications. Along with data flow, they have some other features too.

  • Large amount of data: These types (accounting, payroll, order management) of applications handle quite a large amount of data. And this amount of data is not fixed. It may vary day by day and get more and more than the previous day.
  • Separation of database and application: Handling of data for an application is a major undertaking that needs some persistent storage. We usually separate the database engine from the application. The data object is a multiuser environment. At a given point in time, the data should be overwritten and given a concurrency feature.
  • Modification in business rules is always acceptable: Enterprise applications will always have new rules, so our application should be ready to accept those changes without affecting the past code and logic. These rules can change at any time. At the time of business upgrade, our application should be ready to accept all the changes. Sometimes, business rules have operations that usually deal with the size (data) and display the data on many pages or one page, we may have to display the data from different tables.

We come across these scenarios daily and reach one conclusion, that is,

Enterprise applications function in three different areas

  • Data Access: This layer deals with the data storage and retrieval, such as CRUD (Create, Read, Update, Delete)
  • Business Rule: This layer deals with data access such as reading and writing data and encapsulation with business-specific rules
  • User Interface: Displays the data to the end user and also accepts input from the end user.

We use a repository pattern when the scenario is dealing with a complex set of queries from multiple tables, to avoid duplication so that we will not write the same code at multiple places.

In these scenarios, we use a layer between our domain class and the data access layer. This layer will take care of all operations that can be reused again and again. In short, we can say that "Repository pattern plays a mediator's role in between the data-access layer and all the system."

Once the repository pattern is implemented, the client code won’t invoke the data access directly. Instead, we will invoke the repository to get the job done. The repository offers a collection interface by providing methods to add, modify, remove, and fetch domain objects.

Let’s try to implement a generic repository pattern in the ASP MVC application. Before going to the implementation part, it is a very common question usually asked by many developers - Entity Framework is already developed in a repository pattern. Then, why will we again create a repository pattern?

I am trying to answer that.

In a small application, it is not required at all, but in enterprise applications, it is beneficial.

  • Promoting loose coupling: In the repository pattern, all query code is isolated from the Entity Framework objects, so that it gives a good way to implement loose coupling with other model classes.
  • Preventing Entity Framework objects from business logic layers: Giving all data access (Entity Framework objects) to the business layer is not a good idea. All LINQ queries get embedded inside it, these things are not desirable.

Steps to implement generic repository in ASP.NET MVC

Step 1. Add a new MVC template

Choose an MVC template.

Step 2. Add Entity Framework

Step 3. We are going to choose code first approach for creating a database and respective tables.

After defining the model class for students, now, we are now going to make a schema class for the code-first approach.
[Table("Students")]
public class Student
{
    [Key]
    [Display(Name = "Student Id")]
    public int StId { get; set; }

    [Required]
    [Display(Name = "Student Name")]
    public string StName { get; set; }

    [Display(Name = "Address")]
    public string StAddress { get; set; }

    [Required]
    [Display(Name = "Mobile No.")]
    public string MobileNo { get; set; }
}

Step 4. Create studentConext


Step 5. Add database set Initializer
Usually, in Entity Framework, there are four different ways to create an Initializer.

  • CreateDatabaseIfNotExists: This is the default initializer and it will create the database if it does not exist already, as per the configuration. One more thing; it will throw an exception if the model class is changed and will try to run the application with this initializer.
  • DropCreateDatabaseIfModelChanges: According to this initializer, it drops an existing database and creates a new database if model classes (entity classes) are changed.
  • DropCreateDatabaseAlways: According to this initializer, it drops an existing database every time when we run the application. It doesn’t worry about the model class changes. This is useful when we want a fresh database every time we run the application.
  • Customer db Initializer: We can create our own custom initializer.

Syntax
public class SchoolDataBaseInitializer : CreateDatabaseIfNotExists<StudentContext>
{
    protected override void Seed(StudentContext context)
    {
        base.Seed(context);
    }
}


For this application, I am implementing the “DropCreateDatabaseIfModelChanges” database initializer method, globally.aspx file.
public class MvcApplication : System.Web.HttpApplication
{
    protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();
        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
        RouteConfig.RegisterRoutes(RouteTable.Routes);
        BundleConfig.RegisterBundles(BundleTable.Bundles);
        Database.SetInitializer<StudentContext>(new DropCreateDatabaseIfModelChanges<StudentContext>());
    }
}


Step 6. Now working with Generic Repository
Add an Interface.
public interface IGenericRepo<T> where T : class
{
    IEnumerable<T> GetAll();
    T GetById(int id);
    void Insert(T obj);
    void Update(T obj);
    void Delete(int id);
    void Save();
}


As we can see Interface is very simple and easy to understand here, Here <T> I took to incorporate with any classes whoever needed.
Inside Interface, we are implementing two type parameters - T1, (type parameter is a concept which is taken in C++ for making the generic concept). Here, I am not saying both are the same. The T1 type parameter specifies the class type, which means that we pass the class while implementing the generic interface to any class, and on the other hand, I haven’t specified the T2 with any data, the reason is that - it might be any class having unique id in different type (might be int, string or all).

Implement the generic repository with Entity Framework.
public class GenericRepo<T> : IGenericRepo<T> where T : class
{
    private StudentContext _context = null;
    private DbSet<T> table = null;

    public GenericRepo()
    {
        this._context = new StudentContext();
        table = _context.Set<T>();
    }

    public GenericRepo(StudentContext _context)
    {
        this._context = _context;
        table = _context.Set<T>();
    }

    public IEnumerable<T> GetAll()
    {
        return table.ToList();
    }

    public T GetById(int id)
    {
        return table.Find(id);
    }

    public void Insert(T obj)
    {
        table.Add(obj);
    }

    public void Update(T obj)
    {
        table.Attach(obj);
        _context.Entry(obj).State = EntityState.Modified;
    }

    public void Delete(int id)
    {
        T existing = table.Find(id);
        table.Remove(existing);
    }

    public void Save()
    {
        _context.SaveChanges();
    }
}


Now, it is very easy to implement the controller class to play with CRUD operation with the Repository class. As the code stated first we are creating a private member of our context class, and assigning it to null. same as with the Dbset.

Two constructors I created, as default and parameterized for initializing the context. As the context is get initilize we will able to call implemented Generic methods.

Have a look at the fetch and insert records using the Controller class.
public class HomeController : Controller
{
    private IGenericRepo<Students> repository = null;

    public HomeController()
    {
        this.repository = new GenericRepo<Students>();
    }

    public HomeController(IGenericRepo<Students> repository)
    {
        this.repository = repository;
    }

    public ActionResult Index()
    {
        var obj = repository.GetAll();
        return View(obj);
    }

    [HttpGet]
    public ActionResult AddStudent()
    {
        return View();
    }

    [HttpPost]
    public ActionResult AddStudent(Students obj)
    {
        repository.Insert(obj);
        return RedirectToAction("Index");
    }
}

Summary
This article explained the generic repository pattern, this approach where we can handle multiple databases with a single implemented class, only we have to pass the respective class to operate the desired result. We have also seen the pros and cons of the generic repository pattern.



ASP.NET MVC Hosting - HostForLIFE.eu :: Knowing EF Core MVC's Connected Disconnected Scenarios

clock January 7, 2025 06:56 by author Peter

Working with data is essential in contemporary online applications, and Entity Framework Core (EF Core) offers a productive method of interacting with databases through the use of Object-Relational Mapping (ORM). The Connected and Disconnected scenarios are the two main methods that EF Core provides for handling data in an application. Both situations differ in how data is recorded, accessed, and stored in the database, and they are crucial in distinct use cases. Both of these ideas are examined in this article along with workable solutions in a.NET Core MVC application.

What is a Connected Scenario?
In a Connected scenario, the DbContext is directly connected to the entity instances and actively tracks changes made to the data. This is the default behavior of EF Core when entities are retrieved using a context and modified within the scope of the context.

  • Key Characteristics of a Connected Scenario
  • Change Tracking: EF Core automatically tracks changes made to the entities. When SaveChanges() is called, it updates the database with those changes.
  • Short-lived Context: The DbContext instance is created, used, and disposed of within a limited scope, often tied to a single HTTP request in a web application.
  • Automatic State Management: EF Core keeps track of the state of entities, such as Added, Modified, Deleted, and Unchanged.

Practical Example
Let’s create a simple CRUD operation in a Connected scenario for managing a list of users.

Create the Model
namespace EFCoreConnectedDisconnectedDemo.Model
{
    public class User
    {
        public int Id { get; set; }
        public string? Name { get; set; }
        public string? Email { get; set; }
    }
}

Set Up DbContext
using EFCoreConnectedDisconnectedDemo.Model;
using Microsoft.EntityFrameworkCore;

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

        public DbSet<User> Users { get; set; }
    }
}

Create the controller

using EFCoreConnectedDisconnectedDemo.ApplicationContext;
using EFCoreConnectedDisconnectedDemo.Model;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;

namespace EFCoreConnectedDisconnectedDemo.Controllers
{
    public class UserController : Controller
    {
        private readonly ApplicationDbContext _context;

        public UserController(ApplicationDbContext context)
        {
            _context = context;
        }

        // GET: User
        public IActionResult Index()
        {
            var users = _context.Users.ToList();
            return View(users);
        }

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

        // POST: User/Create
        [HttpPost]
        [ValidateAntiForgeryToken]
        public IActionResult Create([Bind("Id, Name, Email")] User user)
        {
            if (ModelState.IsValid)
            {
                _context.Add(user);
                _context.SaveChanges(); // Save the changes in the connected scenario
                return RedirectToAction(nameof(Index));
            }
            return View(user);
        }

        // GET: User/Edit/5
        public IActionResult Edit(int? id)
        {
            if (id == null)
            {
                return NotFound();
            }

            var user = _context.Users.Find(id);
            if (user == null)
            {
                return NotFound();
            }
            return View(user);
        }

        // POST: User/Edit/5
        [HttpPost]
        [ValidateAntiForgeryToken]
        public IActionResult Edit(int id, [Bind("Id, Name, Email")] User user)
        {
            if (id != user.Id)
            {
                return NotFound();
            }

            if (ModelState.IsValid)
            {
                try
                {
                    _context.Update(user);
                    _context.SaveChanges(); // EF Core automatically tracks changes and updates the database
                }
                catch (DbUpdateConcurrencyException)
                {
                    if (!_context.Users.Any(e => e.Id == user.Id))
                    {
                        return NotFound();
                    }
                    else
                    {
                        throw;
                    }
                }
                return RedirectToAction(nameof(Index));
            }
            return View(user);
        }

        // GET: User/Delete/5
        public IActionResult Delete(int? id)
        {
            if (id == null)
            {
                return NotFound();
            }

            var user = _context.Users
                .FirstOrDefault(m => m.Id == id);
            if (user == null)
            {
                return NotFound();
            }

            return View(user);
        }

        // POST: User/Delete/5
        [HttpPost, ActionName("Delete")]
        [ValidateAntiForgeryToken]
        public IActionResult DeleteConfirmed(int id)
        {
            var user = _context.Users.Find(id);
            _context.Users.Remove(user);
            _context.SaveChanges(); // EF Core will automatically track the deletion
            return RedirectToAction(nameof(Index));
        }
    }
}


Create the Views
Index.cshtml, Create.cshtml, Edit.cshtml, and Delete.cshtml are Razor views for handling the respective CRUD actions.

How it Works in a Connected Scenario?

  • Tracking Changes: EF Core tracks changes made to User entities in the database automatically.
  • Saving Changes: Calling SaveChanges() saves the changes (whether added, updated, or deleted) to the database.

What is a Disconnected Scenario?
In a Disconnected scenario, the DbContext is not available to track changes once the data is retrieved from the database. This scenario is common in applications where the DbContext is disposed of after the entity data is fetched, such as in web APIs where entities are transferred over HTTP.

Key Characteristics of a Disconnected Scenario

  • No Change Tracking: Entities are not tracked once the DbContext is disposed of, meaning changes must be manually managed.
  • Manual State Management: In disconnected scenarios, the entity must be explicitly attached to the context and marked with its state (e.g., Modified) when changes are saved.
  • Common in Web APIs: Commonly used in Web API scenarios where entities are transferred to and from clients and must be handled after detaching from the context.

Practical Example
Let’s handle a simple scenario where data is fetched in a disconnected manner, then updated and saved.

Create a Web API Controller for Disconnected Scenario
using EFCoreConnectedDisconnectedDemo.ApplicationContext;
using EFCoreConnectedDisconnectedDemo.Model;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;

namespace EFCoreConnectedDisconnectedDemo.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class ApiUserController : ControllerBase
    {
        private readonly ApplicationDbContext _context;

        public ApiUserController(ApplicationDbContext context)
        {
            _context = context;
        }

        // GET: api/ApiUser/5
        [HttpGet("{id}")]
        public ActionResult<User> GetUser(int id)
        {
            var user = _context.Users.Find(id);
            if (user == null)
            {
                return NotFound();
            }
            return user;
        }

        // PUT: api/ApiUser/5
        [HttpPut("{id}")]
        public IActionResult PutUser(int id, User user)
        {
            if (id != user.Id)
            {
                return BadRequest();
            }

            // In disconnected scenario, we must attach the entity back to the context and mark it as modified
            _context.Users.Attach(user);
            _context.Entry(user).State = Microsoft.EntityFrameworkCore.EntityState.Modified;

            try
            {
                _context.SaveChanges(); // Save changes to the database manually
            }
            catch (DbUpdateConcurrencyException)
            {
                if (!_context.Users.Any(e => e.Id == user.Id))
                {
                    return NotFound();
                }
                else
                {
                    throw;
                }
            }

            return NoContent();
        }
    }
}


How it Works in a Disconnected Scenario?
The data is fetched and passed to the client (e.g., as JSON).

When the client sends an updated version of the entity, the entity must be explicitly attached to the DbContext.

The entity is marked as modified, and the changes are saved by calling SaveChanges().

When to Use Each Scenario?

  • Connected Scenario: Best suited for web applications or APIs where the DbContext remains active throughout the request lifecycle. It simplifies the development process since EF Core automatically handles change tracking.
  • Disconnected Scenario: Ideal for web APIs and scenarios where the DbContext is not available after data is retrieved. It requires explicit management of entity states and is suitable for distributed systems or client-server models.

Conclusion
In this post, we looked at how to use Entity Framework Core in an ASP.NET Core MVC application to manage connected and disconnected circumstances. Knowing the distinctions between these two methods can help you decide whether your application needs manual state management (disconnected) or automatic change monitoring (connected) for data management. Both situations are necessary for developing effective and reliable data-driven applications and are crucial in various use cases.



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