There are multiple versions of this document. Pick the options that suit you best.

UI
Database

Quick Start

This is a single-part quick-start tutorial to build a simple todo application with the ABP Framework. Here's a screenshot from the final application:

todo-list

You can find the source code of the completed application here.

We are currently preparing a video tutorial for Blazor UI. You can watch other tutorials for the three UI types from here.

Pre-Requirements

Creating a New Solution

In this tutorial, we will use the ABP CLI to create the sample application with the ABP Framework. You can run the following command in a command-line terminal to install the ABP CLI, if you haven't installed it yet:

dotnet tool install -g Volo.Abp.Cli

Then create an empty folder, open a command-line terminal and execute the following command in the terminal:

abp new TodoApp -t app-nolayers -u blazor

This will create a new solution with three projects:

  • A blazor application that contains the Blazor code, the client-side.
  • A host application, hosts and serves the blazor application.
  • A contracts project, shared library between these two projects.

Once the solution is ready, open it in your favorite IDE.

Create the Database

You can run the following command in the directory of your TodoApp.Host project to create the database and seed the initial data:

dotnet run --migrate-database

This command will create the database and seed the initial data for you. Then you can run the application.

Before Running the Application

Installing the Client-Side Packages

ABP CLI runs the abp install-libs command behind the scenes to install the required NPM packages for your solution while creating the application.

However, sometimes this command might need to be manually run. For example, you need to run this command, if you have cloned the application, or the resources from node_modules folder didn't copy to wwwroot/libs folder, or if you have added a new client-side package dependency to your solution.

For such cases, run the abp install-libs command on the root directory of your solution to install all required NPM packages:

abp install-libs

We suggest you install Yarn to prevent possible package inconsistencies, if you haven't installed it yet.

Bundling and Minification

abp bundle command offers bundling and minification support for client-side resources (JavaScript and CSS files) for Blazor projects. This command automatically run when you create a new solution with the ABP CLI.

However, sometimes you might need to run this command manually. To update script & style references without worrying about dependencies, ordering, etc. in a project, you can run this command in the directory of your blazor application:

abp bundle

For more details about managing style and script references in Blazor or MAUI Blazor apps, see Managing Global Scripts & Styles.

Run the Application

It is good to run the application before starting the development. Running the application is pretty straight-forward, you just need to run the TodoApp.Host application with any IDE that supports .NET or by running the dotnet run CLI command in the directory of your project.

Note: The host application hosts and serves the blazor application. Therefore, you should run the host application only.

After the application runs, open the application in your default browser:

todo-ui-initial

You can click on the Login button and use admin as the username and 1q2w3E* as the password to login to the application.

All right. We can start coding!

Defining the Entity

This application will have a single entity and we can start by creating it. So, create a new TodoItem class under the Entities folder of the TodoApp.Host project:

using Volo.Abp.Domain.Entities;

namespace TodoApp.Entities;

public class TodoItem : BasicAggregateRoot<Guid>
{
    public string Text { get; set; }
}

BasicAggregateRoot is the simplest base class to create root entities, and Guid is the primary key (Id) of the entity here.

Database Integration

Next step is to setup the Entity Framework Core configuration.

Mapping Configuration

Open the TodoAppDbContext class (in the Data folder) and add a new DbSet property to this class:

public DbSet<TodoItem> TodoItems { get; set; }

Then navigate to the OnModelCreating method in the same class and add the following mapping code for the TodoItem entity:

protected override void OnModelCreating(ModelBuilder builder)
{
    base.OnModelCreating(builder);

    /* Include modules to your migration db context */

    builder.ConfigurePermissionManagement();
    ...

    /* Configure your own tables/entities inside here */
    builder.Entity<TodoItem>(b =>
    {
        b.ToTable("TodoItems");
    });
}

We've mapped the TodoItem entity to the TodoItems table in the database. The next step is to create a migration and apply the changes to the database.

Code First Migrations

The startup solution is configured to use Entity Framework Core Code First Migrations. Since we've changed the database mapping configuration, we should create a new migration and apply changes to the database.

Open a command-line terminal in the directory of your TodoApp.Host project and type the following command:

dotnet ef migrations add Added_TodoItem

This will add a new migration class to the project. You should see the new migration in the Migrations folder:

todo-efcore-migration

Then, you can apply changes to the database using the following command, in the same command-line terminal:

dotnet ef database update

After the database integrations, now we can start to create application service methods and implement our use-cases.

Creating the Application Service

An application service is used to perform the use cases of the application. We need to perform the following use cases in this application:

  • Get the list of the todo items
  • Create a new todo item
  • Delete an existing todo item

Before starting to implement these use cases, first we need to create a DTO class that will be used in the application service.

Creating the Data Transfer Object (DTO)

Application services typically get and return DTOs (Data Transfer Objects) instead of entities. So, create a new TodoItemDto class under the Services/Dtos folder of your TodoApp.Contracts project:

namespace TodoApp.Services.Dtos;

public class TodoItemDto
{
    public Guid Id { get; set; }
    public string Text { get; set; }
}

This is a very simple DTO class that has the same properties as the TodoItem entity. Now, we are ready to implement our use-cases.

The Application Service Interface

Create a ITodoAppService interface under the Services folder of the TodoApp.Contracts project, as shown below:

using TodoApp.Services.Dtos;
using Volo.Abp.Application.Services;

namespace TodoApp.Services;

public interface ITodoAppService : IApplicationService
{
    Task<List<TodoItemDto>> GetListAsync();

    Task<TodoItemDto> CreateAsync(string text);

    Task DeleteAsync(Guid id);
}

The Application Service Implementation

Create a TodoAppService class under the Services folder of your TodoApp.Host project, as shown below:

using TodoApp.Services;

using TodoApp.Services.Dtos;
using TodoApp.Entities;
using Volo.Abp.Application.Services;
using Volo.Abp.Domain.Repositories;


namespace TodoApp.Services;

public class TodoAppService : TodoAppAppService, ITodoAppService
{
    private readonly IRepository<TodoItem, Guid> _todoItemRepository;
    
    public TodoAppService(IRepository<TodoItem, Guid> todoItemRepository)
    {
        _todoItemRepository = todoItemRepository;
    }
    
    // TODO: Implement the methods here...
}

This class inherits from the TodoAppAppService, which inherits from the ApplicationService class of the ABP Framework and implements our use-cases. ABP provides default generic repositories for the entities. We can use them to perform the fundamental database operations. This class injects IRepository<TodoItem, Guid>, which is the default repository for the TodoItem entity. We will use it to implement our use cases.

Getting the Todo Items

Let's start by implementing the GetListAsync method, which is used to get a list of todo items:

public async Task<List<TodoItemDto>> GetListAsync()
{
    var items = await _todoItemRepository.GetListAsync();
    return items
        .Select(item => new TodoItemDto
        {
            Id = item.Id,
            Text = item.Text
        }).ToList();
}

We are simply getting the TodoItem list from the repository, mapping them to the TodoItemDto objects and returning as the result.

Creating a New Todo Item

The next method is CreateAsync and we can implement it as shown below:

public async Task<TodoItemDto> CreateAsync(string text)
{
    var todoItem = await _todoItemRepository.InsertAsync(
        new TodoItem {Text = text}
    );

    return new TodoItemDto
    {
        Id = todoItem.Id,
        Text = todoItem.Text
    };
}

The repository's InsertAsync method inserts the given TodoItem to the database and returns the same TodoItem object. It also sets the Id, so we can use it on the returning object. We are simply returning a TodoItemDto by creating from the new TodoItem entity.

Deleting a Todo Item

Finally, we can implement the DeleteAsync as the following code block:

public async Task DeleteAsync(Guid id)
{
    await _todoItemRepository.DeleteAsync(id);
}

The application service is ready to be used from the UI layer. So, let's implement it.

User Interface

It is time to show the todo items on the UI! Before starting to write the code, it would be good to remember what we are trying to build. Here's a sample screenshot from the final UI:

todo-list

Index.razor.cs

Open the Index.razor.cs file in the Pages folder in your Todo.Blazor project and replace the content with the following code block:


using Microsoft.AspNetCore.Components;
using TodoApp.Services;
using TodoApp.Services.Dtos;


namespace TodoApp.Pages;

public partial class Index
{
    [Inject]
    private ITodoAppService TodoAppService { get; set; }

    private List<TodoItemDto> TodoItems { get; set; } = new List<TodoItemDto>();
    private string NewTodoText { get; set; }

    protected override async Task OnInitializedAsync()
    {
        TodoItems = await TodoAppService.GetListAsync();
    }
    
    private async Task Create()
    {
        var result = await TodoAppService.CreateAsync(NewTodoText);
        TodoItems.Add(result);
        NewTodoText = null;
    }

    private async Task Delete(TodoItemDto todoItem)
    {
        await TodoAppService.DeleteAsync(todoItem.Id);
        await Notify.Info("Deleted the todo item.");
        TodoItems.Remove(todoItem);
    }
}

This class uses the ITodoAppService to get the list of todo items. It manipulates the TodoItems list after create and delete operations. This way, we don't need to refresh the whole todo list from the server.

Index.razor

Open the Index.razor file in the Pages folder and replace the content with the following code block:

@page "/"
@inherits TodoAppComponentBase

<div class="container">
    <Card>
        <CardHeader>
            <CardTitle>
                TODO LIST
            </CardTitle>
        </CardHeader>
        <CardBody>
            <!-- FORM FOR NEW TODO ITEMS -->
            <form id="NewItemForm" @onsubmit:preventDefault @onsubmit="() => Create()" class="row row-cols-lg-auto g-3 align-items-center">
                <div class="col-12">
                    <div class="input-group">
                        <input name="NewTodoText" type="text" @bind-value="@NewTodoText" class="form-control" placeholder="enter text..." />
                    </div>
                </div>
                <div class="col-12">
                    <button type="submit" class="btn btn-primary">Submit</button>
                </div>
            </form>
            <!-- TODO ITEMS LIST -->
            <ul id="TodoList">
                @foreach (var todoItem in TodoItems)
                {
                    <li data-id="@todoItem.Id">
                        <i class="far fa-trash-alt"
                           @onclick="() => Delete(todoItem)"></i>
                        @todoItem.Text
                    </li>
                }
            </ul>
        </CardBody>
    </Card>
</div>

Index.razor.css

As the final touch, open the Index.razor.css file in the Pages folder and replace it with the following content:

#TodoList{
    list-style: none;
    margin: 0;
    padding: 0;
}

#TodoList li {
    padding: 5px;
    margin: 5px 0px;
    border: 1px solid #cccccc;
    background-color: #f5f5f5;
}

#TodoList li i
{
    opacity: 0.5;
}

#TodoList li i:hover
{
    opacity: 1;
    color: #ff0000;
    cursor: pointer;
}

This is a simple styling for the todo page. We believe that you can do much better :)

Now, you can run the TodoApp.Host project again to see the result.

Conclusion

In this tutorial, we've built a very simple application to warm up with the ABP Framework.

Source Code

You can find the source code of the completed application here.

See Also

Was this page helpful?

Please make a selection.

To help us improve, please share your reason for the negative feedback in the field below.

Please enter a note.

Thank you for your valuable feedback!

Please note that although we cannot respond to feedback, our team will use your comments to improve the experience.

In this document
Community Talks

What’s New with .NET 9 & ABP 9?

21 Nov, 17:00
Online
Watch the Event
Mastering ABP Framework Book
Mastering ABP Framework

This book will help you gain a complete understanding of the framework and modern web application development techniques.

Learn More