由于之前的项目说最好要有日志功能,正好之前看过几篇这方面的文章就弄了点东西。

这是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的日志功能+程序报错修改的更多相关文章

  1. [.Net Core] 在 Mvc 中简单使用日志组件

    在 Mvc 中简单使用日志组件 基于 .Net Core 2.0,本文只是蜻蜓点水,并非深入浅出. 目录 使用内置的日志组件 简单过渡到第三方组件 - NLog 使用内置的日志 下面使用控制器 Hom ...

  2. Window7中Eclipse运行MapReduce程序报错的问题

    按照文档:http://www.micmiu.com/bigdata/hadoop/hadoop2x-eclipse-mapreduce-demo/安装配置好Eclipse后,运行WordCount程 ...

  3. 运行编译后的程序报错 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 ...

  4. Python 程序报错崩溃后,如何倒回到崩溃的位置?

    假设我们有一段程序,从 Redis 中读取数据,解析以后提取出里面的 name 字段: import json import redis client = redis.Redis() def read ...

  5. eclipse下执行maprdeuc程序报错 java.lang.ClassNotFoundException

    最近遇到一个问题,不知怎么突然运行hadoop的map程序报错,困扰了我很久,现在来给大家分享分享.. 错误信息 2017-05-18 21:34:22,104 INFO [main] client. ...

  6. WinDbg抓取程序报错dump文件的方法

    程序崩溃的两种主要现象: a. 程序在运行中的时候,突然弹出错误窗口,然后点错误窗口的确定时,程序直接关闭 例如: “应用程序错误” “C++错误之类的窗口” “程序无响应” “假死”等 此种崩溃特点 ...

  7. 【转载】访问IIS中网站出现 403.14 - Forbidden报错信息

    将网站发布后部署到IIS后,配置完应用程序池以及相关设置项后,在浏览器中访问设置好的网站,出现403.14 - Forbidden的错误信息,从错误信息的提示来看,应该是IIS服务器此网站目录的内容被 ...

  8. 记录微信小程序报错 Unexpected end of JSON input;at pages/flow/checkout page getOrderData function

    微信小程序报错 Unexpected end of JSON input;at pages/flow/checkout page getOrderData function 这个报错是在将数组对象通过 ...

  9. 发送邮件程序报错454 Authentication failed以及POP3和SMTP简介

    一.发现问题 在测试邮件发送程序的时候,发送给自己的QQ邮箱,程序报错454 Authentication failed, please open smtp flag first. 二.解决问题 进入 ...

随机推荐

  1. pm2 的使用

    pm2.json 代码如下 [{ "name" : "dingtalk-mobile", "script" : "app.js&q ...

  2. 微信小程序配置详解

    在之前已经通过微信公众平台的官方网站https://mp.weixin.qq.com/debug/wxadoc/dev/devtools/devtools.html,注册好小程序并且登录成功后(这里主 ...

  3. 【linux】自动删除7天前的文件

    下面的脚本是删除/home目录下7天前的文件 #!/bin/bash -exec rm -f {} \; 把这个脚本保存在/tmp目录下,命名为:clearfile.sh 加入计划任务 crontab ...

  4. python获取本机IP地址

    方法一 通常使用socket.gethostname()方法即可获取本机IP地址,但有时候获取不到(比如没有正确设置主机名称) import socket #获取计算机名称hostname=socke ...

  5. LeetCode:奇偶链表【328】

    LeetCode:奇偶链表[328] 题目描述 给定一个单链表,把所有的奇数节点和偶数节点分别排在一起.请注意,这里的奇数节点和偶数节点指的是节点编号的奇偶性,而不是节点的值的奇偶性. 请尝试使用原地 ...

  6. save create

    其中 create 和 create!就等於 new 完就 save 和 save!,有無驚嘆號的差別 在於 validate 資料驗證不正確的動作,無驚嘆號版本會回傳布林值(true 或 false ...

  7. git设置只允许特定类型的文件

    git设置只允许特定类型的文件 # 忽略所有文件 * # 不忽略目录 !*/ # 不忽略文件.gitignore和*.foo !.gitignore !*.foo

  8. math worksheet作业纸生成器

    https://www.education.com/worksheet-generator/math/ https://www.mathgoodies.com/worksheets/generator ...

  9. matlab的一个疑问?

    把逻辑值放入一个已知矩阵,为啥结果是:真就取矩阵的值,假就不取值? K>> aaaa=randi(10,10,2) aaaa = 6 3 10 4 6 7 5 2 6 3 8 2 1 2 ...

  10. 蓝天白云大草原风景PSD背景素材

    蓝天白云大草原风景PSD源文件背景素材,蓝天白云,大草原,风景,背景素材,自然风景,草原景色,绿色清新背景 地址:http://www.huiyi8.com/psd/