当前位置:   article > 正文

手把手搭建基于.NET 8.0的Web API项目_net8 webapi

net8 webapi
1.背景

工作以后,大部分时间都是在做基于业务的CRUD工作,软件产品或者项目的框架基本都早就搭建好了,程序员只需要在框架内去填格子打代码就行了。于是,我抽了时间来搭建个简单的三层架构模式的web api项目,技术点大概如下:三层架构+EFCore+.Net 8.0 Web Api+AutoMap+IOC容器。本文是我搭建项目的一个过程,比较简单和粗糙,但是完整,适合学习和练手。

2.操作
2.1 项目的架构图

其实图1是我最开始的设计结构,但是设计风格有提到:模块间应该依赖抽象,而不是具体的实现。所以我将结构改造为了图2,针对业务逻辑层和数据访问层开了一个抽象接口层。

2.2 新增项目

按照如下操作,创建项目:SimpleWebApi

2.3 新增类库

按照下图新增类库:SimpleWebApi.Migration、SimpleWebApi.Business.Service、SimpleWebApi.Business.Service.Interface

注意:SimpleWebApi.Migration是数据库访问

SimpleWebApi.Business.Service、SimpleWebApi.Business.Service.Interface是做业务逻辑

2.4 支持EFCore

找到类库:SimpleWebApi.Migration,并给这个项目,添加如下nuget包:Microsoft.EntityFrameworkCore、Microsoft.EntityFrameworkCore.SqlServer、Microsoft.EntityFrameworkCore.Tools、Microsoft.EntityFrameworkCore.Design

按照如下所示:添加Model和DBContext

Commodity的代码如下:

  1. using System;
  2. using System.Collections.Generic;
  3. namespace SimpleWebApi.Migration.Models;
  4. public partial class Commodity
  5. {
  6. public int Id { get; set; }
  7. public long? ProductId { get; set; }
  8. public int? CategoryId { get; set; }
  9. public string? Title { get; set; }
  10. public decimal? Price { get; set; }
  11. public string? Url { get; set; }
  12. public string? ImageUrl { get; set; }
  13. }

CompanyInfo的代码如下:

  1. using System;
  2. using System.Collections.Generic;
  3. namespace SimpleWebApi.Migration.Models;
  4. public partial class CompanyInfo
  5. {
  6. public int CompanyId { get; set; }
  7. public string? Name { get; set; }
  8. public DateTime? CreateTime { get; set; }
  9. public int CreatorId { get; set; }
  10. public int? LastModifierId { get; set; }
  11. public DateTime? LastModifyTime { get; set; }
  12. public virtual ICollection<SysUser> SysUsers { get; set; } = new List<SysUser>();
  13. }

SysUser的代码如下:

  1. using System;
  2. using System.Collections.Generic;
  3. namespace SimpleWebApi.Migration.Models;
  4. public partial class SysUser
  5. {
  6. public int Id { get; set; }
  7. public string? Name { get; set; }
  8. public string? Password { get; set; }
  9. public int Status { get; set; }
  10. public string? Phone { get; set; }
  11. public string? Mobile { get; set; }
  12. public string? Address { get; set; }
  13. public string? Email { get; set; }
  14. public long? Qq { get; set; }
  15. public string? WeChat { get; set; }
  16. public int? Sex { get; set; }
  17. public DateTime? LastLoginTime { get; set; }
  18. public DateTime? CreateTime { get; set; }
  19. public int? CreateId { get; set; }
  20. public DateTime? LastModifyTime { get; set; }
  21. public int? LastModifyId { get; set; }
  22. public int? CompanyId { get; set; }
  23. public virtual CompanyInfo? Company { get; set; }
  24. }

AdvancedCustomerDbContext的代码如下:

  1. using System;
  2. using System.Collections.Generic;
  3. using Microsoft.EntityFrameworkCore;
  4. using SimpleWebApi.Migration.Models;
  5. namespace SimpleWebApi.Migration
  6. {
  7. public class AdvancedCustomerDbContext : DbContext
  8. {
  9. public AdvancedCustomerDbContext()
  10. {
  11. }
  12. public AdvancedCustomerDbContext(DbContextOptions<AdvancedCustomerDbContext> options)
  13. : base(options)
  14. {
  15. }
  16. public virtual DbSet<Commodity> Commodities { get; set; }
  17. public virtual DbSet<CompanyInfo> CompanyInfos { get; set; }
  18. public virtual DbSet<SysUser> SysUsers { get; set; }
  19. protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
  20. #warning To protect potentially sensitive information in your connection string, you should move it out of source code. You can avoid scaffolding the connection string by using the Name= syntax to read it from configuration - see https://go.microsoft.com/fwlink/?linkid=2131148. For more guidance on storing connection strings, see https://go.microsoft.com/fwlink/?LinkId=723263.
  21. => optionsBuilder.UseSqlServer("Data Source=127.0.0.1;Initial Catalog=AdvancedCustomerDB_Init;Persist Security Info=True;User ID=sa;Password=****;Encrypt=False;TrustServerCertificate=true");
  22. protected override void OnModelCreating(ModelBuilder modelBuilder)
  23. {
  24. modelBuilder.Entity<Commodity>(entity =>
  25. {
  26. entity.ToTable("Commodity");
  27. entity.Property(e => e.ImageUrl)
  28. .HasMaxLength(1000)
  29. .IsUnicode(false);
  30. entity.Property(e => e.Price).HasColumnType("decimal(18, 2)");
  31. entity.Property(e => e.Title)
  32. .HasMaxLength(500)
  33. .IsUnicode(false);
  34. entity.Property(e => e.Url)
  35. .HasMaxLength(1000)
  36. .IsUnicode(false);
  37. });
  38. modelBuilder.Entity<CompanyInfo>(entity =>
  39. {
  40. entity.HasKey(e => e.CompanyId).HasName("PK_Company");
  41. entity.ToTable("CompanyInfo");
  42. entity.Property(e => e.CreateTime).HasColumnType("datetime");
  43. entity.Property(e => e.LastModifyTime).HasColumnType("datetime");
  44. entity.Property(e => e.Name)
  45. .HasMaxLength(50)
  46. .IsUnicode(false);
  47. });
  48. modelBuilder.Entity<SysUser>(entity =>
  49. {
  50. entity.ToTable("SysUser");
  51. entity.Property(e => e.Address)
  52. .HasMaxLength(500)
  53. .IsUnicode(false);
  54. entity.Property(e => e.CreateTime).HasColumnType("datetime");
  55. entity.Property(e => e.Email)
  56. .HasMaxLength(50)
  57. .IsUnicode(false);
  58. entity.Property(e => e.LastLoginTime).HasColumnType("datetime");
  59. entity.Property(e => e.LastModifyTime).HasColumnType("datetime");
  60. entity.Property(e => e.Mobile)
  61. .HasMaxLength(12)
  62. .IsUnicode(false);
  63. entity.Property(e => e.Name)
  64. .HasMaxLength(50)
  65. .IsUnicode(false);
  66. entity.Property(e => e.Password)
  67. .HasMaxLength(50)
  68. .IsUnicode(false);
  69. entity.Property(e => e.Phone)
  70. .HasMaxLength(12)
  71. .IsUnicode(false);
  72. entity.Property(e => e.Qq).HasColumnName("QQ");
  73. entity.Property(e => e.WeChat)
  74. .HasMaxLength(50)
  75. .IsUnicode(false);
  76. entity.HasOne(d => d.Company).WithMany(p => p.SysUsers)
  77. .HasForeignKey(d => d.CompanyId)
  78. .OnDelete(DeleteBehavior.Cascade)
  79. .HasConstraintName("FK_SysUser_CompanyInfo");
  80. });
  81. OnModelCreatingPartial(modelBuilder);
  82. }
  83. public void OnModelCreatingPartial(ModelBuilder modelBuilder)
  84. {
  85. }
  86. }
  87. }
2.5 业务逻辑抽象层

找到项目“SimpleWebApi.Business.Service.Interface”,新增项目引用-SimpleWebApi.Migration,如下图:

按照下图添加以下接口:

IBaseService的代码如下:

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Linq.Expressions;
  5. using System.Text;
  6. using System.Threading.Tasks;
  7. namespace SimpleWebApi.Business.Service.Interface
  8. {
  9. public interface IBaseService
  10. {
  11. public IQueryable<T> Query<T>(Expression<Func<T,bool>> funcWhere) where T : class;
  12. }
  13. }

ICommodityService的代码如下:

  1. using SimpleWebApi.Migration.Models;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Text;
  6. using System.Threading.Tasks;
  7. namespace SimpleWebApi.Business.Service.Interface
  8. {
  9. public interface ICommodityService:IBaseService
  10. {
  11. public bool AddCommodity(Commodity commodity);
  12. public IQueryable<Commodity> GetCommodity(int Id);
  13. }
  14. }

ICompanyInfoService的代码如下:

  1. using SimpleWebApi.Migration.Models;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Text;
  6. using System.Threading.Tasks;
  7. namespace SimpleWebApi.Business.Service.Interface
  8. {
  9. public interface ICompanyInfoService:IBaseService
  10. {
  11. CompanyInfo GetCompany(int companyID);
  12. }
  13. }

ISysUserService的代码如下:

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Threading.Tasks;
  6. namespace SimpleWebApi.Business.Service.Interface
  7. {
  8. public interface ISysUserService:IBaseService
  9. {
  10. }
  11. }

2.6 业务逻辑层

找到项目“SimpleWebApi.Business.Service”,新增项目引用如下:SimpleWebApi.Business.Service.Interface和 SimpleWebApi.Migration

按照下图添加以下类:

BaseService的代码如下:

  1. using Microsoft.EntityFrameworkCore;
  2. using SimpleWebApi.Business.Service.Interface;
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Linq;
  6. using System.Linq.Expressions;
  7. using System.Text;
  8. using System.Threading.Tasks;
  9. namespace SimpleWebApi.Business.Service
  10. {
  11. public class BaseService:IBaseService
  12. {
  13. protected DbContext Context;
  14. public BaseService(DbContext context)
  15. {
  16. Console.WriteLine($"{this.GetType().Name}被构造了......");
  17. this.Context= context;
  18. }
  19. public IQueryable<T> Query<T>(Expression<Func<T, bool>> funcWhere) where T : class
  20. {
  21. return this.Context.Set<T>().Where<T>(funcWhere);
  22. }
  23. }
  24. }

CommodityService的代码如下:

  1. using Microsoft.EntityFrameworkCore;
  2. using SimpleWebApi.Business.Service.Interface;
  3. using SimpleWebApi.Migration.Models;
  4. using System;
  5. using System.Collections.Generic;
  6. using System.Linq;
  7. using System.Text;
  8. using System.Threading.Tasks;
  9. namespace SimpleWebApi.Business.Service
  10. {
  11. public class CommodityService : BaseService, ICommodityService
  12. {
  13. public CommodityService(DbContext context):base(context)
  14. {
  15. }
  16. public bool AddCommodity(Commodity commodity)
  17. {
  18. this.Context.Set<Commodity>().Add(commodity);
  19. int num= this.Context.SaveChanges();
  20. return num > 0;
  21. }
  22. public IQueryable<Commodity> GetCommodity(int Id)
  23. {
  24. var list= this.Context.Set<Commodity>().Where(a => a.Id == Id);
  25. return list;
  26. }
  27. }
  28. }

CompanyInfoService的代码如下:

  1. using Microsoft.EntityFrameworkCore;
  2. using SimpleWebApi.Business.Service.Interface;
  3. using SimpleWebApi.Migration.Models;
  4. using System;
  5. using System.Collections.Generic;
  6. using System.Linq;
  7. using System.Text;
  8. using System.Threading.Tasks;
  9. namespace SimpleWebApi.Business.Service
  10. {
  11. public class CompanyInfoService:BaseService, ICompanyInfoService
  12. {
  13. public CompanyInfoService(DbContext context):base(context)
  14. {
  15. }
  16. public CompanyInfo GetCompany(int companyID)
  17. {
  18. var company = this.Context.Set<CompanyInfo>().Where(a=>a.CompanyId==companyID).FirstOrDefault();
  19. return company;
  20. }
  21. }
  22. }

SysUserService的代码如下:

  1. using Microsoft.EntityFrameworkCore;
  2. using SimpleWebApi.Business.Service.Interface;
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Linq;
  6. using System.Text;
  7. using System.Threading.Tasks;
  8. namespace SimpleWebApi.Business.Service
  9. {
  10. public class SysUserService:BaseService, ISysUserService
  11. {
  12. public SysUserService(DbContext context) : base(context)
  13. {
  14. }
  15. }
  16. }
2.6 UI

找到项目“SimpleWebApi”,并新增项目引用:

SimpleWebApi.Business.Service、SimpleWebApi.Business.Service.Interface、SimpleWebApi.Migration

按照如下,新增nuget包引用:AutoMapper、AutoMapper.Extensions.Microsoft.DependencyInjection

新增文件夹DTO和Map,并新增类文件和AutoMapper的规则文件

CommodityDTO的代码如下:

  1. using System;
  2. using System.Collections.Generic;
  3. namespace SimpleWebApi;
  4. public class CommodityDTO
  5. {
  6. public int CommodityId { get; set; }
  7. public long? ProductId { get; set; }
  8. public int? CategoryId { get; set; }
  9. public string? Title { get; set; }
  10. public decimal? Price { get; set; }
  11. public string? Url { get; set; }
  12. public string? ImageUrl { get; set; }
  13. }

CompanyInfoDTO的代码如下:

  1. using SimpleWebApi.Migration.Models;
  2. using System;
  3. using System.Collections.Generic;
  4. namespace SimpleWebApi;
  5. public class CompanyInfoDTO
  6. {
  7. public int CompanyId { get; set; }
  8. public string? Name { get; set; }
  9. public DateTime? CreateTime { get; set; }
  10. public int CreatorId { get; set; }
  11. public int? LastModifierId { get; set; }
  12. public DateTime? LastModifyTime { get; set; }
  13. public virtual ICollection<SysUser> SysUsers { get; set; } = new List<SysUser>();
  14. }

AuotoMapConfig的代码如下:

  1. using AutoMapper;
  2. using SimpleWebApi.Migration.Models;
  3. namespace SimpleWebApi
  4. {
  5. public class AuotoMapConfig:Profile
  6. {
  7. public AuotoMapConfig()
  8. {
  9. CreateMap<Commodity, CommodityDTO>().ForMember(c=>c.CommodityId, s=>s.MapFrom(c=>c.Id))
  10. .ForMember(c=>c.ProductId,s=>s.MapFrom(c=>c.ProductId))
  11. .ForMember(c=>c.CategoryId,s=>s.MapFrom(c=>c.CategoryId))
  12. .ForMember(c=>c.Title,s=>s.MapFrom(c=>c.Title))
  13. .ForMember(c=>c.Price,s=>s.MapFrom(c=>c.Price))
  14. .ForMember(c=>c.Url,s=>s.MapFrom(c=>c.Url))
  15. .ForMember(c=>c.ImageUrl,s=>s.MapFrom(c=>c.ImageUrl));
  16. CreateMap<CompanyInfo, CompanyInfoDTO>();
  17. }
  18. }
  19. }

为了添加对象映射,DBContext,对象注入等,打开Program文件,按照如下添加

上图的红色代码如下:

  1. //查询数据库真实数据的业务逻辑层服务注册
  2. builder.Services.AddTransient<ICommodityService, CommodityService>();
  3. builder.Services.AddTransient<ICompanyInfoService, CompanyInfoService>();
  4. //添加DbContext
  5. //builder.Services.AddDbContext<AdvancedCustomerDbContext>();
  6. builder.Services.AddTransient<DbContext, AdvancedCustomerDbContext>();
  7. //支持AutoMapper
  8. builder.Services.AddAutoMapper(options =>
  9. {
  10. options.AddProfile<AuotoMapConfig>();
  11. });

选中“Controllers”文件夹新增控制器 ApiController

ApiController代码如下:

  1. using AutoMapper;
  2. using Microsoft.AspNetCore.Mvc;
  3. using SimpleWebApi.Business.Service.Interface;
  4. using SimpleWebApi.Migration.Models;
  5. using System.Linq.Expressions;
  6. namespace SimpleWebApi.Controllers
  7. {
  8. [ApiController]
  9. [Route("api/[controller]/[action]")]
  10. public class ApiController : ControllerBase
  11. {
  12. private readonly ILogger<ApiController> _logger;
  13. private ICommodityService _comService;
  14. private ICompanyInfoService _companyService;
  15. private IMapper _mapper;
  16. public ApiController(ILogger<ApiController> logger, ICommodityService comService, ICompanyInfoService companyService, IMapper mapper)
  17. {
  18. _logger = logger;
  19. _comService = comService;
  20. _companyService = companyService;
  21. _mapper = mapper;
  22. }
  23. [HttpGet]
  24. public IEnumerable<CommodityDTO> GetCommodity(int Id)
  25. {
  26. Expression<Func<Commodity, bool>> funcWhere = null;
  27. funcWhere = a => a.Id == Id;
  28. var commodityList = _comService.Query(funcWhere);
  29. List<CommodityDTO> list = new List<CommodityDTO>();
  30. _mapper.Map<IQueryable<Commodity>, List<CommodityDTO>>(commodityList, list);
  31. return list;
  32. }
  33. [HttpGet]
  34. public CompanyInfoDTO GetCompanyInfo(int companyId)
  35. {
  36. var company = _companyService.GetCompany(companyId);
  37. CompanyInfoDTO dto = new CompanyInfoDTO();
  38. _mapper.Map<CompanyInfo, CompanyInfoDTO>(company, dto);
  39. return dto;
  40. }
  41. }
  42. }

 这里有个小坑,打开SimpleWebApi.csproj,按照下图设置,可以解决问题

<InvariantGlobalization>false</InvariantGlobalization>

ps:默认该数据值是true,运行程序后会异常,异常提示如下:

  1. System.Globalization.CultureNotFoundException
  2. HResult=0x80070057
  3. Message=Only the invariant culture is supported in globalization-invariant mode. See https://aka.ms/GlobalizationInvariantMode for more information. (Parameter 'name')
  4. en-us is an invalid culture identifier.
  5. Source=System.Private.CoreLib
  6. StackTrace:
  7. 在 System.Globalization.CultureInfo.GetCultureInfo(String name)
  8. 在 Microsoft.Data.SqlClient.SqlConnection.TryOpen(TaskCompletionSource`1 retry, SqlConnectionOverrides overrides)
  9. 在 Microsoft.Data.SqlClient.SqlConnection.Open(SqlConnectionOverrides overrides)
  10. 在 Microsoft.Data.SqlClient.SqlConnection.Open()
  11. 在 Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerConnection.OpenDbConnection(Boolean errorsExpected)
  12. 在 Microsoft.EntityFrameworkCore.Storage.RelationalConnection.OpenInternal(Boolean errorsExpected)
  13. 在 Microsoft.EntityFrameworkCore.Storage.RelationalConnection.Open(Boolean errorsExpected)
  14. 在 Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReader(RelationalCommandParameterObject parameterObject)
  15. 在 Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable`1.Enumerator.InitializeReader(Enumerator enumerator)
  16. 在 Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable`1.Enumerator.<>c.<MoveNext>b__21_0(DbContext _, Enumerator enumerator)
  17. 在 Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.Execute[TState,TResult](TState state, Func`3 operation, Func`3 verifySucceeded)
  18. 在 Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable`1.Enumerator.MoveNext()
  19. 在 System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
  20. 在 System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
  21. 在 SimpleProjectDemo.Controllers.WeatherForecastController.Get2() 在 E:\Vs_Project\SimpleProjectDemo\SimpleProjectDemo\Controllers\WeatherForecastController.cs 中: 第 50
  22. 在 Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.SyncObjectResultExecutor.Execute(ActionContext actionContext, IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
  23. 在 Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<<InvokeActionMethodAsync>g__Logged|12_1>d.MoveNext()
2.7 运行项目

运行项目,如下图所示显示Swagger页面:

用接口api/Api/GetCommodity 来测试下

3.结论

至此,操作完成。成功的搭建了一个简单的.net 8.0的web api项目。

ps:本项目的代码都很简单,真诚建议跟着指引来敲代码梳理思路,不过考虑极端情况,我还是打包代码上传到了csdn。有需要的童鞋可以按需下载。谢谢。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/IT小白/article/detail/559888
推荐阅读
相关标签
  

闽ICP备14008679号