本章原文地址: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 EFTest.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 EFTest.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 EFTest.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 EFTest.Logging; namespace EFTest.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); } } }
另外,为了模拟在筛选查询的时候产生一个短暂错误,写一个类让用户在输入框内输入 Throw 的触发这个错误,在DAL文件夹里建SchoolInterceptorTransientErrors类:
重载了ReaderExecuting方法,当用户输入的是Throw的时候,做一个特殊处理来进行测试;
如果希望测试其他数据库操作,则可以考虑重载 NonQueryExecuting
和ScalarExecuting这两个方法。
using System; using System.Data.Common; using System.Data.Entity.Infrastructure.Interception; using System.Data.SqlClient; using System.Reflection; using System.Linq; using EFTest.Logging; namespace EFTest.DAL { public class SchoolInterceptorTransientErrors : DbCommandInterceptor { private int _counter = 0; private ILogger _logger = new Logger(); public override void ReaderExecuting(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext) { bool throwTransientErrors = false; if (command.Parameters.Count > 0 && command.Parameters[0].Value.ToString() == "%Throw%") { throwTransientErrors = true; command.Parameters[0].Value = "%an%"; command.Parameters[1].Value = "%an%"; } if (throwTransientErrors && _counter < 4) { _logger.Information("Returning transient error for command: {0}", command.CommandText); _counter++; interceptionContext.Exception = CreateDummySqlException(); } } private SqlException CreateDummySqlException() { // The instance of SQL Server you attempted to connect to does not support encryption var sqlErrorNumber = 20; var sqlErrorCtor = typeof(SqlError).GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic).Where(c => c.GetParameters().Count() == 7).Single(); var sqlError = sqlErrorCtor.Invoke(new object[] { sqlErrorNumber, (byte)0, (byte)0, "", "", "", 1 }); var errorCollection = Activator.CreateInstance(typeof(SqlErrorCollection), true); var addMethod = typeof(SqlErrorCollection).GetMethod("Add", BindingFlags.Instance | BindingFlags.NonPublic); addMethod.Invoke(errorCollection, new[] { sqlError }); var sqlExceptionCtor = typeof(SqlException).GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic).Where(c => c.GetParameters().Count() == 4).Single(); var sqlException = (SqlException)sqlExceptionCtor.Invoke(new object[] { "Dummy", errorCollection, null, Guid.NewGuid() }); return sqlException; } } }
在Global.asax中添加声明:
using System.Data.Entity.Infrastructure.Interception;
并在Application_Start()方法中增加后面两行代码:
protected void Application_Start() { AreaRegistration.RegisterAllAreas(); RouteConfig.RegisterRoutes(RouteTable.Routes); //Database.SetInitializer<SchoolContext>(new SchoolInitializer()); DbInterception.Add(new SchoolInterceptorTransientErrors()); DbInterception.Add(new SchoolInterceptorLogging()); }
当然,这两行代码可以加在SchoolConfiguration类里,初始化的时候直接加载执行:(注:上面和下面两段代码只能选其中一种)
using System.Data.Entity; using System.Data.Entity.Infrastructure.Interception; using System.Data.Entity.SqlServer; namespace EFTest.DAL { public class SchoolConfiguration : DbConfiguration { public SchoolConfiguration() { SetExecutionStrategy("System.Data.SqlClient", () => new SqlAzureExecutionStrategy()); DbInterception.Add(new SchoolInterceptorTransientErrors()); DbInterception.Add(new SchoolInterceptorLogging()); } } }
下面就可以进行测试了,进Home/Index后点Students,进入Student/Index 就会发现VS 的Output 窗口有一些Debug信息输出:
SELECT Count(*) FROM INFORMATION_SCHEMA.TABLES AS t WHERE t.TABLE_SCHEMA + '.' + t.TABLE_NAME IN ('dbo.Course','dbo.Enrollment','dbo.Student') OR t.TABLE_NAME = 'EdmMetadata': Application Insights Telemetry (unconfigured): {"name":"Microsoft.ApplicationInsights.Dev.RemoteDependency","time":"2017-08-25T07:04:11.0689220Z","tags":{"ai.internal.sdkVersion":"rddf: 2.0.0.25000","ai.device.roleInstance":"chnsbopc02.dc01.fujixerox.net","ai.operation.name":"GET Student/Index","ai.operation.parentId":"yqYB5YWz4YY=","ai.operation.id":"yqYB5YWz4YY="},"data":{"baseType":"RemoteDependencyData","baseData":{"ver":2,"name":"(localdb)\ProjectsV13 | master","id":"+xoBYBiNrPM=","value":0.3565,"dependencyKind":0,"success":true,"properties":{"DeveloperMode":"true"}}}} iisexpress.exe Information: 0 : Component:SQL Database;Method:SchoolInterceptor.ScalarExecuted;Timespan:00:00:00.0233412;Properties:Command: IF db_id(N'EFTest') IS NOT NULL SELECT 1 ELSE SELECT Count(*) FROM sys.databases WHERE [name]=N'EFTest': Application Insights Telemetry (unconfigured): {"name":"Microsoft.ApplicationInsights.Dev.RemoteDependency","time":"2017-08-25T07:04:11.2577658Z","tags":{"ai.internal.sdkVersion":"rddf: 2.0.0.25000","ai.device.roleInstance":"chnsbopc02.dc01.fujixerox.net","ai.operation.name":"GET Student/Index","ai.operation.parentId":"yqYB5YWz4YY=","ai.operation.id":"yqYB5YWz4YY="},"data":{"baseType":"RemoteDependencyData","baseData":{"ver":2,"name":"(localdb)\ProjectsV13 | EFTest","id":"hGEE5js+frc=","value":1.2592,"dependencyKind":0,"success":true,"properties":{"DeveloperMode":"true"}}}} iisexpress.exe Information: 0 : Component:SQL Database;Method:SchoolInterceptor.ReaderExecuted;Timespan:00:00:00.0260725;Properties:Command: SELECT [GroupBy1].[A1] AS [C1] FROM ( SELECT COUNT(1) AS [A1] FROM [dbo].[__MigrationHistory] AS [Extent1] WHERE [Extent1].[ContextKey] = @p__linq__0 ) AS [GroupBy1]: Application Insights Telemetry (unconfigured): {"name":"Microsoft.ApplicationInsights.Dev.RemoteDependency","time":"2017-08-25T07:04:11.3081972Z","tags":{"ai.internal.sdkVersion":"rddf: 2.0.0.25000","ai.device.roleInstance":"chnsbopc02.dc01.fujixerox.net","ai.operation.name":"GET Student/Index","ai.operation.parentId":"yqYB5YWz4YY=","ai.operation.id":"yqYB5YWz4YY="},"data":{"baseType":"RemoteDependencyData","baseData":{"ver":2,"name":"(localdb)\ProjectsV13 | EFTest","id":"ULROylXtnGQ=","value":1.6166,"dependencyKind":0,"success":true,"properties":{"DeveloperMode":"true"}}}} iisexpress.exe Information: 0 : Component:SQL Database;Method:SchoolInterceptor.ReaderExecuted;Timespan:00:00:00.0155956;Properties:Command: SELECT TOP (1) [Project1].[C1] AS [C1], [Project1].[MigrationId] AS [MigrationId], [Project1].[Model] AS [Model], [Project1].[ProductVersion] AS [ProductVersion] FROM ( SELECT [Extent1].[MigrationId] AS [MigrationId], [Extent1].[Model] AS [Model], [Extent1].[ProductVersion] AS [ProductVersion], 1 AS [C1] FROM [dbo].[__MigrationHistory] AS [Extent1] WHERE [Extent1].[ContextKey] = @p__linq__0 ) AS [Project1] ORDER BY [Project1].[MigrationId] DESC: Application Insights Telemetry (unconfigured): {"name":"Microsoft.ApplicationInsights.Dev.RemoteDependency","time":"2017-08-25T07:04:11.3703060Z","tags":{"ai.internal.sdkVersion":"rddf: 2.0.0.25000","ai.device.roleInstance":"chnsbopc02.dc01.fujixerox.net","ai.operation.name":"GET Student/Index","ai.operation.parentId":"yqYB5YWz4YY=","ai.operation.id":"yqYB5YWz4YY="},"data":{"baseType":"RemoteDependencyData","baseData":{"ver":2,"name":"(localdb)\ProjectsV13 | EFTest","id":"/xZUhdnSdM8=","value":1.7563,"dependencyKind":0,"success":true,"properties":{"DeveloperMode":"true"}}}} iisexpress.exe Information: 0 : Component:SQL Database;Method:SchoolInterceptor.ReaderExecuted;Timespan:00:00:00.0176946;Properties:Command: SELECT [GroupBy1].[A1] AS [C1] FROM ( SELECT COUNT(1) AS [A1] FROM [dbo].[Student] AS [Extent1] ) AS [GroupBy1]: 'iisexpress.exe' (CLR v4.0.30319: /LM/W3SVC/3/ROOT-1-131481182423379125): Loaded 'EntityFrameworkDynamicProxies-EFTest'. Application Insights Telemetry (unconfigured): {"name":"Microsoft.ApplicationInsights.Dev.RemoteDependency","time":"2017-08-25T07:04:11.4280626Z","tags":{"ai.internal.sdkVersion":"rddf: 2.0.0.25000","ai.device.roleInstance":"chnsbopc02.dc01.fujixerox.net","ai.operation.name":"GET Student/Index","ai.operation.parentId":"yqYB5YWz4YY=","ai.operation.id":"yqYB5YWz4YY="},"data":{"baseType":"RemoteDependencyData","baseData":{"ver":2,"name":"(localdb)\ProjectsV13 | EFTest","id":"bDKxb2FBJ1g=","value":0.9676,"dependencyKind":0,"success":true,"properties":{"DeveloperMode":"true"}}}} iisexpress.exe Information: 0 : Component:SQL Database;Method:SchoolInterceptor.ReaderExecuted;Timespan:00:00:00.0194244;Properties:Command: SELECT [Extent1].[ID] AS [ID], [Extent1].[LastName] AS [LastName], [Extent1].[FirstMidName] AS [FirstMidName], [Extent1].[EnrollmentDate] AS [EnrollmentDate] FROM [dbo].[Student] AS [Extent1] ORDER BY [Extent1].[LastName] ASC OFFSET 0 ROWS FETCH NEXT 3 ROWS ONLY : 'iisexpress.exe' (CLR v4.0.30319: /LM/W3SVC/3/ROOT-1-131481182423379125): Loaded 'C:UsersjaczhangAppDataLocalTempTemporary ASP.NET Files oot644be5f6480b064fApp_Web_jlggwjiw.dll'. Application Insights Telemetry (unconfigured): {"name":"Microsoft.ApplicationInsights.Dev.Request","time":"2017-08-25T07:04:10.5173746Z","tags":{"ai.operation.id":"yqYB5YWz4YY=","ai.internal.sdkVersion":"web: 2.0.0.25000","ai.device.roleInstance":"chnsbopc02.dc01.fujixerox.net","ai.operation.name":"GET Student/Index"},"data":{"baseType":"RequestData","baseData":{"ver":2,"id":"yqYB5YWz4YY=","name":"GET Student/Index","startTime":"2017-08-25T15:04:10.5173746+08:00","duration":"00:00:01.6326287","success":true,"responseCode":"200","url":"http://localhost:9910/Student","httpMethod":"GET","properties":{"DeveloperMode":"true"}}}}
然后在筛选输入框输入Throw 点确定: 可以看到里面触发一个 代码为20的短暂错误:
iisexpress.exe Information: 0 : Returning transient error for command: SELECT [GroupBy1].[A1] AS [C1] FROM ( SELECT COUNT(1) AS [A1] FROM [dbo].[Student] AS [Extent1] WHERE ([Extent1].[LastName] LIKE @p__linq__0 ESCAPE N'~') OR ([Extent1].[FirstMidName] LIKE @p__linq__1 ESCAPE N'~') ) AS [GroupBy1] iisexpress.exe Error: 0 : Error executing command: SELECT [GroupBy1].[A1] AS [C1] FROM ( SELECT COUNT(1) AS [A1] FROM [dbo].[Student] AS [Extent1] WHERE ([Extent1].[LastName] LIKE @p__linq__0 ESCAPE N'~') OR ([Extent1].[FirstMidName] LIKE @p__linq__1 ESCAPE N'~') ) AS [GroupBy1] Exception: System.Data.SqlClient.SqlException (0x80131904): Dummy ClientConnectionId:9b424f7d-306b-450b-8a3e-58198487b8bb Error Number:20,State:0,Class:0 iisexpress.exe Information: 0 : Returning transient error for command: SELECT [GroupBy1].[A1] AS [C1] FROM ( SELECT COUNT(1) AS [A1] FROM [dbo].[Student] AS [Extent1] WHERE ([Extent1].[LastName] LIKE @p__linq__0 ESCAPE N'~') OR ([Extent1].[FirstMidName] LIKE @p__linq__1 ESCAPE N'~') ) AS [GroupBy1] iisexpress.exe Error: 0 : Error executing command: SELECT [GroupBy1].[A1] AS [C1] FROM ( SELECT COUNT(1) AS [A1] FROM [dbo].[Student] AS [Extent1] WHERE ([Extent1].[LastName] LIKE @p__linq__0 ESCAPE N'~') OR ([Extent1].[FirstMidName] LIKE @p__linq__1 ESCAPE N'~') ) AS [GroupBy1] Exception: System.Data.SqlClient.SqlException (0x80131904): Dummy ClientConnectionId:941ac661-66d3-4779-a690-188d13144150 Error Number:20,State:0,Class:0 iisexpress.exe Information: 0 : Returning transient error for command: SELECT [GroupBy1].[A1] AS [C1] FROM ( SELECT COUNT(1) AS [A1] FROM [dbo].[Student] AS [Extent1] WHERE ([Extent1].[LastName] LIKE @p__linq__0 ESCAPE N'~') OR ([Extent1].[FirstMidName] LIKE @p__linq__1 ESCAPE N'~') ) AS [GroupBy1] iisexpress.exe Error: 0 : Error executing command: SELECT [GroupBy1].[A1] AS [C1] FROM ( SELECT COUNT(1) AS [A1] FROM [dbo].[Student] AS [Extent1] WHERE ([Extent1].[LastName] LIKE @p__linq__0 ESCAPE N'~') OR ([Extent1].[FirstMidName] LIKE @p__linq__1 ESCAPE N'~') ) AS [GroupBy1] Exception: System.Data.SqlClient.SqlException (0x80131904): Dummy ClientConnectionId:47685ec4-e837-4e05-bbb3-948e3f3f87a8 Error Number:20,State:0,Class:0 iisexpress.exe Information: 0 : Returning transient error for command: SELECT [GroupBy1].[A1] AS [C1] FROM ( SELECT COUNT(1) AS [A1] FROM [dbo].[Student] AS [Extent1] WHERE ([Extent1].[LastName] LIKE @p__linq__0 ESCAPE N'~') OR ([Extent1].[FirstMidName] LIKE @p__linq__1 ESCAPE N'~') ) AS [GroupBy1] iisexpress.exe Error: 0 : Error executing command: SELECT [GroupBy1].[A1] AS [C1] FROM ( SELECT COUNT(1) AS [A1] FROM [dbo].[Student] AS [Extent1] WHERE ([Extent1].[LastName] LIKE @p__linq__0 ESCAPE N'~') OR ([Extent1].[FirstMidName] LIKE @p__linq__1 ESCAPE N'~') ) AS [GroupBy1] Exception: System.Data.SqlClient.SqlException (0x80131904): Dummy ClientConnectionId:35de62b7-cda6-4db7-88e0-48d708f42826 Error Number:20,State:0,Class:0 Application Insights Telemetry (unconfigured): {"name":"Microsoft.ApplicationInsights.Dev.RemoteDependency","time":"2017-08-25T07:06:51.9163289Z","tags":{"ai.internal.sdkVersion":"rddf: 2.0.0.25000","ai.device.roleInstance":"chnsbopc02.dc01.fujixerox.net","ai.operation.name":"GET Student/Index","ai.operation.parentId":"kZm9/0YA8y4=","ai.operation.id":"kZm9/0YA8y4="},"data":{"baseType":"RemoteDependencyData","baseData":{"ver":2,"name":"(localdb)\ProjectsV13 | EFTest","id":"0iOb6Vj5zqI=","value":3.3859,"dependencyKind":0,"success":true,"properties":{"DeveloperMode":"true"}}}} iisexpress.exe Information: 0 : Component:SQL Database;Method:SchoolInterceptor.ReaderExecuted;Timespan:00:00:00.0382038;Properties:Command: SELECT [GroupBy1].[A1] AS [C1] FROM ( SELECT COUNT(1) AS [A1] FROM [dbo].[Student] AS [Extent1] WHERE ([Extent1].[LastName] LIKE @p__linq__0 ESCAPE N'~') OR ([Extent1].[FirstMidName] LIKE @p__linq__1 ESCAPE N'~') ) AS [GroupBy1]: Application Insights Telemetry (unconfigured): {"name":"Microsoft.ApplicationInsights.Dev.RemoteDependency","time":"2017-08-25T07:06:51.9705260Z","tags":{"ai.internal.sdkVersion":"rddf: 2.0.0.25000","ai.device.roleInstance":"chnsbopc02.dc01.fujixerox.net","ai.operation.name":"GET Student/Index","ai.operation.parentId":"kZm9/0YA8y4=","ai.operation.id":"kZm9/0YA8y4="},"data":{"baseType":"RemoteDependencyData","baseData":{"ver":2,"name":"(localdb)\ProjectsV13 | EFTest","id":"b1S5ExJ8ugQ=","value":2.5604,"dependencyKind":0,"success":true,"properties":{"DeveloperMode":"true"}}}} iisexpress.exe Information: 0 : Component:SQL Database;Method:SchoolInterceptor.ReaderExecuted;Timespan:00:00:00.0195543;Properties:Command: SELECT [Project1].[ID] AS [ID], [Project1].[LastName] AS [LastName], [Project1].[FirstMidName] AS [FirstMidName], [Project1].[EnrollmentDate] AS [EnrollmentDate] FROM ( SELECT [Extent1].[ID] AS [ID], [Extent1].[LastName] AS [LastName], [Extent1].[FirstMidName] AS [FirstMidName], [Extent1].[EnrollmentDate] AS [EnrollmentDate] FROM [dbo].[Student] AS [Extent1] WHERE ([Extent1].[LastName] LIKE @p__linq__0 ESCAPE N'~') OR ([Extent1].[FirstMidName] LIKE @p__linq__1 ESCAPE N'~') ) AS [Project1] ORDER BY [Project1].[LastName] ASC OFFSET 0 ROWS FETCH NEXT 3 ROWS ONLY : Application Insights Telemetry (unconfigured): {"name":"Microsoft.ApplicationInsights.Dev.Request","time":"2017-08-25T07:06:40.0512275Z","tags":{"ai.operation.id":"kZm9/0YA8y4=","ai.internal.sdkVersion":"web: 2.0.0.25000","ai.device.roleInstance":"chnsbopc02.dc01.fujixerox.net","ai.operation.name":"GET Student/Index"},"data":{"baseType":"RequestData","baseData":{"ver":2,"id":"kZm9/0YA8y4=","name":"GET Student/Index","startTime":"2017-08-25T15:06:40.0512275+08:00","duration":"00:00:11.9404844","success":true,"responseCode":"200","url":"http://localhost:9910/Student?SearchString=Throw","httpMethod":"GET","properties":{"DeveloperMode":"true"}}}}
为了看Retry是否其作用,可以先注释掉以下行:
public SchoolConfiguration()
{
//SetExecutionStrategy("System.Data.SqlClient", () => new SqlAzureExecutionStrategy());
}
然后再进入网页,输入Throw后点确定,就会发现出现了数据库错误:
看起来是起作用了;
最后记得把刚才注释掉的行取消注释。