.NETCore .NET6中一些常用組件的配置及使用記錄,持續更新中。。。


NET6App

介紹

.NET 6的CoreApp框架,用來學習.NET6的一些變動和新特性,使用EFCore,等一系列組件的運用,每個用單獨的文檔篇章記錄,持續更新文檔哦,有什么想要了解的組件啊可以留言或私信我。

如果對您有幫助,點擊⭐Star⭐關注 ,感謝支持開源!

軟件架構

分為模型層,服務層,接口層來做測試使用

0.如何使用IConfiguration、Environment

直接在builder后的主機中使用。

builder.Configuration;
builder.Environment

1.如何使用Swagger

.NET 6 自帶模板已經默認添加Swagger,直接使用即可。

builder.Services.AddSwaggerGen();

if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

2. 如何添加EFCore到.NET 6中

按照EFCore常規使用方法,申明表的Entity及Dbcontext后,在program.cs文件中添加

builder.Services.AddDbContext<Service.DataContext>(opt => {
    opt.UseSqlServer(builder.Configuration.GetConnectionString("Default"));
});

即可在其他地方注入使用 DataContext

使用Sqlite數據庫,需要引用 Microsoft.EntityFrameworkCore.Sqlite,
並在添加服務時,改為

opt.UseSqlite(builder.Configuration.GetConnectionString("Default"));

包管理控制台數據庫結構生成方法:
使用 add-migration 創建遷移
使用 update-database 更新數據結構

3.如何注入一個服務

builder.Services.AddScoped<UserIdentyService>();

4.如何定義全局的using引用

在根目錄下新建一個 cs文件,比如Globalusing.cs,在里面添加你的全局引用,和常規引用不同的是,在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()// 無接口的注入方式
            .InstancePerDependency();
 });

此時即可構造函數注入使用。

6.如何使用Log4Net

添加引用

Microsoft.Extensions.Logging.Log4Net.AspNetCore

新建配置文件 log4net.config;
添加service配置

//注入Log4Net
builder.Services.AddLogging(cfg =>
{
    //默認的配置文件路徑是在根目錄,且文件名為log4net.config
    //cfg.AddLog4Net();
    //如果文件路徑或名稱有變化,需要重新設置其路徑或名稱
    //比如在項目根目錄下創建一個名為config的文件夾,將log4net.config文件移入其中,並改名為log4net.config
    //則需要使用下面的代碼來進行配置
    cfg.AddLog4Net(new Log4NetProviderOptions()
    {
        Log4NetConfigFileName = "config/log4net.config",
        Watch = true
    });
});

即可在需要的地方定義使用

_logger = LogManager.GetLogger(typeof(UserController));

7.如何使用全局異常過濾器

首先新建 GlobalExceptionFilter 全局異常過濾器,繼承於 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)//如果異常沒有處理
        {
            var result = new ApiResult
            {
                Code = 500,
                IsSuccess = false,
                Message = "服務器發生未處理的異常"
            };

            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中添加全局異常過濾器

builder.Services.AddControllers(option =>
    {
        option.Filters.Add<GlobalExceptionFilter>();
    }
);

添加控制器方法完成測試

[HttpGet("exception")]
public ApiResult ExceptionAction()
{
    throw new NotImplementedException();
}

8.如何使用redis做緩存

使用 StackExchange.Redis 作為緩存組件(其他組件類似的使用方式)。nuget 安裝 StackExchange.Redis.Extensions.Core
首先,先建立一個類 RedisClient ,用於管理redis的連接和操作,再建立一個 RedisClientFactory 類,用於創建 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分布式緩存服務添加到服務中
builder.Services.AddStackExchangeRedisCache(options =>
    {
        //用於連接Redis的配置  Configuration.GetConnectionString("RedisConnectionString")讀取配置信息的串
        options.Configuration = "Redis_6";// Configuration.GetConnectionString("RedisConnectionString");
        //Redis實例名RedisDistributedCache
        options.InstanceName = "RedisDistributedCache";
    });

引用自 "分布式 Redis 緩存"

9. 如何添加使用定時任務組件

此處使用 Hangfire 定時任務組件,輕便,可持久化,還有面板。
引用 Hangfire 后,即可新增定時任務。

//啟用Hangfire服務.
builder.Services.AddHangfire(x => x.UseStorage(new MemoryStorage()));
builder.Services.AddHangfireServer();

...

//啟用Hangfire面板
app.UseHangfireDashboard();
//開啟一個定時任務
RecurringJob.AddOrUpdate("test",() => Console.WriteLine("Recurring!"), Cron.Minutely());

訪問 https://localhost:7219/hangfire 即可看到任務面板

10. 如何使用業務鎖鎖住下單或者支付操作

首先,做這個事需要能先構建出一個鎖出來,這個鎖有個鎖的標識key,可以根據這個key判定key對應的鎖是否存在,
這樣的話,在某個用戶支付或者下單減庫存啥的時候,就可以按照這個key先上鎖,后面有用戶走其他渠道進行同樣的操作的時候,就可以根據是否上鎖了,來判斷操作能否繼續。

比如一個支付訂單的業務,可以在手機上操作,也可以在電腦上操作,這個時候就可以給支付接口上鎖,只要一個支付過程存在着,並且沒有超時,那就不能在其他渠道進行操作。
我們上面已經使用了redis,下面就用redis構建個鎖來模擬這個操作,具體看代碼:

/// <summary>
    /// 測試業務鎖
    /// </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跨域。先增加一個配置 "Cors": "http:127.0.0.1:5001",配置可以跨域的url,也可以使用默認跨域配置。
host配置以下服務,按需使用:

builder.Services.AddCors(delegate (CorsOptions options)
{
    options.AddPolicy("CorsPolicy", delegate (CorsPolicyBuilder corsBuilder)
    {
        //指定url跨域
        corsBuilder.WithOrigins(builder.Configuration.GetValue<string>("Cors").Split(','));
        //默認跨域
        corsBuilder.SetIsOriginAllowed((string _) => true).AllowAnyMethod().AllowAnyHeader()
            .AllowCredentials();
    });
});

12. 如何使用NewtonsoftJson

.NET6 默認的系列化庫是內置的 System.Text.Json,使用中如果有諸多不熟悉的地方,那肯定是想換回 NewtonsoftJson,需要nuget 引用 Microsoft.AspNetCore.Mvc.NewtonsoftJson 來配置使用,
常用配置包括日期格式、大小寫規則、循環引用配置。。。等,下面是一個配置

builder.Services.AddControllers(option =>
    {
        option.Filters.Add<GlobalExceptionFilter>();
    }
).AddNewtonsoftJson(options =>
{
    options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); //序列化時key為駝峰樣式
    options.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.Local;
    options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss";
    options.SerializerSettings.ReferenceLoopHandling =  ReferenceLoopHandling.Ignore;//忽略循環引用
});

13. 如何使用SignalR

首先添加一個 ChatHub 作為 交互中心處理器

public class ChatHub : Hub
    {
        public async Task SendMessage(string user, string message)
        {
            await Clients.All.SendAsync("ReceiveMessage", user, message);
        }
    }

在主機中使用服務

builder.Services.AddSignalR();
...
app.UseEndpoints(endpoints =>
{
    endpoints.MapHub<ChatHub>("/chatHub");
});

14. 如何使用Dapper

Dapper是大家常用的一個數據庫連接擴展組件,下面介紹下,如何使用常規擴展,來在.net Core中使用Dapper。
首先,建立一個DbComponent ,來獲取由 .netCore 提供的 Configuration 配置文件,並用 DbProviderFactories 工廠,創建數據庫連接,此類只管創建連接,又其他使用類進行銷毀。

/// <summary>
/// 創建連接處理
/// </summary>
public class DbComponent
{
    /// 數據庫連接配置
    private static ConnectionStringSettings connectionSetting;

    public static void InitDapper(ConnectionStringSettings connectionStringSettings)
    {
        connectionSetting = connectionStringSettings;
    }

    //通過工廠模式創建Connection連接 此連接已打開
    public static IDbConnection GetConnection()
    {
        get {
            var cnnection = DbProviderFactories.GetFactory(connectionSetting.ProviderName).CreateConnection();
            if (cnnection == null)
                throw new Exception("數據庫鏈接獲取失敗!");
            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"
});

程序啟動后,就可以在需要的地方使用

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. 如何添加自定義配置文件

有時候我們不想把配置全部放在 appsettings.json ,我們想自己建立一個文件夾來存儲其他配置文件,比如config/...json之類的,咋整呢,
我們新建個文件夾 config,下面建立一個配置文件app.json,里面存幾個配置以便驗證。

使用前添加如下代碼即可

builder.Configuration.AddJsonFile("config/app.json");
Console.WriteLine(builder.Configuration.GetValue<string>("weixin"));

16. 如何簡單上傳文件

上傳文件是每個api框架都會實現的功能,我們先實現一個簡單的文件上傳。
首先做個配置文件,存儲上傳的文件存儲位置、大小及格式限制等的配置

public class UploadConfig
{
    /// <summary>
    /// 最大值
    /// </summary>
    public int MaxSize { get; set; } = 1024 * 1024 * 1024;
    /// <summary>
    /// 存儲路徑
    /// </summary>
    public string UploadDir { get; set; } = @"D://Upload";
    /// <summary>
    /// 站點名稱
    /// </summary>
    public string WebSite { get; set; }
}

添加測試action,完成文件上傳,並返回文件訪問路徑

/// <summary>
/// 上傳文件測試
/// </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("沒有需要上傳的文件");
    }
    var dir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, config.UploadDir);
    
    if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);

    //驗證大小或者格式之類
    foreach (var file in files)
    {
        var fileName = ContentDispositionHeaderValue.Parse(file.ContentDisposition).FileName;
        var fileSize = file.Length;
        if (fileSize > config.MaxSize)
        {
            return ApiResult.Fail($"{fileName}文件過大");
        }
    }
    //存儲文件
    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);
}

上述文件訪問路徑需要配置靜態目錄來進行訪問

//啟動www靜態目錄
app.UseStaticFiles();
//啟動上傳文件目錄
app.UseStaticFiles(new StaticFileOptions { 
    FileProvider =  new PhysicalFileProvider(builder.Configuration.GetValue<string>("UploadConfig:UploadDir")),
    RequestPath = "/upload/view"
});

至此,文件上傳及訪問已添加完成

17. 如何添加驗證碼

驗證碼是常用的一個api功能,需要完成2步,先生成驗證碼字符串值及存儲,后需要輸出驗證碼圖片到前端,即驗證碼功能需要2個api,1.獲取驗證碼,2.驗證驗證碼。
先實現一個類,用於獲取隨機值及生成圖片
為了方便部署到unix系統,使用 ImageSharp 相關類庫

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>
   /// 生成驗證碼隨機值
   /// </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>
   /// 生成驗證碼及圖片
   /// </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)
   {
       //驗證碼顏色集合
       Color[] colors = { Color.Black, Color.Red, Color.DarkBlue, Color.Green, Color.Orange, Color.Brown, Color.DarkCyan, Color.Purple };
       //驗證碼字體集合
       string[] fonts = { "Verdana", "Microsoft Sans Serif", "Comic Sans MS", "Arial" };
       var r = new Random();
       
       if(width == 0) { width = captchaCode.Length * 25; }
       //定義圖像的大小,生成圖像的實例
       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);

           // 畫驗證碼
           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)));
           }

           // 畫干擾線
           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);
           }

           // 畫噪點
           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));
   }
}

測試代碼

/// <summary>
/// 測試驗證碼
/// </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. 如何發布到windows

發布到windows比較簡單,一步步選擇發布,只要注意是按照框架依賴還是獨立發布就好了,框架依賴的意思是, 你的服務器已經安裝好了個.NetCore的運行時,你就可以直接框架依賴來發布就好了,如果你的服務器沒有安裝運行時, 或者是需要兼容以前的老的運行時不能更新啥的,你就使用獨立發布。

19. 如何發布到linux

發布到linux其實也比較簡單,首先,需要有待發布的程序文件,和windows一樣,做好框架依賴和獨立發布的安裝包,目標運行時記得選擇linux.64 或者linux-arm,編譯發布,獲得程序文件。
服務器運行時,和windows一樣,框架依賴需要安裝下.NET Core的運行時,注意匹配版本號,運行時安裝好之后,把程序通過xshell 或者 scp壓縮包上傳到linux服務器你想要的目錄下面,完成程序文件部署,在運行時版本沒問題的情況下,dotnet xxx.dll 即可啟動站點,還有一些其他的配置可能需要注意,比如后台運行,比如開放防火牆端口之類的,照着整就可以了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM