本节我们来看看 ASP.NET Core MVC 中比较常用的功能,对于导入和导出目前仍在探索中,项目需要自定义列合并,所以事先探索了如何在 ASP.NET Core MVC 进行导入、导出,更高级的内容还需等我学习再给出。
在学习 ASP.NET Core MVC 之前我们来看看在 EF Core 中如何更新对象指定属性,这个问题之前我们已经探讨过,但是还是存在一点问题,请往下看。
- public void Update(T entity, params Expressionobject>>[] properties)
- {
- _context.Entry(entity).State = EntityState.Unchanged;
- foreach (var property in properties)
- {
- var propertyName = ExpressionHelper.GetExpressionText(property);
- _context.Entry(entity).Property(propertyName).IsModified = true;
- }
- }
上述方法可以用来更新对象指定属性,使用 lambda 来指定需要更新的属性,如下:
- [HttpGet("[action]")] public IActionResult Index() {
- var blog = new Blog() {
- Id = 1,
- Name = "Jeffcky1"
- };
- _blogRepository.Update(blog, d = >d.Name);
- _blogRepository.SaveChanges();
- }
但是当更新数值类型时会解析不到该属性,如下:
- var blog = new Blog() {
- Id = 1,
- Count = 1
- };
- _blogRepository.Update(blog, d = >d.Count);
- _blogRepository.SaveChanges();
此时得到该属性名称为空字符串
lambda 为何解析不到呢? 原来它进行了 Convert 如下:
既然是将其转换成了 Convert,那么在表达式树中应该用其节点类型,如下:
此时我们需要判断其节点类型是否已经经过 Convert 即可,最终代码如下:
- public void Update(T entity, params Expressionobject>>[] properties)
- {
- EntityEntry entry = _context.Entry(entity);
- entry.State = EntityState.Unchanged;
- foreach (var property in properties)
- {
- string propertyName = "";
- Expression bodyExpression = property.Body;
- if (bodyExpression.NodeType == ExpressionType.Convert && bodyExpression is UnaryExpression)
- {
- Expression operand = ((UnaryExpression)property.Body).Operand;
- propertyName = ((MemberExpression)operand).Member.Name;
- }
- else
- {
- propertyName = ExpressionHelper.GetExpressionText(property);
- }
- entry.Property(propertyName).IsModified = true;
- }
- }
此时将能正确解析到数值类型名称,如下:
为何要封装这一层,只是不想首先查询出对象然后再来进行更新属性,其两步操作合并为一步岂不爽哉。当然我们也可以不通过 lambda 来实现,直接给出属性集合,然后遍历即可,大概如下:
- public void Update(T entity, params object[] properties)
- {
- _context.ChangeTracker.TrackGraph(entity, e =>
- {
- e.Entry.State = EntityState.Unchanged;
- if ((e.Entry.Entity as T) != null)
- {
- foreach (var p in properties)
- {
- _context.Entry(e.Entry.Entity as T).Property(p.ToString()).IsModified = true;
- }
- }
- });
- }
目前比较流行的日志框架则是 Log4net、NLog,之前也一直用的 Log4net,但是在. net core 中已经内置了日志框架 Serilog,在 github 上也已加星不少,想必比较强大,既然这样为何就是使用内置的呢,免去再用其他日志框架还有配置的麻烦。
创建默认项目在 Startup 中已经注入日志,如下:
- loggerFactory.AddConsole(Configuration.GetSection("Logging"));
- loggerFactory.AddDebug();
当利用 dotnet.exe 调试时在控制台会显示调试各种信息,要是直接运行那该如何呢?在 web 中的配置文件 web.config 中,如下:
- "%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" stdoutLogEnabled="true" stdoutLogFile=".\logs\stdout" forwardWindowsAuthToken="false"/>
我们需要设置 stdoutLogEnabled="true" 来启动调试,此时运行项目你会发现屁都没有,此时我们查看 windows 日志发现根本无法创建文件夹,想必是没有权限的缘故。
此时我们需要手动创建 logs 文件夹,最终运行程序则会自动将所有信息写入到日志文件中,如下:
是不是这样就完了呢,这样设置则会将所有信息都会写入日志那样岂不是额外做些无用功,看看如下生成的日志。
光说不练假把式,来,我们来实现一个,让你见识见识 Serilog 的强大。先定义一个自定义输出格式的 Option 并创建输出日志文件夹。
- public class JeffckyLogOptions
- {
- public string LogPath { get; set; } = @"C:\Jeffcky_StudyEFCore\logs";
- public string PathFormat { get; set; }
- public static void EnsurePreConditions(JeffckyLogOptions options)
- {
- if (options == null)
- {
- throw new ArgumentNullException(nameof(options));
- }
- if (string.IsNullOrWhiteSpace(options.LogPath))
- {
- throw new ArgumentException("系统日志文件存储路径未配置", nameof(options.LogPath));
- }
- if (string.IsNullOrWhiteSpace(options.PathFormat))
- {
- throw new ArgumentException("系统日志文件名称未配置", nameof(options.PathFormat));
- }
- if (!Directory.Exists(options.LogPath))
- {
- Directory.CreateDirectory(options.LogPath);
- }
- }
- }
内置实现日志的接口. net core 已经给出,所以我们需要获取内置日志接口服务并实现自定义扩展方法。
- app.ApplicationServices.GetService(typeof(ILoggerFactory));
此时上述 app 来源于 IApplicationBuilder 此时我们则只需要实现该接口的自定义扩展方法。我们开始下载 Serilog 程序包
此时我们需要对日志通过 LoggerConfiguration 类进行初始化配置。
接下来则是将日志写到文件中我们可以自定义格式,此时再下载如下程序包。
里面有个 RollingFile 方法来自定输出日志模板,自此我们就有了如下代码:
- var serilog = new LoggerConfiguration()
- .MinimumLevel.Debug()
- .Enrich.FromLogContext()
- .WriteTo.RollingFile(Path.Combine(options.LogPath, options.PathFormat),
- outputTemplate: "[{Timestamp:HH:mm:ss} {Level}] {Message}{NewLine}{Exception}");
我们获取. net core 内置的日志接口服务。
- ILoggerFactory loggerFactory = (ILoggerFactory)app.ApplicationServices.GetService(typeof(ILoggerFactory));
同时设置我们输出日志的路径选项。
- JeffckyLogOptions.EnsurePreConditions(options);
接下来将 Serilog 创建的日志添加到内置日志服务中,如下:
- loggerFactory.AddSerilog(serilog.CreateLogger());
当然上述 AddSerilog 方法时扩展方法,此时我们仍需要添加如下扩展程序包:
最后还差一步则是日志的生命周期,灰常重要,为什么说灰常重要,我们要确保当程序关闭时或者系统出问题时所有在内存缓冲区的日志都将输送到日志文件夹中,如下:
- // Ensure any buffered events are sent at shutdown
- IApplicationLifetime appLifetime = (IApplicationLifetime)app.ApplicationServices.GetService(typeof(IApplicationLifetime));
- if (appLifetime != null)
- {
- appLifetime.ApplicationStopped.Register(Log.CloseAndFlush);
- }
一气呵成,最终我们在 Startup 的如下方法中进行调用即可:
- public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
- {...}
利用 Serilog 添加自定日志格式输出,进行如下调用:
- app.UseJeffckySelfDefineLog(new JeffckyLogOptions()
- {
- LogPath = Configuration[nameof(JeffckyLogOptions.LogPath)],
- PathFormat = "Jeffcky_StudyEFCore_{Date}.log"
- });
当然还有其他强大功能,比如 Serilog 中可以获取到上下文,这样的话我们就可以过滤对于那个应用程序使用对应的日志输出级别以及其他,如下:
- serilog = serilog.Filter.ByIncludingOnly((e) =>
- {
- var context = e.Properties["SourceContext"].ToString();
- return (context.StartsWith("\"Your Applicion Name") ||
- e.Level == LogEventLevel.Warning ||
- e.Level == LogEventLevel.Error ||
- e.Level == LogEventLevel.Fatal);
- });
说一千到一万,出现结果才是真理,我们一起来看看。
看看日志输出信息,你会看到干净的日志输出。
本节我们详细讲解了. net core 中新生代日志框架 - Serilog,别抱着 Log4net 不放了,Serilog 你值得拥有,跟着老大一直学习中,涨涨见识!因为项目中会用到批量导入和导出,所以在研究导出、导入,下节可能会讲到导入、导出在 ASP.NET Core MVC 中,也有可能会讲 SQL Server 中的最后几节关于死锁的内容,不管怎样都是在积累知识,你说呢,敬请期待!
来源: http://www.cnblogs.com/CreateMyself/p/6438068.html