Hier ist eine Anleitung, die Ihnen hilft, ein Gantt-Diagramm mit ASP.NET Core auf der Serverseite einzurichten.
Wenn Sie daran interessiert sind, andere serverseitige Technologien zu erkunden, sehen Sie sich diese Tutorials an:
Für die Datenbankkommunikation wird das Entity Framework Core verwendet, und die Anwendung wird mit Visual Studio 2022 erstellt.
Der vollständige Quellcode ist auf GitHub verfügbar.
Starten Sie Visual Studio 2022 und erstellen Sie ein neues Projekt. Wählen Sie Create a new project.
Wählen Sie dann "ASP.NET Core Web App" und benennen Sie das Projekt DHX.Gantt.
Jetzt ist das Projekt bereit, und Sie können mit dem Hinzufügen des Markups und Skripts für das Gantt-Diagramm fortfahren.
Navigieren Sie zu wwwroot und erstellen Sie eine neue Datei namens index.html.
Richten Sie in dieser Datei eine einfache Seite für das Gantt-Diagramm ein.
Hier werden die Gantt-Dateien vom CDN geladen. Wenn Sie die professionelle Version der Komponente verwenden, müssen Sie die Dateien manuell hinzufügen.
index.html
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Index</title>
<link href="https://cdn.dhtmlx.com/gantt/edge/dhtmlxgantt.css"
rel="stylesheet" type="text/css" />
<script src="https://cdn.dhtmlx.com/gantt/edge/dhtmlxgantt.js"></script>
<script>
document.addEventListener("DOMContentLoaded", function(event) {
// Setzen Sie das Datumsformat
gantt.config.date_format = "%Y-%m-%d %H:%i";
// Initialisieren Sie das Gantt-Diagramm
gantt.init("gantt_here");
// Daten laden
gantt.load("/api/data");
// Datenprozessor einrichten
var dp = new gantt.dataProcessor("/api/");
dp.init(gantt);
dp.setTransactionMode("REST");
});
</script>
</head>
<body>
<div id="gantt_here" style="width: 100%; height: 100vh;"></div>
</body>
</html>
Wenn die Seite geladen wird, wird das Gantt-Diagramm initialisiert und das Laden der Daten ausgelöst. Ein dataProcessor
wird ebenfalls konfiguriert, um Änderungen am Diagramm im Backend zu speichern. Die Einrichtung des Backends erfolgt später im Prozess.
Als nächstes aktualisieren Sie Program.cs, um die index.html-Seite zu verwenden. Dies beinhaltet die Konfiguration der App, um statische Dateien aus dem wwwroot
-Ordner bereitzustellen. Um dies zu tun, fügen Sie die Methode app.UseDefaultFiles()
hinzu. Weitere Details finden Sie hier.
Program.cs
var builder = WebApplication.CreateBuilder(args);
// Dienste zum Container hinzufügen.
builder.Services.AddRazorPages();
var app = builder.Build();
// Konfigurieren Sie die HTTP-Anforderungspipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseDefaultFiles();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapRazorPages();
app.Run();
Die Methode app.UseDefaultFiles()
ermöglicht das Bereitstellen von Standarddateien wie:
In diesem Tutorial wird "index.html" verwendet. Beachten Sie, dass UseDefaultFiles()
nur URLs umschreibt – es dient nicht tatsächlich der Bereitstellung der Dateien. Um Dateien bereitzustellen, benötigen Sie auch UseStaticFiles()
.
An diesem Punkt sollte das Ausführen der Anwendung ein leeres Gantt-Diagramm anzeigen. Das "Invalid data"-Label in der oberen rechten Ecke erscheint, weil gantt.load()
aufgerufen wird, aber es gibt noch kein Backend, das Daten bereitstellt. Sobald das Backend implementiert ist, zeigt das Diagramm Aufgaben und Verknüpfungen.
Mit der grundlegenden Einrichtung abgeschlossen, können Sie nun mit der Implementierung des Backends fortfahren. Beginnen Sie mit dem Erstellen von Modellklassen und gehen Sie dann zum WebAPI-Controller über.
Das Gantt-Datenmodell umfasst Aufgaben und Verknüpfungen. dhtmlxGantt verwendet nicht standardmäßige Eigenschaftsnamen im Vergleich zu .NET-Konventionen. Manchmal werden zusätzliche Eigenschaften für die Client-Seite oder Backend-Logik verwendet, aber diese müssen nicht in der Datenbank gespeichert werden.
Um dies zu handhaben, wird das Data Transfer Object (DTO) Muster angewendet. Es werden zwei Arten von Modellen erstellt:
Die Zuordnung zwischen diesen Modellen wird ebenfalls implementiert.
Erstellen Sie einen Models-Ordner in Ihrem Projektverzeichnis, um die Modellklassen und den EF-Kontext zu speichern.
Beginnen Sie mit dem Erstellen einer Klasse für Aufgaben. Fügen Sie eine Datei namens Task.cs im Models-Ordner hinzu.
Hier ist die Struktur des Aufgabenmodells:
DHX.Gantt/Models/Task.cs
namespace DHX.Gantt.Models
{
public class Task
{
public int Id { get; set; }
public string? Text { get; set; }
public DateTime StartDate { get; set; }
public int Duration { get; set; }
public decimal Progress { get; set; }
public int? ParentId { get; set; }
public string? Type { get; set; }
}
}
Die vollständige Liste der Aufgabeneigenschaften ist zur Referenz verfügbar.
Erstellen Sie als nächstes eine Klasse für Verknüpfungen, indem Sie eine Datei namens Link.cs im Models-Ordner hinzufügen.
Hier ist die Struktur des Verknüpfungsmodells:
DHX.Gantt/Models/Link.cs
namespace DHX.Gantt.Models
{
public class Link
{
public int Id { get; set; }
public string? Type { get; set; }
public int SourceTaskId { get; set; }
public int TargetTaskId { get; set; }
}
}
Mit den Modellen bereit, ist der nächste Schritt, die Datenbankverbindung einzurichten.
Führen Sie die folgenden Schritte aus, um die Datenbankverbindung zu konfigurieren:
Das Entity Framework Core wird die Datenbankkommunikation der App verwalten. Um es zu installieren:
Oder verwenden Sie die Befehlszeile des Paketmanagers:
PM> Install-Package Microsoft.EntityFrameworkCore.SqlServer
PM> Install-Package Microsoft.EntityFrameworkCore
PM> Install-Package Microsoft.EntityFrameworkCore.Design
Um die Interaktion mit der Datenbank zu ermöglichen, definieren Sie einen Kontext:
DHX.Gantt/Models/GanttContext.cs
using Microsoft.EntityFrameworkCore;
namespace DHX.Gantt.Models
{
public class GanttContext : DbContext
{
public GanttContext(DbContextOptions<GanttContext> options)
: base(options)
{
}
public DbSet<Task> Tasks { get; set; } = null;
public DbSet<Link> Links { get; set; } = null;
}
}
Um mit dem Hinzufügen von Datensätzen zur Datenbank zu beginnen, müssen Sie einen Datenbank-Initializer erstellen, um sie mit Aufgaben zu füllen. Erstellen Sie im Models-Ordner eine Klasse namens GanttSeeder. Diese Klasse enthält eine Seed()-Methode, um Aufgaben und Verknüpfungen zur Datenbank hinzuzufügen.
DHX.Gantt/Models/GanttSeeder.cs
using Microsoft.EntityFrameworkCore;
namespace DHX.Gantt.Models
{
public static class GanttSeeder
{
public static void Seed(GanttContext context)
{
if (context.Tasks.Any())
{
return; // Die Datenbank ist bereits gefüllt
}
using (var transaction = context.Database.BeginTransaction())
{
List<Task> tasks = new List<Task>()
{
new Task()
{
Id = 1,
Text = "Projekt #2",
StartDate = DateTime.Today.AddDays(-3),
Duration = 18,
Progress = 0.4m,
ParentId = null
},
new Task()
{
Id = 2,
Text = "Aufgabe #1",
StartDate = DateTime.Today.AddDays(-2),
Duration = 8,
Progress = 0.6m,
ParentId = 1
},
new Task()
{
Id = 3,
Text = "Aufgabe #2",
StartDate = DateTime.Today.AddDays(-1),
Duration = 8,
Progress = 0.6m,
ParentId = 1
}
};
tasks.ForEach(s => context.Tasks.Add(s));
context.Database.ExecuteSqlRaw("SET IDENTITY_INSERT Tasks ON;");
context.SaveChanges();
context.Database.ExecuteSqlRaw("SET IDENTITY_INSERT Tasks OFF;");
List<Link> links = new List<Link>()
{
new Link() {Id = 1, SourceTaskId = 1, TargetTaskId = 2, Type = "1"},
new Link() {Id = 2, SourceTaskId = 2, TargetTaskId = 3, Type = "0"}
};
links.ForEach(s => context.Links.Add(s));
context.Database.ExecuteSqlRaw("SET IDENTITY_INSERT Links ON;");
context.SaveChanges();
context.Database.ExecuteSqlRaw("SET IDENTITY_INSERT Links OFF;");
transaction.Commit();
}
}
}
}
Bevor Sie die Datenbank in Program.cs registrieren, ist ein Verbindungsstring erforderlich. Dieser wird in einer JSON-Datei innerhalb der Anwendungseinstellungen gespeichert. Öffnen oder erstellen Sie die appsettings.json-Datei und fügen Sie den Verbindungsstring ein:
appsettings.json
{
"ConnectionStrings": {
"DefaultConnection": "Server=(localdb)\\mssqllocaldb;
Database=GanttDatabase;Trusted_Connection=True;"
}
}
Der Datenbankkontext wird mit Dependency Injection registriert.
Fügen Sie diese Namensräume zu Program.cs hinzu:
Program.cs
using Microsoft.EntityFrameworkCore;
using DHX.Gantt.Models;
Die Registrierung sieht folgendermaßen aus:
Program.cs
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<GanttContext>(
options => options.UseSqlServer(connectionString));
Um Controller zu aktivieren, fügen Sie die services.AddControllers()-Methode hinzu:
Program.cs
builder.Services.AddControllers();
Und um Controller-Routen zu registrieren, verwenden Sie app.MapControllers():
Program.cs
app.MapControllers();
Hier ist die vollständige Program.cs-Datei:
Program.cs
using Microsoft.EntityFrameworkCore;
using DHX.Gantt.Models;
var builder = WebApplication.CreateBuilder(args);
// Dienste zum Container hinzufügen.
builder.Services.AddRazorPages();
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<GanttContext>(
options => options.UseSqlServer(connectionString));
builder.Services.AddControllers();
var app = builder.Build();
// Konfigurieren Sie die HTTP-Anforderungspipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
// Der Standard-HSTS-Wert beträgt 30 Tage.
// Sie können dies für Produktionsszenarien ändern,
// siehe https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseDefaultFiles();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapRazorPages();
app.MapControllers();
app.Run();
Um die Datenbank beim Start der App einzurichten und zu füllen, erstellen Sie eine Klasse zur Initialisierung. Erstellen Sie im Models-Ordner eine Datei namens GanttInitializerExtension.cs:
Models/GanttInitializerExtension.cs
namespace DHX.Gantt.Models
{
public static class GanttInitializerExtension
{
public static IHost InitializeDatabase(this IHost webHost)
{
var serviceScopeFactory =
(IServiceScopeFactory?)webHost.Services.GetService(typeof(IServiceScopeFactory));
using (var scope = serviceScopeFactory!.CreateScope())
{
var services = scope.ServiceProvider;
var dbContext = services.GetRequiredService<GanttContext>();
dbContext.Database.EnsureDeleted();
dbContext.Database.EnsureCreated();
GanttSeeder.Seed(dbContext);
}
return webHost;
}
}
}
Rufen Sie nun InitializeDatabase() auf:
Program.cs
app.InitializeDatabase();
In diesem Beispiel werden Migrationen der Einfachheit halber übersprungen. Stattdessen werden die Methoden EnsureCreated und seed verwendet.
Mit dieser Einrichtung können Sie mit Gantt fortfahren.
Als nächstes definieren Sie DTO-Klassen für die Web-API. Beginnen Sie mit der DTO-Klasse für Aufgaben. Erstellen Sie im Models-Ordner eine Datei namens WebApiTask.cs:
Models/WebApiTask.cs
namespace DHX.Gantt.Models
{
public class WebApiTask
{
public int id { get; set; }
public string? text { get; set; }
public string? start_date { get; set; }
public int duration { get; set; }
public decimal progress { get; set; }
public int? parent { get; set; }
public string? type { get; set; }
public bool open
{
get { return true; }
set { }
}
public static explicit operator WebApiTask(Task task)
{
return new WebApiTask
{
id = task.Id,
text = task.Text,
start_date = task.StartDate.ToString("yyyy-MM-dd HH:mm"),
duration = task.Duration,
parent = task.ParentId,
type = task.Type,
progress = task.Progress
};
}
public static explicit operator Task(WebApiTask task)
{
return new Task
{
Id = task.id,
Text = task.text,
StartDate = task.start_date != null ? DateTime.Parse(task.start_date,
System.Globalization.CultureInfo.InvariantCulture) : new DateTime(),
Duration = task.duration,
ParentId = task.parent,
Type = task.type,
Progress = task.progress
};
}
}
}
Erstellen Sie nun die DTO-Klasse für Verknüpfungen in einer Datei namens WebApiLink.cs, ebenfalls im Models-Ordner:
Models/WebApiLink.cs
namespace DHX.Gantt.Models
{
public class WebApiLink
{
public int id { get; set; }
public string? type { get; set; }
public int source { get; set; }
public int target { get; set; }
public static explicit operator WebApiLink(Link link)
{
return new WebApiLink
{
id = link.Id,
type = link.Type,
source = link.SourceTaskId,
target = link.TargetTaskId
};
}
public static explicit operator Link(WebApiLink link)
{
return new Link
{
Id = link.id,
Type = link.type,
SourceTaskId = link.source,
TargetTaskId = link.target
};
}
}
}
Nach Abschluss dieses Schritts sollte Ihre Ordnerstruktur wie folgt aussehen:
Sie können nun die App ausführen, um sicherzustellen, dass alles korrekt eingerichtet ist. Wenn keine Laufzeitfehler auftreten, sind Sie bereit.
Nun ist es an der Zeit, die REST-API zu implementieren.
Erstellen Sie einen Controllers-Ordner und fügen Sie drei leere API-Controller hinzu: einen für Aufgaben, einen weiteren für Verknüpfungen und einen dritten für den gesamten Datensatz:
Ein Controller zur Verwaltung von Aufgaben ist erforderlich. Er wird grundlegende CRUD-Operationen für Gantt-Aufgaben abwickeln.
So funktioniert es:
WebAPITask
-Objekte gesendet, was das von dhtmlxGantt verwendete Format ist. Diese müssen in das Task
-Klassenformat konvertiert werden, das von EntityFramework verwendet wird. Einmal konvertiert, können die Änderungen im DatabaseContext
gespeichert werden.Controllers/TaskController.cs
using Microsoft.AspNetCore.Mvc;
using DHX.Gantt.Models;
namespace DHX.Gantt.Controllers
{
[Produces("application/json")]
[Route("api/task")]
public class TaskController : Controller
{
private readonly GanttContext _context;
public TaskController(GanttContext context)
{
_context = context;
}
// GET api/task
[HttpGet]
public IEnumerable<WebApiTask> Get()
{
return _context.Tasks
.ToList()
.Select(t => (WebApiTask)t);
}
// GET api/task/5
[HttpGet("{id}")]
public Models.Task? Get(int id)
{
return _context
.Tasks
.Find(id);
}
// POST api/task
[HttpPost]
public ObjectResult Post(WebApiTask apiTask)
{
var newTask = (Models.Task)apiTask;
_context.Tasks.Add(newTask);
_context.SaveChanges();
return Ok(new
{
tid = newTask.Id,
action = "inserted"
});
}
// PUT api/task/5
[HttpPut("{id}")]
public ObjectResult? Put(int id, WebApiTask apiTask)
{
var updatedTask = (Models.Task)apiTask;
var dbTask = _context.Tasks.Find(id);
if (dbTask == null)
{
return null;
}
dbTask.Text = updatedTask.Text;
dbTask.StartDate = updatedTask.StartDate;
dbTask.Duration = updatedTask.Duration;
dbTask.ParentId = updatedTask.ParentId;
dbTask.Progress = updatedTask.Progress;
dbTask.Type = updatedTask.Type;
_context.SaveChanges();
return Ok(new
{
action = "updated"
});
}
// DELETE api/task/5
[HttpDelete("{id}")]
public ObjectResult DeleteTask(int id)
{
var task = _context.Tasks.Find(id);
if (task != null)
{
_context.Tasks.Remove(task);
_context.SaveChanges();
}
return Ok(new
{
action = "deleted"
});
}
}
}
Nun ist ein Controller zur Verwaltung von Verknüpfungen erforderlich:
Controllers/LinkController.cs
using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.Mvc;
using DHX.Gantt.Models;
namespace DHX.Gantt.Controllers
{
[Produces("application/json")]
[Route("api/link")]
public class LinkController : Controller
{
private readonly GanttContext _context;
public LinkController(GanttContext context)
{
_context = context;
}
// GET api/Link
[HttpGet]
public IEnumerable<WebApiLink> Get()
{
return _context.Links
.ToList()
.Select(t => (WebApiLink)t);
}
// GET api/Link/5
[HttpGet("{id}")]
public Link? Get(int id)
{
return _context
.Links
.Find(id);
}
// POST api/Link
[HttpPost]
public ObjectResult Post(WebApiLink apiLink)
{
var newLink = (Link)apiLink;
_context.Links.Add(newLink);
_context.SaveChanges();
return Ok(new
{
tid = newLink.Id,
action = "inserted"
});
}
// PUT api/Link/5
[HttpPut("{id}")]
public ObjectResult Put(int id, WebApiLink apiLink)
{
var updatedLink = (Link)apiLink;
updatedLink.Id = id;
_context.Entry(updatedLink).State = EntityState.Modified;
_context.SaveChanges();
return Ok(new
{
action = "updated"
});
}
// DELETE api/Link/5
[HttpDelete("{id}")]
public ObjectResult DeleteLink(int id)
{
var Link = _context.Links.Find(id);
if (Link != null)
{
_context.Links.Remove(Link);
_context.SaveChanges();
}
return Ok(new
{
action = "deleted"
});
}
}
}
Schließlich ist ein Controller zur Handhabung von Datenaktionen erforderlich:
Controllers/DataController.cs
using Microsoft.AspNetCore.Mvc;
using DHX.Gantt.Models;
namespace DHX.Gantt.Controllers
{
[Produces("application/json")]
[Route("api/data")]
public class DataController : Controller
{
private readonly GanttContext _context;
public DataController(GanttContext context)
{
_context = context;
}
// GET api/data
[HttpGet]
public object Get()
{
return new
{
data = _context.Tasks.ToList().Select(t => (WebApiTask)t),
links = _context.Links.ToList().Select(l => (WebApiLink)l)
};
}
}
}
Das ist alles eingerichtet. Sie können nun die Anwendung ausführen und ein voll funktionsfähiges Gantt-Diagramm sehen.
Der vollständige Quellcode ist auf GitHub verfügbar.
Um Fehler zu handhaben, kann eine Middleware-Klasse erstellt werden, um Laufzeitausnahmen abzufangen und Antworten zu schreiben. Diese Klasse wird zur Anforderungspipeline der App hinzugefügt. So richten Sie sie ein:
1. Beginnen Sie mit dem Erstellen einer Middleware-Klasse mithilfe einer Vorlage im Projektordner.
2. Installieren Sie das JSON-Framework für ASP.NET Core. Dies können Sie über den NuGet-Paketmanager tun:
Oder verwenden Sie die Befehlszeile des Paketmanagers:
PM> Install-Package NewtonSoft.JSON
3. Umwickeln Sie im Invoke
-Methode der Middleware den _next
-Aufruf mit einem try-catch
-Block, um Ausnahmen abzufangen und den Handler auszuführen, wenn ein Fehler auftritt.
GanttErrorMiddleware.cs
public async Task Invoke(HttpContext httpContext)
{
try
{
await _next(httpContext);
}catch(Exception e)
{
await HandleExceptionAsync(httpContext, e);
}
}
private static Task HandleExceptionAsync(HttpContext context, Exception exception)
{
var result = JsonConvert.SerializeObject(new {
action = "error"
});
context.Response.ContentType = "application/json";
context.Response.StatusCode = StatusCodes.Status500InternalServerError;
return context.Response.WriteAsync(result);
}
4. Fügen Sie den erforderlichen Namensraum zu GanttErrorMiddleware.cs hinzu:
using Newtonsoft.Json;
5. Schließlich verbinden Sie die Middleware in Program.cs. Fügen Sie den erforderlichen Namensraum hinzu:
Program.cs
using DHX.Gantt;
Dann fügen Sie die Middleware in die Pipeline ein, indem Sie aufrufen:
Program.cs
app.UseGanttErrorMiddleware();
Wenn Benutzer Aufgaben per Drag-and-Drop umordnen, kann die neue Reihenfolge in der Datenbank gespeichert werden. Weitere Details sind in diesem Abschnitt verfügbar.
Um die Umordnung auf der Client-Seite zu aktivieren, fügen Sie die folgenden Zeilen zu index.html hinzu:
wwwroot/index.html
gantt.config.order_branch = true;
gantt.config.order_branch_free = true;
// Das Datumsformat angeben
gantt.config.date_format = "%Y-%m-%d %H:%i";
// Gantt initialisieren
gantt.init("gantt_here");
Um die Änderungen der Aufgabenreihenfolge im Backend widerzuspiegeln, aktualisieren Sie das Task
-Modell, indem Sie eine neue Eigenschaft hinzufügen:
Models/Task.cs
namespace DHX.Gantt.Models
{
public class Task
{
public int Id { get; set; }
public string? Text { get; set; }
public DateTime StartDate { get; set; }
public int Duration { get; set; }
public decimal Progress { get; set; }
public int? ParentId { get; set; }
public string? Type { get; set; }
public int SortOrder { get; set; } }
}
Es ist Zeit, einige Aktualisierungen an den Controllern vorzunehmen.
DataController
hinzu:Controllers/DataController.cs
[HttpGet]
public object Get()
{
return new
{
data = _context.Tasks
.OrderBy(t => t.SortOrder) .ToList()
.Select(t => (WebApiTask)t),
links = _context.Links
.ToList()
.Select(l => (WebApiLink)l)
};
}
controllers/TaskController.cs
// POST api/task
[HttpPost]
public IActionResult Post(WebApiTask apiTask)
{
var newTask = (Models.Task)apiTask;
newTask.SortOrder = _context.Tasks.Max(t => t.SortOrder) + 1; _context.Tasks.Add(newTask);
_context.SaveChanges();
return Ok(new
{
tid = newTask.Id,
action = "inserted"
});
}
PUT
-Anfrage mit den neuen Aufgabenpositionen in der target
-Eigenschaft zusammen mit anderen Aufgabendetails. Um dies zu handhaben, fügen Sie die target
-Eigenschaft zur WebApiTask
-Klasse hinzu:
Models/WebApiTask.cs
public class WebApiTask
{
public int id { get; set; }
public string? text { get; set; }
public string? start_date { get; set; }
public int duration { get; set; }
public decimal progress { get; set; }
public int? parent { get; set; }
public string? type { get; set; }
public string? target { get; set; } public bool open
{
get { return true; }
set { }
}
}
Ändern Sie als nächstes die Put
-Aktion im TaskController
, um die Umordnung zu handhaben:
Controllers/TaskController.cs
// PUT api/task/5
[HttpPut("{id}")]
public IActionResult? Put(int id, WebApiTask apiTask)
{
var updatedTask = (Models.Task)apiTask;
updatedTask.Id = id;
var dbTask = _context.Tasks.Find(id);
if (dbTask == null)
{
return null;
}
dbTask.Text = updatedTask.Text;
dbTask.StartDate = updatedTask.StartDate;
dbTask.Duration = updatedTask.Duration;
dbTask.ParentId = updatedTask.ParentId;
dbTask.Progress = updatedTask.Progress;
dbTask.Type = updatedTask.Type;
if (!string.IsNullOrEmpty(apiTask.target)) { // Umordnung erfolgte this._UpdateOrders(dbTask, apiTask.target); }
_context.SaveChanges();
return Ok(new
{
action = "updated"
});
}
Fügen Sie schließlich eine Methode hinzu, um die Aktualisierungen der Aufgabenreihenfolge zu handhaben:
Controllers/TaskController.cs
private void _UpdateOrders(Models.Task updatedTask, string orderTarget)
{
int adjacentTaskId;
var nextSibling = false;
var targetId = orderTarget;
// Die ID der benachbarten Aufgabe wird entweder als '{id}' oder als 'next:{id}' gesendet, je nachdem, ob es sich um das nächste oder das vorherige Geschwister handelt
if (targetId.StartsWith("next:"))
{
targetId = targetId.Replace("next:", "");
nextSibling = true;
}
if (!int.TryParse(targetId, out adjacentTaskId))
{
return;
}
var adjacentTask = _context.Tasks.Find(adjacentTaskId);
var startOrder = adjacentTask!.SortOrder;
if (nextSibling)
startOrder++;
updatedTask.SortOrder = startOrder;
var updateOrders = _context.Tasks
.Where(t => t.Id != updatedTask.Id)
.Where(t => t.SortOrder >= startOrder)
.OrderBy(t => t.SortOrder);
var taskList = updateOrders.ToList();
taskList.ForEach(t => t.SortOrder++);
}
Gantt selbst enthält keinen Schutz gegen Sicherheitsrisiken wie SQL-Injection, XSS oder CSRF-Angriffe. Es liegt an den Entwicklern, ihre Anwendungen zu sichern. Weitere Details finden Sie im relevanten Artikel.
Eine einfache Möglichkeit, sich gegen XSS zu schützen, besteht darin, die Texteigenschaften Ihrer Daten zu codieren, bevor Sie sie an den Client senden. Hier ist ein Beispiel, das den eingebauten HtmlEncoder
verwendet, um HTML im Aufgabentext zu escapen. Dies stellt sicher, dass die Datenbank unveränderte Daten enthält, der Client jedoch bereinigte Werte erhält.
Models/WebApiTask.cs
using System.Text.Encodings.Web;
public static explicit operator WebApiTask(Task task)
{
return new WebApiTask
{
id = task.Id,
text = HtmlEncoder.Default.Encode(task.Text != null ? task.Text : ""), start_date = task.StartDate.ToString("yyyy-MM-dd HH:mm"),
duration = task.Duration,
parent = task.ParentId,
type = task.Type,
progress = task.Progress
};
}
Alternativ könnten Sie eine Bibliothek wie HtmlAgilityPack verwenden, um HTML-Tags beim Speichern oder Laden von Daten vollständig zu entfernen.
Wenn Sie alle Schritte befolgt haben, aber Gantt immer noch keine Aufgaben und Verknüpfungen anzeigt, lesen Sie den Leitfaden zur Fehlerbehebung in diesem Artikel. Er bietet Tipps zur Diagnose und Lösung von Problemen.
Mit diesen Schritten haben Sie jetzt ein voll funktionsfähiges Gantt. Der vollständige Code ist auf GitHub verfügbar, damit Sie ihn klonen oder herunterladen und in Ihren Projekten verwenden können.
Für weitere Funktionen erkunden Sie die Gantt-Leitfäden oder sehen Sie sich Tutorials zur Integration von Gantt mit anderen Backend-Frameworks an.
Zurück nach oben