Skip to content

Instantly share code, notes, and snippets.

@fnmssteam
Created February 18, 2025 06:51
Show Gist options
  • Save fnmssteam/2d611f58cb17eeb519b6d945619d0d7b to your computer and use it in GitHub Desktop.
Save fnmssteam/2d611f58cb17eeb519b6d945619d0d7b to your computer and use it in GitHub Desktop.
DeepSeek EF Core API Exercise
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace DeepSeekApiCore.Migrations
{
/// <inheritdoc />
public partial class InitialCreate : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Categories",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Name = table.Column<string>(type: "nvarchar(max)", nullable: false),
Description = table.Column<string>(type: "nvarchar(max)", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Categories", x => x.Id);
});
migrationBuilder.CreateTable(
name: "Tags",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Name = table.Column<string>(type: "nvarchar(max)", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Tags", x => x.Id);
});
migrationBuilder.CreateTable(
name: "Posts",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Title = table.Column<string>(type: "nvarchar(max)", nullable: false),
Content = table.Column<string>(type: "nvarchar(max)", nullable: false),
CreatedDate = table.Column<DateOnly>(type: "date", nullable: false),
CategoryId = table.Column<int>(type: "int", nullable: false),
AuthorName = table.Column<string>(type: "nvarchar(max)", nullable: false),
IsDeleted = table.Column<bool>(type: "bit", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Posts", x => x.Id);
table.ForeignKey(
name: "FK_Posts_Categories_CategoryId",
column: x => x.CategoryId,
principalTable: "Categories",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "Comments",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Text = table.Column<string>(type: "nvarchar(max)", nullable: false),
CreatedDate = table.Column<DateOnly>(type: "date", nullable: false),
PostId = table.Column<int>(type: "int", nullable: false),
UserEmail = table.Column<string>(type: "nvarchar(max)", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Comments", x => x.Id);
table.ForeignKey(
name: "FK_Comments_Posts_PostId",
column: x => x.PostId,
principalTable: "Posts",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "PostTag",
columns: table => new
{
PostsId = table.Column<int>(type: "int", nullable: false),
TagsId = table.Column<int>(type: "int", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_PostTag", x => new { x.PostsId, x.TagsId });
table.ForeignKey(
name: "FK_PostTag_Posts_PostsId",
column: x => x.PostsId,
principalTable: "Posts",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_PostTag_Tags_TagsId",
column: x => x.TagsId,
principalTable: "Tags",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_Comments_PostId",
table: "Comments",
column: "PostId");
migrationBuilder.CreateIndex(
name: "IX_Posts_CategoryId",
table: "Posts",
column: "CategoryId");
migrationBuilder.CreateIndex(
name: "IX_PostTag_TagsId",
table: "PostTag",
column: "TagsId");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "Comments");
migrationBuilder.DropTable(
name: "PostTag");
migrationBuilder.DropTable(
name: "Posts");
migrationBuilder.DropTable(
name: "Tags");
migrationBuilder.DropTable(
name: "Categories");
}
}
}
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
#pragma warning disable CA1814 // Prefer jagged arrays over multidimensional
namespace DeepSeekApiCore.Migrations
{
/// <inheritdoc />
public partial class AddSeedData : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.InsertData(
table: "Categories",
columns: new[] { "Id", "Description", "Name" },
values: new object[,]
{
{ 1, "Health is good.", "Health" },
{ 2, "Math is hard.", "Math" },
{ 3, "AI is scary.", "AI" }
});
migrationBuilder.InsertData(
table: "Tags",
columns: new[] { "Id", "Name" },
values: new object[,]
{
{ 1, "Linear Algebra" },
{ 2, "Deep Learning" },
{ 3, "Weightlifting" }
});
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DeleteData(
table: "Categories",
keyColumn: "Id",
keyValue: 1);
migrationBuilder.DeleteData(
table: "Categories",
keyColumn: "Id",
keyValue: 2);
migrationBuilder.DeleteData(
table: "Categories",
keyColumn: "Id",
keyValue: 3);
migrationBuilder.DeleteData(
table: "Tags",
keyColumn: "Id",
keyValue: 1);
migrationBuilder.DeleteData(
table: "Tags",
keyColumn: "Id",
keyValue: 2);
migrationBuilder.DeleteData(
table: "Tags",
keyColumn: "Id",
keyValue: 3);
}
}
}
namespace DeepSeekApiCore.Entities;
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public List<Post> Posts { get; set; } = new();
}
namespace DeepSeekApiCore.Entities;
public class Comment
{
public int Id { get; set; }
public string Text { get; set; }
public DateOnly CreatedDate { get; set; }
public int PostId { get; set; }
public string UserEmail { get; set; }
public Post Post { get; set; } = new();
}
namespace DeepSeekApiCore.DTOs.Comment;
public class CommentDto
{
public string PostTitle { get; set; }
public string Text { get; set; }
public string UserEmail { get; set; }
}
using System.ComponentModel.DataAnnotations;
namespace DeepSeekApiCore.DTOs.Comment;
public class CreateCommentDto
{
[Required]
[MaxLength(500)]
public string Text { get; set; }
[EmailAddress]
public string UserEmail { get; set; }
}
using System.ComponentModel.DataAnnotations;
namespace DeepSeekApiCore.DTOs.Post;
public class CreatePostDto
{
[Required]
[MaxLength(100)]
public string Title { get; set; }
public string Content { get; set; }
public int CategoryId { get; set; }
public string AuthorName { get; set; }
}
using DeepSeekApiCore.Entities;
using Microsoft.EntityFrameworkCore;
namespace DeepSeekApiCore.Persistence;
public class DeepSeekApiCoreDbContext(DbContextOptions<DeepSeekApiCoreDbContext> context) : DbContext(context)
{
public DbSet<Category> Categories { get; set; }
public DbSet<Comment> Comments { get; set; }
public DbSet<Post> Posts { get; set; }
public DbSet<Tag> Tags { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder options)
{
options.UseSqlServer("Server=(localdb)\\mssqllocaldb;Database=DeepSeekApiCore;Trusted_Connection=True;");
}
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<Post>()
.HasOne(p => p.Category)
.WithMany(c => c.Posts)
.HasForeignKey(p => p.CategoryId);
builder.Entity<Post>()
.HasMany(p => p.Comments)
.WithOne(c => c.Post)
.HasForeignKey(c => c.PostId);
builder.Entity<Post>()
.HasMany(p => p.Tags)
.WithMany(t => t.Posts);
builder.Entity<Category>()
.HasData(
new Category() { Id = 1, Name = "Health", Description = "Health is good." },
new Category() { Id = 2, Name = "Math", Description = "Math is hard." },
new Category() { Id = 3, Name = "AI", Description = "AI is scary." }
);
builder.Entity<Tag>()
.HasData(
new Tag() { Id = 1, Name = "Linear Algebra" },
new Tag() { Id = 2, Name = "Deep Learning" },
new Tag() { Id = 3, Name = "Weightlifting" }
);
}
}
namespace DeepSeekApiCore.Entities;
public class Post
{
public int Id { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public DateOnly CreatedDate { get; set; } = DateOnly.FromDateTime(DateTime.Now);
public int CategoryId { get; set; }
public string AuthorName { get; set; }
public bool IsDeleted { get; set; } = false;
public Category Category { get; set; } = new();
public List<Comment> Comments { get; set; } = new();
public List<Tag> Tags { get; set; } = new();
}
using DeepSeekApiCore.DTOs.Comment;
using DeepSeekApiCore.DTOs.Post;
using DeepSeekApiCore.Entities;
using DeepSeekApiCore.Persistence;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
namespace DeepSeekApiCore.Controllers;
[ApiController]
[Route("[controller]")]
public class PostController : ControllerBase
{
private readonly DeepSeekApiCoreDbContext _context;
public PostController(DeepSeekApiCoreDbContext context)
{
_context = context;
}
[HttpGet]
public async Task<ActionResult<IEnumerable<PostDto>>> Index([FromQuery] string? search, [FromQuery] string? filter)
{
var query = _context.Posts
.Include(p => p.Comments)
.Include(p => p.Tags)
.Include(p => p.Category)
.Where(p => !p.IsDeleted);
if (!search.IsNullOrEmpty())
{
search = search.ToLower();
query = query.Where(p => p.Title.ToLower().Contains(search) || p.Content.ToLower().Contains(search));
}
if (!filter.IsNullOrEmpty())
{
filter = filter.ToLower();
query = query.Where(p => p.Category.Name.ToLower() == filter || p.Tags.Any(t => t.Name.ToLower() == filter));
}
var posts = await query.ToListAsync();
List<PostDto> mappedPosts = new List<PostDto>();
foreach (var post in posts)
{
mappedPosts.Add(new PostDto()
{
Id = post.Id,
Title = post.Title,
CommentCount = post.Comments.Count,
RecentCommentDate = post.Comments.OrderByDescending(c => c.CreatedDate).FirstOrDefault()?.CreatedDate,
Tags = post.Tags.Select(t => t.Name).ToList()
});
}
return Ok(mappedPosts);
}
[HttpGet("/{id}")]
public async Task<ActionResult<PostDto>> Get(int id)
{
var post = await _context.Posts
.Include(p => p.Comments)
.Where(p => !p.IsDeleted && p.Id == id)
.FirstOrDefaultAsync();
if (post == null)
{
return NotFound();
}
var postDto = new PostDto()
{
Id = post.Id,
Title = post.Title,
CommentCount = post.Comments.Count,
RecentCommentDate = post.Comments.OrderByDescending(c => c.CreatedDate).FirstOrDefault()?.CreatedDate,
Tags = post.Tags.Select(t => t.Name).ToList()
};
return Ok(postDto);
}
[HttpPost]
public async Task<ActionResult> Create(CreatePostDto dto)
{
Post post = new Post()
{
Title = dto.Title,
Content = dto.Content,
CreatedDate = DateOnly.FromDateTime(DateTime.UtcNow),
AuthorName = dto.AuthorName,
CategoryId = dto.CategoryId,
};
_context.Posts.Add(post);
await _context.SaveChangesAsync();
return Ok();
}
[HttpDelete("/{id}")]
public async Task<ActionResult> Delete(int id)
{
var post = await _context.Posts
.Where(p => p.Id == id && !p.IsDeleted)
.FirstOrDefaultAsync();
if (post == null)
{
return NotFound();
}
post.IsDeleted = true;
await _context.SaveChangesAsync();
return Ok();
}
[HttpPatch("/{id}/restore")]
public async Task<ActionResult> Restore(int id)
{
var post = await _context.Posts
.Where(p => p.Id == id && p.IsDeleted)
.FirstOrDefaultAsync();
if (post == null)
{
return NotFound();
}
post.IsDeleted = false;
await _context.SaveChangesAsync();
return Ok();
}
[HttpPost("/{id}/comments")]
public async Task<ActionResult<CommentDto>> CreatePostComment(int id, CreateCommentDto dto)
{
var post = await _context.Posts
.Where(p => p.Id == id && !p.IsDeleted)
.Include(p => p.Comments)
.FirstOrDefaultAsync();
if (post == null)
{
return NotFound();
}
Comment comment = new Comment()
{
Text = dto.Text,
UserEmail = dto.UserEmail,
CreatedDate = DateOnly.FromDateTime(DateTime.UtcNow),
};
post.Comments.Add(comment);
await _context.SaveChangesAsync();
return Ok(new CommentDto()
{
PostTitle = post.Title,
Text = comment.Text,
UserEmail = comment.UserEmail,
});
}
}
namespace DeepSeekApiCore.DTOs.Post;
public class PostDto
{
public int Id { get; set; }
public string Title { get; set; }
public int CommentCount { get; set; }
public DateOnly? RecentCommentDate { get; set; }
public List<string> Tags { get; set; }
}
using DeepSeekApiCore.Persistence;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddDbContext<DeepSeekApiCoreDbContext>();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
namespace DeepSeekApiCore.Entities;
public class Tag
{
public int Id { get; set; }
public string Name { get; set; }
public List<Post> Posts { get; set; } = new();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment