CRUD operations in ASP.NET Core with EasyData library
Implementing CRUD operations in your ASP.NET Core application can be a very tedious and time-consuming task. EasyData helps to add all necessary functionality (and even more) in a matter of minutes.
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. The main advantage of EasyData is that it employs a declarative approach.
The whole process can be split into two main steps:
-
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).
-
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:
Getting Started
First of all, to test EasyData you can open and run one of the sample projects 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
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.
@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:
"Edit Record" dialog:
"Lookup" dialog (was opened from "Edit Record"):
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:
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 IDEasyDataContainer
, but this can also be configured with options. -
<script>
element with a reference toeasydata.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 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, especially if this library helped you and saved some time.