React, TypeScript ve Asp.Net Core 5 ile CRUD işlemleri
Merhaba, bu gönderide asp.net core 5, react ve typescript ile crud (ekleme, güncelleme, silme) işlemlerini yapacak bir proje yapacağız. projenin son halini gönderinin en altında github linkinden ulaşabilirsiniz.
Yazı iki aşamadan oluşacak ilk bölümde bir api service yazacağız, postman ile test edeceğiz daha sonra react ile de önyüzü kodlayacağız.
Geliştirme Yaptığım Ortam
- Windows 11
- Visual Studio 2019
- .Net 5.0
- Node.js (Npm paketleri için)
Proje Aşamaları
- Proje oluşturma (DDD)
- EntityFramework Core implemantasyonu
- Crud işlemlerini yapan servis ve DTO’ların eklenmesi
- Api controller’ın yazılması
- Api Test işlemleri
- React’ın eklenmesi (Create-React-App) ve asp.net core middleware ayarları
- CRUD işlemleri, önyüzün tasarlanması
- Hata sayfalarının eklenmesi
- Listeleme-Silme
- Ekleme-Güncelleme
- Projenin github linki
Proje Başlangıç
Projemiz basit olacak personel ekleme, listeleme işlemleri yapacağız. DDD (Domain Driven Design) mimarisine uygun tasarlayacağız. Blank Solution açarak projemizi oluşturalım.




EntityFrameworkCore Code First
Öncelikle Infastructure katmanına Domain katmanını, Web katmanına da Infastructure ve Domain katmanını referans olarak ekleyelim.
Data işlemlerini yapacağımız Infastructure katmanına aşağıdaki paketleri kuralım

Microsoft.EntityFrameworkCore.Design paketinini web katmanına da yükleyelim.
Domain katmanına entities klasörü oluşturup Employee ve EmployeeType adındaki entityleri oluşturalım.

Employee.cs :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public class Employee { public int Id { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public DateTime? BirthDate { get; set; } public int EmployeeTypeId { get; set; } public bool IsDeleted { get; set; } public string Email { get; set; } public virtual EmployeeType EmployeeType { get; set; } [NotMapped] public int? Age => BirthDate.HasValue ? (DateTime.Now - BirthDate.Value).Days / 365 : (int?)null; } |
EmployeeType.cs :
1 2 3 4 5 |
public class EmployeeType { public int Id { get; set; } public string Name { get; set; } } |
Infastructure katmanına AppDbContext sınıfımızı ekleyelim
1 2 3 4 5 6 7 8 9 |
public class AppDbContext : DbContext { public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { } public DbSet<Employee> Employees { get; set; } public DbSet<EmployeeType> EmployeeTypes { get; set; } } |
appsettings.json‘a connection stringi yazalım
1 2 3 |
"ConnectionStrings": { "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=ReactCore;Trusted_Connection=True;MultipleActiveResultSets=true" } |
Startup.cs’de AppDbContext‘in tanımını yapalım.
1 2 |
services.AddDbContext<AppDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"))); |
şimdi console’u açalım default proje olarak Infastructure katmanını seçelim sonra da Web katmanında sağ tik ile set as startup project seçelim. Console’a aşağıdaki komutları sırayla yazalım.
1 2 |
add-migration InitalDb update-database |
Bu işlemleri gerçekleştirdikten sonra Infastructure katmanında aşağıdaki migration klasörü ve sql server local veritabanımızda belirttiğimiz database oluşacaktır.


Servis ve DTO
Veritabanımız hazır şimdi Employee ile ilgili database işlemlerimizi yapacak EmployeeService ve IEmployeeService servislerini oluşturalım.
Aşağıdaki gibi iki katmana da services klasörü oluşturup içerilerine servisleri ekleyelim.

IEmployeeService :
1 2 3 4 5 6 7 8 9 |
public interface IEmployeeService { void Add(Employee employee); void Update(Employee employee); void Delete(Employee employee); Employee Get(int id); IEnumerable<Employee> GetAll(); IEnumerable<EmployeeType> GetEmployeeTypes(); } |
EmployeeService :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
public class EmployeeService : IEmployeeService { private readonly AppDbContext db; public EmployeeService(AppDbContext db) { this.db = db; } public void Add(Employee employee) { db.Add(employee); db.SaveChanges(); } public void Delete(Employee employee) { employee.IsDeleted = true; db.Update(employee); db.SaveChanges(); } public Employee Get(int id) { return db.Employees.FirstOrDefault(e => e.Id == id && !e.IsDeleted); } public IEnumerable<Employee> GetAll() { return db.Employees.Where(em => !em.IsDeleted).AsEnumerable(); } public void Update(Employee employee) { db.Employees.Update(employee); db.SaveChanges(); } public IEnumerable<EmployeeType> GetEmployeeTypes() { return db.EmployeeTypes.ToList(); } } |
Startup.cs’de servisimizi tanımlayalım
1 |
services.AddScoped<IEmployeeService, EmployeeService>(); |
Apiyi yazmadan önce veri modelleme işlemlerini yapmalıyız. Öncelikle Domain altında DTOs (Data Transfer Object) adında bir klasör oluşturup içine EmployeeIndexDTO ve EmployeeDTO adında iki tane class ekleyelim.
EmployeeDTO :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
public class EmployeeDTO { [Required] [StringLength(50, MinimumLength = 2)] public string FirstName { get; set; } [Required] [StringLength(50, MinimumLength = 2)] public string LastName { get; set; } [DisplayFormat(DataFormatString ="{0:yyyy-MM-dd}",ApplyFormatInEditMode =true)] public DateTime? BirthDate { get; set; } [Required] [EmailAddress] public string Email { get; set; } [Required] public int EmployeeTypeId { get; set; } } |
EmployeeIndexDTO :
1 2 3 4 5 6 7 8 9 |
public class EmployeeIndexDTO { public int Id { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public string Email { get; set; } public string EmployeeTypeName { get; set; } public int? Age { get; set; } } |
Mapper işlemleri için web katmanına automapper paketlerini yükleyelim

Web katmanın kök dizinine MapperProfile.cs ekleyelim ve mapleme işlemlerini yapalım
1 2 3 4 5 6 7 8 9 |
public class MapperProfile : Profile { public MapperProfile() { CreateMap<Employee, EmployeeIndexDTO>() .ForMember(src => src.EmployeeTypeName, opts => opts.MapFrom(dest => dest.EmployeeType.Name)); CreateMap<Employee, EmployeeDTO>().ReverseMap(); } } |
startup.cs :
1 2 3 |
var mapperConfig = new MapperConfiguration(cfg => cfg.AddProfile(new MapperProfile())); services.AddAutoMapper(typeof(Startup)); services.AddSingleton(mapperConfig); |
EmployeeController adında bir api controller açalım
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
[Route("api/employee")] [ApiController] public class EmployeeController : ControllerBase { private IEmployeeService EmployeeService; private IMapper Mapper; public EmployeeController(IEmployeeService employeeService, IMapper mapper) { EmployeeService = employeeService; Mapper = mapper; } [HttpGet] public IActionResult Get(int? id) { if (id.HasValue) { var employee = EmployeeService.Get(id.Value); if (employee != null) { var dto = Mapper.Map<EmployeeDTO>(employee); return Ok(dto); } else { return NotFound(); } } var employees = EmployeeService.GetAll(); var model = Mapper.Map<IEnumerable<EmployeeIndexDTO>>(employees); return Ok(model); } [HttpPost] public IActionResult Post(EmployeeDTO dto) { if (!ModelState.IsValid) { return Ok(ModelState); } var employee = Mapper.Map<Employee>(dto); EmployeeService.Add(employee); return Ok(); } [HttpPut] public IActionResult Update(EmployeeDTO dto, int id) { if (!ModelState.IsValid) { return Ok(ModelState); } var employee = EmployeeService.Get(id); employee = Mapper.Map(dto, employee); EmployeeService.Update(employee); return Ok(); } [HttpDelete] public IActionResult Delete(int? id) { if (id.HasValue) { var employee = EmployeeService.Get(id.Value); if (employee != null) { EmployeeService.Delete(employee); return Ok(); } } return NotFound(); } [Route("/api/employeeTypes")] [HttpGet] public IActionResult GetEmployeeTypes() { var employees = EmployeeService.GetEmployeeTypes(); return Ok(employees); } } |
EmployeeType tablosuna sql server üzerinden aşağıdaki gibi eklemeler yapabilirsiniz.

Apiyi Test edelim
Apiyi test etmek için postman yada swagger kullanabilirsiniz ben popüler olan postman üzerinden yapacağım.
Employee Post Api test:


Şimdi bu api projemizin içine bir react projesi ekleyelim ve crud işlemlerini ekran üzerinden yapalım.
React – Frontend
projemize react eklemek için önümüzde iki yol var, UI adında 4. bir katman açmak yada Web katmanın içine eklemek. Biz ikinci yoldan devam edeceğiz
Create Rreact App uygulamasını web katmanında client klasörü altında oluşturacağız, typescript kullanacağız.
Aşağıdaki komutu web katmanı içinde çalıştırınız.
1 |
npx create-react-app client --template typescript |
Create react app başarılı bir şekilde yüklendi şimdi middlewareda bazı ayarları yapmadan önce Nuget üzerinden aşağıdaki paketleri yükleyelim.

startup.cs:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
app.UseEndpoints(endpoints => { endpoints.MapControllerRoute( name: "default", pattern: "{controller}/{action=Index}/{id?}"); }); app.UseSpa(spa => { spa.Options.SourcePath = Path.Join(env.ContentRootPath, "client"); if (env.IsDevelopment()) { spa.UseReactDevelopmentServer(npmScript: "start"); } }); |
Typescript build edebilmek için Microsoft.TypeScript.MSBuild paketini nuget üzerinden kuralım. reactı başarılı bir şekilde ekledik vs code üzerinden açtığımızda aşağıdaki şekilde görünecektir. (componenets ve pages klasörlerini de ekleyelim)

http bağlantıları için axios, routing işlemleri için de react-router‘ı ve tasarım için de bootstrapı kuralım
1 |
npm i axios react-router-dom react-bootstrap |
1 |
npm i --save @types/react-router-dom |
Bootstap’ın css dosyasını şu linkten inidirip ve dist klasörü altına ekleyelim
EmployeeService.ts:
Bir tablo üzerinde personelleri listeleyeceğiz. Bu tablo üzerinden güncelleme, ekleme ve silme işlemleri yapacağız.
Bu işlemleri yapabilmek için employeeService adında bir servis oluşturacağız. Bu servisi oluşturacağımız Services klasörü altına ekleyeceğiz.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
import axios from "axios"; import history from "../history"; export interface EmployeeDTO { firstName: string; lastName: string; birthDate?: string; email: string; employeeTypeId: number | undefined; } export interface EmployeeIndexDTO { id: number; firstName: string; lastName: string; age?: number; email: string; employeeTypeName: string; } export interface EmployeeType { id: string; name: string; } const baseUrl = `/api/`; const http = axios.create({ baseURL: baseUrl, headers: { "Content-type": "application/json", }, }); const getAll = () => { return http.get<EmployeeIndexDTO[]>("employee"); }; const getById = (id: number) => { return http.get<EmployeeDTO>(`employee?id=${id}`); }; const create = (model: EmployeeDTO) => { return http.post<EmployeeDTO>("employee", model); }; const update = (model: EmployeeDTO, id: number) => { return http.put<EmployeeDTO>(`employee?id=${id}`, { ...model }); }; const _delete = (id: number) => { return http.delete(`employee?id=${id}`); }; const getEmployeeTypes = () => { return http.get<EmployeeType[]>("employeeTypes"); }; http.interceptors.response.use( (response) => { return response; }, (error) => { if (!error.response || typeof error.response.data !== "object") { setTimeout(() => {}, 1000); return Promise.reject(error); } const { status, config, data } = error.response; console.log(status, config, data); if (config.method === "get") { if (status === 401) { history.push("/exception/type=401"); } else if (status === 403) { history.push("/exception/type=403"); } else if (status === 404) { history.push("/exception/type=404"); } else if (status === 500) { history.push("/exception/type=500"); } } } ); export const employeeService = { getAll, getById, create, update, delete: _delete, getEmployeeTypes, }; |
İki tane EmployeeDTO veri tipimiz var EmployeeIndexDTO listelemede kullanmak için EmployeeDTO ise güncelleme ve ekleme yaparken kullanacağımız tip. axios üzerinden bir http nesnesi oluşturup get, post, put, delete gibi işlemleri bunun üzerinden yaptık.
Servis hazır, şimdi Pages klasörü altında bir Employee klasörü oluşturalım buraya index ve createOrEdit adında tsx tipinde sayfalarımız ekleyelim.

react-router ve bootstrap ayarları
kök klasörümüzdeki index.tsx dosyasına bootstrap css dosyasını import edelim ve App componentini Router tagleri içine alalım
index.tsx:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
import React from "react"; import ReactDOM from "react-dom"; import "./index.css"; import App from "./App"; import { Router } from "react-router-dom"; import "./dist/bootstrap/bootstrap.min.css"; import history from "./history.js"; ReactDOM.render( <React.StrictMode> <Router history={history}> <App /> </Router> </React.StrictMode>, document.getElementById("root") ); |
history.js
1 2 |
import { createBrowserHistory } from "history"; export default createBrowserHistory(); |
app.tsx:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
import { Switch, Route } from "react-router-dom"; import Edit from "./pages/Employee/edit"; import Create from "./pages/Employee/create"; import Index from "./pages/Employee"; import "./App.css"; function App() { return ( <div className="App"> <Switch> <Route path="/employee/edit/:id" component={Edit} /> <Route path="/employee/create" component={Create} /> <Route path="/employee/index" component={Index} /> <Route path="/exception/type=:type" component={Exception} /> </Switch> </div> ); } export default App; |
Hata sayfalarının ekleme (404, 500 vs.)
Yanlış bir url sonucunda örneğin, Employee Controller’a 5 idli bir get işlemi yaptık ve cevap olarak 404 geldi, employeeService bunu yakalıyor ve ilgili hataya göre sayfayı yönlendiriyor. Bunun için Exception adından bir klasör oluşturup içine Index.tsx dosyası ekleyelim
Exception/Index.tsx
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
import { useParams, useHistory } from "react-router-dom"; import { Button } from "react-bootstrap"; interface RouteParams { type: string; } const Exception = () => { const params = useParams<RouteParams>(); const history = useHistory(); return ( <> <div> <p>Hata : {params.type}</p> <Button variant="primary" onClick={() => history.push("/employee/index")} > Back to Home </Button> </div> </> ); }; export default Exception; |
useParams: query ile gelen değerleri yönetmemiz sağlayan bir hook.
Listeleme-Silme (Employee/Index.tsx)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
import { useState, useEffect } from "react"; import { employeeService, EmployeeIndexDTO, } from "../../services/EmployeeService"; import { Table, Button } from "react-bootstrap"; import { useHistory } from "react-router-dom"; function Index() { const [employees, setEmployees] = useState<EmployeeIndexDTO[]>([]); const history = useHistory(); useEffect(() => { employeeService .getAll() .then((response) => { setEmployees(response.data); }) .catch((e) => { console.log(e); }); }, []); const deleteRow = (id: number) => { console.log(id); employeeService.delete(id).then((response) => { if (response.status === 200) { setEmployees(employees.filter((e) => e.id !== id)); } }); }; return ( <div className="card col-md-10 text-start"> <div className="card-header"> <h4>Personeller</h4> </div> <div className="card-body"> <div className=""> <div className="d-flex justify-content-end me-3 mt-3"> <Button variant="danger" onClick={() => history.push("/employee/create")} > Personel Ekle </Button> </div> <Table hover responsive> <thead> <tr> <th>Ad Soyad</th> <th className="d-none d-sm-table-cell">Pozisyon</th> <th className="d-none d-sm-table-cell">Yaş</th> <th className="d-none d-md-table-cell">E-posta</th> <th></th> </tr> </thead> <tbody> {employees.map((pt, index) => { return ( <tr key={index}> <td>{pt.firstName + pt.lastName}</td> <td className="d-none d-sm-table-cell"> {pt.employeeTypeName} </td> <td className="d-none d-sm-table-cell">{pt.age}</td> <td className="d-none d-md-table-cell">{pt.email}</td> <td> <Button variant="primary" className="mx-1" onClick={() => history.push("/employee/edit/" + pt.id)} > Güncelle </Button> <Button variant="danger" onClick={() => deleteRow(pt.id)}> Sil </Button> </td> </tr> ); })} </tbody> </Table> </div> </div> </div> ); } export default Index; |
Listeleme sayfamız ilk açılışta useEffect ile state’i dolduruyor, deleteRow fonksiyonu da hem clienttan hem serverdan ilgili veriyi kaldırıyor
Ekleme-Güncelleme (createOrEdit.tsx)
Create ve Edit sayfalarında html elementleri tekrar ettiği için aynı sayfada işlemleri yapacağız. useParam kullanarak id parametresi olup olmadığını kontrol edeceğiz eğer id var ise servise gidip o idli verimizi alıp ilgili state’e atacağız değil ise de initial state’i kullanacağız.
State ve Interface tanımları
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
interface RouteParams { id: string; } interface EmployeeValidationState { firstName: boolean; lastName: boolean; birthDate: boolean; email: boolean; employeeTypeId: boolean; } const params = useParams<RouteParams>(); const history = useHistory(); const [employee, setEmployee] = useState<EmployeeDTO>({ firstName: "", lastName: "", birthDate: new Date().toISOString().substring(0, 10), email: "", employeeTypeId: undefined, }); const [employeeTypes, setEmployeeTypes] = useState<EmployeeType[]>([]); const [employeeValidation, setEmployeeValidation] = useState<EmployeeValidationState>({ firstName: true, lastName: true, birthDate: true, email: true, employeeTypeId: true, }); |
Verilerin yüklenmesi (Employee ve EmployeeTypes)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
useEffect(() => { const idParam = parseInt(params.id); employeeService .getEmployeeTypes() .then((response) => { setEmployeeTypes(response.data); }) .catch((e) => { console.log(e); }); if (idParam) { employeeService .getById(idParam) .then((response) => { setEmployee(response.data); }) .catch((e) => { console.log(e); }); } }, [params.id]); |
Validation fonksiyonları
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
const setValid = () => { if ( employee.firstName === "" || employee.firstName == null || employee.firstName?.length < 2 ) { employeeValidation.firstName = false; } else { employeeValidation.firstName = true; } if ( employee.lastName === "" || employee.lastName == null || employee.lastName?.length < 2 ) { employeeValidation.lastName = false; } else { employeeValidation.lastName = true; } if ( employee.email === "" || employee.email == null || !/\S+@\S+\.\S+/.test(employee.email) ) { employeeValidation.email = false; } else { employeeValidation.email = true; } if ( employee.employeeTypeId === undefined || employee.employeeTypeId == null ) { employeeValidation.employeeTypeId = false; } else { employeeValidation.employeeTypeId = true; } setEmployeeValidation({ ...employeeValidation }); return employeeValidation; }; const isValidForm = (model: EmployeeValidationState) => { return ( model.firstName && model.lastName && model.email && model.employeeTypeId ); }; |
formu submit eden fonskiyon:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
const submitForm = () => { const idParam = parseInt(params.id); var model = setValid(); if (isValidForm(model)) { if (!idParam) { employeeService .create(employee) .then((response) => { history.push("/employee/index"); }) .catch((error) => { console.log(error.response); }); } else { employeeService .update(employee, idParam) .then((response) => { history.push("/employee/index"); }) .catch((error) => { console.log(error); }); } } }; |
html:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 |
<div className="card col-md-8"> <div className="card-header text-start">Ekleme/Güncelleme</div> <div className="card-body"> <div className="text-start"> <div className="col-md-6"> <label className="form-label">Ad*</label> <input type="text" className={`form-control ${ employeeValidation.firstName ? "" : "is-invalid" }`} id="name" required value={employee?.firstName} onChange={(e) => setEmployee({ ...employee, firstName: e.target.value }) } /> {employeeValidation.firstName ? ( "" ) : ( <div className="invalid-feedback"> En az iki karakter girmelisiniz </div> )} </div> <div className="col-md-6"> <label className="form-label">Soyad*</label> <input type="text" className={`form-control ${ employeeValidation.lastName ? "" : "is-invalid" }`} id="name" value={employee?.lastName} onChange={(e) => setEmployee({ ...employee, lastName: e.target.value }) } /> {employeeValidation.lastName ? ( "" ) : ( <div className="invalid-feedback"> En az iki karakter girmelisiniz </div> )} </div> <div className="col-md-6"> <label className="form-label">E-posta*</label> <input type="email" className={`form-control ${ employeeValidation.email ? "" : "is-invalid" }`} id="email" required value={employee?.email} onChange={(e) => setEmployee({ ...employee, email: e.target.value }) } /> {employeeValidation.email ? ( "" ) : ( <div className="invalid-feedback"> E-posta alanı boş ya da doğru biçimde değil </div> )} </div> <div className="col-6"> <label className="form-label">Doğum Tarihi</label> <input type="date" className="form-control" id="birthDate" value={employee.birthDate?.substring(0, 10) || ""} onChange={(e) => setEmployee({ ...employee, birthDate: Date.parse(e.currentTarget.value) ? new Date(e.currentTarget.value) .toISOString() .substring(0, 10) : undefined, }) } /> </div> <div className="col-md-6"> <label className="form-label">Pozisyon*</label> <select id="inputState" className={`form-control ${ employeeValidation.employeeTypeId ? "" : "is-invalid" }`} value={employee?.employeeTypeId} onChange={(e) => setEmployee({ ...employee, employeeTypeId: parseInt(e.target.value), }) } > <option value="">Seçiniz...</option> {employeeTypes.map((et) => ( <option key={et.id} value={et.id}> {et.name} </option> ))} </select> {employeeValidation.employeeTypeId ? ( "" ) : ( <div className="invalid-feedback"> Lütfen pozisyon tipi seçiniz </div> )} </div> </div> </div> <div className="card-footer text-start"> <div className="col-12"> <button type="submit" onClick={submitForm} className="btn btn-primary me-1" > Kaydet </button> <a href="/employee/index" className="btn btn-secondary"> Çıkış </a> </div> </div> </div |
Gif:

Github Linki : https://github.com/okankrdg/ReactCoreCRUD
MVC ile react için: Asp.Net Core (5.0) MVC projesinde Webpack ve React Kullanmak
Özet:
Özet olarak yaptıklarımızın üstünden geçelim. Domain Driven Design mimarsiyle bir Asp.Net core 5 web api projesi inşa ettik, veritabanımızı bağladık arka kısmı bitirdikten sonra react-typescript uygulamasını ekledik (create-react-app), services, pages, exception, dist şeklinde uygulamamızı parçaladık, router, bootstrap ayarlarını yaptık,service klasöründe EmployeeService adında bir servis ekledik bu servis, axios vasıtasıyla apiyle bağlantı kurmamıza yaradı, son olarak da pages altında Employee klasörü ekleyip altına index ve createOrEdit sayfalarını ekledik.