How to add CRUD functionality in ASP.NET Core app in 10 minutes
Tags: dotnet, aspnetcore, crud, entityframework
Published: 2021-06-01 06:15:36

## Problem

One of the first tasks for most business applications is to implement CRUD (Create, Read, Update, Delete) operations for the main entities the app works with. 

Every developer faces the following problems as part of solving the task:

* The creation of CRUD pages and forms is very boring and time-consuming. Believe me, I’ve been there a lot of times.

* If you do it manually, it can be very slow and error-prone (missed fields, forgotten validators, etc).

* Of course, you can use the scaffolding tool provided by Visual Studio. 
However, it’s also not a quick process either, because you need to run that tool for each model class. 
As a result, you get many .cs and .cshtml files, which you'll have to edit manually if something in the default behavior or appearance doesn't suit your needs. 
In the event of changes in the model classes, you'll need to update those generated controllers and pages manually 
or regenerate the code and forms from scratch for each affected model class.

* Moreover, even the built-in scaffolding doesn't provide some important, often essential functions such as paging or search.

## Solution: EasyData library

To solve most (if not all) of those problems we created the [EasyData library](https://github.com/KorzhCom/EasyData). 
The main advantage of EasyData is that it employs a declarative approach. 

The whole process can be split into two main steps:

1. You “describe” what data (entities and attributes) you want to work with and 
how your application should work with that data (types, constraints, relations between entities, etc). 

2. Based on this information, the EasyData library establishes an API endpoint for CRUD operations and a vanilla JavaScript-based UI 
that lets your users perform those operations via the API.

The most wonderful thing here is that when using Entity Framework Core, 
all you need for the first step (“describing” the data) is your DbContext. 
Simply “feed” it to the library, and EasyData automatically extracts all the information needed to create the API endpoints and CRUD UI.

## Quick demo

Here's a small introduction video that shows how EasyData works:

![EasyData quick demo](https://cdn.korzh.com/img/easydata-demo01.gif "EasyData quick demo")

## Getting Started

First of all, to test EasyData you can open and run one of the [sample projects](https://github.com/korzh/EasyData/tree/master/samples) available in this repository. 

Installing EasyData to your project takes the following three simple steps:

### 1. Install EasyData NuGet packages

* EasyData.AspNetCore
* EasyData.EntityFrameworkCore.Relational

### 2. Add EasyData middleware in `Startup.Configure`

```c#
using EasyData.Services;
.    .    .    .    .

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapEasyData(options => {
            options.UseDbContext<AppDbContext>();
        });

        endpoints.MapRazorPages();
    });

```

In the middleware options, we also specify the type of DbContext object that will be used as the source of the metadata.

### 3. Set up a catch-all page for all CRUD operations

If you're using Razor Pages, add a new page (for example `EasyData.chstml`). If it’s MVC, you'll need a controller and a view.
This page will "catch" all URLs that begin with a certain prefix (`/easydata` by default but it's configurable). So, we use a special catch-all parameter in the route definition (`"/easydata/{**entity}"`).

We also add EasyData styles and the script file (`easydata.min.js`), which renders the data-management UI and handles all CRUD operations on the client-side.

```html
@page "/easydata/{**entity}"
@{
    ViewData["Title"] = "EasyData";
}
<link rel="stylesheet" href="https://cdn.korzh.com/ed/1.2.2/easydata.min.css" />

<div id="EasyDataContainer"></div>

@section Scripts {
    <script src="https://cdn.korzh.com/ed/1.2.2/easydata.min.js" type="text/javascript"></script>
    <script>
        window.addEventListener('load', function () {
            new easydata.crud.EasyDataViewDispatcher().run()
        });
    </script>
}
```

That’s it. Now you can run your web app, open the `/easydata` URL and enjoy CRUD functionality.

The result will be something like this:

#### List view screen for one entity:

<p style="text-align:center">
    <img class="krh-blog-img" src="https://cdn.korzh.com/img/blog/edintro/easydata-sshot01.jpg" alt="List view screen for Orders entity" />
</p>


#### "Edit Record" dialog:

<p style="text-align:center">
    <img class="krh-blog-img" src="https://cdn.korzh.com/img/blog/edintro/easydata-sshot02.jpg" alt="Edit Record dialog" />
</p>


#### "Lookup" dialog (was opened from "Edit Record"):

<p style="text-align:center">
    <img class="krh-blog-img" src="https://cdn.korzh.com/img/blog/edintro/easydata-sshot03.jpg" alt="Lookup dialog" />
</p>


## How it works

Let's briefly go over how all this magic works. 
As we mentioned before EasyData takes care of three main things:

* It collects database metadata.

* It establishes an API for the main CRUD operations.

* It renders UI (again, based on the metadata) and processes all user interactions in that UI.

Let’s explore all these parts in detail.

### Metadata

Metadata is the data about your data: what entities (tables) are stored in your database, 
how they're connected, what attributes (fields) they have, what  the types of attributes are 
and what the constraints are with respect to the values we can store in those attributes.

EasyData collects metadata (in one way or another) and stores it in the instance of `MetaData` class. 
This object contains the list of entities (tables), the attributes (fields) for each entity, the connections between entities, 
and some additional information used in API and during UI rendering and processing.

To fill the MetaData object, we specify a metadata loader. In the example above, we did it with the UseDbContext call. 
So, the metadata is loaded from a DbConext object. Currently (in version 1.2) this is the only metadata loader available. 
In future versions, it will be possible to load metadata directly from a DbConnection object or perhaps with some other method.

### EasyData middleware

EasyData middleware is responsible for establishing a REST API for all CRUD (and not only) operations initiated by the client side.

To add the middleware to your pipeline use `MapEasyData` extension function inside `UseEndpoints` configuration delegate:

```c#
   app.UseEndpoints(endpoints =>
    {
       endpoints.MapEasyData(options => {
            options.UseDbContext<AppDbContext>();
        });
    }
```

This call should be made before `MapMvc` or `MapRazorPages`. 
By default, EasyData middleware is assigned to `/api/easydata endpoint`, but you can change it via the configuration function (action) passed in the parameter.

The only thing that's required in order to configure for `MapEasyData` is to tell it where to take the metadata. 
Currently, there's just one option available: getting metadata from a DbContext. 
So, that’s why we add `UseDbContext<AppDbContext>()` call in the example above. 
Besides getting metadata, `UseDbContext` also provides our middleware with all the means for performing CRUD operations (via the DbContext object).

### EasyData UI root page

The third part of the EasyData setup process is the page where the CRUD user interface is rendered. 
It must be a so-called "catch-all" Razor page or MVC view. 
By default, this page must be opened for any path that starts with the `/easydata/` prefix.
(So, all paths like `/easydata/student` or `/easydata/invoice` must be processed by this page.)

NB: `/easydata` is the default prefix. 
You can use another name, but in this case, it will be necessary to specify it in the options of our `RootDispatcherView` object.

Our catch-all page can contain any HTML element of your choice. 
However, to ensure the visualization and normal operation of the CRUD UI, it must include the following 4 elements:

* `<link>` element with a reference to EasyData CSS file (`easydata.min.cs`)

* Container (empty `div` element), in which our interface will be displayed. 
By default, it must have the ID `EasyDataContainer`, but this can also be configured with options.

* `<script>` element with a reference to `easydata.min.js`.

* And a small script that creates and launches the `EasyDataViewDispatcher` object on page load.

An example of the simplest “catch-all” page you can see in the Getting Started section above.

## Wrapping up
 
Currently, EasyData can work with .NET Core 3.1 and .NET 5. 
Obviously, all ASP.NET Core and Entity Framework Core versions that can work with the specified releases of .NET (Core) are supported. 
However, it won't be a problem to add support for previous versions of .NET Core or even .NET Framework 4.x. 
If anyone needs it, please [submit an issue](https://github.com/KorzhCom/EasyData/issues) in EasyData's GitHub repository.

EasyData can be a good tool with which to quickly prototype a new project or create a POC (proof of concept) 
when we already understand what data we'll need but don't want to spend a lot of time on the simplest operations with that data. 
However, we hope that in time it will be possible to fully use this solution in the production environment.


So, we look forward to hearing from you with any comments or advice you may have. Of course, don't forget to ad a start for the EasyData repository on GitHub, especially if this library helped you and saved some time.

So, we look forward to hearing from you with any comments or advice you may have. 
Of course, don't forget to add a star for the [EasyData repository on GitHub](https://github.com/KorzhCom/EasyData), 
especially if this library helped you and saved some time.