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

这是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. protobuf json xml比较

    1 protobuf/xml/json对比 从数据的存储格式的角度进行对比 假如要存储一个键值对: {price:150} 1.1 protobuf的表示方式 message  Test { opti ...

  2. 《Linux 鸟哥私房菜》 第一部分 Linux文件、目录与磁盘格式

    1.Linux就是内核层与系统调用接口层这2层.

  3. 登录令牌 Token 介绍

     Token值介绍 token 值: 登录令牌.利用 token 值来判断用户的登录状态.类似于 MD5 加密之后的长字符串. 用户登录成功之后,在后端(服务器端)会根据用户信息生成一个唯一的值.这个 ...

  4. spring-boot5代码

    App.java package com.kfit.spring_boot_mybatis; import org.mybatis.spring.annotation.MapperScan; impo ...

  5. windows下的git使用指令

    经常使用mac和linux 这次使用window开发了一些小项目 废话不多说: git init git add . git commit -m 'note' git remote add origi ...

  6. BZOJ 3314 [Usaco2013 Nov]Crowded Cows:单调队列

    题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=3314 题意: N头牛在一个坐标轴上,每头牛有个高度.现给出一个距离值D. 如果某头牛在它的 ...

  7. Elasticsearch mapping文档相似性算法

    Elasticsearch allows you to configure a scoring algorithm or similarity per field. The similarityset ...

  8. 线程池ThreadPool的常用方法介绍

    线程池ThreadPool的常用方法介绍 如果您理解了线程池目的及优点后,让我们温故下线程池的常用的几个方法: 1. public static Boolean QueueUserWorkItem(W ...

  9. hdu-5781 ATM Mechine(dp+概率期望)

    题目链接: ATM Mechine Time Limit: 6000/3000 MS (Java/Others)     Memory Limit: 65536/65536 K (Java/Other ...

  10. 基于jQuery Ajax实现无刷新文件上传

    最近因项目需求更改,需要实现选择文件后即时上传至服务器,然后提交后,加载xls表格内容到jqgrid表格中,文件上传功能实现示例: 前端jsp页面: <form id="uploadF ...