码迷,mamicode.com
首页 > 其他好文 > 详细

EF6学习笔记(四) 弹性连接及命令拦截调试

时间:2017-08-25 12:19:48      阅读:245      评论:0      收藏:0      [点我收藏+]

标签:.com   mod   sch   api   跟踪   put   style   进一步   zha   

EF6学习笔记总目录:ASP.NET MVC5 及 EF6 学习笔记 - (目录整理)

本章原文地址:Connection Resiliency and Command Interception

原文有些地方讲的比较细,个人根据实际理解做些缩减,或者加入一些个人理解;

第1部分 弹性连接

为什么要弹性连接?什么是弹性连接?

在实际的网络应用中,尤其是在Internet上的网络应用,就算Web服务器和数据库服务器在一个数据中心,也不能保证WEB服务器和数据库服务器没有任何延迟或者其他网络问题;

尤其如PaaS层的Azure的SQL或者阿里的SQL、MySQL数据库服务器,都是做了网络负载均衡的,在一定的条件下能提供的服务是有限的,在其SLA里都会有定义;

而超出其SLA的部分请求就会被取消响应,那么在WEB网页应用设计的时候,就需要考虑这一点,在出现一些异常情况的时候,需要能够在短时间内进行一次或多次Retry.

这就称为:弹性的连接

原文中对EF6实现弹性连接功能仅做了简单代码就实现了,至于具体在实际项目中是不是就这么简单,本人还需要进一步深入学习。

先看看原文如何做的:

在DAL文件夹定义 SchoolConfiguration类,继承自DbConfiguration类, 在这个类中设置SQL数据库的执行策略 (execution strategy ,这个名词是EF6 对于Retry Policy 的命名

using System.Data.Entity;
using System.Data.Entity.SqlServer;

namespace ContosoUniversity.DAL
{
    public class SchoolConfiguration : DbConfiguration
    {
        public SchoolConfiguration()
        {
            SetExecutionStrategy("System.Data.SqlClient", () => new SqlAzureExecutionStrategy());
        }
    }
}

EF6会自动运行继承自DbConfiguration类的代码;当然也可以通过配置Web.config 来实现,可以参考 EntityFramework Code-Based Configuration
然后在 Student 控制器里增加申明:

using System.Data.Entity.Infrastructure;

最后就是将所有 Try-catch代码块的 catch 后面捕获的 Exception 类型转为 RetryLimitExceededException

catch (RetryLimitExceededException /* dex */)
{
    //Log the error (uncomment dex variable name and add a line here to write a log.
    ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists see your system administrator.");
}

这样就会是出现数据库连接异常的时候,进行Retry,多次Retry达到Limit值还是错误,则抛出异常。

更多的说明,需要参考: Entity Framework Connection Resiliency / Retry Logic
 

第2部分 命令拦截调试

设置好了弹性连接后,如何能够看到到底是不是Retry了,则需要一些调试手段;

当然你可以使用Web请求Log等来查看,或者EF6 提供的Dedicated Logging API (dedicated logging API)

本次直接采用EF6的 Interception 功能来实现。

对于日志记录来说,最好的方式是做一个接口来定义,而不是直接每次用硬代码来Call System.Diagnostics.Trace

因为这样,对于以后如果日志记录机制有调整的话,就容易很多了。

实际操作体验:

1、新建一个Logging 的文件夹

2、在文件夹中新建一个ILogger 接口,接口中定义了不同等级的信息处理方式;

TraceApi 可以跟踪连接外部服务例如SQL服务的每一步延迟情况;

using System;

namespace ContosoUniversity.Logging
{
    public interface ILogger
    {
        void Information(string message);
        void Information(string fmt, params object[] vars);
        void Information(Exception exception, string fmt, params object[] vars);

        void Warning(string message);
        void Warning(string fmt, params object[] vars);
        void Warning(Exception exception, string fmt, params object[] vars);

        void Error(string message);
        void Error(string fmt, params object[] vars);
        void Error(Exception exception, string fmt, params object[] vars);

        void TraceApi(string componentName, string method, TimeSpan timespan);
        void TraceApi(string componentName, string method, TimeSpan timespan, string properties);
        void TraceApi(string componentName, string method, TimeSpan timespan, string fmt, params object[] vars);
    }
}

3、在文件夹内新建Logger这个类,集成自ILogger接口,并实现接口内方法;

using System;
using System.Diagnostics;
using System.Text;

namespace ContosoUniversity.Logging
{
    public class Logger : ILogger
    {
        public void Information(string message)
        {
            Trace.TraceInformation(message);
        }

        public void Information(string fmt, params object[] vars)
        {
            Trace.TraceInformation(fmt, vars);
        }

        public void Information(Exception exception, string fmt, params object[] vars)
        {
            Trace.TraceInformation(FormatExceptionMessage(exception, fmt, vars));
        }

        public void Warning(string message)
        {
            Trace.TraceWarning(message);
        }

        public void Warning(string fmt, params object[] vars)
        {
            Trace.TraceWarning(fmt, vars);
        }

        public void Warning(Exception exception, string fmt, params object[] vars)
        {
            Trace.TraceWarning(FormatExceptionMessage(exception, fmt, vars));
        }

        public void Error(string message)
        {
            Trace.TraceError(message);
        }

        public void Error(string fmt, params object[] vars)
        {
            Trace.TraceError(fmt, vars);
        }

        public void Error(Exception exception, string fmt, params object[] vars)
        {
            Trace.TraceError(FormatExceptionMessage(exception, fmt, vars));
        }

        public void TraceApi(string componentName, string method, TimeSpan timespan)
        {
            TraceApi(componentName, method, timespan, ""); 
        }

        public void TraceApi(string componentName, string method, TimeSpan timespan, string fmt, params object[] vars)
        {
            TraceApi(componentName, method, timespan, string.Format(fmt, vars));
        }
        public void TraceApi(string componentName, string method, TimeSpan timespan, string properties)
        {
            string message = String.Concat("Component:", componentName, ";Method:", method, ";Timespan:", timespan.ToString(), ";Properties:", properties);
            Trace.TraceInformation(message);
        }

        private static string FormatExceptionMessage(Exception exception, string fmt, object[] vars)
        {
            // Simple exception formatting: for a more comprehensive version see 
            // http://code.msdn.microsoft.com/windowsazure/Fix-It-app-for-Building-cdd80df4
            var sb = new StringBuilder();
            sb.Append(string.Format(fmt, vars));
            sb.Append(" Exception: ");
            sb.Append(exception.ToString());
            return  sb.ToString();
        }
    }
}

看代码可以知道,实际是通过.NET 的 System.Diagnostics 来跟踪记录日志的;日志信息可以写到很多其他位置,例如Azure 的blob storage ;

本次,只是把日志输出到VS 的Output窗口;

在实际项目中,如果用其他记录日志机制来代替 System.Diagnostics的话,ILogging接口方式比较容易来实现这个切换。

下一步需要建一个拦截类来进行对EF6每一次向数据库发SQL请求的时候进行拦截记录日志或者发一个模拟的短暂错误;

这个类必须继承自 DbCommandInterceptor

在DAL文件夹新建 SchoolInterceptorLogging类,集成自DbCommandInterceptor

 

using System;
using System.Data.Common;
using System.Data.Entity;
using System.Data.Entity.Infrastructure.Interception;
using System.Data.Entity.SqlServer;
using System.Data.SqlClient;
using System.Diagnostics;
using System.Reflection;
using System.Linq;
using ContosoUniversity.Logging;

namespace ContosoUniversity.DAL
{
    public class SchoolInterceptorLogging : DbCommandInterceptor
    {
        private ILogger _logger = new Logger();
        private readonly Stopwatch _stopwatch = new Stopwatch();

        public override void ScalarExecuting(DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
        {
            base.ScalarExecuting(command, interceptionContext);
            _stopwatch.Restart();
        }

        public override void ScalarExecuted(DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
        {
            _stopwatch.Stop();
            if (interceptionContext.Exception != null)
            {
                _logger.Error(interceptionContext.Exception, "Error executing command: {0}", command.CommandText);
            }
            else
            {
                _logger.TraceApi("SQL Database", "SchoolInterceptor.ScalarExecuted", _stopwatch.Elapsed, "Command: {0}: ", command.CommandText);
            }
            base.ScalarExecuted(command, interceptionContext);
        }

        public override void NonQueryExecuting(DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
        {
            base.NonQueryExecuting(command, interceptionContext);
            _stopwatch.Restart();
        }

        public override void NonQueryExecuted(DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
        {
            _stopwatch.Stop();
            if (interceptionContext.Exception != null)
            {
                _logger.Error(interceptionContext.Exception, "Error executing command: {0}", command.CommandText);
            }
            else
            {
                _logger.TraceApi("SQL Database", "SchoolInterceptor.NonQueryExecuted", _stopwatch.Elapsed, "Command: {0}: ", command.CommandText);
            }
            base.NonQueryExecuted(command, interceptionContext);
        }

        public override void ReaderExecuting(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
        {
            base.ReaderExecuting(command, interceptionContext);
            _stopwatch.Restart();
        }
        public override void ReaderExecuted(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
        {
            _stopwatch.Stop();
            if (interceptionContext.Exception != null)
            {
                _logger.Error(interceptionContext.Exception, "Error executing command: {0}", command.CommandText);
            }
            else
            {
                _logger.TraceApi("SQL Database", "SchoolInterceptor.ReaderExecuted", _stopwatch.Elapsed, "Command: {0}: ", command.CommandText);
            }
            base.ReaderExecuted(command, interceptionContext);
        }
    }
}

 

未完

 

EF6学习笔记(四) 弹性连接及命令拦截调试

标签:.com   mod   sch   api   跟踪   put   style   进一步   zha   

原文地址:http://www.cnblogs.com/jacky-zhang/p/7426939.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!