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

这是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. 【题解】Codeforces 961G Partitions

    [题解]Codeforces 961G Partitions cf961G 好题啊哭了,但是如果没有不小心看了一下pdf后面一页的提示根本想不到 题意 已知\(U=\{w_i\}\),求: \[ \s ...

  2. Shell parameter expansion

    使用sh写一些小型的脚本会使工作更加简单.有部分内容可能大家都比較陌生(至少我是这样). 就是变量有关的參数展开,以下就是一些简单的描写叙述和使用方法.能够使代码更加简洁 展开运算符 替换运算 ${v ...

  3. GIT笔记:将项目发布到码云

    GIT笔记:将项目发布到码云 发布步骤 1.码云创建项目 记录下项目的远程地址: https://gitee.com/mrsaber/ms_supplyAndSale.git 2.在本地创建GIT仓库 ...

  4. redis的安装与类型

    redis Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库.缓存和消息中间件 源码安装 redis , 编译安装 为何用源码安装,不用yum安装, 编译安装的优势 ...

  5. Linux中常用文件传输命令及使用方法

    sftp sftp即Secure Ftp 是一个基于SSH安全协议的文件传输管理工具.由于它是基于SSH的,会在传输过程中对用户的密码.数据等敏感信息进行加密,因此可以有效的防止用户信息在传输的过程中 ...

  6. html5+css3酷炫音频播放器代码

    1. [代码][JavaScript]代码         (function($){    jQuery.fn.extend({        "initAudio" : fun ...

  7. NO0:重新拾起C语言

    因工作所需,重新捡起C语言,之前在学校里有接触过,但现在已经忘的一干二净了,现决定重新开始学习,为工作,为生活. 以<标准 C程序设计 第5版>的课程进行基础学习,同时以另外两本书为辅助, ...

  8. Spring笔记01(基础知识)

    1.基础知识 01.Spring:轻量级Java EE开源框架,它是由Rod Johnson为了解决企业应用程序开发的复杂性而创建. 02.目标:实现一个全方位的整合框架,实现“一站式”的企业应用开发 ...

  9. python 之生成器

    斐波拉契数列: In [31]: def func(times): ...: alist = [0,1] ...: sum = 0 ...: for i in range(times): ...: . ...

  10. MySQL计算指标连续两月金额相比_20160929

    在正常的业务逻辑中,对客户的分析是必须的也是最经常用到的,根据时间维度计算计算指标连续两月环比情况也是一道必须面对的题目. 国庆放假 先写代码 后面再拆分解释 SELECT a.*,b.年月 AS 上 ...