Asp.Net Core实战(干货)

序言使用.NET Core,团队可以更容易专注的在.net core上工作。比如核心类库(如System.Collections)的更改仍然需要与.NET Framework相同的活力,但是ASP.NET Core或Entity Framework Core可以更轻松地进行实质性更改,而不受向后兼容性的限制。.NET Core借鉴了.NET Framework的最佳实践,并将软件工程的最新进展结合在一起。寒暄、扯淡已经完毕,,,下面是我最近时间对.Net Core整理的相关知识,觉得这些在项目中是最基础且最会应用到的,,,,不喜欢扯理论,直接撸码:1、浅谈Startup类2、自定义路由3、跨域设置4、自定义读取配置文件信息5、程序集批量依赖注入6、使用NLog写入文件日志7、使用NLog写入数据库日志8、Nlog标签解读一、浅谈Startup类在ASP.NET Core应用程序中,使用一个按约定Startup命名Startup,在Program.cs中使用WebHostBuilderExtensions UseStartup <TStartup>方法指定,但通常使用系统默认的startup,可以通过startup的构造函数进行依赖注入,startup类中必须包含Configure方法同时可以根据实际情况添加ConfigureServices方法,这两个方法均在应用程序运行时被调用。Startup 类的 执行顺序:构造 -> configureServices ->configureConfigureServices方法:主要用于服务配置,比如依赖注入(DI)的配置,使用时该方法必须在Configure方法之前Configure方法:用于应用程序响应HTTP请求,通过向IApplicationBuilder实例添加中间件组件来配置请求管道二、自定义路由在Startup类的Configure方法配置
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            #region 自定义路由配置
            app.UseMvc(routes =>
            {
                // 自定义路由
                routes.MapRoute(
                  name: "default1",
                  template: "api/{controller}/{action}/{id?}",
                  defaults: new { controller = "Values", action = "Index" });
                // 默认路由
                routes.MapRoute(
                   name: "default",
                   template: "{controller}/{action}/{id?}",
                   defaults: new { controller = "Values", action = "Index" });
            });
            #endregion
        }
View Code三、跨域设置在Startup类的ConfigureServices方法配置
public void ConfigureServices(IServiceCollection services)
        {
            #region 跨域设置
            services.AddCors(options =>
            {
                options.AddPolicy("AppDomain", builder =>
                {
                    builder.AllowAnyOrigin() // Allow access to any source from the host
                    .AllowAnyMethod()        // Ensures that the policy allows any method
                    .AllowAnyHeader()        // Ensures that the policy allows any header
                    .AllowCredentials();     // Specify the processing of cookie
                });
            });
            #endregion

            services.AddMvc();
        }
View Code其中“AppDomain”这个名字是自定义的,大家可以根据自己的喜好定义不同的名字,配置完成之后,在控制器上面添加[EnableCors("AppDomain")]特性即可,如果要实现全局的跨域设置,可以在Configure方法里面配置app.UseCors("AppDomain"),即能实现全局的跨域设置四、自定义读取配置文件信息 这里是写的一个公共方法去读取配置文件appsettings.json
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Configuration.Json;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using System.IO;

public class JsonConfigurationHelper
    {
        public static T GetAppSettings<T>(string key,string path= "appsettings.json") where T : class, new()
        {
            var currentClassDir = Directory.GetCurrentDirectory();
            IConfiguration config = new ConfigurationBuilder()
                .SetBasePath(currentClassDir)
                .Add(new JsonConfigurationSource { Path = path, Optional = false, ReloadOnChange = true })
                .Build();
            var appconfig = new ServiceCollection()
                .AddOptions()
                .Configure<T>(config.GetSection(key))
                .BuildServiceProvider()
                .GetService<IOptions<T>>()
                .Value;
            return appconfig;
        }
    }
View Code
/// <summary>
        /// 读取配置文件
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        public dynamic JsonConfig()
        {
            var jsonStr = JsonConfigurationHelper.GetAppSettings<ConfigDTO>("config");
            return Ok(jsonStr);
        }

        /// <summary>
        /// 实体类
        /// </summary>
        public class ConfigDTO
        {
            public dynamic name { get; set; }
        }
View Code
{
  "config": {
    "name": "Core.Api"
  }
}
View Code截图看效果五、程序集批量依赖注入我们都知道依赖注入主要是为了方便解耦,解除应用程序之间的依赖关系,在我看来DI、IOC这两者差不多是一样的,DI是从应用程序的角度而IOC是从容器的角度,它们主要是对同一件事情的不同角度的描述。然而,,,,,,当我们项目业务比较多的时候,如果要实现多个业务的注入,通常方法是手动一个个的添加注入,这样可能有点太繁琐,所以就想到了利用反射实现批量注入,,,,,,帮助类
public class RuntimeHelper
    {
        /// <summary>
        /// 获取项目程序集,排除所有的系统程序集(Microsoft.***、System.***等)、Nuget下载包
        /// </summary>
        /// <returns></returns>
        public static IList<Assembly> GetAllAssemblies()
        {
            var list = new List<Assembly>();
            var deps = DependencyContext.Default;
            var libs = deps.CompileLibraries.Where(lib => !lib.Serviceable && lib.Type != "package");//排除所有的系统程序集、Nuget下载包
            foreach (var lib in libs)
            {
                try
                {
                    var assembly = AssemblyLoadContext.Default.LoadFromAssemblyName(new AssemblyName(lib.Name));
                    list.Add(assembly);
                }
                catch (Exception)
                {
                    // ignored
                }
            }
            return list;
        }

        public static Assembly GetAssembly(string assemblyName)
        {
            return GetAllAssemblies().FirstOrDefault(assembly => assembly.FullName.Contains(assemblyName));
        }

        public static IList<Type> GetAllTypes()
        {
            var list = new List<Type>();
            foreach (var assembly in GetAllAssemblies())
            {
                var typeInfos = assembly.DefinedTypes;
                foreach (var typeInfo in typeInfos)
                {
                    list.Add(typeInfo.AsType());
                }
            }
            return list;
        }

        public static IList<Type> GetTypesByAssembly(string assemblyName)
        {
            var list = new List<Type>();
            var assembly = AssemblyLoadContext.Default.LoadFromAssemblyName(new AssemblyName(assemblyName));
            var typeInfos = assembly.DefinedTypes;
            foreach (var typeInfo in typeInfos)
            {
                list.Add(typeInfo.AsType());
            }
            return list;
        }

        public static Type GetImplementType(string typeName, Type baseInterfaceType)
        {
            return GetAllTypes().FirstOrDefault(t =>
            {
                if (t.Name == typeName &&
                    t.GetTypeInfo().GetInterfaces().Any(b => b.Name == baseInterfaceType.Name))
                {
                    var typeInfo = t.GetTypeInfo();
                    return typeInfo.IsClass && !typeInfo.IsAbstract && !typeInfo.IsGenericType;
                }
                return false;
            });
        }
    }
View Code
public static class ServiceExtension
    {
        /// <summary>
        /// 用DI批量注入接口程序集中对应的实现类。
        /// </summary>
        /// <param name="service"></param>
        /// <param name="interfaceAssemblyName"></param>
        /// <returns></returns>
        public static IServiceCollection RegisterAssembly(this IServiceCollection service, string interfaceAssemblyName)
        {
            if (service == null)
                throw new ArgumentNullException(nameof(service));
            if (string.IsNullOrEmpty(interfaceAssemblyName))
                throw new ArgumentNullException(nameof(interfaceAssemblyName));

            var assembly = RuntimeHelper.GetAssembly(interfaceAssemblyName);
            if (assembly == null)
            {
                throw new DllNotFoundException($"the dll "{interfaceAssemblyName}" not be found");
            }

            //过滤掉非接口及泛型接口
            var types = assembly.GetTypes().Where(t => t.GetTypeInfo().IsInterface && !t.GetTypeInfo().IsGenericType);

            foreach (var type in types)
            {
                var implementTypeName = type.Name.Substring(1);
                var implementType = RuntimeHelper.GetImplementType(implementTypeName, type);
                if (implementType != null)
                    service.AddSingleton(type, implementType);
            }
            return service;
        }

        /// <summary>
        /// 用DI批量注入接口程序集中对应的实现类。
        /// </summary>
        /// <param name="service"></param>
        /// <param name="interfaceAssemblyName">接口程序集的名称(不包含文件扩展名)</param>
        /// <param name="implementAssemblyName">实现程序集的名称(不包含文件扩展名)</param>
        /// <returns></returns>
        public static IServiceCollection RegisterAssembly(this IServiceCollection service, string interfaceAssemblyName, string implementAssemblyName)
        {
            if (service == null)
                throw new ArgumentNullException(nameof(service));
            if (string.IsNullOrEmpty(interfaceAssemblyName))
                throw new ArgumentNullException(nameof(interfaceAssemblyName));
            if (string.IsNullOrEmpty(implementAssemblyName))
                throw new ArgumentNullException(nameof(implementAssemblyName));

            var interfaceAssembly = RuntimeHelper.GetAssembly(interfaceAssemblyName);
            if (interfaceAssembly == null)
            {
                throw new DllNotFoundException($"the dll "{interfaceAssemblyName}" not be found");
            }

            var implementAssembly = RuntimeHelper.GetAssembly(implementAssemblyName);
            if (implementAssembly == null)
            {
                throw new DllNotFoundException($"the dll "{implementAssemblyName}" not be found");
            }

            //过滤掉非接口及泛型接口
            var types = interfaceAssembly.GetTypes().Where(t => t.GetTypeInfo().IsInterface && !t.GetTypeInfo().IsGenericType);

            foreach (var type in types)
            {
                //过滤掉抽象类、泛型类以及非class
                var implementType = implementAssembly.DefinedTypes
                    .FirstOrDefault(t => t.IsClass && !t.IsAbstract && !t.IsGenericType &&
                                         t.GetInterfaces().Any(b => b.Name == type.Name));
                if (implementType != null)
                {
                    service.AddSingleton(type, implementType.AsType());
                }
            }

            return service;
        }
    }
View Code 在Startupl类的ConfigureServices方法中添加
// This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            #region 程序集批量依赖注入
            services.RegisterAssembly("Core.BLL");
            #endregion

            services.AddMvc();
        }
View Code调用(Ps:Core.BLL这个类库里面分别有一个接口IAccountService和一个类AccountService,AccountService类去继承接口IAccountService并实现接口里面的方法)
public interface IAccountService
    {
        int GetLst();
    }

public class AccountService: IAccountService
    {
        public int GetLst()
        {
            return 1;
        }
    }
View Code
public class ValuesController : Controller
    {
        private readonly IAccountService _accountService;
        public ValuesController(IAccountService accountService)
        {
            _accountService = accountService;
        }

        [HttpGet]
        public dynamic GetAccount()
        {
            var result = this._accountService.GetLst();
            return Ok();
        }
}
View Code 六、使用NLog写入文件日志新建配置文件命名为Nlog.configView Code 在Startup类Configure方法中添加配置
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            #region NLog配置
            loggerFactory.AddNLog(); // 添加NLog
            loggerFactory.ConfigureNLog($"{Directory.GetCurrentDirectory()}\Nlog.config"); // 添加Nlog.config配置文件
            loggerFactory.AddDebug();
            #endregion
        }
View Code写入日志到文件
public class ValuesController : Controller
    {
        private readonly Logger _logger;
       
        public ValuesController()
        {
            _logger = LogManager.GetLogger("FileLogger");
        }

        /// <summary>
        /// 写入文件日志
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        public dynamic WriteLogToFile()
        {
            _logger.Info("写入Info文件");
            _logger.Debug("写入Debug文件");
            _logger.Error("写入Error文件");
            return Ok();
        }
}
View Code七、使用NLog写入数据库日志添加依赖项:Microsoft.Extensions.LoggingNLog.Extensions.Logging新建配置文件命名为Nlog.configView Code同第六项代码一样,也是在Configure方法设置,写入日志到数据库
/// <summary>
        /// 将日志写入数据库
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        public dynamic WriteLogToDb()
        {
            Logger _dblogger = LogManager.GetLogger("DbLogger");
            LogEventInfo ei = new LogEventInfo();
            ei.Properties["Desc"] = "我是自定义消息";
            _dblogger.Info(ei);
            _dblogger.Debug(ei);
            _dblogger.Trace(ei);
            return Ok();
        }
View Code
USE [MyDb]
GO

/****** Object:  Table [dbo].[NLog_Log]    Script Date: 08/09/2018 17:13:20 ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE [dbo].[NLog_Log](
    [ID] [int] IDENTITY(1,1) NOT NULL,
    [Origin] [nvarchar](500) NULL,
    [LogLevel] [nvarchar](500) NULL,
    [Message] [nvarchar](500) NULL,
    [Desc] [nvarchar](500) NULL,
    [Exception] [nvarchar](500) NULL,
    [StackTrace] [nvarchar](500) NULL,
    [CreateOn] [datetime] NULL
) ON [PRIMARY]

GO
View Code八、Nlog标签解读NLog的使用方式基本上和其它的Log库差不多,用于输出日志的级别包括:Trace,Debug,Info,Warn,Error,Fatal标签autoReload 修改配置文件后是否允许自动加载无须重启程序throwExceptions 内部日志系统抛出异常internalLogLevel 可选Trace|Debug|Info|Warn|Error|Fatal决定内部日志的级别 Off 关闭internalLogFile 把内部的调试和异常信息都写入指定文件里建议throwExceptions的值设为“false”,这样由于日志引发的问题不至于导致应用程序的崩溃。标签区域定义了日志的目标或者说输出 ,,在这里可以按需设置文件名称和格式,输出方式。name:自定义该target的名字,可供rule规则里使用type: 定义类型,官方提供的可选类型有:Chainsaw|ColoredConsole |Console |Database|Debug|Debugger|EventLog|File|LogReceiverService|Mail|Memory|MethodCall|Network |NLogViewer|Null |OutputDebugString|PerfCounter|Trace|WebService不过常用的还是 File Database Colored Console Maillayouts 用来规定布局样式,语法“${属性}”,可以把上下文信息插入到日志中,更多布局渲染器可参考https://github.com/nlog/NLog/wiki/Layout%20Renderers标签各种规则配置在logger里name - 记录者的名字minlevel - 最低级别maxlevel - 最高级别level - 单一日志级别levels - 一系列日志级别,由逗号分隔。writeTo - 规则匹配时日志应该被写入的一系列目标,由逗号分隔。   

 目前只整理了这些,后续会持续更新到这里面,如有不合理的地方,请大家加以斧正,,,希望能和大家共同学习、共同进步,,

 

 

权责申明

 

相关内容推荐