前言 WebAPI是一种协议,用于允许网络应用程序(如浏览器)与网络服务器(如Web服务器)之间进行通信。它可以用于处理数据,访问数据库,处理图像和视频,以及进行其他高级功能。
前言
WebAPI是一种协议,用于允许网络应用程序(如浏览器)与网络服务器(如Web服务器)之间进行通信。它可以用于处理数据,访问数据库,处理图像和视频,以及进行其他高级功能。
本文涉及的知识量巨大主要有如下:
- EFCore
- Autofac
- Serilog
- Swagger
- 非常多底层知识
一、使用控制台手搭webapi框架
1.配置文件
appsettings.Development.json
{
"Serilog": {
"MinimumLevel": {
"Default": "Information",
"Override": {
"Default": "Information",
"System": "Information",
"Microsoft": "Information"
}
},
"WriteTo": [
{ "Name": "Console" },
{
"Name": "Async",
"Args": {
"configure": [
{
"Name": "File",
"Args": {
"outputTemplate": "Date:{Timestamp:yyyy-MM-dd HH:mm:ss.fff} LogLevel:{Level} Class:{SourceContext} Message:{Message}{Exception}{NewLine}",
"rollingInterval": "4"
}
}
]
}
}
]
},
"Mysql": {
"ConnectionString": "Server=127.0.0.1;Port=3306;database=testlib;uid=root;pwd=sa12345;",
"Version": "8.0.20",
"MigrationAssembly": "EFCoreEleganceUse.EF.Migrations"
}
}
2.控制台配置
using Autofac;
using Autofac.Extensions.DependencyInjection;
using EFCoreEleganceUse.EF.Mysql;
using Microsoft.AspNetCore.Builder;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Serilog;
internal class Program
{
async static Task Main(string[] args)
{
//设置当前文件夹
Directory.SetCurrentDirectory(AppDomain.CurrentDomain.BaseDirectory);
//注入相关服务启动程序
var host = CreateHostBuilder(args).Build();
host.UseSwagger();
host.UseSwaggerUI();
host.MapControllers();
host.UseAuthentication();
host.UseAuthorization();
await host.RunAsync();
}
public static WebApplicationBuilder CreateHostBuilder(string[] args)
{
//配置参数和环境
var hostBuilder = WebApplication.CreateBuilder(options:new WebApplicationOptions()
{
Args = args,
EnvironmentName = Environments.Development
});
//使用日志和aotufac
hostBuilder.Host.UseSerilog((context, logger) =>//Serilog
{
string date = DateTime.Now.ToString("yyyy-MM-dd");//按时间创建文件夹
logger.ReadFrom.Configuration(context.Configuration);
logger.Enrich.FromLogContext();
logger.WriteTo.File($"Logs/{date}/", rollingInterval: RollingInterval.Hour);//按小时分日志文件
}).UseServiceProviderFactory(new AutofacServiceProviderFactory()).UseEnvironment(Environments.Development);
//生产下需要通过命令行参数或者配置文件设置环境:开发,测试,生产
hostBuilder.Host.ConfigureServices((hostContext, services) =>
{
//注入mysql,生产中应该放置在应用层
var mysqlConfig = hostContext.Configuration.GetSection("Mysql").Get<MysqlOptions>();
var serverVersion = new MariaDbServerVersion(new Version(mysqlConfig.Version));
services.AddDbContext<LibraryDbContext>(options =>
{
options.UseMySql(mysqlConfig.ConnectionString, serverVersion, optionsBuilder =>
{
optionsBuilder.MinBatchSize(4);
optionsBuilder.CommandTimeout(10);
optionsBuilder.MigrationsAssembly(mysqlConfig.MigrationAssembly);
optionsBuilder.UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery);
}).UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);
if (hostContext.HostingEnvironment.IsDevelopment())
{
options.EnableSensitiveDataLogging();
options.EnableDetailedErrors();
}
});
services.AddControllers();
services.AddEndpointsApiExplorer();
services.AddSwaggerGen();
});
//注册模块
hostBuilder.Host.ConfigureContainer<ContainerBuilder>((hcontext, containerBuilder) =>
{
//生产中由应用层聚合各种基础设置等模块,最后交由Host程序注册应用层模块
containerBuilder.RegisterModule<EFCoreEleganceUseEFCoreModule>();
});
return hostBuilder;
}
}
控制台配置最主要的是LibraryDbContext和EFCoreEleganceUseEFCoreModule,下面着重详解
二、EFCore框架DBSet配置详解
1.实体统一配置
EF实体继承统一的接口,方便我们反射获取所有EF实体,接口可以设置一个泛型,来泛化我们的主键类型,因为可能存在不同的表的主键类型也不一样。
接口如下:
public interface IEFEntity<TKey>
{
public TKey Id { get; set; }
}
public abstract class AggregateRoot<TKey> : IEFEntity<TKey>
{
public TKey Id { get; set; }
}
2.实体继承统一接口
public class User : AggregateRoot<string>
{
public string UserName { get; set; }
public DateTime Birthday { get; set; }
public virtual ICollection<Book> Books { get; set; }
}
public class Book : AggregateRoot<long>
{
public string BookName { get; set; }
public string Author { get; set; }
public double Price { get; set; }
public string Publisher { get; set; }
public string SN { get; set; } //图书序列号
public string? UserId { get; set; }
public virtual User? User { get; set; } //租借该书的用户
}
3.获取程序集所有类
public class EFEntityInfo
{
public (Assembly Assembly, IEnumerable<Type> Types) EFEntitiesInfo => (GetType().Assembly, GetEntityTypes(GetType().Assembly));
private IEnumerable<Type> GetEntityTypes(Assembly assembly)
{
//获取当前程序集下所有的实现了IEFEntity的实体类
var efEntities = assembly.GetTypes().Where(m => m.FullName != null
&& Array.Exists(m.GetInterfaces(), t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IEFEntity<>))
&& !m.IsAbstract && !m.IsInterface).ToArray();
return efEntities;
}
}
4.批量注入模型类到EF中
using EFCoreEleganceUse.Domain.Entities;
using Microsoft.EntityFrameworkCore;
namespace EFCoreEleganceUse.EF.Mysql
{
/// <summary>
/// 图书馆数据库上下文
/// </summary>
public class LibraryDbContext : DbContext
{
private readonly EFEntityInfo _efEntitysInfo;
public LibraryDbContext(DbContextOptions options, EFEntityInfo efEntityInfo) : base(options)
{
_efEntitysInfo = efEntityInfo;
ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.HasCharSet("utf8mb4 ");
var (Assembly, Types) = _efEntitysInfo.EFEntitiesInfo;
foreach (var entityType in Types)
{
modelBuilder.Entity(entityType);
}
modelBuilder.ApplyConfigurationsFromAssembly(Assembly);
base.OnModelCreating(modelBuilder);
}
}
}
所有的实体类都被注册到DBContext中作为DBSets,再也不需要一个个写DBSet了,可以用过DbContext.Set<User>()获取用户的DBSet。
三、EFCore框架表配置详解
1.配置基类,
创建一个配置基类,继承自IEntityTypeConfiguration,做一些通用的配置,比如设置主键,软删除等。
public abstract class EntityTypeConfiguration<TEntity, TKey> : IEntityTypeConfiguration<TEntity>
where TEntity : AggregateRoot<TKey>
{
public virtual void Configure(EntityTypeBuilder<TEntity> builder)
{
var entityType = typeof(TEntity);
builder.HasKey(x => x.Id);
if (typeof(ISoftDelete).IsAssignableFrom(entityType))
{
builder.HasQueryFilter(d => EF.Property<bool>(d, "IsDeleted") == false);
}
}
}
2.实体表统一配置
public class BookConfig : EntityTypeConfiguration<Book, long>
{
public override void Configure(EntityTypeBuilder<Book> builder)
{
base.Configure(builder);
builder.Property(x => x.Id).ValueGeneratedOnAdd(); //设置book的id自增
builder.Property(x => x.BookName).HasMaxLength(500).IsRequired();
builder.HasIndex(x => x.Author);//作者添加索引
builder.HasIndex(x => x.SN).IsUnique();//序列号添加唯一索引
builder.HasOne(r => r.User).WithMany(x => x.Books)
.HasForeignKey(r => r.UserId).IsRequired(false);//导航属性,本质就是创建外键,虽然查询很方便,生产中不建议使用!!!
}
}
public class UserConfig : EntityTypeConfiguration<User, string>
{
public override void Configure(EntityTypeBuilder<User> builder)
{
base.Configure(builder);
builder.Property(x => x.UserName).HasMaxLength(50);
//mock一条数据
builder.HasData(new User()
{
Id = "090213204",
UserName = "Bruce",
Birthday = DateTime.Parse("1996-08-24")
});
}
}
3.DBContext中应用配置
using EFCoreEleganceUse.Domain.Entities;
using Microsoft.EntityFrameworkCore;
namespace EFCoreEleganceUse.EF.Mysql
{
/// <summary>
/// 图书馆数据库上下文
/// </summary>
public class LibraryDbContext : DbContext
{
private readonly EFEntityInfo _efEntitysInfo;
public LibraryDbContext(DbContextOptions options, EFEntityInfo efEntityInfo) : base(options)
{
_efEntitysInfo = efEntityInfo;
ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.HasCharSet("utf8mb4 ");
var (Assembly, Types) = _efEntitysInfo.EFEntitiesInfo;
foreach (var entityType in Types)
{
modelBuilder.Entity(entityType);
}
//只需要将配置类所在的程序集给到,它会自动加载
modelBuilder.ApplyConfigurationsFromAssembly(Assembly);
base.OnModelCreating(modelBuilder);
}
}
}
四、仓储配置
1.仓储基类
public interface IAsyncRepository<TEntity, Tkey> where TEntity : class
{
// query
IQueryable<TEntity> All();
IQueryable<TEntity> All(string[] propertiesToInclude);
IQueryable<TEntity> Where(Expression<Func<TEntity, bool>> filter);
IQueryable<TEntity> Where(Expression<Func<TEntity, bool>> filter, string[] propertiesToInclude);
}
2.仓储实现类
public class GenericRepository<TEntity, Tkey> : IAsyncRepository<TEntity, Tkey> where TEntity : class
{
protected readonly LibraryDbContext _dbContext;
public GenericRepository(LibraryDbContext dbContext)
{
_dbContext = dbContext;
}
~GenericRepository()
{
_dbContext?.Dispose();
}
public virtual IQueryable<TEntity> All()
{
return All(null);
}
public virtual IQueryable<TEntity> All(string[] propertiesToInclude)
{
var query = _dbContext.Set<TEntity>().AsNoTracking();
if (propertiesToInclude != null)
{
foreach (var property in propertiesToInclude.Where(p => !string.IsNullOrWhiteSpace(p)))
{
query = query.Include(property);
}
}
return query;
}
public virtual IQueryable<TEntity> Where(Expression<Func<TEntity, bool>> filter)
{
return Where(filter, null);
}
public virtual IQueryable<TEntity> Where(Expression<Func<TEntity, bool>> filter, string[] propertiesToInclude)
{
var query = _dbContext.Set<TEntity>().AsNoTracking();
if (filter != null)
{
query = query.Where(filter);
}
if (propertiesToInclude != null)
{
foreach (var property in propertiesToInclude.Where(p => !string.IsNullOrWhiteSpace(p)))
{
query = query.Include(property);
}
}
return query;
}
}
五、Autofac配置
1.注入DBContext到Repository
public class EFCoreEleganceUseEFCoreModule : Module
{
protected override void Load(ContainerBuilder builder)
{
base.Load(builder);
builder.RegisterModule<EFCoreEleganceUseDomainModule>(); //注入domain模块
builder.RegisterGeneric(typeof(GenericRepository<,>))//将dbcontext注入到仓储的构造中
.UsingConstructor(typeof(LibraryDbContext))
.AsImplementedInterfaces()
.InstancePerDependency();
builder.RegisterType<WorkUnit>().As<IWorkUnit>().InstancePerDependency();
}
}
2.Domain注入EFEntityInfo
public class EFCoreEleganceUseDomainModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<EFEntityInfo>().SingleInstance();
}
}
六、运行
1.数据库迁移
官方文档如下:https://learn.microsoft.com/zh-cn/ef/core/managing-schemas/migrations/managing?tabs=vs
Add-Migration InitialCreate -OutputDir Your\Directory
Update-Database
2.Users控制器
[ApiController]
[Route("[controller]")]
public class UsersController : ControllerBase
{
private readonly ILogger<UsersController> _logger;
//生产中可以在应用层下创建service层,聚合各种实体仓储
private readonly IAsyncRepository<User, string> _userAsyncRepository;
public UsersController(ILogger<UsersController> logger, IAsyncRepository<User, string> userAsyncRepository)
{
_logger = logger;
_userAsyncRepository = userAsyncRepository;
}
[HttpGet]
[AllowAnonymous]
public async Task<ActionResult> Get()
{
return Ok(await _userAsyncRepository.All().ToListAsync());
}
}
相关源码:https://download.csdn.net/download/aa2528877987/87474302