The configuration and usage records of some common components in. NET6 are continuously updated...

NET6App

introduce

. NET 6 CoreApp framework for learning Some changes and new features of NET6, the use of EFCore and a series of components, each with a separate document chapter record, constantly update the document Oh, what components you want to know, you can leave a message or send a private letter to me.

Software architecture

It is divided into model layer, service layer and interface layer for testing

0. How to use IConfiguration and Environment

It is directly used in the host after the builder.

builder.Configuration;
builder.Environment

1. How to use Swagger

Swagger has been added to the built-in template of. NET 6 by default. You can use it directly.

builder.Services.AddSwaggerGen();

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

2. How to add EFCore to NET 6

According to the normal use method of EFCore, after declaring the Entity and Dbcontext of the table, in the program Add to CS file

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

You can inject and use DataContext elsewhere

To use SQLite database, you need to reference Microsoft EntityFrameworkCore. Sqlite,
And when adding services, change to

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

Package management console database structure generation method:
Create a migration using Add migration
Update data structure using update database

3. How to inject a service

builder.Services.AddScoped<UserIdentyService>();

4. How to define a global using reference

Create a new cs file in the root directory, such as globalusing cs, add your global reference in it. Different from the regular reference, add global before using

global using Service;
global using Entity;
global using Entity.Dto;

5. How to use Autofac

Add Nuget reference

Autofac.Extensions.DependencyInjection

program.cs file to add the usage and injection configuration of autofac

builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory());
builder.Host.ConfigureContainer<ContainerBuilder>(builder =>
 {
     Assembly assembly = Assembly.Load("Service.dll");
     builder.RegisterAssemblyTypes(assembly)
            //. AsImplementedInterfaces() / / no interface injection method
            .InstancePerDependency();
 });

Constructor injection is now available.

6. How to use Log4Net

Add reference

Microsoft.Extensions.Logging.Log4Net.AspNetCore

Create a new configuration file log4net config;
Add service configuration

//Inject Log4Net
builder.Services.AddLogging(cfg =>
{
    //The default configuration file path is in the root directory and the file name is log4net config
    //cfg.AddLog4Net();
    //If the file path or name changes, you need to reset its path or name
    //For example, create a folder named config in the root directory of the project, and set log4net The config file is moved into it and renamed log4net config
    //You need to use the following code for configuration
    cfg.AddLog4Net(new Log4NetProviderOptions()
    {
        Log4NetConfigFileName = "config/log4net.config",
        Watch = true
    });
});

It can be defined and used where needed

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

7. How to use global exception filter

First, create a GlobalExceptionFilter global exception filter, which inherits from ExceptionFilter and is used to receive and handle the thrown exceptions

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)//If the exception is not handled
        {
            var result = new ApiResult
            {
                Code = 500,
                IsSuccess = false,
                Message = "An unhandled exception occurred on the server"
            };

            if (hostEnvironment.IsDevelopment())
            {
                result.Message += "," + context.Exception.Message;
                result.Data = context.Exception.StackTrace;
            }

            logger.Error(result);

            context.Result = new JsonResult(result);
            context.ExceptionHandled = true;//Exception handled
        }
    }
}

Then add a global exception filter in the Service

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

Add controller method to complete the test

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

8. How to use redis for caching

Using stackexchange Redis is used as a caching component (similar to other components). nuget install stackexchange.com Redis. Extensions. Core
First, establish a RedisClient class to manage redis connections and operations, and then establish a RedisClientFactory class to create redis connections;

public class RedisClient{...}
public class RedisClientFactory{...}

appsettings. Add redis configuration in JSON

"RedisConfig": {
    "Redis_Default": {
      "Connection": "127.0.0.1:6379",
      "InstanceName": "Redis1:"
    },
    "Redis_6": {
      "Connection": "127.0.0.1:6379",
      "DefaultDatabase": 6,
      "InstanceName": "Redis2:"
    }
  }

Add a reference to the redis client in the service

//Add redis usage
builder.Services.AddSingleton<RedisClient>(_=> RedisClientFactory.GetInstance(builder.Configuration));

After one operation, you can reference it where you want to use redis

RedisClient redisClient
...
this.redisDb = redisClient.GetDatabase("Redis_Default");
redisDb.StringSet("clientId", "clientId", TimeSpan.FromSeconds(10));

To use redis for distributed caching, first reference Microsoft Extensions. Caching. StackExchangeRedis

//Add Redis distributed cache service to the service
builder.Services.AddStackExchangeRedisCache(options =>
    {
        //Configuration for connecting to Redis Getconnectionstring ("redisconnectionstring") string to read configuration information
        options.Configuration = "Redis_6";// Configuration.GetConnectionString("RedisConnectionString");
        //Redis instance name RedisDistributedCache
        options.InstanceName = "RedisDistributedCache";
    });

Quoted from "Distributed Redis cache"

9. How to add and use scheduled task components

The Hangfire timed task component is used here, which is lightweight, persistent, and has a panel.
After referencing Hangfire, you can add a scheduled task.

//Enable Hangfire service
builder.Services.AddHangfire(x => x.UseStorage(new MemoryStorage()));
builder.Services.AddHangfireServer();

...

//Enable Hangfire panel
app.UseHangfireDashboard();
//Start a scheduled task
RecurringJob.AddOrUpdate("test",() => Console.WriteLine("Recurring!"), Cron.Minutely());

Visit https://localhost:7219/hangfire You can see the task panel

10. How to use a business lock to lock an order or payment operation

First, to do this, you need to build a lock. The lock has a lock ID key. You can judge whether the lock corresponding to the key exists according to the key,
In this way, when a user pays or places an order to reduce inventory, it can be locked first according to this key. Later, when a user uses other channels for the same operation, it can judge whether the operation can continue according to whether it is locked or not.

For example, a payment order business can be operated on a mobile phone or a computer. At this time, the payment interface can be locked. As long as a payment process exists and there is no timeout, it cannot be operated in other channels.
We have used redis above. Now we will use redis to build a lock to simulate this operation. See the code for details:

/// <summary>
    ///Test service lock
    /// </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} The lock has been obtained and the operation is normal,connectId:{Request.HttpContext.Connection.Id}"));
            }
            else
            {
                return await Task.FromResult(ApiResult.Fail($"There are locks in operation,connectId:{Request.HttpContext.Connection.Id}"));
            }
        }
        catch (Exception)
        {
            throw;
        }
        finally
        {
            redisDb.LockRelease(key, token);
        }
    }

11. How to configure cross domain

The global cross domain is mainly recorded here, excluding the specified api cross domain. First, add a configuration "Cors": "http:127.0.0.1:5001". You can configure cross domain URLs or use the default cross domain configuration.
host configure the following services to use as needed:

builder.Services.AddCors(delegate (CorsOptions options)
{
    options.AddPolicy("CorsPolicy", delegate (CorsPolicyBuilder corsBuilder)
    {
        //Specify url cross domain
        corsBuilder.WithOrigins(builder.Configuration.GetValue<string>("Cors").Split(','));
        //Default cross domain
        corsBuilder.SetIsOriginAllowed((string _) => true).AllowAnyMethod().AllowAnyHeader()
            .AllowCredentials();
    });
});

12. How to use newtonsoftjason

The default serialization library for. Net 6 is the built-in system Text. Jason, if there are many unfamiliar places in use, it must be to switch back to newtonsoftjason. nuget needs to refer to Microsoft AspNetCore. Mvc. Newtonsoftjason to configure and use,
Common configurations include date format, capitalization rules, circular reference configuration... Wait, here is a configuration

builder.Services.AddControllers(option =>
    {
        option.Filters.Add<GlobalExceptionFilter>();
    }
).AddNewtonsoftJson(options =>
{
    options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); //When serializing, the key is hump style
    options.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.Local;
    options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss";
    options.SerializerSettings.ReferenceLoopHandling =  ReferenceLoopHandling.Ignore;//Ignore circular references
});

13. How to use SignalR

First, add a ChatHub as the interaction center processor

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

Using services in hosts

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

14. How to use Dapper

Dapper is a commonly used database connection extension component. The following describes how to use conventional extensions to net Core.
First, create a DbComponent to get the netCore provides the Configuration configuration file and uses the DbProviderFactories factory to create database connections. This class only creates connections and uses other classes to destroy them.

/// <summary>
///Create connection processing
/// </summary>
public class DbComponent
{
    ///Database connection configuration
    private static ConnectionStringSettings connectionSetting;

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

    //Create a Connection through factory mode. The Connection is open
    public static IDbConnection GetConnection()
    {
        get {
            var cnnection = DbProviderFactories.GetFactory(connectionSetting.ProviderName).CreateConnection();
            if (cnnection == null)
                throw new Exception("Database link acquisition failed!");
            cnnection.ConnectionString = connectionSetting.ConnectionString;
            cnnection.Open();
            return cnnection;
        }
    }
}

Before use, you need to initialize the components in the program

/// <summary>
///Initialize Dapper component
/// </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"
});

After the program starts, it can be used where needed

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. How to add a custom profile

Sometimes we don't want to put all the configuration in Appsettings JSON, we want to create a folder to store other configuration files, such as config / JSON or something, how to fix it,
Let's create a new folder config, and then create a configuration file app JSON, which stores several configurations for verification.

Add the following code before use

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

16. How to simply upload files

Uploading files is a function that every api framework will implement. Let's implement a simple file upload first.
First, make a configuration file to store the configuration of the uploaded file storage location, size and format restrictions

public class UploadConfig
{
    /// <summary>
    ///Maximum
    /// </summary>
    public int MaxSize { get; set; } = 1024 * 1024 * 1024;
    /// <summary>
    ///Storage path
    /// </summary>
    public string UploadDir { get; set; } = @"D://Upload";
    /// <summary>
    ///Site name
    /// </summary>
    public string WebSite { get; set; }
}

Add a test action, complete the file upload, and return the file access path

/// <summary>
///Upload file test
/// </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("There are no files to upload");
    }
    var dir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, config.UploadDir);
    
    if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);

    //Verify size or format
    foreach (var file in files)
    {
        var fileName = ContentDispositionHeaderValue.Parse(file.ContentDisposition).FileName;
        var fileSize = file.Length;
        if (fileSize > config.MaxSize)
        {
            return ApiResult.Fail($"{fileName}The file is too large");
        }
    }
    //Storage file
    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);
}

The above file access path needs to be configured with a static directory for access

//Start www static directory
app.UseStaticFiles();
//Start uploading file directory
app.UseStaticFiles(new StaticFileOptions { 
    FileProvider =  new PhysicalFileProvider(builder.Configuration.GetValue<string>("UploadConfig:UploadDir")),
    RequestPath = "/upload/view"
});

So far, file upload and access have been added

17. How to add verification code

The verification code is a commonly used api function, which needs to complete two steps. After the verification code string value is generated and stored, the verification code image needs to be output to the front end, that is, the verification code function needs two APIs, 1 Get verification code, 2 Verification code.
First implement a class for obtaining random values and generating pictures
To facilitate deployment to unix systems, ImageSharp related class libraries are used

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>
   ///Generate random value of verification code
   /// </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>
   ///Generate verification code and picture
   /// </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)
   {
       //Verification code color set
       Color[] colors = { Color.Black, Color.Red, Color.DarkBlue, Color.Green, Color.Orange, Color.Brown, Color.DarkCyan, Color.Purple };
       //Verification code font collection
       string[] fonts = { "Verdana", "Microsoft Sans Serif", "Comic Sans MS", "Arial" };
       var r = new Random();
       
       if(width == 0) { width = captchaCode.Length * 25; }
       //Define the size of the image and generate an instance of the image
       using var image = new Image<Rgba32>(width, height);

       // typeface
       var font = SystemFonts.CreateFont(SystemFonts.Families.First().Name, 25, FontStyle.Bold);

       image.Mutate(ctx =>
       {
           // White background
           ctx.Fill(Color.White);

           // Draw verification code
           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)));
           }

           // Draw interference line
           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);
           }

           // Draw noise
           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 format
       image.SaveAsGif(ms);
       return Task.FromResult((captchaCode, ms));
   }
}

Test code

/// <summary>
///Test verification code
/// </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. How to publish to windows

Publishing to windows is relatively simple. Choose to publish step by step. Just pay attention to whether to publish according to framework dependency or independent. Framework dependency means that your server has been installed NetCore runtime, you can directly rely on the framework to publish. If your server does not have the runtime installed, or you need to be compatible with the old runtime and can't update anything, you can use independent publishing.

19. How to publish to linux

In fact, publishing to linux is relatively simple. First, you need to release the program files. Like windows, do a good job in the framework dependent and independent release installation package. Remember to select linux when the target runs 64 or linux arm, compile and release to obtain program files.
When the server is running, like windows, the framework dependency needs to be installed NET Core runtime, pay attention to matching the version number. After the runtime is installed, upload the program to the directory you want on the linux server through xshell or scp compressed package to complete the program file deployment. If the runtime version is OK, dotnet XXX DLL can start the site. There are other configurations that may need attention, such as background operation, such as opening firewall ports, and so on.

 

See the original text: https://www.cnblogs.com/Start201505/p/15713345.html

Added by xerodefect on Wed, 19 Jan 2022 04:37:16 +0200