Overview

This tutorial uses the monthly calendar component from the open-source DayPilot Lite for JavaScript package to implement a maintenance schedule application.

This maintenance scheduling application has the following features:

  • The application works with maintenance types that define a blueprint for each maintenance task.

  • Each maintenance type has a specified color, and defines a checklist of actions that need to be performed during maintenance.

  • The scheduled tasks are displayed in a monthly scheduling calendar view.

  • You can adjust the plan using drag and drop and move the scheduled tasks as needed.

  • When all checklist actions are completed, you can easily schedule the date of the next maintenance.

  • The fronted is implemented in HTML5/JavaScript.

  • The backend is implemented using ASP.NET Core, Entity Framework and SQL Server.

This tutorial doesn’t cover the calendar basics. For an introduction to using the monthly calendar component in ASP.NET Core, please see the following tutorial:

You can also use the visual UI Builder app to configure the monthly calendar component and generate a starting project:

License

Apache License 2.0

Scheduling maintenance tasks

asp.net core maintenance scheduling open source task type

This ASP.NET Core application lets you create and schedule regular maintenance tasks.

To create a new tasks, click a day where you want to schedule the task. The application opens a modal dialog where you can enter the task details:

  • Resource name (machine, room, etc.)

  • Due date of the maintenance task (this is set to the day clicked)

  • Maintenance type.

The maintenance task details are stored in a MaintenanceTask model class with the following structure. The MaintenanceTask class stores information about a maintenance task scheduled for a specific date. Each task always has a set type (MaintenanceType) and checklist items (TaskItems).

public class MaintenanceTask
{
    public int Id { get; set; }
    
    public DateTime DueDate { get; set; }

    public string Text { get; set; }
    
    [JsonPropertyName("type")]
    public int MaintenanceTypeId { get; set; }
    public MaintenanceType MaintenanceType { get; set; }
    public List<MaintenanceTaskItem> TaskItems { get; set; }
    
    public MaintenanceTask? Next { get; set; }
    
    public MaintenanceTask()
    {
        TaskItems = new List<MaintenanceTaskItem>();
    }
}

Choosing the right maintenance type is important - it defines the actions that need to be performed during task completion.

As soon as you confirm the details, the application will add the task to the scheduling calendar:

asp.net core maintenance scheduling open source machine

The creation of a new task is handled using the onTimeRangeSelected event of the calendar component:

onTimeRangeSelected: async args => {
    const form = [
        {name: "Resource", id: "text"},
        {name: "Date", id: "date", type: "date", disabled: true},
        {name: "Type", id: "type", type: "select", options: app.data.types}
    ];
    const data = {
        start: args.start,
        end: args.end,
        type: app.data.types[0].id,
        text: "",
    };
    
    const modal = await DayPilot.Modal.form(form, data);
    
    if (modal.canceled) {
        return;
    }
    
    const result = modal.result;
    
    const {data:task} = await DayPilot.Http.post("/api/Tasks", result);
    
    dp.events.add(task);
}

If the user enters the task details and confirms the submission, the app creates a new tasks and send the details to the /api/Tasks endpoint which creates a new DB record using Entity Framework.

Maintenance task types

asp.net core maintenance scheduling open source task types

Each task has a specified types which defines the checklist items.

Adding a checklist to the maintenance task helps with following the correct procedure during maintenance. It helps users keep track of what needs to be done and eliminates errors. Other users will be to see the current task status.

In the sample database, there are a couple of sample maintenance types defined:

  • Basic Cleanup (once a week)

  • Safety Inspection (once a month)

  • Machine Calibration (once every 3 months)

  • Preventive Maintenance (once every 6 months)

Each maintenance type defines the following task properties:

  • checklist items

  • color that will be used in the calendar

  • interval between repeated tasks

This is the structure of the MaintenanceType models class:

public class MaintenanceType
{
    public int Id { get; set; }
    public string Name { get; set; }

    public string Period { get; set; }

    public string? Color { get; set; }
    
    public ICollection<MaintenanceTypeItem> TypeItems { get; set; }
}

The period is encoded as a simple string with the following structure "{number}{unit}", e.g. "1w" for 1 week.

The event content is customized using the onBeforeEventRender event handler:

onBeforeEventRender: args => {
    const type = app.findType(args.data.type);
    const items = type.checklist || [];
    const checked = args.data.checklist || {};
    const completed = items.filter(i => checked[i.id]);
    args.data.html = "";
    
    const total = items.length;
    const done = completed.length === total && args.data.next;

    args.data.backColor = type.color;
    if (done) {
        args.data.backColor = "#aaaaaa";
    }
    args.data.borderColor = "darker";
    args.data.barColor = DayPilot.ColorUtil.darker(args.data.backColor, 2);
    args.data.fontColor = "#ffffff";
    
    const barColor = DayPilot.ColorUtil.darker(args.data.backColor, 3);
    const barColorChecked = DayPilot.ColorUtil.darker(args.data.backColor, 3);
    
    const barWidth = 15;
    const barHeight = 15;
    const barSpace = 2;
    
    args.data.areas = [
        {
            top: 4,
            left: 10,
            text: args.data.text,
            fontColor: "#ffffff",
            style: "font-weight: bold"
        },
        {
            top: 24,
            left: 10,
            text: `${type.name} (${completed.length}/${total})`,
            fontColor: "#ffffff",
        },
        {
            top: 4,
            right: 4,
            height: 22,
            width: 22,
            padding: 2,
            fontColor: "#ffffff",
            backColor: barColor, 
            cssClass: "area-action",
            symbol: "icons/daypilot.svg#threedots-v",
            style: "border-radius: 20px",
            action: "ContextMenu",
            toolTip: "Menu"
        }
    ];
    
    const nextIcon = {
        bottom: 5,
        right: 5,
        height: 20,
        width: 20,
        padding: 2,
        backColor: barColor,
        fontColor: "#ffffff",
        cssClass: "area-action",
        toolTip: "Next",
        action: "None",
        onClick: async args => {
            app.scheduleNext(args.source);
        }
    };
    args.data.areas.push(nextIcon);
    
    if (args.data.next) {
        args.data.moveDisabled = true;
        nextIcon.symbol = "#next";
    }
    
    items.forEach((it, x) => {
        const isChecked = checked[it.id];
        
        const area = {
             bottom: barSpace + 4,
             height: barHeight,
             left: x * (barWidth + barSpace) + 10,
             width: barWidth,
             backColor: isChecked ? barColorChecked : barColor,
             fontColor: "#ffffff",
             padding: 0,
             toolTip: it.name,
             action: "None",
             cssClass: "area-action",
             onClick: async args => {
                 const e = args.source;
                 const itemId = it.id;
                 if (!e.data.checklist) {
                     e.data.checklist = {};
                 }
                 e.data.checklist[itemId] = !isChecked;
             
                 // Send the updated checklist to the server
                 await DayPilot.Http.post(`/api/tasks/${e.data.id}/update-checklist`, e.data.checklist);
             
                 dp.events.update(args.source);
             }           
        };
        if (isChecked) {
            area.symbol = "icons/daypilot.svg#checkmark-4";
        }
        args.data.areas.push(area);           
    });
                
}

This event handler is a bit more complex than usual. Instead of displaying the default text, it uses active areas to create rich content and interactive elements:

  • It sets the background color depending on the maintenance task type

  • It displays the resource name and maintenance type at the specified positions.

  • It generates checkboxes for the maintenance checklist items.

  • It creates and icon for scheduling the next task after the specified interval.

Maintenance checklist

asp.net core maintenance scheduling open source checklist

The maintenance task consists of multiple checklist items. The checklist items are defined for the maintenance type associated with the task.

The status of each checklist item is stored using the MaintenanceTaskItem model class. The task checklist consists of items defined by the task type, which are copied from the task type upon task creation. The items are stored using the MaintenanceTaskItem class. The task checklist items are independent. This database schema allows for adding or removing items for specific tasks, differing from the task type.

public class MaintenanceTaskItem
{
    public int Id { get; set; }
    
    public int MaintenanceTypeItemId { get; set; }
    public MaintenanceTypeItem MaintenanceTypeItem { get; set; }
    public bool Checked { get; set; }
}

The number of checklist items and their status are displayed directly in the task box in the scheduling calendar.

  • You can display the full checklist by clicking on the maintenance task box in the calendar.

  • You can also check the item directly by clicking on the respective box without opening the full checking.

The checklist item text is displayed on hover.

asp.net core maintenance scheduling open source task

You can show the full checklist by clicking on the task box. This fires the onEventClick event handler that opens a modal dialog with task details.

onEventClick: async args => {
    const e = args.e;
    const type = app.findType(e.data.type);
    
    const nextDate = e.data.nextDate ? new DayPilot.Date(e.data.nextDate).toString("dddd M/d/yyyy") : "N/A";
    
    const form = [
        {name: "Checklist"},
        ...type.checklist.map(i => ({...i, id: i.id.toString(), type: "checkbox" }) ),
        {text: `Next: ${nextDate}`},
    ];
    const data = e.data.checklist;
    
    const modal = await DayPilot.Modal.form(form, data);
    if (modal.canceled) {
        return;
    }
    
    e.data.checklist = modal.result;
    
    await DayPilot.Http.post(`/api/tasks/${e.data.id}/update-checklist`, e.data.checklist);
    
    dp.events.update(e);            
},

Scheduling a follow-up maintenance task

asp.net core maintenance scheduling open source next

As soon as the checklist is complete, it is possible to schedule a next task. If you click the icon in the bottom-right corner of the task box the application will schedule the next maintenance task based on the predefined period.

After clicking the “next” icon the application opens a confirmation modal dialog with the calculated date of the next occurrence:

asp.net core maintenance scheduling open source next task

The scheduleNext() function checks if the next task has already been scheduled. If not, it opens a modal dialog and asks for a confirmation.

async scheduleNext(e) {
    const type = app.findType(e.data.type);
    const nextDate = app.nextDate(e.start(), type);
    
    if (e.data.next) {
        DayPilot.Modal.alert("Already scheduled.");
        return;
    }
    
    const modal = await DayPilot.Modal.confirm(`Schedule next task for: ${nextDate.toString("M/d/yyyy")}?`);
    if (modal.canceled) {
        return;
    }
    
    const next = {
        start: nextDate,
        end: nextDate,
        type: e.data.type,
        text: e.data.text,
    };
    
    const {data:task} = await DayPilot.Http.post(`/api/Tasks/${e.data.id}/schedule-next`, next);
    
    dp.events.add(task);
    e.data.next = task.id;
    e.data.nextDate = nextDate;
    dp.events.update(e);
},

The nextDate() function calculates the date of the next task:

nextDate(date, type) {
    const spec = type.period;
    
    const {number, unit} = app.parsePeriod(spec);
    
    if (unit.days) {
        return date.addDays(number);              
    }
    if (unit.weeks) {
        return date.addDays(number*7);
    }
    if (unit.months) {
        return date.addMonths(number);
    }
    if (unit.years) {
        return date.addYears(number);
    }
    
},

First, it parses the maintenance period associated with the task type using the parsePeriod() function:

parsePeriod(spec) {
    const number = parseInt(spec);
    
    const unit = {
        days: spec.includes("d"),                
        weeks: spec.includes("w"),                
        months: spec.includes("m"),                
        years: spec.includes("y"), 
        get value() {
            if (this.days) {
                return "days";
            }
            if (this.weeks) {
                return "weeks";
            }
            if (this.months) {
                return "months";
            }
            if (this.years) {
                return "years";
            }
        }
    };
    
    return {
        number,
        unit   
    }
    
}

Then it calculates the next date. The scheduleNext() function creates the follow-up task in the database using an HTTP call to the /api/Tasks/{id}/schedule-next endpoint, adds the new task to the schedule using the events.add() method and updates the current task.

Task completion and next date

asp.net core maintenance scheduling open source complete task

The task is complete when all checklist items are checked off and when the next task is scheduled.

A complete task will be displayed in gray color.

ASP.NET Core API Controller: Tasks

This is the C# source code of the Tasks controller that defines the /api/Tasks endpoints.

using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Project.Models;

namespace Project.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class Tasks : ControllerBase
    {
        private readonly MaintenanceDbContext _context;

        public Tasks(MaintenanceDbContext context)
        {
            _context = context;
        }

        [HttpGet]
        public async Task<ActionResult<IEnumerable<object>>> GetTasks([FromQuery] DateTime? start, [FromQuery] DateTime? end)
        {
            if (_context.Tasks == null)
            {
                return NotFound();
            }

            IQueryable<MaintenanceTask> query = _context.Tasks
                .Include(t => t.MaintenanceType)
                .Include(t => t.TaskItems)
                .Include(t => t.Next);

            if (start.HasValue)
            {
                query = query.Where(t => t.DueDate >= start.Value);
            }

            if (end.HasValue)
            {
                query = query.Where(t => t.DueDate <= end.Value);
            }

            var tasks = await query.ToListAsync();

            // prevent the whole chain from loading in TaskTransformer.TransformTask
            foreach(var task in tasks)
            {
                if(task.Next != null) {
                    task.Next = new MaintenanceTask
                    {
                        Id = task.Next.Id,
                        DueDate = task.Next.DueDate,
                    };
                }
                else {
                    task.Next = null;
                }
            }

            var result = tasks.Select(TaskTransformer.TransformTask).ToList();

            return result;
        }



        // GET: api/Tasks/5
        [HttpGet("{id}")]
        public async Task<ActionResult<MaintenanceTask>> GetMaintenanceTask(int id)
        {
          if (_context.Tasks == null)
          {
              return NotFound();
          }
            var maintenanceTask = await _context.Tasks.FindAsync(id);

            if (maintenanceTask == null)
            {
                return NotFound();
            }

            return maintenanceTask;
        }

        // PUT: api/Tasks/5
        // To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
        [HttpPut("{id}")]
        public async Task<IActionResult> PutMaintenanceTask(int id, MaintenanceTask maintenanceTask)
        {
            if (id != maintenanceTask.Id)
            {
                return BadRequest();
            }

            _context.Entry(maintenanceTask).State = EntityState.Modified;

            try
            {
                await _context.SaveChangesAsync();
            }
            catch (DbUpdateConcurrencyException)
            {
                if (!MaintenanceTaskExists(id))
                {
                    return NotFound();
                }
                else
                {
                    throw;
                }
            }

            return NoContent();
        }

        // POST: api/Tasks
        [HttpPost]
        public async Task<ActionResult<MaintenanceTask>> PostMaintenanceTask(MaintenanceTaskDto dto)
        {
            var maintenanceType = await _context.Types.FindAsync(dto.Type);
            if (maintenanceType == null)
            {
                return NotFound();
            }

            var task = new MaintenanceTask 
            {
                DueDate = dto.Start,
                Text = dto.Text,
                MaintenanceType = maintenanceType,  // Link to MaintenanceType
            };

            _context.Tasks.Add(task);
            await _context.SaveChangesAsync();

            // Re-fetch the task, including related entities
            var newTask = await _context.Tasks
                .Include(t => t.MaintenanceType)
                .Include(t => t.TaskItems)
                .FirstOrDefaultAsync(t => t.Id == task.Id);

            return CreatedAtAction("GetMaintenanceTask", new { id = newTask.Id }, TaskTransformer.TransformTask(newTask));
        }



        // DELETE: api/Tasks/5
        [HttpDelete("{id}")]
        public async Task<IActionResult> DeleteMaintenanceTask(int id)
        {
            var maintenanceTask = await _context.Tasks
                .Include(t => t.Next)
                .Include(t => t.TaskItems)  // Include TaskItems
                .FirstOrDefaultAsync(t => t.Id == id);
            if (maintenanceTask == null)
            {
                return NotFound();
            }

            // Don't allow deletion if the next task was scheduled
            if (maintenanceTask.Next != null)
            {
                return BadRequest("Can't delete a task that has a next task scheduled.");
            }

            // If this task is the "next" task of another task, clear that link
            var previousTask = await _context.Tasks.FirstOrDefaultAsync(t => t.Next == maintenanceTask);
            if (previousTask != null)
            {
                previousTask.Next = null;
                _context.Entry(previousTask).State = EntityState.Modified;
            }

            // Remove the TaskItems
            _context.TaskItems.RemoveRange(maintenanceTask.TaskItems);

            _context.Tasks.Remove(maintenanceTask);
            await _context.SaveChangesAsync();

            return NoContent();
        }

        
        [HttpPost("{id}/update-checklist")]
        public async Task<IActionResult> UpdateChecklist(int id, Dictionary<int, bool> checklist)
        {
            var task = await _context.Tasks
                .Include(t => t.TaskItems)
                .FirstOrDefaultAsync(t => t.Id == id);

            if (task == null)
            {
                return NotFound();
            }

            // Check each item in the received checklist
            foreach (var item in checklist)
            {
                var taskItem = task.TaskItems.FirstOrDefault(ti => ti.MaintenanceTypeItemId == item.Key);

                if (item.Value)  // If item is checked
                {
                    if (taskItem == null)  // If item doesn't exist, create it
                    {
                        taskItem = new MaintenanceTaskItem
                        {
                            Checked = true,
                            MaintenanceTypeItemId = item.Key
                        };
                        task.TaskItems.Add(taskItem);
                    }
                    else  // If item exists, check it
                    {
                        taskItem.Checked = true;
                    }
                }
                else if (taskItem != null)  // If item is unchecked and it exists, delete it
                {
                    _context.TaskItems.Remove(taskItem);
                }
            }

            await _context.SaveChangesAsync();

            return NoContent();
        }
        
        [HttpPost("{id}/schedule-next")]
        public async Task<ActionResult<MaintenanceTask>> ScheduleNextTask(int id, MaintenanceTaskDto dto)
        {
            var originalTask = await _context.Tasks.Include(t => t.Next).FirstOrDefaultAsync(t => t.Id == id);
            if (originalTask == null)
            {
                return NotFound();
            }

            if (originalTask.Next != null)
            {
                return BadRequest("A next task is already scheduled for this task.");
            }

            var maintenanceType = await _context.Types.FindAsync(dto.Type);
            if (maintenanceType == null)
            {
                return NotFound();
            }

            var nextTask = new MaintenanceTask 
            {
                DueDate = dto.Start,
                Text = dto.Text,
                MaintenanceType = maintenanceType,  // Link to MaintenanceType
            };

            _context.Tasks.Add(nextTask);
            await _context.SaveChangesAsync();

            // Linking the next task to the original one
            originalTask.Next = nextTask;
            await _context.SaveChangesAsync();

            return CreatedAtAction("GetMaintenanceTask", new { id = nextTask.Id }, TaskTransformer.TransformTask(nextTask));
        }

        // POST: api/Tasks/{id}/due-date
        [HttpPost("{id}/due-date")]
        public async Task<IActionResult> UpdateTaskDueDate(int id, DueDateUpdateDto dto)
        {
            var task = await _context.Tasks.FindAsync(id);
            if (task == null)
            {
                return NotFound();
            }

            task.DueDate = dto.Date;
            _context.Entry(task).State = EntityState.Modified;

            try
            {
                await _context.SaveChangesAsync();
            }
            catch (DbUpdateConcurrencyException)
            {
                if (!MaintenanceTaskExists(id))
                {
                    return NotFound();
                }
                else
                {
                    throw;
                }
            }

            return NoContent();
        }



        private bool MaintenanceTaskExists(int id)
        {
            return (_context.Tasks?.Any(e => e.Id == id)).GetValueOrDefault();
        }
    }
    
    
    public class DueDateUpdateDto
    {
        public DateTime Date { get; set; }
    }
    
    public class MaintenanceTaskDto
    {
        public DateTime Start { get; set; }
        public DateTime End { get; set; }
        public string Text { get; set; }
        public int Type { get; set; }
        public int Resource { get; set; }  // Assuming this is an integer ID
    }
    
    public static class TaskTransformer {
        public static object TransformTask(MaintenanceTask task)
        {
            var result = new
            {
                id = task.Id,
                start = task.DueDate.ToString("yyyy-MM-dd"),
                end = task.DueDate.ToString("yyyy-MM-dd"),
                text = task.Text,
                type = task.MaintenanceType.Id,
                checklist = task.TaskItems.ToDictionary(ti => ti.MaintenanceTypeItemId.ToString(), ti => ti.Checked),
                next = task.Next?.Id,
                nextDate = task.Next?.DueDate,
            };

            return result;
        }
    }
}

Entity Framework Model Classes

Our ASP.NET Core application using Entity Framework to handle the database access. This allows defining the database structure using modal-first approach by describing the storage using .NET classes.

The project defines four model classes:

  1. MaintenanceTask Class: Represents a maintenance task with properties like ID, due date, description text, and a reference to its type (MaintenanceTypeId and MaintenanceType). It includes a list of MaintenanceTaskItem objects and an optional Next task, forming a linked list structure. The constructor initializes an empty list of MaintenanceTaskItem.

  2. MaintenanceType Class: Represents different types of maintenance activities. Each type has an ID, a name, a periodicity (Period), and an optional color. It also includes a collection of MaintenanceTypeItem objects that define specific items/tasks associated with that type.

  3. MaintenanceTypeItem Class: Defines individual items or tasks within a maintenance type. Each item has an ID, a name, and a reference to its parent maintenance type (MaintenanceTypeId and MaintenanceType).

  4. MaintenanceTaskItem Class: Represents individual items or tasks within a MaintenanceTask. It includes an ID, a reference to the corresponding MaintenanceTypeItem, and a boolean to indicate if the item has been checked or completed.

These classes are defined in the Models/Data.cs file, along with the MaintenanceDbContext class. The MaintenanceDbContext class provides the data access interface (using DBSets).

In the OnModelCreating method, it seeds the database with initial data for MaintenanceType and MaintenanceTypeItem, providing pre-defined maintenance types and their associated checklists.

public class MaintenanceDbContext : DbContext
{
    public DbSet<MaintenanceTask> Tasks { get; set; }
    public DbSet<MaintenanceTaskItem> TaskItems { get; set; }
    public DbSet<MaintenanceType> Types { get; set; }
    public DbSet<MaintenanceTypeItem> TypeItems { get; set; }

    public MaintenanceDbContext(DbContextOptions<MaintenanceDbContext> options) : base(options)
    {
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<MaintenanceType>().HasData(new MaintenanceType
            { Id = 1, Name = "Basic Cleanup", Period = "1w", Color = "#6aa84f"});
        modelBuilder.Entity<MaintenanceType>().HasData(new MaintenanceType
            { Id = 2, Name = "Safety Inspection", Period = "1m", Color = "#f1c232"});
        modelBuilder.Entity<MaintenanceType>().HasData(new MaintenanceType
            { Id = 3, Name = "Machine Calibration", Period = "3m", Color = "#4a86e8" });
        modelBuilder.Entity<MaintenanceType>().HasData(new MaintenanceType
            { Id = 4, Name = "Preventive Maintenance", Period = "6m", Color = "#e06666" });

        modelBuilder.Entity<MaintenanceTypeItem>().HasData(new MaintenanceTypeItem
            { Id = 1, Name = "Clean and sanitize work surfaces", MaintenanceTypeId = 1 });
        modelBuilder.Entity<MaintenanceTypeItem>().HasData(new MaintenanceTypeItem
            { Id = 2, Name = "Remove waste material", MaintenanceTypeId = 1 });

        modelBuilder.Entity<MaintenanceTypeItem>().HasData(new MaintenanceTypeItem
            { Id = 3, Name = "Inspect safety equipment", MaintenanceTypeId = 2 });
        modelBuilder.Entity<MaintenanceTypeItem>().HasData(new MaintenanceTypeItem
            { Id = 4, Name = "Check emergency exits", MaintenanceTypeId = 2 });
        modelBuilder.Entity<MaintenanceTypeItem>().HasData(new MaintenanceTypeItem
            { Id = 5, Name = "Test fire alarms", MaintenanceTypeId = 2 });
        modelBuilder.Entity<MaintenanceTypeItem>().HasData(new MaintenanceTypeItem
            { Id = 6, Name = "Inspect first aid kits", MaintenanceTypeId = 2 });

        modelBuilder.Entity<MaintenanceTypeItem>().HasData(new MaintenanceTypeItem
            { Id = 7, Name = "Calibrate machine sensors", MaintenanceTypeId = 3 });
        modelBuilder.Entity<MaintenanceTypeItem>().HasData(new MaintenanceTypeItem
            { Id = 8, Name = "Check machine alignment", MaintenanceTypeId = 3 });
        modelBuilder.Entity<MaintenanceTypeItem>().HasData(new MaintenanceTypeItem
            { Id = 9, Name = "Check for any abnormal sounds", MaintenanceTypeId = 3 });

        modelBuilder.Entity<MaintenanceTypeItem>().HasData(new MaintenanceTypeItem
            { Id = 10, Name = "Inspect for wear and tear", MaintenanceTypeId = 4 });
        modelBuilder.Entity<MaintenanceTypeItem>().HasData(new MaintenanceTypeItem
            { Id = 11, Name = "Replace worn parts", MaintenanceTypeId = 4 });
        modelBuilder.Entity<MaintenanceTypeItem>().HasData(new MaintenanceTypeItem
            { Id = 12, Name = "Check for leaks", MaintenanceTypeId = 4 });
        modelBuilder.Entity<MaintenanceTypeItem>().HasData(new MaintenanceTypeItem
            { Id = 13, Name = "Lubricate moving parts", MaintenanceTypeId = 4 });
    }
}

SQL Server Database

The database connection string is defined in appsettings.json file:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "ConnectionStrings": {
    "MaintenanceContext": "Server=(localdb)\\mssqllocaldb;Database=DayPilot.TutorialAspNetCoreMaintenance;Trusted_Connection=True"
  },
  "AllowedHosts": "*"
}

The database uses SQL Server with the LocalDB interface. The database file will be automatically created during the initial start of the ASP.NET Core application, using context.Database.EnsureCreated(); in Program.cs.

Download

You can download the full source code of this application using the link at the beginning of the tutorial.

History

  • December 1, 2023: Upgraded to .NET 8, DayPilot Lite for JavaScript 2023.4.504

  • June 26, 2023: Initial release, .NET 7