介紹
.NET 6的CoreApp框架,用來(lái)學(xué)習(xí).NET6的一些變動(dòng)和新特性,使用EFCore,等一系列組件的運(yùn)用;
軟件架構(gòu)
分為模型層,服務(wù)層,接口層來(lái)做測(cè)試使用
0.如何使用IConfiguration、Environment
直接在builder后的主機(jī)中使用。
builder.Configuration;
builder.Environment
1.如何使用Swagger
.NET 6 自帶模板已經(jīng)默認(rèn)添加Swagger,直接使用即可。
builder.Services.AddSwaggerGen();
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
2. 如何添加EFCore到.NET 6中
按照EFCore常規(guī)使用方法,申明表的Entity及Dbcontext后,在program.cs文件中添加
builder.Services.AddDbContext<Service.DataContext>(opt => {
opt.UseSqlServer(builder.Configuration.GetConnectionString("Default"));
});
即可在其他地方注入使用 DataContext
使用Sqlite數(shù)據(jù)庫(kù),需要引用 Microsoft.EntityFrameworkCore.Sqlite,
并在添加服務(wù)時(shí),改為
opt.UseSqlite(builder.Configuration.GetConnectionString("Default"));
包管理控制臺(tái)數(shù)據(jù)庫(kù)結(jié)構(gòu)生成方法:
使用 add-migration 創(chuàng)建遷移
使用 update-database 更新數(shù)據(jù)結(jié)構(gòu)
3.如何注入一個(gè)服務(wù)
builder.Services.AddScoped<UserIdentyService>();
4.如何定義全局的using引用
在根目錄下新建一個(gè) cs文件,比如Globalusing.cs,在里面添加你的全局引用,和常規(guī)引用不同的是,在using前面添加 global
global using Service;
global using Entity;
global using Entity.Dto;
5.如何使用Autofac
添加 Nuget 引用
Autofac.Extensions.DependencyInjection
program.cs文件添加autofac的使用和注入配置
builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory());
builder.Host.ConfigureContainer<ContainerBuilder>(builder =>
{
Assembly assembly = Assembly.Load("Service.dll");
builder.RegisterAssemblyTypes(assembly)
//.AsImplementedInterfaces()// 無(wú)接口的注入方式
.InstancePerDependency();
});
此時(shí)即可構(gòu)造函數(shù)注入使用。
6.如何使用Log4Net
添加引用
Microsoft.Extensions.Logging.Log4Net.AspNetCore
新建配置文件 log4net.config;
添加service配置
//注入Log4Net
builder.Services.AddLogging(cfg =>
{
//默認(rèn)的配置文件路徑是在根目錄,且文件名為log4net.config
//cfg.AddLog4Net();
//如果文件路徑或名稱有變化,需要重新設(shè)置其路徑或名稱
//比如在項(xiàng)目根目錄下創(chuàng)建一個(gè)名為config的文件夾,將log4net.config文件移入其中,并改名為log4net.config
//則需要使用下面的代碼來(lái)進(jìn)行配置
cfg.AddLog4Net(new Log4NetProviderOptions()
{
Log4NetConfigFileName = "config/log4net.config",
Watch = true
});
});
即可在需要的地方定義使用
_logger = LogManager.GetLogger(typeof(UserController));
7.如何使用全局異常過(guò)濾器
首先新建 GlobalExceptionFilter 全局異常過(guò)濾器,繼承于 ExceptionFilter ,用于接收處理拋出的異常
public class GlobalExceptionFilter : IExceptionFilter
{
readonly IWebHostEnvironment hostEnvironment;
readonly ILog logger;
public GlobalExceptionFilter(IWebHostEnvironment _hostEnvironment)
{
this.hostEnvironment = _hostEnvironment;
this.logger = LogManager.GetLogger(typeof(GlobalExceptionFilter));
}
public void OnException(ExceptionContext context)
{
if (!context.ExceptionHandled)//如果異常沒(méi)有處理
{
var result = new ApiResult
{
Code = 500,
IsSuccess = false,
Message = "服務(wù)器發(fā)生未處理的異常"
};
if (hostEnvironment.IsDevelopment())
{
result.Message += "," + context.Exception.Message;
result.Data = context.Exception.StackTrace;
}
logger.Error(result);
context.Result = new JsonResult(result);
context.ExceptionHandled = true;//異常已處理
}
}
}
然后在Service中添加全局異常過(guò)濾器
builder.Services.AddControllers(option =>
{
option.Filters.Add<GlobalExceptionFilter>();
}
);
添加控制器方法完成測(cè)試
[HttpGet("exception")]
public ApiResult ExceptionAction()
{
throw new NotImplementedException();
}
8.如何使用redis做緩存
使用 StackExchange.Redis 作為緩存組件(其他組件類(lèi)似的使用方式)。nuget 安裝 StackExchange.Redis.Extensions.Core
首先,先建立一個(gè)類(lèi) RedisClient ,用于管理redis的連接和操作,再建立一個(gè) RedisClientFactory 類(lèi),用于創(chuàng)建 redis的連接;
public class RedisClient{...}
public class RedisClientFactory{...}
appsettings.json 中添加redis的配置
"RedisConfig": {
"Redis_Default": {
"Connection": "127.0.0.1:6379",
"InstanceName": "Redis1:"
},
"Redis_6": {
"Connection": "127.0.0.1:6379",
"DefaultDatabase": 6,
"InstanceName": "Redis2:"
}
}
service中添加 redis客戶端的引用
//添加redis的使用
builder.Services.AddSingleton<RedisClient>(_=> RedisClientFactory.GetInstance(builder.Configuration));
一頓操作后,就可以在你想要使用redis的地方引用了
RedisClient redisClient
...
this.redisDb = redisClient.GetDatabase("Redis_Default");
redisDb.StringSet("clientId", "clientId", TimeSpan.FromSeconds(10));
要使用redis做分布式緩存,先引用 Microsoft.Extensions.Caching.StackExchangeRedis
//將Redis分布式緩存服務(wù)添加到服務(wù)中
builder.Services.AddStackExchangeRedisCache(options =>
{
//用于連接Redis的配置 Configuration.GetConnectionString("RedisConnectionString")讀取配置信息的串
options.Configuration = "Redis_6";// Configuration.GetConnectionString("RedisConnectionString");
//Redis實(shí)例名RedisDistributedCache
options.InstanceName = "RedisDistributedCache";
});
引用自?"分布式 Redis 緩存"
9. 如何添加使用定時(shí)任務(wù)組件
此處使用 Hangfire 定時(shí)任務(wù)組件,輕便,可持久化,還有面板。
引用 Hangfire 后,即可新增定時(shí)任務(wù)。
//啟用Hangfire服務(wù).
builder.Services.AddHangfire(x => x.UseStorage(new MemoryStorage()));
builder.Services.AddHangfireServer();
...
//啟用Hangfire面板
app.UseHangfireDashboard();
//開(kāi)啟一個(gè)定時(shí)任務(wù)
RecurringJob.AddOrUpdate("test",() => Console.WriteLine("Recurring!"), Cron.Minutely());
訪問(wèn)?https://localhost:7219/hangfire?即可看到任務(wù)面板
10. 如何使用業(yè)務(wù)鎖鎖住下單或者支付操作
首先,做這個(gè)事需要能先構(gòu)建出一個(gè)鎖出來(lái),這個(gè)鎖有個(gè)鎖的標(biāo)識(shí)key,可以根據(jù)這個(gè)key判定key對(duì)應(yīng)的鎖是否存在,
這樣的話,在某個(gè)用戶支付或者下單減庫(kù)存啥的時(shí)候,就可以按照這個(gè)key先上鎖,后面有用戶走其他渠道進(jìn)行同樣的操作的時(shí)候,就可以根據(jù)是否上鎖了,來(lái)判斷操作能否繼續(xù)。
比如一個(gè)支付訂單的業(yè)務(wù),可以在手機(jī)上操作,也可以在電腦上操作,這個(gè)時(shí)候就可以給支付接口上鎖,只要一個(gè)支付過(guò)程存在著,并且沒(méi)有超時(shí),那就不能在其他渠道進(jìn)行操作。
我們上面已經(jīng)使用了redis,下面就用redis構(gòu)建個(gè)鎖來(lái)模擬這個(gè)操作,具體看代碼:
/// <summary>
/// 測(cè)試業(yè)務(wù)鎖
/// </summary>
/// <returns></returns>
[HttpGet("lockhandle")]
public async Task<ApiResult> LockHandle(int userId)
{
var key = "user";
var token = $"ID:{userId}";
try
{
if (redisDb.LockTake(key, token, TimeSpan.FromSeconds(50)))
{
await Task.Delay(30 * 1000);
return await Task.FromResult(ApiResult.Success($"ID:{userId} 獲取到鎖了,操作正常,connectId:{Request.HttpContext.Connection.Id}"));
}
else
{
return await Task.FromResult(ApiResult.Fail($"有正在操作的鎖,connectId:{Request.HttpContext.Connection.Id}"));
}
}
catch (Exception)
{
throw;
}
finally
{
redisDb.LockRelease(key, token);
}
}
11. 如何配置跨域
此處主要記錄全局跨域,不包括指定api跨域。先增加一個(gè)配置 "Cors": "http:127.0.0.1:5001",配置可以跨域的url,也可以使用默認(rèn)跨域配置。
host配置以下服務(wù),按需使用:
builder.Services.AddCors(delegate (CorsOptions options)
{
options.AddPolicy("CorsPolicy", delegate (CorsPolicyBuilder corsBuilder)
{
//指定url跨域
corsBuilder.WithOrigins(builder.Configuration.GetValue<string>("Cors").Split(','));
//默認(rèn)跨域
corsBuilder.SetIsOriginAllowed((string _) => true).AllowAnyMethod().AllowAnyHeader()
.AllowCredentials();
});
});
12. 如何使用NewtonsoftJson
.NET6 默認(rèn)的系列化庫(kù)是內(nèi)置的 System.Text.Json,使用中如果有諸多不熟悉的地方,那肯定是想換回 NewtonsoftJson,需要nuget 引用 Microsoft.AspNetCore.Mvc.NewtonsoftJson 來(lái)配置使用,
常用配置包括日期格式、大小寫(xiě)規(guī)則、循環(huán)引用配置。。。等,下面是一個(gè)配置
builder.Services.AddControllers(option =>
{
option.Filters.Add<GlobalExceptionFilter>();
}
).AddNewtonsoftJson(options =>
{
options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); //序列化時(shí)key為駝峰樣式
options.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.Local;
options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss";
options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;//忽略循環(huán)引用
});
13. 如何使用SignalR
首先添加一個(gè) ChatHub 作為 交互中心處理器
public class ChatHub : Hub
{
public async Task SendMessage(string user, string message)
{
await Clients.All.SendAsync("ReceiveMessage", user, message);
}
}
在主機(jī)中使用服務(wù)
builder.Services.AddSignalR();
...
app.UseEndpoints(endpoints =>
{
endpoints.MapHub<ChatHub>("/chatHub");
});
14. 如何使用Dapper
Dapper是大家常用的一個(gè)數(shù)據(jù)庫(kù)連接擴(kuò)展組件,下面介紹下,如何使用常規(guī)擴(kuò)展,來(lái)在.net Core中使用Dapper。
首先,建立一個(gè)DbComponent ,來(lái)獲取由 .netCore 提供的 Configuration 配置文件,并用 DbProviderFactories 工廠,創(chuàng)建數(shù)據(jù)庫(kù)連接,此類(lèi)只管創(chuàng)建連接,又其他使用類(lèi)進(jìn)行銷(xiāo)毀。
/// <summary>
/// 創(chuàng)建連接處理
/// </summary>
public class DbComponent
{
/// 數(shù)據(jù)庫(kù)連接配置
private static ConnectionStringSettings connectionSetting;
public static void InitDapper(ConnectionStringSettings connectionStringSettings)
{
connectionSetting = connectionStringSettings;
}
//通過(guò)工廠模式創(chuàng)建Connection連接 此連接已打開(kāi)
public static IDbConnection GetConnection()
{
get {
var cnnection = DbProviderFactories.GetFactory(connectionSetting.ProviderName).CreateConnection();
if (cnnection == null)
throw new Exception("數(shù)據(jù)庫(kù)鏈接獲取失敗!");
cnnection.ConnectionString = connectionSetting.ConnectionString;
cnnection.Open();
return cnnection;
}
}
}
使用前,需要在program中初始化一下組件
/// <summary>
/// 初始化Dapper組件
/// </summary>
DbProviderFactories.RegisterFactory("Microsoft.Data.Sqlite", Microsoft.Data.Sqlite.SqliteFactory.Instance);
DbComponent.InitDapper(new System.Configuration.ConnectionStringSettings
{
ConnectionString = builder.Configuration.GetConnectionString("Default"),
ProviderName = "Microsoft.Data.Sqlite"
});
程序啟動(dòng)后,就可以在需要的地方使用
public class UserIdentyService
{
public ApiResult DapperList()
{
using (var connect = DbComponent.Connection)
{
var users= connect.Query<User>("SELECT * FROM Users").ToList();
return ApiResult.Success(users);
}
}
}
15. 如何添加自定義配置文件
有時(shí)候我們不想把配置全部放在 appsettings.json ,我們想自己建立一個(gè)文件夾來(lái)存儲(chǔ)其他配置文件,比如config/...json之類(lèi)的,咋整呢,
我們新建個(gè)文件夾 config,下面建立一個(gè)配置文件app.json,里面存幾個(gè)配置以便驗(yàn)證。
使用前添加如下代碼即可
builder.Configuration.AddJsonFile("config/app.json");
Console.WriteLine(builder.Configuration.GetValue<string>("weixin"));
16. 如何簡(jiǎn)單上傳文件
上傳文件是每個(gè)api框架都會(huì)實(shí)現(xiàn)的功能,我們先實(shí)現(xiàn)一個(gè)簡(jiǎn)單的文件上傳。
首先做個(gè)配置文件,存儲(chǔ)上傳的文件存儲(chǔ)位置、大小及格式限制等的配置
public class UploadConfig
{
/// <summary>
/// 最大值
/// </summary>
public int MaxSize { get; set; } = 1024 * 1024 * 1024;
/// <summary>
/// 存儲(chǔ)路徑
/// </summary>
public string UploadDir { get; set; } = @"D://Upload";
/// <summary>
/// 站點(diǎn)名稱
/// </summary>
public string WebSite { get; set; }
}
添加測(cè)試action,完成文件上傳,并返回文件訪問(wèn)路徑
/// <summary>
/// 上傳文件測(cè)試
/// </summary>
/// <param name="files"></param>
/// <returns></returns>
[HttpPost("upload")]
public async Task<ApiResult> Upload([FromForm(Name ="file")] List<IFormFile> files)
{
var config = configuration.GetSection("UploadConfig").Get<UploadConfig>();
if (files.Count == 0)
{
return ApiResult.Fail("沒(méi)有需要上傳的文件");
}
var dir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, config.UploadDir);
if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);
//驗(yàn)證大小或者格式之類(lèi)
foreach (var file in files)
{
var fileName = ContentDispositionHeaderValue.Parse(file.ContentDisposition).FileName;
var fileSize = file.Length;
if (fileSize > config.MaxSize)
{
return ApiResult.Fail($"{fileName}文件過(guò)大");
}
}
//存儲(chǔ)文件
var result = new List<string>();
foreach (var file in files)
{
var fileName = file.FileName;
using (var stream = System.IO.File.Create(Path.Combine(dir, fileName)))
{
await file.CopyToAsync(stream);
}
result.Add(string.Join('/',config.WebSite,"upload/view" fileName));
}
return ApiResult.Success(result);
}
上述文件訪問(wèn)路徑需要配置靜態(tài)目錄來(lái)進(jìn)行訪問(wèn)
//啟動(dòng)www靜態(tài)目錄
app.UseStaticFiles();
//啟動(dòng)上傳文件目錄
app.UseStaticFiles(new StaticFileOptions {
FileProvider = new PhysicalFileProvider(builder.Configuration.GetValue<string>("UploadConfig:UploadDir")),
RequestPath = "/upload/view"
});
至此,文件上傳及訪問(wèn)已添加完成
17. 如何添加驗(yàn)證碼
驗(yàn)證碼是常用的一個(gè)api功能,需要完成2步,先生成驗(yàn)證碼字符串值及存儲(chǔ),后需要輸出驗(yàn)證碼圖片到前端,即驗(yàn)證碼功能需要2個(gè)api,1.獲取驗(yàn)證碼,2.驗(yàn)證驗(yàn)證碼。
先實(shí)現(xiàn)一個(gè)類(lèi),用于獲取隨機(jī)值及生成圖片
為了方便部署到unix系統(tǒng),使用 ImageSharp 相關(guān)類(lèi)庫(kù)
public class CaptchaHelper
{
private const string Letters = "1,2,3,4,5,6,7,8,9,A,B,C,D,E,F,G,H,J,K,L,M,N,P,Q,R,S,T,U,V,W,X,Y,Z";
/// <summary>
/// 生成驗(yàn)證碼隨機(jī)值
/// </summary>
/// <param name="codeLength"></param>
/// <returns></returns>
public Task<string> GenerateRandomCaptchaAsync(int codeLength = 4)
{
var array = Letters.Split(new[] { ',' });
var random = new Random();
var temp = -1;
var captcheCode = string.Empty;
for (int i = 0; i < codeLength; i++)
{
if (temp != -1)
random = new Random(i * temp * unchecked((int)DateTime.Now.Ticks));
var index = random.Next(array.Length);
if (temp != -1 && temp == index)
return GenerateRandomCaptchaAsync(codeLength);
temp = index;
captcheCode += array[index];
}
return Task.FromResult(captcheCode);
}
/// <summary>
/// 生成驗(yàn)證碼及圖片
/// </summary>
/// <param name="captchaCode"></param>
/// <param name="width"></param>
/// <param name="height"></param>
/// <returns></returns>
public Task<(string code, MemoryStream ms)> GenerateCaptchaImageAsync(string captchaCode, int width = 0, int height = 30)
{
//驗(yàn)證碼顏色集合
Color[] colors = { Color.Black, Color.Red, Color.DarkBlue, Color.Green, Color.Orange, Color.Brown, Color.DarkCyan, Color.Purple };
//驗(yàn)證碼字體集合
string[] fonts = { "Verdana", "Microsoft Sans Serif", "Comic Sans MS", "Arial" };
var r = new Random();
if(width == 0) { width = captchaCode.Length * 25; }
//定義圖像的大小,生成圖像的實(shí)例
using var image = new Image<Rgba32>(width, height);
// 字體
var font = SystemFonts.CreateFont(SystemFonts.Families.First().Name, 25, FontStyle.Bold);
image.Mutate(ctx =>
{
// 白底背景
ctx.Fill(Color.White);
// 畫(huà)驗(yàn)證碼
for (int i = 0; i < captchaCode.Length; i++)
{
ctx.DrawText(captchaCode[i].ToString()
, font
, colors[r.Next(colors.Length)]
, new PointF(20 * i + 10, r.Next(2, 12)));
}
// 畫(huà)干擾線
for (int i = 0; i < 10; i++)
{
var pen = new Pen(colors[r.Next(colors.Length)], 1);
var p1 = new PointF(r.Next(width), r.Next(height));
var p2 = new PointF(r.Next(width), r.Next(height));
ctx.DrawLines(pen, p1, p2);
}
// 畫(huà)噪點(diǎn)
for (int i = 0; i < 80; i++)
{
var pen = new Pen(colors[r.Next(colors.Length)], 1);
var p1 = new PointF(r.Next(width), r.Next(height));
var p2 = new PointF(p1.X + 1f, p1.Y + 1f);
ctx.DrawLines(pen, p1, p2);
}
});
using var ms = new MemoryStream();
// gif 格式
image.SaveAsGif(ms);
return Task.FromResult((captchaCode, ms));
}
}
測(cè)試代碼
/// <summary>
/// 測(cè)試驗(yàn)證碼
/// </summary>
/// <returns></returns>
[HttpGet("captcha")]
public async Task<IActionResult> GetCaptcha()
{
var captchaHelper = new CaptchaHelper();
var captcha = await captchaHelper.GenerateCaptchaImageAsync();
this.HttpContext.Session.SetString("captcha", captcha.code);
return File(captcha.ms.ToArray(), "image/gif");
}
18. 如何發(fā)布到windows
發(fā)布到windows比較簡(jiǎn)單,一步步選擇發(fā)布,只要注意是按照框架依賴還是獨(dú)立發(fā)布就好了,框架依賴的意思是, 你的服務(wù)器已經(jīng)安裝好了個(gè).NetCore的運(yùn)行時(shí),你就可以直接框架依賴來(lái)發(fā)布就好了,如果你的服務(wù)器沒(méi)有安裝運(yùn)行時(shí), 或者是需要兼容以前的老的運(yùn)行時(shí)不能更新啥的,你就使用獨(dú)立發(fā)布。文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-466529.html
19. 如何發(fā)布到linux
發(fā)布到linux其實(shí)也比較簡(jiǎn)單,首先,需要有待發(fā)布的程序文件,和windows一樣,做好框架依賴和獨(dú)立發(fā)布的安裝包,目標(biāo)運(yùn)行時(shí)記得選擇linux.64 或者linux-arm,編譯發(fā)布,獲得程序文件。
服務(wù)器運(yùn)行時(shí),和windows一樣,框架依賴需要安裝下.NET Core的運(yùn)行時(shí),注意匹配版本號(hào),運(yùn)行時(shí)安裝好之后,把程序通過(guò)xshell 或者 scp壓縮包上傳到linux服務(wù)器你想要的目錄下面,完成程序文件部署,在運(yùn)行時(shí)版本沒(méi)問(wèn)題的情況下,dotnet xxx.dll 即可啟動(dòng)站點(diǎn),還有一些其他的配置可能需要注意,比如后臺(tái)運(yùn)行,比如開(kāi)放防火墻端口之類(lèi)的,照著整就可以了。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-466529.html
到了這里,關(guān)于收集.NET6中一些常用組件的配置的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!