(C# ASP.NET Core) Repository Pattern and Dependency Injection with XML as Database

This is a walkthrough on how to make calls to an XML database via repository pattern and dependency injection. We also give a brief introduction to the repository pattern and dependency injection, and also explain the associated benefits.
(Rev. 19-Mar-2024)

Categories | About |     |  

Parveen,

The Objective of this Tutorial

We will use the repository pattern, paired with dependency injection, to make communication with an XML database.

We will run only two queries to keep the things simple.

Primer on Repository Pattern
(please also watch the linked video)

First: write an interface to list all the functions that your project needs for database communication.

Second: write a class to implement all those functions. This class is called the repository.

Third: use dependency injection to obtain an instance to the repository, and call the functions.

Benefits: first is that you have a readable code. (1) The code for database connectivity is placed in a [dedicated] repository class. (2) The UI part of your project remains simple because now it can make neat-and-brief one-line calls to the function of the repository class.

Primer on Dependency Injection
(please also watch the linked video)

Step 1: modify the ConfigureServices method of your Startup.cs file to add a call to AddSingleton OR AddTransient OR AddScoped [depending on the specific requirements] to specify the name of your interface and the implementing repository class.

Step 2: The ASP.NET Core runtime can now create an object of your repository class and provide it to you through a constructor of the class where you need it.

See the code below to understand it in action.

Video Explanation with a Working Example

Please watch the following youtube video:

Step 1 of 6: Write the Model class
(see the linked video for clarity)

The first thing that you should always do is decide on the structure of your data, and write your model class.

Create a folder called Models and write this class there -

namespace DBaseCon.Models
{

  public class MyData
  {

    // only one property, for simplicity 
    public string Name { get; set; }
  }

}

Step 2 of 6: Write the Interface
(see the linked video for clarity)

Next, make a list of functions that you will need for your communication with the database. Write these functions in an interface.

Create a folder called "Repository", then add a file called "IMyRepos.cs", and write this interface there -

// create a folder called Repository and 
// add a file called IMyRepos.cs there 
// write this code in this file 

// namespace references 
using DBaseCon.Models;

using Microsoft.AspNetCore.Hosting;

using System.IO;

using System.Xml.Serialization;

namespace DBaseCon.Repository
{

  // we keep only two functions 
  // for tutorial and simplicity 
  public interface IMyRepos
  {

    MyData GetRecord();

    void UpdateRecord(MyData mydata);

  }

}

Step 3 of 6: Implement the Interface
(see the linked video for clarity)

Provide an implementation in a class that implements the functionality listed in the interface.

You can place the class in the same file as your interface. Or, alternatively, you can create another folder that contains all implementation classes.

To understand the implementation better, please first go through How to use XML as a database in ASP.NET Core

namespace DBaseCon.Repository
{

  // implementation of the interface 
  public class CReposImpl : IMyRepos
  {

    // path to xml file 
    readonly string mPath;

    // dependency injection through constructor 
    // this needs TO BE NOTED !!! 
    public CReposImpl(IWebHostEnvironment env)
    {

      // path to the database folder 
      string dbDir = Path.Combine(env.ContentRootPath, "dbFolder");

      // create if not exists 
      // it is better to create the directory 
      // in advance so that this code 
      // can be avoided 
      if (!Directory.Exists(dbDir))
      {

        Directory.CreateDirectory(dbDir);

      }

      // now construct the path to xml file 
      mPath = Path.Combine(dbDir, "myfile.xml");

    }

    public MyData GetRecord()
    {

      if (!File.Exists(mPath))
      {

        return new MyData() { Name = string.Empty };

      }

      // de-serialize 
      XmlSerializer ser = new XmlSerializer(typeof(MyData));

      using (Stream stm = new FileStream(mPath, FileMode.Open, FileAccess.Read))

      {

        return ((MyData)ser.Deserialize(stm));

      }

    }

    public void UpdateRecord(MyData mydata)
    {

      XmlSerializer ser = new XmlSerializer(typeof(MyData));

      using (Stream stm = new FileStream (mPath, FileMode.OpenOrCreate, FileAccess.Write))

      {

        ser.Serialize(stm, mydata);

      }

    }

  }

}

Step 4 of 6: Startup.cs - ConfigureServices
(see the linked video for clarity)

Modify the ConfigureServices method and add one line of code for configuring dependency injection of the repository instance. Below I have given the completed Startup.cs file.

// Startup.cs file 
// Notice the AddSingelton call in 
// the ConfigureServices method 

// namespace references 
using DBaseCon.Repository;

using Microsoft.AspNetCore.Builder;

using Microsoft.AspNetCore.Hosting;

using Microsoft.Extensions.DependencyInjection;

using Microsoft.Extensions.Hosting;

namespace Modular
{

  public class Startup
  {

    public void ConfigureServices(IServiceCollection services)
    {

      // added this --- for dependency injection 
      services.AddSingleton<IMyRepos, CReposImpl>();

      // add support for razor pages 
      services.AddRazorPages();

    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {

      if (env.IsDevelopment())
      {

        app.UseDeveloperExceptionPage();

      }

      app.UseRouting();

      app.UseEndpoints(endpoints =>
      {

        // add razor pages as the end-points 
        endpoints.MapRazorPages();

      });

    }

  }

}

Step 5 of 6: Index.cshtml.cs - backing class
(see the linked video for clarity)

Complete the backing class.

Notice how we use the constructor to obtain an instance of the repository class. The class is very neat now, and readable because most of the database work is now delegated to the repository class.

using DBaseCon.Models;

using DBaseCon.Repository;

using Microsoft.AspNetCore.Mvc;

using Microsoft.AspNetCore.Mvc.RazorPages;

namespace Modular.Pages
{

  public class IndexModel : PageModel
  {

    // dependency injection 
    readonly IMyRepos _myRepos;

    public IndexModel(IMyRepos myRepos)
    {

      _myRepos = myRepos;

    }

    // property for 2-way binding 
    [BindProperty]
    public MyData mydata { get; set; }

    // read xml on the get request 
    public void OnGet()
    {

      mydata = _myRepos.GetRecord();

    }


    // update the database on post 
    public void OnPost()
    {

      _myRepos.UpdateRecord(mydata);

    }

  }

}

Step 6 of 6: Index.cshtml - the Razor Page
(see the linked video for clarity)

Complete the razor page as below.

@page

@model Modular.Pages.IndexModel

@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

<form method="post">
  Name: <input asp-for="mydata.Name" /> <input type="submit" />
</form>

After this run the project to verify that database is read and updated as desired.


This Blog Post/Article "(C# ASP.NET Core) Repository Pattern and Dependency Injection with XML as Database" by Parveen is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.