ASP.NET Core MVC project (SQL Server + EF Core + Identity) that supports Create + Assign + List + Edit + Complete operations and role-wise views (Admin / Manager / User). Below I’ll deliver a complete, opinionated starter project you can paste into a new solution and run. I made sensible defaults so it works out-of-the-box:
Defaults I used
.NET 8 / ASP.NET Core MVC (minimal hosting Program.cs).
EF Core with SQL Server (use LocalDB or your SQL Server instance).
ASP.NET Identity for users & roles (Admin / Manager / User).
Entity: TaskItem with create/assign/edit/complete flows.
Role-wise view behavior:
Admin: sees all tasks and can manage them + manage users/roles.
Manager: sees tasks they created + tasks assigned to users in their team (simple demo: managers can view all tasks but UI filters for managers).
User: sees only tasks assigned to them.
Seeded roles + initial admin user automatically on startup.
EF Migrations recommended (I show commands).
Below are the main files — copy them into a new project (or I can generate more files if required). I included the essential Controllers, Models, DbContext, Program, Views examples, and steps to run.
1. Create the project
From a terminal:
dotnet new mvc -n TaskManagerApp
cd TaskManagerApp
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Microsoft.EntityFrameworkCore.Tools
dotnet add package Microsoft.AspNetCore.Identity.EntityFrameworkCore
2. appsettings.json
Add a SQL Server connection string (LocalDB used here):
{
"ConnectionStrings": {
"DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=TaskManagerAppDb;Trusted_Connection=True;MultipleActiveResultSets=true"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}
3. Models
Models/ApplicationUser.cs
using Microsoft.AspNetCore.Identity;
using System.Collections.Generic;
namespace TaskManagerApp.Models
{
public class ApplicationUser : IdentityUser
{
// Example extra property
public string FullName { get; set; }
// Optional: list of tasks assigned
public ICollection<TaskItem> AssignedTasks { get; set; }
}
}
Models/TaskItem.cs
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace TaskManagerApp.Models
{
public enum TaskStatus
{
Pending,
InProgress,
Completed
}
public class TaskItem
{
[Key]
public int Id { get; set; }
[Required, StringLength(200)]
public string Title { get; set; }
public string Description { get; set; }
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
public DateTime? DueDate { get; set; }
public TaskStatus Status { get; set; } = TaskStatus.Pending;
// assigned user
public string AssignedUserId { get; set; }
[ForeignKey("AssignedUserId")]
public ApplicationUser AssignedUser { get; set; }
// who created the task (user id)
public string CreatedByUserId { get; set; }
}
}
4. Data / DbContext
Data/AppDbContext.cs
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using TaskManagerApp.Models;
namespace TaskManagerApp.Data
{
public class AppDbContext : IdentityDbContext<ApplicationUser>
{
public AppDbContext(DbContextOptions<AppDbContext> opts) : base(opts) { }
public DbSet<TaskItem> TaskItems { get; set; }
}
}
5. Program.cs (minimal hosting) — seeds roles & admin
Program.cs
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using TaskManagerApp.Data;
using TaskManagerApp.Models;
var builder = WebApplication.CreateBuilder(args);
// Add services
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<AppDbContext>(options =>
options.UseSqlServer(connectionString));
builder.Services.AddIdentity<ApplicationUser, IdentityRole>(options =>
{
options.Password.RequireNonAlphanumeric = false;
options.Password.RequireUppercase = false;
})
.AddEntityFrameworkStores<AppDbContext>()
.AddDefaultTokenProviders();
builder.Services.AddControllersWithViews();
var app = builder.Build();
// Ensure DB and seed roles
using (var scope = app.Services.CreateScope())
{
var services = scope.ServiceProvider;
var db = services.GetRequiredService<AppDbContext>();
db.Database.Migrate();
// Seed roles and admin user
var roleManager = services.GetRequiredService<RoleManager<IdentityRole>>();
var userManager = services.GetRequiredService<UserManager<ApplicationUser>>();
string[] roles = new[] { "Admin", "Manager", "User" };
foreach (var r in roles)
{
if (!await roleManager.RoleExistsAsync(r))
await roleManager.CreateAsync(new IdentityRole(r));
}
// create default admin
var adminEmail = "[email protected]";
var admin = await userManager.FindByEmailAsync(adminEmail);
if (admin == null)
{
admin = new ApplicationUser { UserName = "admin", Email = adminEmail, EmailConfirmed = true, FullName = "System Admin" };
var result = await userManager.CreateAsync(admin, "Admin@123"); // change for prod
if (result.Succeeded)
{
await userManager.AddToRoleAsync(admin, "Admin");
}
}
}
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Tasks}/{action=Index}/{id?}");
app.Run();
6. TasksController (main CRUD + assign + complete + role-wise list)
Controllers/TasksController.cs
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using TaskManagerApp.Data;
using TaskManagerApp.Models;
namespace TaskManagerApp.Controllers
{
[Authorize]
public class TasksController : Controller
{
private readonly AppDbContext _db;
private readonly UserManager<ApplicationUser> _userManager;
public TasksController(AppDbContext db, UserManager<ApplicationUser> userManager)
{
_db = db;
_userManager = userManager;
}
// Role-wise listing
public async Task<IActionResult> Index(string status = null)
{
var user = await _userManager.GetUserAsync(User);
var roles = await _userManager.GetRolesAsync(user);
IQueryable<TaskItem> query = _db.TaskItems.Include(t => t.AssignedUser);
if (roles.Contains("Admin"))
{
// Admin sees all - optionally filter by status
}
else if (roles.Contains("Manager"))
{
// Manager sees tasks they created OR assigned to any user
query = query.Where(t => t.CreatedByUserId == user.Id || t.AssignedUserId != null);
}
else
{
// Regular user sees only tasks assigned to them
query = query.Where(t => t.AssignedUserId == user.Id);
}
if (!string.IsNullOrEmpty(status) && Enum.TryParse<TaskStatus>(status, out var st))
{
query = query.Where(t => t.Status == st);
}
var list = await query.OrderByDescending(t => t.CreatedAt).ToListAsync();
return View(list);
}
// GET: Create
public IActionResult Create()
{
return View();
}
// POST: Create
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(TaskItem model)
{
var user = await _userManager.GetUserAsync(User);
if (ModelState.IsValid)
{
model.CreatedAt = DateTime.UtcNow;
model.CreatedByUserId = user.Id;
_db.TaskItems.Add(model);
await _db.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View(model);
}
// GET: Edit
public async Task<IActionResult> Edit(int id)
{
var task = await _db.TaskItems.FindAsync(id);
if (task == null) return NotFound();
return View(task);
}
// POST: Edit
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, TaskItem model)
{
var task = await _db.TaskItems.FindAsync(id);
if (task == null) return NotFound();
if (ModelState.IsValid)
{
task.Title = model.Title;
task.Description = model.Description;
task.DueDate = model.DueDate;
task.Status = model.Status;
task.AssignedUserId = model.AssignedUserId;
await _db.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View(model);
}
// GET: Assign (simple assignment UI)
public async Task<IActionResult> Assign(int id)
{
var task = await _db.TaskItems.FindAsync(id);
if (task == null) return NotFound();
var users = await _userManager.Users.ToListAsync();
ViewBag.Users = users;
return View(task);
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Assign(int id, string assignedUserId)
{
var task = await _db.TaskItems.FindAsync(id);
if (task == null) return NotFound();
task.AssignedUserId = assignedUserId;
task.Status = TaskStatus.InProgress;
await _db.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
// Mark complete
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Complete(int id)
{
var task = await _db.TaskItems.FindAsync(id);
if (task == null) return NotFound();
task.Status = TaskStatus.Completed;
await _db.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
// Details
public async Task<IActionResult> Details(int id)
{
var t = await _db.TaskItems.Include(x => x.AssignedUser).FirstOrDefaultAsync(x => x.Id == id);
if (t == null) return NotFound();
return View(t);
}
// Delete (Admin only)
[Authorize(Roles = "Admin")]
public async Task<IActionResult> Delete(int id)
{
var t = await _db.TaskItems.FindAsync(id);
if (t == null) return NotFound();
_db.TaskItems.Remove(t);
await _db.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
}
}
7. Views (Razor) — minimal examples
Place under Views/Tasks/
Index.cshtml
@model IEnumerable<TaskManagerApp.Models.TaskItem>
@using TaskManagerApp.Models
@inject Microsoft.AspNetCore.Identity.UserManager<TaskManagerApp.Models.ApplicationUser> UserManager
@{
ViewData["Title"] = "Tasks";
}
<h2>Tasks</h2>
<p>
<a asp-action="Create" class="btn btn-primary">Create Task</a>
</p>
<table class="table table-striped">
<thead>
<tr>
<th>Title</th>
<th>Assigned To</th>
<th>Status</th>
<th>Due</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach(var t in Model) {
<tr>
<td>@t.Title</td>
<td>@(t.AssignedUser?.FullName ?? t.AssignedUser?.UserName ?? "Unassigned")</td>
<td>@t.Status</td>
<td>@(t.DueDate?.ToLocalTime().ToString("yyyy-MM-dd") ?? "-")</td>
<td>
<a asp-action="Details" asp-route-id="@t.Id">Details</a> |
<a asp-action="Edit" asp-route-id="@t.Id">Edit</a> |
<a asp-action="Assign" asp-route-id="@t.Id">Assign</a>
<form asp-action="Complete" asp-route-id="@t.Id" method="post" style="display:inline">
<button type="submit" class="btn btn-link">Complete</button>
</form>
</td>
</tr>
}
</tbody>
</table>
Create.cshtml (simple)
@model TaskManagerApp.Models.TaskItem
@{
ViewData["Title"] = "Create Task";
}
<h2>Create Task</h2>
<form asp-action="Create" method="post">
<div class="form-group">
<label asp-for="Title"></label>
<input asp-for="Title" class="form-control" />
<span asp-validation-for="Title" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Description"></label>
<textarea asp-for="Description" class="form-control"></textarea>
</div>
<div class="form-group">
<label asp-for="DueDate"></label>
<input asp-for="DueDate" type="date" class="form-control" />
</div>
<button type="submit" class="btn btn-primary">Create</button>
</form>
Assign.cshtml
@model TaskManagerApp.Models.TaskItem
@{
ViewData["Title"] = "Assign Task";
var users = ViewBag.Users as List<TaskManagerApp.Models.ApplicationUser>;
}
<h2>Assign Task: @Model.Title</h2>
<form asp-action="Assign" method="post">
<input type="hidden" asp-for="Id" />
<div class="form-group">
<label>Select User</label>
<select name="assignedUserId" class="form-control">
<option value="">-- Unassigned --</option>
@foreach(var u in users)
{
<option value="@u.Id" @(u.Id == Model.AssignedUserId ? "selected" : "")>@u.FullName ?? u.UserName</option>
}
</select>
</div>
<button class="btn btn-primary" type="submit">Assign</button>
</form>
Edit.cshtml and Details.cshtml are straightforward — follow the pattern above (bind fields and show status dropdown).
8. Identity / Account pages
You can scaffold Identity pages (Register/Login) or rely on the default templates. For simplicity you can scaffold or add links to /Identity/Account/Login. In production, scaffold the pages to capture FullName during registration and to allow role assignment (Admin page for user/role management).
9. Database migrations
From terminal:
dotnet ef migrations add InitialCreate
dotnet ef database update
This will create the DB and tables. Program.cs seeds roles + admin user.
10. Additional suggestions for role-wise UX & security
Add [Authorize(Roles="Admin,Manager")] attributes where appropriate.
Create an Admin area (Area = Admin) for user & role management using RoleManager + UserManager.
For manager-team relationships you might add a Team entity and a ManagerId to associate users with managers.
For large teams add paging, search, and assignment by team.
11. How to run
Update connection string in appsettings.json to point to your SQL Server (LocalDB works).
dotnet ef database update (or let Program.cs migrate on startup).
dotnet run
Login with seeded admin: [email protected] / Admin@123 (change immediately in production).