REST API ด้วย C# และ AspNet ได้อย่างง่ายดายกว่าการเขียนตั้งแต่เริ่มต้นซ้ำแล้วซ้ำอีกในโครงการต่าง ๆ 
REST API Controller ที่มี CRUD เต็ม มีรหัส เพียง 20 บรรทัด (~ 10 คือการนำเข้า)GET วิธีการสนับสนุน การเพจในตัวGET วิธี การเรียงลำดับและตัวกรองในตัว โดยพารามิเตอร์การสืบค้นCreate จำนวนมาก Update และ Delete ) ในระดับคอนโทรลเลอร์ && อินเตอร์เฟสIModelManager Interface); การสนับสนุน EntityFramework ในตัวที่ดี (ดูคลาส EfModelManager ) ดูแอพ WeatherControl ซึ่งมี 2 โครงการ Web API:Wissance.WeatherControl.WebApi ใช้ EntityFramework ;Wissance.WeatherControl.WebApi.V2 ใช้ EdgeDbแนวคิดหลัก:
Controller เป็นคลาสที่จัดการ HTTP-requests เพื่อ REST ResourceREST Resource เท่ากับ Entity class / Database TableREST Resource จะสร้าง JSON ด้วย DTO เป็นเอาต์พุต เราสมมติว่าใช้คลาส DTO เพียงคลาสเดียวกับวิธี REST ทั้งหมด คลาส DTO :
OperationResultDto แสดงถึงผลลัพธ์ของการดำเนินการที่เปลี่ยนแปลงข้อมูลใน db;PagedDataDto แสดงส่วน (หน้า) ของวัตถุเดียวกัน (ประเภทใดก็ได้); คลาส Controllers - คลาสนามธรรม
BasicReadController ) มี 2 วิธี:GET /api/[controller]/?[page={page}&size={size}&sort={sort}&order={order}] เพื่อรับ PagedDataDto<T> ตอนนี้เรายังมีความเป็นไปได้ที่จะส่ง EfModelManager แบบสอบถามจำนวนหนึ่ง นอกจากนี้เรายังผ่านการเรียงลำดับ (ชื่อคอลัมน์) && คำสั่งซื้อ ( asc หรือ desc ) ไปยังคลาส Manager, EfModelManager อนุญาตให้เรียงลำดับ ตามคอลัมน์ใด ๆ Swagger เพื่อแสดงการใช้พารามิเตอร์การสืบค้น !!!1.6.0 เป็นไปได้ที่จะเห็นพารามิเตอร์ทั้งหมดใน Swagger และใช้งานGET /api/[controller]/{id} เพื่อรับวัตถุหนึ่งโดย idCRUD แบบเต็ม ( BasicCrudController ) = ตัวควบคุมการอ่านพื้นฐาน ( BasicReadController ) + Create , Update และ Delete การดำเนินงาน:POST /api/[controller] - สำหรับการสร้างวัตถุใหม่PUT /api/[controller]/{id} - สำหรับแก้ไขวัตถุโดย idDELETE /api/[controller]/{id} - สำหรับลบวัตถุโดย idCRUD เต็มรูปแบบพร้อมการดำเนินการ จำนวนมาก (การดำเนินการกับวัตถุหลาย ๆ ครั้งในครั้งเดียว) คลาสพื้นฐาน - BasicBulkCrudController = ตัวควบคุมการอ่านพื้นฐาน ( BasicReadController ) + BulkCreate , BulkUpdate และ BulkDelete :POST /api/bulk/[controller] - สำหรับการสร้างวัตถุใหม่PUT /api/bulk/[controller] - สำหรับการแก้ไขวัตถุที่ส่งผ่านในร่างกายคำขอDELETE /api/bulk/[controller]/{idList} - สำหรับการลบหลายวัตถุด้วย IDคลาสคอนโทรลเลอร์คาดว่าการดำเนินการทั้งหมดจะดำเนินการโดยใช้คลาสผู้จัดการ (คอนโทรลเลอร์แต่ละตัวต้องมีตัวจัดการไอที)
คลาสผู้จัดการ - คลาสที่ใช้ตรรกะทางธุรกิจของแอปพลิเคชัน
IModelManager - อินเทอร์เฟซที่อธิบายการดำเนินการขั้นพื้นฐานEfModelManager - เป็นคลาสนามธรรมที่มีการดำเนินการของการดำเนินการ Get และ DeleteEfSoftRemovableModelManager เป็นคลาสนามธรรมที่มีการใช้งานของการดำเนินการ Get และ Delete ด้วยโมเดลที่ถอดออกได้แบบอ่อน ( IsDeleted = true แบบจำลองค่าเฉลี่ยถูกลบออก) ตัวอย่างว่ากลุ่มที่ไม่ได้เป็นจำนวนมากและไม่ต้องใช้: 
Elapsed time in Non-Bulk REST API with EF is 0.9759984016418457 secs.
Elapsed time in Bulk API with EF is 0.004002094268798828 secs.
เป็นผลให้เราได้รับ API ที่เร็วกว่า ~ 250 x
มี เพียงข้อกำหนดเดียวเท่านั้น : คลาสเอนทิตีทั้งหมดสำหรับการจัดเก็บข้อมูลการคงอยู่ใด ๆ ที่ใช้กับคอนโทรลเลอร์และผู้จัดการจะต้องใช้ IModelIdentifiable<T> จาก Wissance.WebApiToolkit.Data.Entity หากควรใช้ชุดเครื่องมือนี้กับ EntityFramework คุณควรได้รับตัวจัดการทรัพยากรจาก EfModelManager มันมีวิธีการในตัวสำหรับ:
get manyget one รายการ by iddelete รายการ by id ตัวอย่างเต็มรูปแบบถูกกล่าวถึงในส่วนที่ 6 (ดูด้านล่าง) แต่ถ้าคุณเริ่มสร้าง REST Resource API ใหม่คุณควรทำตาม:
model ( entity ) ที่ใช้ IModelIdentifiable<T> และคลาส DTO สำหรับการเป็นตัวแทน ( สำหรับการลบแบบนุ่มนวล ยัง เพิ่ม การใช้งาน IModelSoftRemovable ), IE: public class BookEntity : IModelIdentifiable < int >
{
public int Id { get ; set ; }
public string Title { get ; set ; }
public string Authors { get ; set ; } // for simplicity
public DateTimeOffset Created { get ; set ; }
public DateTimeOffset Updated { get ; set ; }
}
public class BookDto
{
public int Id { get ; set ; }
public string Title { get ; set ; }
public string Authors { get ; set ; }
}Model เป็น DTO IE: public static class BookFactory
{
public static BookDto Create ( BookEntity entity )
{
return new BookDto
{
Id = entity . Id ,
Title = entity . Title ,
Authors = entity . Authors ;
} ;
}
}IModelContext ที่มีคุณเป็น BookEntity เป็น DbSet และคลาสการใช้งานที่ได้มาจาก DbContext ( EF Abstract Class ): public interface IModelContext
{
DbSet < BookEntity > Books { get ; set ; }
}
public MoidelContext : DbContext < ModelContext > , IModelContext
{
// todo: not mrntioned here constructor, entity mapping and so on
public DbSet < BookEntity > Books { get ; set ; }
}ModelContext เป็น DbContext ผ่าน DI ดูคลาสเริ่มต้นController และคู่คลาสผู้จัดการเช่นพิจารณาที่นี่เต็มรูปแบบ CRUD [ ApiController ]
public class BookController : BasicCrudController < BookDto , BookEntity , int , EmptyAdditionalFilters >
{
public BookController ( BookManager manager )
{
Manager = manager ; // this is for basic operations
_manager = manager ; // this for extended operations
}
private BookManager _manager ;
}
public class BookManager : EfModelManager < BookEntity , BookDto , int , EmptyAdditionalFilters >
{
public BookManager ( ModelContext modelContext , ILoggerFactory loggerFactory ) : base ( modelContext , BookFactory . Create , loggerFactory )
{
_modelContext = modelContext ;
}
public override async Task < OperationResultDto < StationDto > > CreateAsync ( StationDto data )
{
// todo: implement
}
public override async Task < OperationResultDto < StationDto > > UpdateAsync ( int id , StationDto data )
{
// todo: implement
}
private readonly ModelContext _modelContext ;
} พารามิเตอร์ทั่วไปสุดท้ายในตัวอย่างข้างต้น - EmptyAdditionalFilters เป็นคลาสที่เก็บพารามิเตอร์เพิ่มเติมสำหรับการค้นหาเพื่อดูใน Swagger เพียงแค่ระบุคลาสใหม่ที่ใช้ IReadFilterable IE:
public class BooksFilterable : IReadFilterable
{
public IDictionary < string , string > SelectFilters ( )
{
IDictionary < string , string > additionalFilters = new Dictionary < string , string > ( ) ;
if ( ! string . IsNullOrEmpty ( Title ) )
{
additionalFilters . Add ( FilterParamsNames . TitleParameter , Title ) ;
}
if ( Authors != null && Authors . Length > 0 )
{
additionalFilters . Add ( FilterParamsNames . AuthorsParameter , string . Join ( "," , Authors ) ) ;
}
return additionalFilters ;
}
[ FromQuery ( Name = "title" ) ] public string Title { get ; set ; }
[ FromQuery ( Name = "author" ) ] public string [ ] Authors { get ; set ; }
}คุณสามารถค้นหา Nuget-Package ได้ที่นี่
[ ApiController ]
public class StationController : BasicCrudController < StationDto , StationEntity , int , EmptyAdditionalFilters >
{
public StationController ( StationManager manager )
{
Manager = manager ; // this is for basic operations
_manager = manager ; // this for extended operations
}
private StationManager _manager ;
} public class StationManager : EfModelManager < StationEntity , StationDto , int >
{
public StationManager ( ModelContext modelContext , ILoggerFactory loggerFactory ) : base ( modelContext , StationFactory . Create , loggerFactory )
{
_modelContext = modelContext ;
}
public override async Task < OperationResultDto < StationDto > > CreateAsync ( StationDto data )
{
try
{
StationEntity entity = StationFactory . Create ( data ) ;
await _modelContext . Stations . AddAsync ( entity ) ;
int result = await _modelContext . SaveChangesAsync ( ) ;
if ( result >= 0 )
{
return new OperationResultDto < StationDto > ( true , ( int ) HttpStatusCode . Created , null , StationFactory . Create ( entity ) ) ;
}
return new OperationResultDto < StationDto > ( false , ( int ) HttpStatusCode . InternalServerError , "An unknown error occurred during station creation" , null ) ;
}
catch ( Exception e )
{
return new OperationResultDto < StationDto > ( false , ( int ) HttpStatusCode . InternalServerError , $ "An error occurred during station creation: { e . Message } " , null ) ;
}
}
public override async Task < OperationResultDto < StationDto > > UpdateAsync ( int id , StationDto data )
{
try
{
StationEntity entity = StationFactory . Create ( data ) ;
StationEntity existingEntity = await _modelContext . Stations . FirstOrDefaultAsync ( s => s . Id == id ) ;
if ( existingEntity == null )
{
return new OperationResultDto < StationDto > ( false , ( int ) HttpStatusCode . NotFound , $ "Station with id: { id } does not exists" , null ) ;
}
// Copy only name, description and positions, create measurements if necessary from MeasurementsManager
existingEntity . Name = entity . Name ;
existingEntity . Description = existingEntity . Description ;
existingEntity . Latitude = existingEntity . Latitude ;
existingEntity . Longitude = existingEntity . Longitude ;
int result = await _modelContext . SaveChangesAsync ( ) ;
if ( result >= 0 )
{
return new OperationResultDto < StationDto > ( true , ( int ) HttpStatusCode . OK , null , StationFactory . Create ( entity ) ) ;
}
return new OperationResultDto < StationDto > ( false , ( int ) HttpStatusCode . InternalServerError , "An unknown error occurred during station update" , null ) ;
}
catch ( Exception e )
{
return new OperationResultDto < StationDto > ( false , ( int ) HttpStatusCode . InternalServerError , $ "An error occurred during station update: { e . Message } " , null ) ;
}
}
private readonly ModelContext _modelContext ;
}เพียง 2 คลาสที่ง่ายมาก ^^ ใช้ webapitoolkit
พิจารณาว่าเราต้องการเพิ่มวิธีการค้นหาในคอนโทรลเลอร์ของเรา:
[ HttpGet ]
[ Route ( "api/[controller]/search" ) ]
public async Task < PagedDataDto < BookDto > > > SearchAsync ( [ FromQuery ] string query , [ FromQuery ] int page , [ FromQuery ] int size )
{
OperationResultDto < Tuple < IList < BookDto > , long > > result = await Manager . GetAsync ( page , size , query ) ;
if ( result == null )
{
HttpContext . Response . StatusCode = ( int ) HttpStatusCode . InternalServerError ;
}
HttpContext . Response . StatusCode = result . Status ;
return new PagedDataDto < TRes > ( pageNumber , result . Data . Item2 , GetTotalPages ( result . Data . Item2 , pageSize ) , result . Data . Item1 ) ;
} เรามีโครงการเพิ่มเติมเพื่อปกป้อง API ด้วย Keycloak OpenId-Connect ผ่าน IHttpContextAccessor ไปยังคลาส Manager และตรวจสอบบางอย่างเช่นนี้: ClaimsPrincipal principal = _httpContext.HttpContext.User;
คุณสามารถดูบทความของเราเกี่ยวกับการใช้งานชุดเครื่องมือ: