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.