【愚公系列】2023年03月 .NET_C#知识点-使用控制台手搭webapi框架

举报
愚公搬代码 发表于 2023/03/31 23:03:45 2023/03/31
【摘要】 前言WebAPI是一种协议,用于允许网络应用程序(如浏览器)与网络服务器(如Web服务器)之间进行通信。它可以用于处理数据,访问数据库,处理图像和视频,以及进行其他高级功能。本文涉及的知识量巨大主要有如下:• EFCore• Autofac• Serilog• Swagger• 非常多底层知识一、使用控制台手搭webapi框架1.配置文件appsettings.Development.jso...

前言

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()获取用户的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

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。