Bu yazımda Minimal API projesine Database ve Repository eklemeyi, Repository kullanarak nasıl CRUD işlemlerinin gerçekleştirilebileceğini ve Minimal API’yi nasıl organize edebileceğimizi göstermeye çalışacağım.
Nasıl Minimal API oluşturulacağını, AutoMapper ve Validator kullanımını, yanıt türlerini göstermeye çalıştığım yazıma aşağıdaki linkten ulaşabilirsiniz.
Bu yazının içeriği ise aşağıdaki gibidir.
- Projeye ApplicationDbContext’in eklenmesi
- Database ile CRUD işlemlerinin yapılması
- Repository oluşturulması
- Repository ile CRUD işlemlerinin yapılması
- Minimal API’nin organize edilmesi
Projeye ApplicationDbContext’in eklenmesi
Bu projede Database olarak SQL Server kullanacağım.
İlk olarak NuGet Package Manager ile Microsoft.EntityFrameworkCore.SqlServer ve Microsoft.EntityFrameworkCore.Tools paketleri projeye indirilir. Microsoft SQL Server Management Studio kullanarak Server’a bağlanılır. Aşağıdaki gibi Database Connection String’i appsettings.json dosyasına eklenir.

"ConnectionStrings": {
"DefaultConnection": "Server=<SERVER NAME>;Database=<DATABASE NAME>;Trusted_Connection=True;MultipleActiveResultSets=true;TrustServerCertificate=True"
}
Minimal API (Part 1) yazısında anlatılan Data klasörü içerisine ApplicationDbContext.cs adında dosya oluşturulur ve ApplicationDbContext sınıfı oluşturulur.

using Microsoft.EntityFrameworkCore;
using MinimalAPI_Examples.Models;
namespace MinimalAPI_Examples.Data
{
public class ApplicationDbContext : DbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options)
{
}
// Films adinda tablo olusturulmasi saglanir.
public DbSet<Film> Films { get; set; }
// Films tablosunda ilklendirilmiş verilerin olusturulmasi saglanir.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Film>().HasData(
new Film () { Id = 1, Name = "The Shawshank Redemption", Imdb = 9.3 },
new Film () { Id = 2, Name = "The Godfather", Imdb = 9.2 },
new Film () { Id = 3, Name = "The Dark Knight", Imdb = 9.0 }
);
}
}
}
Program.cs dosyasına, SQL Server bağlantısının kurulabilmesi için servislerin eklendiği kısma DbContext servisi eklenir.
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
// Eklenen servis
builder.Services.AddDbContext<ApplicationDbContext>(option =>
option.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
builder.Services.AddAutoMapper(typeof(MappingConfig));
builder.Services.AddValidatorsFromAssemblyContaining<Program>();
NuGet Package Manager Console açılarak aşağıdaki komutlar girilir ve benzer çıktılar gözlemlenir.
$ add-migration AddFilmDb
Output: Build started...
Output: Build succeeded.
$ update-database
Output: ...
Output: Done.
Microsoft SQL Server Management Studio ile belirlediğiniz isimde database, Films adında tablo ve Film modeline ait sütunlar oluşur. İlklendirilen veriler tabloda gözükür.

Database ile CRUD işlemlerinin yapılması
Minimal API (Part 1) yazısında oluşturduğumuz FilmStore.cs dosyasını ve sınıfı artık silebiliriz. Bu sınıfı kullandığımız CRUD endpoint’leri güncellenir.
Get All
// Get All Films
app.MapGet("/api/films", (ApplicationDbContext _db) =>
{
return Results.Ok(_db.Films);
}).WithName("GetFilms");
Get Film By Id
// Get Film By Id
app.MapGet("/api/films/{id}", async (ApplicationDbContext _db, int id) =>
{
return Results.Ok(await _db.Films.FirstOrDefaultAsync(x => x.Id == id));
}).WithName("GetFilm");
Create Film
// Create Film
app.MapPost("/api/films", async (ApplicationDbContext _db, IValidator<FilmCreateDTO> _validator, IMapper _mapper, [FromBody] FilmCreateDTO filmCreateDTO) =>
{
var validationResult = await _validator.ValidateAsync(filmCreateDTO);
if (!validationResult.IsValid)
{
return Results.BadRequest(validationResult.Errors.FirstOrDefault().ToString());
}
if (await _db.Films.FirstOrDefaultAsync(x => x.Name == filmCreateDTO.Name) != null)
{
return Results.BadRequest("Film name already exists");
}
Film film = _mapper.Map<Film>(filmCreateDTO);
_db.Films.Add(film);
await _db.SaveChangesAsync();
FilmDTO filmDTO = _mapper.Map<FilmDTO>(film);
return Results.CreatedAtRoute("GetFilm", new { id = film.Id }, filmDTO);
}).WithName("CreateFilm");
Update Film
// Update Film
app.MapPut("/api/films", async (ApplicationDbContext _db, IMapper _mapper, IValidator<FilmUpdateDTO> _validator, [FromBody] FilmUpdateDTO filmUpdateDTO) =>
{
var validationResult = await _validator.ValidateAsync(filmUpdateDTO);
if (!validationResult.IsValid)
{
return Results.BadRequest(validationResult.Errors.FirstOrDefault().ToString());
}
Film filmFromStore = await _db.Films.FirstOrDefaultAsync(u => u.Id == filmUpdateDTO.Id);
filmFromStore.Name = filmUpdateDTO.Name;
filmFromStore.Imdb = filmUpdateDTO.Imdb;
filmFromStore.LastUpdated = DateTime.Now;
await _db.SaveChangesAsync();
FilmDTO filmDTO = _mapper.Map<FilmDTO>(filmFromStore);
return Results.Ok(filmDTO);
}).WithName("UpdateFilm");
Delete Film
// Delete Film
app.MapDelete("/api/films/{id}", async (ApplicationDbContext _db, int id) =>
{
Film filmFromStore = await _db.Films.FirstOrDefaultAsync(u => u.Id == id);
if (filmFromStore != null)
{
_db.Films.Remove(filmFromStore);
await _db.SaveChangesAsync();
return Results.Ok();
}
else
{
return Results.BadRequest("Invalid Id");
}
}).WithName("DeleteFilm");
Repository oluşturulması
Projelerde CRUD işlemlerinin tekrar tekrar kullanılması gerekebilir. Böyle durumlarda CRUD işlemleri için yazılan kodların tekrar yazılmasını engellemek için Repository’ler oluşturulabilir. Bu bölümde repository oluşturarak endpoint fonksiyonlarında kullanımını anlatmaya çalışacağım.
İlk olarak projeye “Repository” klasörü oluşturup içerisine interface’ler için “IRepository” klasörü oluşturulur. IRepository klasörüne IFilmRepository.cs dosyası oluşturulur ve IFilmRepository interface’i oluşturulur. Repository klasörü içerisine ise FilmRepository.cs dosyası oluşturularak FilmRepository sınıfı oluşturulur.

using MinimalAPI_Examples.Models;
namespace MinimalAPI_Examples.Repository.IRepository
{
public interface IFilmRepository
{
Task<ICollection<Film>> GetAllAsync();
Task<Film> GetAsync(int id);
Task<Film> GetAsync(string filmName);
Task CreateAsync(Film film);
Task UpdateAsync(Film film);
Task RemoveAsync(Film film);
Task SaveAsync();
}
}
FilmRepository sınıfı ve implement edilen interface aşağıdaki gibidir.
using Microsoft.EntityFrameworkCore;
using MinimalAPI_Examples.Data;
using MinimalAPI_Examples.Models;
using MinimalAPI_Examples.Repository.IRepository;
namespace MinimalAPI_Examples.Repository
{
public class FilmRepository : IFilmRepository
{
private readonly ApplicationDbContext _db;
public FilmRepository(ApplicationDbContext db)
{
_db = db;
}
public async Task CreateAsync(Film film)
{
_db.Add(film);
await SaveAsync();
}
public async Task<ICollection<Film>> GetAllAsync()
{
return await _db.Films.ToListAsync();
}
public async Task<Film> GetAsync(int id)
{
return await _db.Films.FirstOrDefaultAsync(u => u.Id == id);
}
public async Task<Film> GetAsync(string filmName)
{
return await _db.Films.FirstOrDefaultAsync(u => u.Name == filmName);
}
public async Task RemoveAsync(Film film)
{
_db.Films.Remove(film);
await SaveAsync();
}
public async Task SaveAsync()
{
await _db.SaveChangesAsync();
}
public async Task UpdateAsync(Film film)
{
_db.Films.Update(film);
await SaveAsync();
}
}
}
Repository ile CRUD işlemlerinin yapılması
Program.cs dosyası içerisine servislerin eklendiği kısma Repository servisi aşağıdaki gibi eklenir.
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
// Eklenen servis
builder.Services.AddScoped<IFilmRepository, FilmRepository>();
builder.Services.AddDbContext<ApplicationDbContext>(option =>
option.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
builder.Services.AddAutoMapper(typeof(MappingConfig));
builder.Services.AddValidatorsFromAssemblyContaining<Program>();
Program.cs içerisinde bulunan endpoint fonksiyonları ise aşağıdaki gibi güncellenir.
Artık ApplicationDbContext’i direkt kullanmak yerine Repository aracılığıyla tekrar tekrar kullanabileceğimiz fonksiyonların yardımıyla CRUD işlemlerini gerçekleştirebiliriz.
Get All
// Get All Films
app.MapGet("/api/films", async (IFilmRepository _filmRepository) =>
{
return Results.Ok(await _filmRepository.GetAllAsync());
}).WithName("GetFilms");
Get Film By Id
// Get Film By Id
app.MapGet("/api/films/{id}", async (IFilmRepository _filmRepository, int id) =>
{
return Results.Ok(await _filmRepository.GetAsync(id));
}).WithName("GetFilm");
Create Film
// Create Film
app.MapPost("/api/films", async (IFilmRepository _filmRepository, IValidator<FilmCreateDTO> _validator, IMapper _mapper, [FromBody] FilmCreateDTO filmCreateDTO) =>
{
var validationResult = await _validator.ValidateAsync(filmCreateDTO);
if (!validationResult.IsValid)
{
return Results.BadRequest(validationResult.Errors.FirstOrDefault().ToString());
}
if (await _filmRepository.GetAsync(filmCreateDTO.Name) != null)
{
return Results.BadRequest("Film name already exists");
}
Film film = _mapper.Map<Film>(filmCreateDTO);
await _filmRepository.CreateAsync(film);
FilmDTO filmDTO = _mapper.Map<FilmDTO>(film);
return Results.CreatedAtRoute("GetFilm", new { id = film.Id }, filmDTO);
}).WithName("CreateFilm");
Update Film
// Update Film
app.MapPut("/api/films", async (IFilmRepository _filmRepository, IMapper _mapper, IValidator<FilmUpdateDTO> _validator, [FromBody] FilmUpdateDTO filmUpdateDTO) =>
{
var validationResult = await _validator.ValidateAsync(filmUpdateDTO);
if (!validationResult.IsValid)
{
return Results.BadRequest(validationResult.Errors.FirstOrDefault().ToString());
}
Film filmFromStore = await _filmRepository.GetAsync(filmUpdateDTO.Id);
filmFromStore.Name = filmUpdateDTO.Name;
filmFromStore.Imdb = filmUpdateDTO.Imdb;
filmFromStore.LastUpdated = DateTime.Now;
await _filmRepository.UpdateAsync(filmFromStore);
FilmDTO filmDTO = _mapper.Map<FilmDTO>(filmFromStore);
return Results.Ok(filmDTO);
}).WithName("UpdateFilm");
Delete Film
// Delete Film
app.MapDelete("/api/films/{id}", async (IFilmRepository _filmRepository, int id) =>
{
Film filmFromStore = await _filmRepository.GetAsync(id);
if (filmFromStore != null)
{
await _filmRepository.RemoveAsync(filmFromStore);
return Results.Ok();
}
else
{
return Results.BadRequest("Invalid Id");
}
}).WithName("DeleteFilm");
Minimal API’nin organize edilmesi
Projenin daha rahat okunabilir ve düzenli hale getirilebilmesi için endpoint’leri farklı dosyaya aktarabiliriz.
- İlk olarak projeye Endpoints adında bir klasör oluşturulur.
Program.csiçerisinde bulunan endpoint’ler yeni oluşturulanFilmEndpointsstatic sınıfı içerisine alınır.Program.csiçerisindenConfigureFilmEndpointsstatic fonksiyonu çağırılır.
//...
app.ConfigureFilmEndpoints();
//...

using AutoMapper;
using FluentValidation;
using Microsoft.AspNetCore.Mvc;
using MinimalAPI_Examples.Models;
using MinimalAPI_Examples.Models.DTO;
using MinimalAPI_Examples.Repository.IRepository;
namespace MinimalAPI_Examples.Endpoints
{
public static class FilmEndpoints
{
public static void ConfigureFilmEndpoints(this WebApplication app)
{
// Get All Films
app.MapGet("/api/films", GetAllFilms).WithName("GetFilms");
// Get Film By Id
app.MapGet("/api/films/{id}", GetFilmById).WithName("GetFilm");
// Create Film
app.MapPost("/api/films", CreateFilm).WithName("CreateFilm");
// Update Film
app.MapPut("/api/films", UpdateFilm).WithName("UpdateFilm");
// Delete Film
app.MapDelete("/api/films/{id}", DeleteFilm).WithName("DeleteFilm");
}
private static async Task<IResult> GetAllFilms(IFilmRepository _filmRepository)
{
return Results.Ok(await _filmRepository.GetAllAsync());
}
private static async Task<IResult> GetFilmById(IFilmRepository _filmRepository, int id)
{
return Results.Ok(await _filmRepository.GetAsync(id));
}
private static async Task<IResult> CreateFilm(IFilmRepository _filmRepository, IValidator<FilmCreateDTO> _validator, IMapper _mapper, [FromBody] FilmCreateDTO filmCreateDTO)
{
var validationResult = await _validator.ValidateAsync(filmCreateDTO);
if (!validationResult.IsValid)
{
return Results.BadRequest(validationResult.Errors.FirstOrDefault().ToString());
}
if (await _filmRepository.GetAsync(filmCreateDTO.Name) != null)
{
return Results.BadRequest("Film name already exists");
}
Film film = _mapper.Map<Film>(filmCreateDTO);
await _filmRepository.CreateAsync(film);
FilmDTO filmDTO = _mapper.Map<FilmDTO>(film);
return Results.CreatedAtRoute("GetFilm", new { id = film.Id }, filmDTO);
}
private static async Task<IResult> UpdateFilm(IFilmRepository _filmRepository, IMapper _mapper, IValidator<FilmUpdateDTO> _validator, [FromBody] FilmUpdateDTO filmUpdateDTO)
{
var validationResult = await _validator.ValidateAsync(filmUpdateDTO);
if (!validationResult.IsValid)
{
return Results.BadRequest(validationResult.Errors.FirstOrDefault().ToString());
}
Film filmFromStore = await _filmRepository.GetAsync(filmUpdateDTO.Id);
filmFromStore.Name = filmUpdateDTO.Name;
filmFromStore.Imdb = filmUpdateDTO.Imdb;
filmFromStore.LastUpdated = DateTime.Now;
await _filmRepository.UpdateAsync(filmFromStore);
FilmDTO filmDTO = _mapper.Map<FilmDTO>(filmFromStore);
return Results.Ok(filmDTO);
}
private static async Task<IResult> DeleteFilm(IFilmRepository _filmRepository, int id)
{
Film filmFromStore = await _filmRepository.GetAsync(id);
if (filmFromStore != null)
{
await _filmRepository.RemoveAsync(filmFromStore);
return Results.Ok();
}
else
{
return Results.BadRequest("Invalid Id");
}
}
}
}
Son Söz
Bu yazıda Database ve Repository eklemeyi, DbContext ve Repository kullanarak nasıl CRUD işlemlerinin gerçekleştirilebileceğini ve Minimal API’nin nasıl organize edilebileceğini göstermeye çalıştım.
Bu yazının Part 1’i için → ✅ Minimal API (Part 1)
Vaktinizi ayırıp okuduğunuz için teşekkür ederim. Bir sonraki yazımda görüşmek dileğiyle.