一、创建 ASP.NET Core WebApi项目
二、添加
三、
-----------------------------------------------------------
一、创建项目,WideWorldImporters.API,选项按照下列图操作
二、引用需要的Nuget包
- Microsoft.EntityFrameworkCore.SqlServer
Swashbuckle.AspNetCore
Swashbuckle.AspNetCore包允许为Web API启用帮助页。
试运行一下项目
OK, 没任何错误。??
添加一个文件夹Models,在里面添加4个.cs文件,
Entities.cs //实体,为了简单些把多个实体放在这一个文件里,实际项目里可以一个文件一个类
Extensions.cs //扩展类,
Requests.cs //请求类,
Responses.cs //响应类,
4个文件的完整代码如下:
Entities.cs
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Threading.Tasks; 5 using Microsoft.EntityFrameworkCore; 6 using Microsoft.EntityFrameworkCore.Metadata.Builders; 7 8 namespace WideWorldImporters.API.Models 9 { 10 public partial class StockItem 11 { 12 public StockItem() 13 { 14 } 15 16 public StockItem(int? stockItemID) 17 { 18 StockItemID = stockItemID; 19 } 20 21 public int? StockItemID { get; set; } 22 23 public string StockItemName { get; set; } 24 25 public int? SupplierID { get; set; } 26 27 public int? ColorID { get; set; } 28 29 public int? UnitPackageID { get; set; } 30 31 public int? OuterPackageID { get; set; } 32 33 public string Brand { get; set; } 34 35 public string Size { get; set; } 36 37 public int? LeadTimeDays { get; set; } 38 39 public int? QuantityPerOuter { get; set; } 40 41 public bool? IsChillerStock { get; set; } 42 43 public string Barcode { get; set; } 44 45 public decimal? TaxRate { get; set; } 46 47 public decimal? UnitPrice { get; set; } 48 49 public decimal? RecommendedRetailPrice { get; set; } 50 51 public decimal? TypicalWeightPerUnit { get; set; } 52 53 public string MarketingComments { get; set; } 54 55 public string InternalComments { get; set; } 56 57 public string CustomFields { get; set; } 58 59 public string Tags { get; set; } 60 61 public string SearchDetails { get; set; } 62 63 public int? LastEditedBy { get; set; } 64 65 public DateTime? ValidFrom { get; set; } 66 67 public DateTime? ValidTo { get; set; } 68 } 69 70 public class StockItemsConfiguration : IEntityTypeConfiguration<StockItem> 71 { 72 public void Configure(EntityTypeBuilder<StockItem> builder) 73 { 74 // Set configuration for entity 75 builder.ToTable("StockItems", "Warehouse"); 76 77 // Set key for entity 78 builder.HasKey(p => p.StockItemID); 79 80 // Set configuration for columns 81 82 builder.Property(p => p.StockItemName).HasColumnType("nvarchar(200)").IsRequired(); 83 builder.Property(p => p.SupplierID).HasColumnType("int").IsRequired(); 84 builder.Property(p => p.ColorID).HasColumnType("int"); 85 builder.Property(p => p.UnitPackageID).HasColumnType("int").IsRequired(); 86 builder.Property(p => p.OuterPackageID).HasColumnType("int").IsRequired(); 87 builder.Property(p => p.Brand).HasColumnType("nvarchar(100)"); 88 builder.Property(p => p.Size).HasColumnType("nvarchar(40)"); 89 builder.Property(p => p.LeadTimeDays).HasColumnType("int").IsRequired(); 90 builder.Property(p => p.QuantityPerOuter).HasColumnType("int").IsRequired(); 91 builder.Property(p => p.IsChillerStock).HasColumnType("bit").IsRequired(); 92 builder.Property(p => p.Barcode).HasColumnType("nvarchar(100)"); 93 builder.Property(p => p.TaxRate).HasColumnType("decimal(18, 3)").IsRequired(); 94 builder.Property(p => p.UnitPrice).HasColumnType("decimal(18, 2)").IsRequired(); 95 builder.Property(p => p.RecommendedRetailPrice).HasColumnType("decimal(18, 2)"); 96 builder.Property(p => p.TypicalWeightPerUnit).HasColumnType("decimal(18, 3)").IsRequired(); 97 builder.Property(p => p.MarketingComments).HasColumnType("nvarchar(max)"); 98 builder.Property(p => p.InternalComments).HasColumnType("nvarchar(max)"); 99 builder.Property(p => p.CustomFields).HasColumnType("nvarchar(max)"); 100 builder.Property(p => p.LastEditedBy).HasColumnType("int").IsRequired(); 101 102 // Computed columns 103 104 builder 105 .Property(p => p.StockItemID) 106 .HasColumnType("int") 107 .IsRequired() 108 .HasComputedColumnSql("NEXT VALUE FOR [Sequences].[StockItemID]"); 109 110 builder 111 .Property(p => p.Tags) 112 .HasColumnType("nvarchar(max)") 113 .HasComputedColumnSql("json_query([CustomFields],N‘$.Tags‘)"); 114 115 builder 116 .Property(p => p.SearchDetails) 117 .HasColumnType("nvarchar(max)") 118 .IsRequired() 119 .HasComputedColumnSql("concat([StockItemName],N‘ ‘,[MarketingComments])"); 120 121 // Columns with generated value on add or update 122 123 builder 124 .Property(p => p.ValidFrom) 125 .HasColumnType("datetime2") 126 .IsRequired() 127 .ValueGeneratedOnAddOrUpdate(); 128 129 builder 130 .Property(p => p.ValidTo) 131 .HasColumnType("datetime2") 132 .IsRequired() 133 .ValueGeneratedOnAddOrUpdate(); 134 } 135 } 136 137 public class WideWorldImportersDbContext : DbContext 138 { 139 public WideWorldImportersDbContext(DbContextOptions<WideWorldImportersDbContext> options) 140 : base(options) 141 { 142 } 143 144 protected override void OnModelCreating(ModelBuilder modelBuilder) 145 { 146 // Apply configurations for entity 147 148 modelBuilder 149 .ApplyConfiguration(new StockItemsConfiguration()); 150 151 base.OnModelCreating(modelBuilder); 152 } 153 154 public DbSet<StockItem> StockItems { get; set; } 155 } 156 }
Extensions.cs
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; namespace WideWorldImporters.API.Models { public static class WideWorldImportersDbContextExtensions { public static IQueryable<StockItem> GetStockItems(this WideWorldImportersDbContext dbContext, int pageSize = 10, int pageNumber = 1, int? lastEditedBy = null, int? colorID = null, int? outerPackageID = null, int? supplierID = null, int? unitPackageID = null) { // Get query from DbSet var query = dbContext.StockItems.AsQueryable(); // Filter by: ‘LastEditedBy‘ if (lastEditedBy.HasValue) query = query.Where(item => item.LastEditedBy == lastEditedBy); // Filter by: ‘ColorID‘ if (colorID.HasValue) query = query.Where(item => item.ColorID == colorID); // Filter by: ‘OuterPackageID‘ if (outerPackageID.HasValue) query = query.Where(item => item.OuterPackageID == outerPackageID); // Filter by: ‘SupplierID‘ if (supplierID.HasValue) query = query.Where(item => item.SupplierID == supplierID); // Filter by: ‘UnitPackageID‘ if (unitPackageID.HasValue) query = query.Where(item => item.UnitPackageID == unitPackageID); return query; } public static async Task<StockItem> GetStockItemsAsync(this WideWorldImportersDbContext dbContext, StockItem entity) => await dbContext.StockItems.FirstOrDefaultAsync(item => item.StockItemID == entity.StockItemID); public static async Task<StockItem> GetStockItemsByStockItemNameAsync(this WideWorldImportersDbContext dbContext, StockItem entity) => await dbContext.StockItems.FirstOrDefaultAsync(item => item.StockItemName == entity.StockItemName); } public static class IQueryableExtensions { public static IQueryable<TModel> Paging<TModel>(this IQueryable<TModel> query, int pageSize = 0, int pageNumber = 0) where TModel : class => pageSize > 0 && pageNumber > 0 ? query.Skip((pageNumber - 1) * pageSize).Take(pageSize) : query; } }
Requests.cs
using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Threading.Tasks; namespace WideWorldImporters.API.Models { public class PostStockItemsRequest { [Key] public int? StockItemID { get; set; } [Required] [StringLength(200)] public string StockItemName { get; set; } [Required] public int? SupplierID { get; set; } public int? ColorID { get; set; } [Required] public int? UnitPackageID { get; set; } [Required] public int? OuterPackageID { get; set; } [StringLength(100)] public string Brand { get; set; } [StringLength(40)] public string Size { get; set; } [Required] public int? LeadTimeDays { get; set; } [Required] public int? QuantityPerOuter { get; set; } [Required] public bool? IsChillerStock { get; set; } [StringLength(100)] public string Barcode { get; set; } [Required] public decimal? TaxRate { get; set; } [Required] public decimal? UnitPrice { get; set; } public decimal? RecommendedRetailPrice { get; set; } [Required] public decimal? TypicalWeightPerUnit { get; set; } public string MarketingComments { get; set; } public string InternalComments { get; set; } public string CustomFields { get; set; } public string Tags { get; set; } [Required] public string SearchDetails { get; set; } [Required] public int? LastEditedBy { get; set; } public DateTime? ValidFrom { get; set; } public DateTime? ValidTo { get; set; } } public class PutStockItemsRequest { [Required] [StringLength(200)] public string StockItemName { get; set; } [Required] public int? SupplierID { get; set; } public int? ColorID { get; set; } [Required] public decimal? UnitPrice { get; set; } } public static class Extensions { public static StockItem ToEntity(this PostStockItemsRequest request) => new StockItem { StockItemID = request.StockItemID, StockItemName = request.StockItemName, SupplierID = request.SupplierID, ColorID = request.ColorID, UnitPackageID = request.UnitPackageID, OuterPackageID = request.OuterPackageID, Brand = request.Brand, Size = request.Size, LeadTimeDays = request.LeadTimeDays, QuantityPerOuter = request.QuantityPerOuter, IsChillerStock = request.IsChillerStock, Barcode = request.Barcode, TaxRate = request.TaxRate, UnitPrice = request.UnitPrice, RecommendedRetailPrice = request.RecommendedRetailPrice, TypicalWeightPerUnit = request.TypicalWeightPerUnit, MarketingComments = request.MarketingComments, InternalComments = request.InternalComments, CustomFields = request.CustomFields, Tags = request.Tags, SearchDetails = request.SearchDetails, LastEditedBy = request.LastEditedBy, ValidFrom = request.ValidFrom, ValidTo = request.ValidTo }; } }
Responses.cs
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Net; 5 using System.Threading.Tasks; 6 using Microsoft.AspNetCore.Mvc; 7 8 namespace WideWorldImporters.API.Models 9 { 10 public interface IResponse 11 { 12 string Message { get; set; } 13 14 bool DidError { get; set; } 15 16 string ErrorMessage { get; set; } 17 } 18 19 public interface ISingleResponse<TModel> : IResponse 20 { 21 TModel Model { get; set; } 22 } 23 24 public interface IListResponse<TModel> : IResponse 25 { 26 IEnumerable<TModel> Model { get; set; } 27 } 28 29 public interface IPagedResponse<TModel> : IListResponse<TModel> 30 { 31 int ItemsCount { get; set; } 32 33 double PageCount { get; } 34 } 35 36 public class Response : IResponse 37 { 38 public string Message { get; set; } 39 40 public bool DidError { get; set; } 41 42 public string ErrorMessage { get; set; } 43 } 44 45 public class SingleResponse<TModel> : ISingleResponse<TModel> 46 { 47 public string Message { get; set; } 48 49 public bool DidError { get; set; } 50 51 public string ErrorMessage { get; set; } 52 53 public TModel Model { get; set; } 54 } 55 56 public class ListResponse<TModel> : IListResponse<TModel> 57 { 58 public string Message { get; set; } 59 60 public bool DidError { get; set; } 61 62 public string ErrorMessage { get; set; } 63 64 public IEnumerable<TModel> Model { get; set; } 65 } 66 67 public class PagedResponse<TModel> : IPagedResponse<TModel> 68 { 69 public string Message { get; set; } 70 71 public bool DidError { get; set; } 72 73 public string ErrorMessage { get; set; } 74 75 public IEnumerable<TModel> Model { get; set; } 76 77 public int PageSize { get; set; } 78 79 public int PageNumber { get; set; } 80 81 public int ItemsCount { get; set; } 82 83 public double PageCount 84 => ItemsCount < PageSize ? 1 : (int)(((double)ItemsCount / PageSize) + 1); 85 } 86 87 public static class ResponseExtensions 88 { 89 public static IActionResult ToHttpResponse(this IResponse response) 90 { 91 var status = response.DidError ? HttpStatusCode.InternalServerError : HttpStatusCode.OK; 92 93 return new ObjectResult(response) 94 { 95 StatusCode = (int)status 96 }; 97 } 98 99 public static IActionResult ToHttpResponse<TModel>(this ISingleResponse<TModel> response) 100 { 101 var status = HttpStatusCode.OK; 102 103 if (response.DidError) 104 status = HttpStatusCode.InternalServerError; 105 else if (response.Model == null) 106 status = HttpStatusCode.NotFound; 107 108 return new ObjectResult(response) 109 { 110 StatusCode = (int)status 111 }; 112 } 113 114 public static IActionResult ToHttpResponse<TModel>(this IListResponse<TModel> response) 115 { 116 var status = HttpStatusCode.OK; 117 118 if (response.DidError) 119 status = HttpStatusCode.InternalServerError; 120 else if (response.Model == null) 121 status = HttpStatusCode.NoContent; 122 123 return new ObjectResult(response) 124 { 125 StatusCode = (int)status 126 }; 127 } 128 } 129 }
接下来开始添加Api控制器 WarehouseController
右击项目文件夹Controllers
WarehouseController.cs
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using WideWorldImporters.API.Models; namespace WideWorldImporters.API.Controllers { [Route("api/v1/[controller]")] [ApiController] public class WarehouseController : ControllerBase { protected readonly ILogger _logger; protected readonly WideWorldImportersDbContext _dbContext; public WarehouseController(ILogger<WarehouseController> logger, WideWorldImportersDbContext dbContext) { _logger = logger; _dbContext = dbContext; } /// <summary> /// 获取库存项列表 /// </summary> /// <param name="pageSize">页大小</param> /// <param name="pageNumber">页号</param> /// <param name="lastEditedBy">最后编辑者</param> /// <param name="colorId">颜色ID</param> /// <param name="outerPackageId">外包装ID</param> /// <param name="supplierId">供应商ID</param> /// <param name="unitPackageId"></param> /// <returns></returns> public async Task<IActionResult> GetStockItemsAsync(int pageSize = 10, int pageNumber = 1, int? lastEditedBy = null, int? colorId = null, int? outerPackageId = null, int? supplierId = null, int? unitPackageId = null ) { _logger?.LogDebug($"‘{nameof(GetStockItemsAsync)}‘ 被调用了"); var response = new PagedResponse<StockItem>();//创建分页对象 try { var query = _dbContext.GetStockItems(); response.PageSize = pageSize; response.PageNumber = pageNumber; response.ItemsCount = await query.CountAsync();//获取总记录数 response.Model = await query.Paging(pageSize, pageNumber).ToListAsync(); response.Message = $"第 {pageNumber} 共 {response.PageCount}, 产品总数量: {response.ItemsCount}"; _logger?.LogInformation("已成功检索库存项!"); } catch (Exception e) { response.DidError = true; response.ErrorMessage = "有一个内部错误,请联系技术支持!"; _logger?.LogCritical($"There was an error on ‘{nameof(GetStockItemsAsync)}‘ invocation: {e}"); } return response.ToHttpResponse(); } /// <summary> /// 获取单记录 /// </summary> /// <param name="id"></param> /// <returns></returns> [HttpGet("StockItem/{id}")] [ProducesResponseType(200)] [ProducesResponseType(404)] [ProducesResponseType(500)] public async Task<IActionResult> GetStockItemAsync(int id) { _logger?.LogDebug($"‘{nameof(GetStockItemAsync)}‘ 被执行了!"); var response = new SingleResponse<StockItem>(); try { response.Model = await _dbContext.GetStockItemsAsync(new StockItem(id)); } catch (Exception e) { response.DidError = true; response.ErrorMessage = "有错误,请联系技术支持!"; _logger?.LogCritical($"There was an error on ‘{nameof(GetStockItemAsync)}‘ invocation: {e}"); } return response.ToHttpResponse(); } // POST // api/v1/Warehouse/StockItem/ /// <summary> /// Creates a new stock item /// </summary> /// <param name="request">Request model</param> /// <returns>A response with new stock item</returns> /// <response code="200">Returns the stock items list</response> /// <response code="201">A response as creation of stock item</response> /// <response code="400">For bad request</response> /// <response code="500">If there was an internal server error</response> [HttpPost("StockItem")] [ProducesResponseType(200)] [ProducesResponseType(201)] [ProducesResponseType(400)] [ProducesResponseType(500)] public async Task<IActionResult> PostStockItemAsync([FromBody]PostStockItemsRequest request) { _logger?.LogDebug("‘{0}‘ has been invoked", nameof(PostStockItemAsync)); var response = new SingleResponse<StockItem>(); try { var existingEntity = await _dbContext.GetStockItemsByStockItemNameAsync(new StockItem { StockItemName = request.StockItemName }); if (existingEntity != null) ModelState.AddModelError("StockItemName", "Stock item name already exists"); if (!ModelState.IsValid) return BadRequest(); // Create entity from request model var entity = request.ToEntity(); // Add entity to repository _dbContext.Add(entity); // Save entity in database await _dbContext.SaveChangesAsync(); // Set the entity to response model response.Model = entity; } catch (Exception ex) { response.DidError = true; response.ErrorMessage = "There was an internal error, please contact to technical support."; _logger?.LogCritical("There was an error on ‘{0}‘ invocation: {1}", nameof(PostStockItemAsync), ex); } return response.ToHttpResponse(); } // PUT // api/v1/Warehouse/StockItem/5 /// <summary> /// Updates an existing stock item /// </summary> /// <param name="id">Stock item ID</param> /// <param name="request">Request model</param> /// <returns>A response as update stock item result</returns> /// <response code="200">If stock item was updated successfully</response> /// <response code="400">For bad request</response> /// <response code="404">If stock item is not exists</response> /// <response code="500">If there was an internal server error</response> [HttpPut("StockItem/{id}")] [ProducesResponseType(200)] [ProducesResponseType(400)] [ProducesResponseType(404)] [ProducesResponseType(500)] public async Task<IActionResult> PutStockItemAsync(int id, [FromBody]PutStockItemsRequest request) { _logger?.LogDebug("‘{0}‘ has been invoked", nameof(PutStockItemAsync)); var response = new Response(); try { // Get stock item by id var entity = await _dbContext.GetStockItemsAsync(new StockItem(id)); // Validate if entity exists if (entity == null) return NotFound(); // Set changes to entity entity.StockItemName = request.StockItemName; entity.SupplierId = request.SupplierID; entity.ColorId = request.ColorID; entity.UnitPrice = request.UnitPrice; // Update entity in repository _dbContext.Update(entity); // Save entity in database await _dbContext.SaveChangesAsync(); } catch (Exception ex) { response.DidError = true; response.ErrorMessage = "There was an internal error, please contact to technical support."; _logger?.LogCritical("There was an error on ‘{0}‘ invocation: {1}", nameof(PutStockItemAsync), ex); } return response.ToHttpResponse(); } // DELETE // api/v1/Warehouse/StockItem/5 /// <summary> /// Deletes an existing stock item /// </summary> /// <param name="id">Stock item ID</param> /// <returns>A response as delete stock item result</returns> /// <response code="200">If stock item was deleted successfully</response> /// <response code="404">If stock item is not exists</response> /// <response code="500">If there was an internal server error</response> [HttpDelete("StockItem/{id}")] [ProducesResponseType(200)] [ProducesResponseType(404)] [ProducesResponseType(500)] public async Task<IActionResult> DeleteStockItemAsync(int id) { _logger?.LogDebug("‘{0}‘ has been invoked", nameof(DeleteStockItemAsync)); var response = new Response(); try { // Get stock item by id var entity = await _dbContext.GetStockItemsAsync(new StockItem(id)); // Validate if entity exists if (entity == null) return NotFound(); // Remove entity from repository _dbContext.Remove(entity); // Delete entity in database await _dbContext.SaveChangesAsync(); } catch (Exception ex) { response.DidError = true; response.ErrorMessage = "There was an internal error, please contact to technical support."; _logger?.LogCritical("There was an error on ‘{0}‘ invocation: {1}", nameof(DeleteStockItemAsync), ex); } return response.ToHttpResponse(); } } }
接下来设置依赖注入,ASP.NET Core自带依赖注入,不需要第三方框架
修改根目录下Startup 类,
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Swashbuckle.AspNetCore.Swagger; using WideWorldImporters.API.Controllers; using WideWorldImporters.API.Models; namespace WideWorldImporters.API { public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2); //注入数据库上下文对象 services.AddDbContext<WideWorldImportersDbContext>(options => { options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"));//从配置文件中获取数据库连接字符串 }); //--------------上面是框架系统的访问,下面是应用程序的注入-------------------- services.AddScoped<ILogger, Logger<WarehouseController>>();//只用这里写了这一句,apiController里的注入才有效 //注入丝袜哥Swagger services.AddSwaggerGen(options => { options.SwaggerDoc("v1", new Info {Title = "WideWorldImporters API", Version = "v1"}); var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml"; var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile); options.IncludeXmlComments(xmlPath); }); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IHostingEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseSwagger(); app.UseSwaggerUI(c => { c.SwaggerEndpoint("/swagger/v1/swagger.json", "WideWorldImporters API V1"); }); app.UseMvc(); } } }
在appsettings.json文件中添加数据库连接字符串
{ "ConnectionStrings": { "DefaultConnection": "Server=(localdb);Database=WideWorldImportersDb;Trusted_Connection=True;MultipleActiveResultSets=true" }, "Logging": { "LogLevel": { "Default": "Warning" } }, "AllowedHosts": "*" }
设置项目属性的swagger生成的文件,丝袜哥就是靠这个文件来生成UI的。
设置完,记得保存!
代码写完了,数据库还没有,需要利用EF Core迁移并生成数据库
在命令行输入迁移命令: add-migration AddEntity
生成了迁移文件
然后更新到数据库,输入:update-database
数据库创建完毕。
运行项目试试!!!!
访问地址:
http://localhost:49956/api/v1/Warehouse/StockItem
好像有问题,明天继续吧.................................郁闷了
原文地址:https://www.cnblogs.com/wanghaibin/p/10296774.html