曂沅仴駦 发表于 2024-6-21 12:58:40

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

1.配景

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

2.1 项目的架构图

其实图1是我最开始的设计布局,但是设计风格有提到:模块间应该依赖抽象,而不是详细的实现。所以我将布局改造为了图2,针对业务逻辑层和数据访问层开了一个抽象接口层。
https://img-blog.csdnimg.cn/direct/7658dc96407b43faa3270ca2b6a37917.png
2.2 新增项目

按照如下操作,创建项目:SimpleWebApi
https://img-blog.csdnimg.cn/direct/3d140f4bc32043c3a31cce0cf237ab8d.png
https://img-blog.csdnimg.cn/direct/b02b138f460049b8ad90d627fc08e6d2.png
https://img-blog.csdnimg.cn/direct/dcde806525e54fd89b4210ad6b89f7d0.png
https://img-blog.csdnimg.cn/direct/fd8766f12f734fc3a383af2fbcc2ed36.png
2.3 新增类库

按照下图新增类库:SimpleWebApi.Migration、SimpleWebApi.Business.Service、SimpleWebApi.Business.Service.Interface
注意:SimpleWebApi.Migration是数据库访问
SimpleWebApi.Business.Service、SimpleWebApi.Business.Service.Interface是做业务逻辑
https://img-blog.csdnimg.cn/direct/a9d6dd5e3b424d81bbbf07e9be2ca7b7.png
https://img-blog.csdnimg.cn/direct/c98fd60a910444b4a82b6647eb8adb88.png
https://img-blog.csdnimg.cn/direct/a24a69e765484d7888ce2fe259940d57.png
2.4 支持EFCore

找到类库:SimpleWebApi.Migration,并给这个项目,添加如下nuget包:Microsoft.EntityFrameworkCore、Microsoft.EntityFrameworkCore.SqlServer、Microsoft.EntityFrameworkCore.Tools、Microsoft.EntityFrameworkCore.Design
https://img-blog.csdnimg.cn/direct/7ae7529a49df4fa8a33e7fac953d00e3.png
按照如下所示:添加Model和DBContext
https://img-blog.csdnimg.cn/direct/b5dff42abfa24fd9b5fef2d745309599.png
Commodity的代码如下:
using System;
using System.Collections.Generic;

namespace SimpleWebApi.Migration.Models;

public partial class Commodity
{
    public int Id { get; set; }

    public long? ProductId { get; set; }

    public int? CategoryId { get; set; }

    public string? Title { get; set; }

    public decimal? Price { get; set; }

    public string? Url { get; set; }

    public string? ImageUrl { get; set; }
}
CompanyInfo的代码如下:
using System;
using System.Collections.Generic;

namespace SimpleWebApi.Migration.Models;

public partial class CompanyInfo
{
    public int CompanyId { get; set; }

    public string? Name { get; set; }

    public DateTime? CreateTime { get; set; }

    public int CreatorId { get; set; }

    public int? LastModifierId { get; set; }

    public DateTime? LastModifyTime { get; set; }

    public virtual ICollection<SysUser> SysUsers { get; set; } = new List<SysUser>();
}
SysUser的代码如下:
using System;
using System.Collections.Generic;

namespace SimpleWebApi.Migration.Models;

public partial class SysUser
{
    public int Id { get; set; }

    public string? Name { get; set; }

    public string? Password { get; set; }

    public int Status { get; set; }

    public string? Phone { get; set; }

    public string? Mobile { get; set; }

    public string? Address { get; set; }

    public string? Email { get; set; }

    public long? Qq { get; set; }

    public string? WeChat { get; set; }

    public int? Sex { get; set; }

    public DateTime? LastLoginTime { get; set; }

    public DateTime? CreateTime { get; set; }

    public int? CreateId { get; set; }

    public DateTime? LastModifyTime { get; set; }

    public int? LastModifyId { get; set; }

    public int? CompanyId { get; set; }

    public virtual CompanyInfo? Company { get; set; }
}
AdvancedCustomerDbContext的代码如下:
using System;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore;
using SimpleWebApi.Migration.Models;

namespace SimpleWebApi.Migration
{

    public class AdvancedCustomerDbContext : DbContext
    {
      public AdvancedCustomerDbContext()
      {
      }

      public AdvancedCustomerDbContext(DbContextOptions<AdvancedCustomerDbContext> options)
            : base(options)
      {
      }

      public virtual DbSet<Commodity> Commodities { get; set; }

      public virtual DbSet<CompanyInfo> CompanyInfos { get; set; }

      public virtual DbSet<SysUser> SysUsers { get; set; }

      protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
#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.
            => optionsBuilder.UseSqlServer("Data Source=127.0.0.1;Initial Catalog=AdvancedCustomerDB_Init;Persist Security Info=True;User ID=sa;Password=****;Encrypt=False;TrustServerCertificate=true");

      protected override void OnModelCreating(ModelBuilder modelBuilder)
      {
            modelBuilder.Entity<Commodity>(entity =>
            {
                entity.ToTable("Commodity");

                entity.Property(e => e.ImageUrl)
                  .HasMaxLength(1000)
                  .IsUnicode(false);
                entity.Property(e => e.Price).HasColumnType("decimal(18, 2)");
                entity.Property(e => e.Title)
                  .HasMaxLength(500)
                  .IsUnicode(false);
                entity.Property(e => e.Url)
                  .HasMaxLength(1000)
                  .IsUnicode(false);
            });

            modelBuilder.Entity<CompanyInfo>(entity =>
            {
                entity.HasKey(e => e.CompanyId).HasName("PK_Company");

                entity.ToTable("CompanyInfo");

                entity.Property(e => e.CreateTime).HasColumnType("datetime");
                entity.Property(e => e.LastModifyTime).HasColumnType("datetime");
                entity.Property(e => e.Name)
                  .HasMaxLength(50)
                  .IsUnicode(false);
            });

            modelBuilder.Entity<SysUser>(entity =>
            {
                entity.ToTable("SysUser");

                entity.Property(e => e.Address)
                  .HasMaxLength(500)
                  .IsUnicode(false);
                entity.Property(e => e.CreateTime).HasColumnType("datetime");
                entity.Property(e => e.Email)
                  .HasMaxLength(50)
                  .IsUnicode(false);
                entity.Property(e => e.LastLoginTime).HasColumnType("datetime");
                entity.Property(e => e.LastModifyTime).HasColumnType("datetime");
                entity.Property(e => e.Mobile)
                  .HasMaxLength(12)
                  .IsUnicode(false);
                entity.Property(e => e.Name)
                  .HasMaxLength(50)
                  .IsUnicode(false);
                entity.Property(e => e.Password)
                  .HasMaxLength(50)
                  .IsUnicode(false);
                entity.Property(e => e.Phone)
                  .HasMaxLength(12)
                  .IsUnicode(false);
                entity.Property(e => e.Qq).HasColumnName("QQ");
                entity.Property(e => e.WeChat)
                  .HasMaxLength(50)
                  .IsUnicode(false);

                entity.HasOne(d => d.Company).WithMany(p => p.SysUsers)
                  .HasForeignKey(d => d.CompanyId)
                  .OnDelete(DeleteBehavior.Cascade)
                  .HasConstraintName("FK_SysUser_CompanyInfo");
            });

            OnModelCreatingPartial(modelBuilder);
      }

      public void OnModelCreatingPartial(ModelBuilder modelBuilder)
      {

      }
    }
} 2.5 业务逻辑抽象层

找到项目“SimpleWebApi.Business.Service.Interface”,新增项目引用-SimpleWebApi.Migration,如下图:
https://img-blog.csdnimg.cn/direct/b2be8976a7b1413eaf4cc611f0cde2d9.png

按照下图添加以下接口:
https://img-blog.csdnimg.cn/direct/b33ac716b9b647dc8cd029cdcfb2db17.png
IBaseService的代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;

namespace SimpleWebApi.Business.Service.Interface
{
    public interface IBaseService
    {

      public IQueryable<T> Query<T>(Expression<Func<T,bool>> funcWhere) where T : class;
    }
}
ICommodityService的代码如下:
using SimpleWebApi.Migration.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SimpleWebApi.Business.Service.Interface
{
    public interface ICommodityService:IBaseService
    {

      public bool AddCommodity(Commodity commodity);

      public IQueryable<Commodity> GetCommodity(int Id);

   

    }
}
ICompanyInfoService的代码如下:
using SimpleWebApi.Migration.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SimpleWebApi.Business.Service.Interface
{
    public interface ICompanyInfoService:IBaseService
    {
      CompanyInfo GetCompany(int companyID);
    }
}
ISysUserService的代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SimpleWebApi.Business.Service.Interface
{
    public interface ISysUserService:IBaseService
    {
    }
}
2.6 业务逻辑层
找到项目“SimpleWebApi.Business.Service”,新增项目引用如下:SimpleWebApi.Business.Service.Interface和 SimpleWebApi.Migration
https://img-blog.csdnimg.cn/direct/762d16d40b3844de92292bb828a308e5.png
按照下图添加以下类:
https://img-blog.csdnimg.cn/direct/51ffce9d8a6b403ab752dd68ce9a9780.png
BaseService的代码如下:
using Microsoft.EntityFrameworkCore;
using SimpleWebApi.Business.Service.Interface;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;

namespace SimpleWebApi.Business.Service
{
    public class BaseService:IBaseService
    {
      protected DbContext Context;
      public BaseService(DbContext context)
      {
            Console.WriteLine($"{this.GetType().Name}被构造了......");
            this.Context= context;   
      }
      public IQueryable<T> Query<T>(Expression<Func<T, bool>> funcWhere) where T : class
      {
            return this.Context.Set<T>().Where<T>(funcWhere);   
      }

    }
}

CommodityService的代码如下:
using Microsoft.EntityFrameworkCore;
using SimpleWebApi.Business.Service.Interface;
using SimpleWebApi.Migration.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SimpleWebApi.Business.Service
{
    public class CommodityService : BaseService, ICommodityService
    {
      public CommodityService(DbContext context):base(context)
      {
               
      }

      public bool AddCommodity(Commodity commodity)
      {
            this.Context.Set<Commodity>().Add(commodity);
            int num= this.Context.SaveChanges();
            return num > 0;
      }

      public IQueryable<Commodity> GetCommodity(int Id)
      {
            var list= this.Context.Set<Commodity>().Where(a => a.Id == Id);
            return list;
      }
    }
}

CompanyInfoService的代码如下:
using Microsoft.EntityFrameworkCore;
using SimpleWebApi.Business.Service.Interface;
using SimpleWebApi.Migration.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SimpleWebApi.Business.Service
{
    public class CompanyInfoService:BaseService, ICompanyInfoService
    {
      public CompanyInfoService(DbContext context):base(context)
      {
               
      }

      public CompanyInfo GetCompany(int companyID)
      {
            var company = this.Context.Set<CompanyInfo>().Where(a=>a.CompanyId==companyID).FirstOrDefault();
            return company;
      }
    }
}

SysUserService的代码如下:
using Microsoft.EntityFrameworkCore;
using SimpleWebApi.Business.Service.Interface;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SimpleWebApi.Business.Service
{
    public class SysUserService:BaseService, ISysUserService
    {
      public SysUserService(DbContext context) : base(context)
      {
               
      }
    }
}
2.6 UI

找到项目“SimpleWebApi”,并新增项目引用:
SimpleWebApi.Business.Service、SimpleWebApi.Business.Service.Interface、SimpleWebApi.Migration
https://img-blog.csdnimg.cn/direct/b64c89d5945a47e9aa11d9c78f4b0f33.png
按照如下,新增nuget包引用:AutoMapper、AutoMapper.Extensions.Microsoft.DependencyInjection
https://img-blog.csdnimg.cn/direct/c21b6f9d300d4510af6907d21fa4a954.png

新增文件夹DTO和Map,并新增类文件和AutoMapper的规则文件
https://img-blog.csdnimg.cn/direct/ae3d9a54966b437fa8734ef1a7dc94d0.png
CommodityDTO的代码如下:
using System;
using System.Collections.Generic;

namespace SimpleWebApi;

public class CommodityDTO
{
    public int CommodityId { get; set; }

    public long? ProductId { get; set; }

    public int? CategoryId { get; set; }

    public string? Title { get; set; }

    public decimal? Price { get; set; }

    public string? Url { get; set; }

    public string? ImageUrl { get; set; }
}
CompanyInfoDTO的代码如下:
using SimpleWebApi.Migration.Models;
using System;
using System.Collections.Generic;

namespace SimpleWebApi;

public class CompanyInfoDTO
{
    public int CompanyId { get; set; }

    public string? Name { get; set; }

    public DateTime? CreateTime { get; set; }

    public int CreatorId { get; set; }

    public int? LastModifierId { get; set; }

    public DateTime? LastModifyTime { get; set; }

    public virtual ICollection<SysUser> SysUsers { get; set; } = new List<SysUser>();
}
AuotoMapConfig的代码如下:
using AutoMapper;
using SimpleWebApi.Migration.Models;

namespace SimpleWebApi
{
    public class AuotoMapConfig:Profile
    {
      public AuotoMapConfig()
      {
            CreateMap<Commodity, CommodityDTO>().ForMember(c=>c.CommodityId, s=>s.MapFrom(c=>c.Id))
                .ForMember(c=>c.ProductId,s=>s.MapFrom(c=>c.ProductId))
                .ForMember(c=>c.CategoryId,s=>s.MapFrom(c=>c.CategoryId))
                .ForMember(c=>c.Title,s=>s.MapFrom(c=>c.Title))
                .ForMember(c=>c.Price,s=>s.MapFrom(c=>c.Price))
                .ForMember(c=>c.Url,s=>s.MapFrom(c=>c.Url))
                .ForMember(c=>c.ImageUrl,s=>s.MapFrom(c=>c.ImageUrl));

            CreateMap<CompanyInfo, CompanyInfoDTO>();

      }

    }
}
为了添加对象映射,DBContext,对象注入等,打开Program文件,按照如下添加
https://img-blog.csdnimg.cn/direct/2f631d1c076c4bbdb45f4e8e55ee2253.png
https://img-blog.csdnimg.cn/direct/3f57747293dc4968801703ef0e432590.png
上图的红色代码如下:
//查询数据库真实数据的业务逻辑层服务注册
builder.Services.AddTransient<ICommodityService, CommodityService>();
builder.Services.AddTransient<ICompanyInfoService, CompanyInfoService>();

//添加DbContext
//builder.Services.AddDbContext<AdvancedCustomerDbContext>();
builder.Services.AddTransient<DbContext, AdvancedCustomerDbContext>();

//支持AutoMapper
builder.Services.AddAutoMapper(options =>
{
      options.AddProfile<AuotoMapConfig>();

}); 选中“Controllers”文件夹新增控制器 ApiController
https://img-blog.csdnimg.cn/direct/e90f621a95674be4bf3d08c89ddf4adc.png
ApiController代码如下:
using AutoMapper;
using Microsoft.AspNetCore.Mvc;
using SimpleWebApi.Business.Service.Interface;
using SimpleWebApi.Migration.Models;
using System.Linq.Expressions;

namespace SimpleWebApi.Controllers
{
   
    /")]
    public class ApiController : ControllerBase
    {
      private readonly ILogger<ApiController> _logger;

      private ICommodityService _comService;
      private ICompanyInfoService _companyService;

      private IMapper _mapper;

      public ApiController(ILogger<ApiController> logger, ICommodityService comService, ICompanyInfoService companyService, IMapper mapper)
      {
            _logger = logger;
            _comService = comService;
            _companyService = companyService;
            _mapper = mapper;
      }

      
      public IEnumerable<CommodityDTO> GetCommodity(int Id)
      {
            Expression<Func<Commodity, bool>> funcWhere = null;
            funcWhere = a => a.Id == Id;
            var commodityList = _comService.Query(funcWhere);
            List<CommodityDTO> list = new List<CommodityDTO>();
            _mapper.Map<IQueryable<Commodity>, List<CommodityDTO>>(commodityList, list);

            return list;

      }

      
      public CompanyInfoDTO GetCompanyInfo(int companyId)
      {
            var company = _companyService.GetCompany(companyId);
            CompanyInfoDTO dto = new CompanyInfoDTO();
            _mapper.Map<CompanyInfo, CompanyInfoDTO>(company, dto);

            return dto;

      }
    }
}
 这里有个小坑,打开SimpleWebApi.csproj,按照下图设置,可以办理题目
<InvariantGlobalization>false</InvariantGlobalization> https://img-blog.csdnimg.cn/direct/15dcadbd6afd4bb096b382ccf191a36e.png
ps:默认该数据值是true,运行步伐后会非常,非常提示如下:
System.Globalization.CultureNotFoundException
HResult=0x80070057
Message=Only the invariant culture is supported in globalization-invariant mode. See https://aka.ms/GlobalizationInvariantMode for more information. (Parameter 'name')
en-us is an invalid culture identifier.
Source=System.Private.CoreLib
StackTrace:
   在 System.Globalization.CultureInfo.GetCultureInfo(String name)
   在 Microsoft.Data.SqlClient.SqlConnection.TryOpen(TaskCompletionSource`1 retry, SqlConnectionOverrides overrides)
   在 Microsoft.Data.SqlClient.SqlConnection.Open(SqlConnectionOverrides overrides)
   在 Microsoft.Data.SqlClient.SqlConnection.Open()
   在 Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerConnection.OpenDbConnection(Boolean errorsExpected)
   在 Microsoft.EntityFrameworkCore.Storage.RelationalConnection.OpenInternal(Boolean errorsExpected)
   在 Microsoft.EntityFrameworkCore.Storage.RelationalConnection.Open(Boolean errorsExpected)
   在 Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReader(RelationalCommandParameterObject parameterObject)
   在 Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable`1.Enumerator.InitializeReader(Enumerator enumerator)
   在 Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable`1.Enumerator.<>c.<MoveNext>b__21_0(DbContext _, Enumerator enumerator)
   在 Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.Execute(TState state, Func`3 operation, Func`3 verifySucceeded)
   在 Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable`1.Enumerator.MoveNext()
   在 System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
   在 System.Linq.Enumerable.ToList(IEnumerable`1 source)
   在 SimpleProjectDemo.Controllers.WeatherForecastController.Get2() 在 E:\Vs_Project\SimpleProjectDemo\SimpleProjectDemo\Controllers\WeatherForecastController.cs 中: 第 50 行
   在 Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.SyncObjectResultExecutor.Execute(ActionContext actionContext, IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
   在 Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<<InvokeActionMethodAsync>g__Logged|12_1>d.MoveNext()
2.7 运行项目

运行项目,如下图所示显示Swagger页面:
https://img-blog.csdnimg.cn/direct/1a1c6dbc2dbc4cd78064eb40f82afa48.png
用接口api/Api/GetCommodity 来测试下
https://img-blog.csdnimg.cn/direct/b1f4315dccb24ecda03b1f9737864adf.png
3.结论

至此,操作完成。成功的搭建了一个简单的.net 8.0的web api项目。
ps:本项目的代码都很简单,朴拙建议跟着指引来敲代码梳理思路,不过思量极端情况,我还是打包代码上传到了csdn。有需要的童鞋可以按需下载。谢谢。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: 手把手搭建基于.NET 8.0的Web API项目