前言
在《EF Core使用Simple Logging输出日志》中,我们介绍了查询标记 TagWith
,它可以帮助我们快速定位到需要的日志:
而在 .NET 6 中,新增了另外一个查询标记 TagWithCallSite
,它可以标记出代码的位置:
var user = await new DefaultDbContext().User.Where(p => p.Name == "My IO").TagWith("Find My IO").TagWithCallSite().FirstOrDefaultAsync();
那它是怎么做到的呢?
原理探究
查看 TagWithCallSite
的源代码:
public static IQueryable<T> TagWithCallSite<T>(this IQueryable<T> source,[NotParameterized] [CallerFilePath] string? filePath = null,[NotParameterized] [CallerLineNumber] int lineNumber = 0)
原来,它使用了 CallerFilePathAttribute
和 CallerLineNumberAttribute
来获取包含调用方的源文件完整路径和调用方法的行号。
原理利用
这让我们想到了,在《.NET 6新特性试用 | ArgumentNullException卫语句》中发现的 CallerArgumentExpressionAttribute
,它可以获取执行的表达式。
通过添加此 Attribute,我们可以创建自己的自定义查询标记。实现代码如下:
public static IQueryable<T> TagWithCallInfo<T>(this IQueryable<T> source,string? tag = null,[NotParameterized][CallerArgumentExpression("source")] string? argument = null,[NotParameterized][CallerMemberName] string? memberName = null,[NotParameterized][CallerFilePath] string? filePath = null,[NotParameterized][CallerLineNumber] int lineNumber = 0)
{var stringBuilder = new StringBuilder();stringBuilder.AppendLine(tag);foreach (var str in argument.Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries)){stringBuilder.AppendLine(str);}stringBuilder.AppendLine($@"at {memberName}");stringBuilder.AppendLine($@"File: {filePath}:{lineNumber}");return source.TagWith(stringBuilder.ToString());
}
该方法不仅包含自定义标记文本,还自动包括了被调用的 LINQ 查询表达式、方法名称、文件路径、行号。
Demo
运行下列代码进行验证,完全满足了我们的要求:
var user = await new DefaultDbContext().User.Where(p => p.Name == "My IO") .TagWithCallInfo("Find My IO").FirstOrDefaultAsync();
结论
今天,通过扩展 TagWithCallSite
,我们实现了自定义查询标记。
添加微信号【MyIO666】,邀你加入技术交流群