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

这是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. 使用cocos2d-js-3.0RC1中的物理引擎chipmunk模拟的“别碰钉子”源码分享(含碰撞检测)

    分别用box2d和chipmunk实现了一下,不过box2d没整理,也懒得整理了.chipmunk整理了一下,分享给大家吧. 刚开始研究,抛砖引玉 简要说明:1.初始化物理环境,增加边界 initPh ...

  2. Allegro PCB中添加汉字

    注明出处:http://www.cnblogs.com/einstein-2014731/p/5650943.html Cadence用起来比AltiumDesigner要麻烦些,但是也更开放,更灵活 ...

  3. Flask中的CBV和上下文初步解读

    一 . flask中的CBV 相对于Django中的CBV,让我们来看看flask中的CBV是如何实现的 ? from flask import Flask, render_template, url ...

  4. echarts如何显示在页面上

    echarts如何显示在页面上 1.引入echarts的相关.js文件 <script src="js/echarts.min.js"></script> ...

  5. vue 升降排序

    本实例是根据工作进度的百分比来进行排序. html <div class="ibox-content"> <li v-for="(rangeItem,i ...

  6. 一步一步教你简单完成 Android USB开发

    项目中有一个新的需求,要求可以连接一个USB体温枪,APP可以从体温枪中读取到体温数据,一番搜寻之后发现一个封装很棒的USB通信库. github地址:usb-serial-for-android 准 ...

  7. 51nod1671【货物运输】

    开始天真的我以为这道题和运输计划是一样的套路.于是写了一发,debug后发现过了第一个点,十分开心的交了一发,结果只过了第一个点.后来发现这个并不是一样的,因为修建黑洞之后路径法变了,而运输计划没有( ...

  8. 迁移学习——使用Tensorflow和VGG16预训模型进行预测

    使用Tensorflow和VGG16预训模型进行预测 from:https://zhuanlan.zhihu.com/p/28997549   fast.ai的入门教程中使用了kaggle: dogs ...

  9. k8s-应用快速入门(ma)

    [root@k8s-master ~]# kubectl run nginx-deploy --image=nginx:1.14-alpine --port=80 --replicas=1 deplo ...

  10. poj1456 Supermarket[另类的并查集做法]

    1.Supermarket(题目地址) 跟很久以前模拟的打地鼠那题一样,贪心+优先队列.这次换用并查集做法. 还是基于贪心,但这次换一种策略,先选价值最大的, 同时使其尽可能晚的被选上(因为早选会将之 ...