MVC 中dapper的日志功能+程序报错修改
由于之前的项目说最好要有日志功能,正好之前看过几篇这方面的文章就弄了点东西。
这是EF日志受启发很大的一个原文:
http://www.cnblogs.com/GuZhenYin/p/5556732.html
下面说开发经历~
由于之前有一个开发了一半的.net core的项目M,这个项目的框架都是由一个大牛来搭起来的。期中有几个比较好的功能,一个是报错拦截和日志记录功能。但是现在开发的项目C是没有上面的两个功能的,然后项目C的前辈说最好C也能实现这几个功能,正好我又看了上面的那个文章,就想着来自己搞一下~。
(下面说的内容如果没有看过上面EF日志的文章可能会看不懂)
最初:
一开始我想先把项目的SQL日志给完成了,但是项目C使用的ORM是dapper,之前文章里面用的是EF(这里不得不吐槽一下,一开始项目C都是没有ORM的,就有一个SqlHelper简单封装了一下,其实几乎就是ado.net了),我“天真”的以为直接下一个DatabaseLogger然后在Global.asax里面注入一下就可以了,但是注入好之后发现根本没有起任何作用。最后在一个Chloe.ORM的作者的提醒下发现了这个错误原因:

这个注入的方法是EF空间下的,明显就是EF的功能,对dapper当然没有用了。然后查了一下发现dapper原生的是没有这个功能的,只能自己写~(囧),然后我就对照着Chloe.ORM的写法,自己在dapper的源码里面修改了几个方法(只要修改sqlmapper.cs里面的代码就可以了)
private static int ExecuteImpl(this IDbConnection cnn, ref CommandDefinition command)
{
object param = command.Parameters;
IEnumerable multiExec = GetMultiExec(param);
Identity identity;
CacheInfo info = null;
IDbCommandInterceptor[] log = DbInterception.GetInterceptors();
IDbCommand cmd = null;
DbCommandInterceptionContext<IDataReader> dbCommandInterceptionContext = new DbCommandInterceptionContext<IDataReader>();
if (multiExec != null)
{
#if ASYNC
if((command.Flags & CommandFlags.Pipelined) != )
{
// this includes all the code for concurrent/overlapped query
return ExecuteMultiImplAsync(cnn, command, multiExec).Result;
}
#endif
bool isFirst = true;
int total = ;
bool wasClosed = cnn.State == ConnectionState.Closed;
try
{
if (wasClosed) cnn.Open();
using (cmd = command.SetupCommand(cnn, null))
{
foreach (var l in log)
{
l.ReaderExecuting(cmd, dbCommandInterceptionContext);
}
string masterSql = null;
foreach (var obj in multiExec)
{
if (isFirst)
{
masterSql = cmd.CommandText;
isFirst = false;
identity = new Identity(command.CommandText, cmd.CommandType, cnn, null, obj.GetType(), null);
info = GetCacheInfo(identity, obj, command.AddToCache);
}
else
{
cmd.CommandText = masterSql; // because we do magic replaces on "in" etc
cmd.Parameters.Clear(); // current code is Add-tastic
}
info.ParamReader(cmd, obj);
total += cmd.ExecuteNonQuery();
}
}
command.OnCompleted();
}
catch (Exception e)
{
dbCommandInterceptionContext.Exception = e;
foreach (var l in log)
{
l.ReaderExecuted(cmd, dbCommandInterceptionContext);
} }
finally
{
foreach (var l in log)
{
l.ReaderExecuted(cmd, dbCommandInterceptionContext);
}
if (wasClosed) cnn.Close();
}
return total;
} // nice and simple
if (param != null)
{
identity = new Identity(command.CommandText, command.CommandType, cnn, null, param.GetType(), null);
info = GetCacheInfo(identity, param, command.AddToCache);
}
return ExecuteCommand(cnn, ref command, param == null ? null : info.ParamReader);
}
private static IEnumerable<T> QueryImpl<T>(this IDbConnection cnn, CommandDefinition command, Type effectiveType)
{
object param = command.Parameters;
var identity = new Identity(command.CommandText, command.CommandType, cnn, effectiveType, param?.GetType(), null);
var info = GetCacheInfo(identity, param, command.AddToCache);
List<T> temp = new List<T>();
IDbCommand cmd = null;
IDataReader reader = null;
IDbCommandInterceptor[] log = DbInterception.GetInterceptors();
bool wasClosed = cnn.State == ConnectionState.Closed;
DbCommandInterceptionContext<IDataReader> dbCommandInterceptionContext = new DbCommandInterceptionContext<IDataReader>();
try
{ cmd = command.SetupCommand(cnn, info.ParamReader);
foreach (var l in log)
l.ReaderExecuting(cmd, dbCommandInterceptionContext);
if (wasClosed) cnn.Open();
reader = ExecuteReaderWithFlagsFallback(cmd, wasClosed, CommandBehavior.SequentialAccess | CommandBehavior.SingleResult);
wasClosed = false; // *if* the connection was closed and we got this far, then we now have a reader
// with the CloseConnection flag, so the reader will deal with the connection; we
// still need something in the "finally" to ensure that broken SQL still results
// in the connection closing itself
var tuple = info.Deserializer;
int hash = GetColumnHash(reader);
if (tuple.Func == null || tuple.Hash != hash)
{
if (reader.FieldCount == ) //https://code.google.com/p/dapper-dot-net/issues/detail?id=57
return null;
tuple = info.Deserializer = new DeserializerState(hash, GetDeserializer(effectiveType, reader, , -, false));
if (command.AddToCache) SetQueryCache(identity, info);
} var func = tuple.Func;
var convertToType = Nullable.GetUnderlyingType(effectiveType) ?? effectiveType;
while (reader.Read())
{
object val = func(reader);
if (val == null || val is T)
{
temp.Add((T)val);
//return (T)val;
}
else
{
temp.Add((T)Convert.ChangeType(val, convertToType, CultureInfo.InvariantCulture));
}
}
while (reader.NextResult()) { }
// happy path; close the reader cleanly - no
// need for "Cancel" etc
reader.Dispose();
reader = null; command.OnCompleted();
return temp;
}
catch(Exception e)
{
dbCommandInterceptionContext.Exception = e;
foreach (var l in log)
l.ReaderExecuted(cmd, dbCommandInterceptionContext);
return new List<T>();
}
finally
{
foreach (var l in log)
l.ReaderExecuted(cmd, dbCommandInterceptionContext);
if (reader != null)
{
if (!reader.IsClosed) try { cmd.Cancel(); }
catch { /* don't spoil the existing exception */ }
reader.Dispose();
}
if (wasClosed) cnn.Close();
cmd?.Dispose();
}
}
private static T QueryRowImpl<T>(IDbConnection cnn, Row row, ref CommandDefinition command, Type effectiveType)
{
object param = command.Parameters;
var identity = new Identity(command.CommandText, command.CommandType, cnn, effectiveType, param?.GetType(), null);
var info = GetCacheInfo(identity, param, command.AddToCache); IDbCommand cmd = null;
IDataReader reader = null;
IDbCommandInterceptor[] log = DbInterception.GetInterceptors();
bool wasClosed = cnn.State == ConnectionState.Closed;
DbCommandInterceptionContext<IDataReader> dbCommandInterceptionContext = new DbCommandInterceptionContext<IDataReader>();
try
{
cmd = command.SetupCommand(cnn, info.ParamReader);
foreach (var l in log)
l.ReaderExecuting(cmd, dbCommandInterceptionContext);
if (wasClosed) cnn.Open();
reader = ExecuteReaderWithFlagsFallback(cmd, wasClosed, (row & Row.Single) !=
? CommandBehavior.SequentialAccess | CommandBehavior.SingleResult // need to allow multiple rows, to check fail condition
: CommandBehavior.SequentialAccess | CommandBehavior.SingleResult | CommandBehavior.SingleRow);
wasClosed = false; // *if* the connection was closed and we got this far, then we now have a reader T result = default(T);
if (reader.Read() && reader.FieldCount != )
{
// with the CloseConnection flag, so the reader will deal with the connection; we
// still need something in the "finally" to ensure that broken SQL still results
// in the connection closing itself
var tuple = info.Deserializer;
int hash = GetColumnHash(reader);
if (tuple.Func == null || tuple.Hash != hash)
{
tuple = info.Deserializer = new DeserializerState(hash, GetDeserializer(effectiveType, reader, , -, false));
if (command.AddToCache) SetQueryCache(identity, info);
} var func = tuple.Func;
object val = func(reader);
if (val == null || val is T)
{
result = (T)val;
}
else
{
var convertToType = Nullable.GetUnderlyingType(effectiveType) ?? effectiveType;
result = (T)Convert.ChangeType(val, convertToType, CultureInfo.InvariantCulture);
}
if ((row & Row.Single) != && reader.Read()) ThrowMultipleRows(row);
while (reader.Read()) { }
}
else if ((row & Row.FirstOrDefault) == ) // demanding a row, and don't have one
{
ThrowZeroRows(row);
}
while (reader.NextResult()) { }
// happy path; close the reader cleanly - no
// need for "Cancel" etc
reader.Dispose();
reader = null; command.OnCompleted();
return result;
}
catch (Exception e)
{
dbCommandInterceptionContext.Exception = e;
foreach (var l in log)
l.ReaderExecuted(cmd, dbCommandInterceptionContext);
return default(T);
}
finally
{
foreach (var l in log)
l.ReaderExecuted(cmd, dbCommandInterceptionContext);
if (reader != null)
{
if (!reader.IsClosed) try { cmd.Cancel(); }
catch { /* don't spoil the existing exception */ }
reader.Dispose();
}
if (wasClosed) cnn.Close();
cmd?.Dispose();
}
}
这个就是在dapper数据操作的时候记录日志的,为了体现DI的思想,还要新增一个类
public static class DbInterception
{
static volatile List<IDbCommandInterceptor> _interceptors = new List<IDbCommandInterceptor>();
static readonly object _lockObject = new object();
public static void Add(IDbCommandInterceptor interceptor)
{
lock (_lockObject)
{
List<IDbCommandInterceptor> newList = _interceptors.ToList();
newList.Add(interceptor);
newList.TrimExcess();
_interceptors = newList;
}
}
public static void Remove(IDbCommandInterceptor interceptor)
{
lock (_lockObject)
{
List<IDbCommandInterceptor> newList = _interceptors.ToList();
newList.Remove(interceptor);
newList.TrimExcess();
_interceptors = newList;
}
} public static IDbCommandInterceptor[] GetInterceptors()
{
return _interceptors.ToArray();
}
}
这样子当要注入日志操作的时候只要在Global.asax中向DbInterception插入实现IDbCommandInterceptor的类就可以了。
由于篇幅过长,关于程序报错的日志处理就稍后发表了。
MVC 中dapper的日志功能+程序报错修改的更多相关文章
- [.Net Core] 在 Mvc 中简单使用日志组件
在 Mvc 中简单使用日志组件 基于 .Net Core 2.0,本文只是蜻蜓点水,并非深入浅出. 目录 使用内置的日志组件 简单过渡到第三方组件 - NLog 使用内置的日志 下面使用控制器 Hom ...
- Window7中Eclipse运行MapReduce程序报错的问题
按照文档:http://www.micmiu.com/bigdata/hadoop/hadoop2x-eclipse-mapreduce-demo/安装配置好Eclipse后,运行WordCount程 ...
- 运行编译后的程序报错 error while loading shared libraries: lib*.so: cannot open shared object file: No such file or directory
运行编译后的程序报错 error while loading shared libraries: lib*.so: cannot open shared object file: No such f ...
- Python 程序报错崩溃后,如何倒回到崩溃的位置?
假设我们有一段程序,从 Redis 中读取数据,解析以后提取出里面的 name 字段: import json import redis client = redis.Redis() def read ...
- eclipse下执行maprdeuc程序报错 java.lang.ClassNotFoundException
最近遇到一个问题,不知怎么突然运行hadoop的map程序报错,困扰了我很久,现在来给大家分享分享.. 错误信息 2017-05-18 21:34:22,104 INFO [main] client. ...
- WinDbg抓取程序报错dump文件的方法
程序崩溃的两种主要现象: a. 程序在运行中的时候,突然弹出错误窗口,然后点错误窗口的确定时,程序直接关闭 例如: “应用程序错误” “C++错误之类的窗口” “程序无响应” “假死”等 此种崩溃特点 ...
- 【转载】访问IIS中网站出现 403.14 - Forbidden报错信息
将网站发布后部署到IIS后,配置完应用程序池以及相关设置项后,在浏览器中访问设置好的网站,出现403.14 - Forbidden的错误信息,从错误信息的提示来看,应该是IIS服务器此网站目录的内容被 ...
- 记录微信小程序报错 Unexpected end of JSON input;at pages/flow/checkout page getOrderData function
微信小程序报错 Unexpected end of JSON input;at pages/flow/checkout page getOrderData function 这个报错是在将数组对象通过 ...
- 发送邮件程序报错454 Authentication failed以及POP3和SMTP简介
一.发现问题 在测试邮件发送程序的时候,发送给自己的QQ邮箱,程序报错454 Authentication failed, please open smtp flag first. 二.解决问题 进入 ...
随机推荐
- AWS:1.相关概念、创建云主机的过程
概念 EC2是弹性的云计算 云主机 也即虚拟机,由分配的CPU.内存.网络和磁盘等资源组成 好处:维护成本低(主机替换).环境升级成本低 AMI:映像 创建云主机的蓝图,指定初始状态1 预装什么操作系 ...
- vue如何做分页?
原创作品转载请注明出处 先来看一下效果图: 功能描述: 1. 点击页面序号跳转到相应页面: 2. 点击单左/单右,向后/向前跳转一个页面: 3. 点击双左/双右,直接跳转到最后一页/第一页: 3 ...
- STO存在哪些潜在隐患?
STO(Security Token Offering),即证券型通证发行,无疑是现目前区块链圈子讨论最热门的话题之一,纵使STO有很好的前景,但是其潜在隐患也不得不引起重视. 第一,STO与分布式网 ...
- Windows 安装nginx
http://nginx.org/en/docs/windows.html 在nginx.exe目录,打开命令行工具,用命令 启动/关闭/重启nginx start nginx : 启动ngin ...
- SpringBoot学习笔记(10):使用MongoDB来访问数据
SpringBoot学习笔记(10):使用MongoDB来访问数据 快速开始 本指南将引导您完成使用Spring Data MongoDB构建应用程序的过程,该应用程序将数据存储在MongoDB(基于 ...
- centos下安装wordpress
https://www.jianshu.com/p/2439dc2187b2 https://blog.csdn.net/liuhelong/article/details/79924014
- curl的安装与使用
linux 下的curl扩展安装,记录一下(发现网上好多抄袭的也不检测一下能不能用) 1.下载curl安装包: https://curl.haxx.se/download.html 2.解压: 如 t ...
- BZOJ 1638 [Usaco2007 Mar]Cow Traffic 奶牛交通:记忆化搜索【图中边的经过次数】
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1638 题意: 给你一个有向图,n个点,m条有向边. 对于所有从入度为0的点到n的路径,找出 ...
- tensorflow kmeans 聚类
iris: # -*- coding: utf-8 -*- # K-means with TensorFlow #---------------------------------- # # This ...
- SpringMVC拦截器的配置与使用详解
一.SpringMVC拦截器简介 Spring MVC的处理器拦截器类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理.在springmvc中,定义拦截 ...