【EntityFramework 6.1.3】个人理解与问题记录(3)

前言

说点题外话:前几天接连微软老爹发布了 .net core 2.0 / asp.net core 2.0 / ef core 2.0 / .net standard 2.0(此处撒花,不管是否后面如何,但是我们至少看到了微软的努力和成果,||ヽ( ̄▽ ̄)ノミ|Ю,至于说国内环境使然的情况下, 是否能够引来一丝生机般的阳光那就另当别论了!个人还是比较看好,当然这是许许多多.Neter需要一起,才能做到的),其中ef core github wiki 也列出了下季度的计划路线图,详情传送门:https://github.com/aspnet/EntityFrameworkCore/wiki/Roadmap,略微吐槽一下: 咦!这么久了,groupby+lazyload都还没做好,╮(╯▽╰)╭,坐等 ef core 2.1实现,毕竟微软自己都说出口了,还有 netstandard 2.0 API 是比较全了,建议新项目创建 lib 选择使用,这里再说多一句希望ef core新特性也能够在ef 6.x中共享!!!。好了上面说了一通都和我们今天的主题无关,下面我们将开始上期未完成的问题,上期文地址:http://www.cnblogs.com/DjlNet/p/7291865.html,开始我们今天的对EF未完问题的解读.....


声明

本文欢迎转载原文地址:http://www.cnblogs.com/DjlNet/p/7360545.html


正片

注意本文内容一般,可能文中提到的,大部分同学都知道,所以大佬、知晓的园友可绕道(并无不尊之意哈哈),只是博主备份记录以及完成这个系列文。


7、开发者怎么审查EF翻译的SQL语句?

时常在使用EF的途中偶尔觉得怎么访问数据库有点慢呢,这里慢就可能有多种原因,例如:网络延时,应用程序资源争夺,本身数据库响应等等,这里我们仅仅局限于提交到数据库的SQL语句本身执行慢的问题上,那么怎么才能捕获到在生成SQL到提交到数据库执行期间的SQL语句呢,注意:这里下面的部分方法不限于数据库种类,可以拿到SQL语句的。

方法一:使用VS在debug调试环境下的,一般情况在调试界面的右侧会出现如图所示(不同VS版本略有不同,建议使用VS2017):
,其中的筛选器已经默认捕获了ado.net的事件,鼠标点击便可以展开查看事件包含执行的SQL语句,但是你会发现右键没有复制,其实这里直接ctrl+c也可以复制出来选中的信息,但是不能拿到纯粹的SQL语句这点不好且只能在DEBUG模式下才能使用哦,但是如果想快速再不改动项目的情况下用这种方式何尝不是一种好的选择呐。

方法二:使用微软已经默认对 IQueryable 的 ToString 方法重写的实现,拿到的字符串便是纯粹翻译的SQL语句,这里得特别说明一下:不一定拿到的翻译SQL语句在数据库当中能执行,就能在EF正确的体现,这里博主在第二篇博文已经实践过了,所以各位同学得稍稍注意一下。

方法三:使用 DbContext.Database.Log实现,这里EF知道有这方便的需求,所以提供了一个委托类型Action<string>来让开发者自定义处理的Handler,例如:db.Database.Log = Console.WriteLine;将所有关联的SQL和其他信息输出到控制台,注意这里包括了执行的SQL,以及数据库连接的开闭,事务的打开和关闭等等,所以这里需要开发者自己查找自己想看的那部分SQL。

方法四:使用数据库层面的监控工具,例如:数据库是Sqlserver的情况下,即可使用Sql Server Profiler,这个工具顾名思义将是对整个数据库的一个全面监控,更多该工具详情功能介绍参考微软官网把,所以这里肯定会把我们EF所执行的SQL也能监控到了拉,如图所示:,这里便是对上篇文章的查询监控示意图。

方法五:使用社区的第三方EF扩展nuget包,例如:MiniProfiler.EF6,相信它的原理也是在EF提供的API或者横切面实现的呐,这里借用一下官方的贴图示意监控的EF的SQL语句: ,这里我贴出MiniProfiler官方地址:http://miniprofiler.com/,这里还包含了对asp.net mvc 网站的监控,例如一下视图渲染,controller+action 耗时等等,还有一些调用审查日志记录持久化等等,博主将会再下面问题部分展示。


8、开发者怎么监控EF在网站运行情况?

相信大家在使用EF的时候都有过这种想法,但是一般情况下EF都能胜任大部分情况,所有部分开发者都没有稍稍注意到在asp.net mvc 的应用程序中 EF的表现,到这里博主要安利一个监控扩展包了,那就是上面 方法五 提到的 MiniProfiler 全家桶,链接参考上面,其中面对监控这个东西肯定对性能有着一定影响的,所以我们下面的演示将会提供一个可控配置的监控开关来启动或者关闭监控功能,这样就是不是更加灵活了呐!!!接下来,我们一步一步构建可控的网站。

第一步:在你的asp.net mvc应用程序中添加如下的nuget包,相信大家从名字都可以看出来各自nuget包的作用和依赖关系:

 <package id="MiniProfiler" version="3.2.0.157" targetFramework="net45" />
 <package id="MiniProfiler.EF6" version="3.0.11" targetFramework="net45" />
 <package id="MiniProfiler.MVC4" version="3.0.11" targetFramework="net45" />

以及在 Web.config文件AppSettings节点添加Node:<add key="MiniProfilerEnabled" value="true" />,当然这里也可以通过#if DEBUG #endif的方式来实现也是可以的参考链接地址:http://www.cnblogs.com/jiekzou/p/6374726.html,还有在如下的handlers添加miniprofiler自带处理器

  <system.webServer>
    <handlers>
      <add name="MiniProfiler" path="mini-profiler-resources/*" verb="*" type="System.Web.Routing.UrlRoutingModule" resourceType="Unspecified" preCondition="integratedMode" />
    </handlers>
  </system.webServer>

第二步:在文件夹App_Start,添加如下两个文件:其一MiniProfilerStartUpModule.cs,这里说明一下使用了IHttpModule接口来实现对asp.net框架管道的横切面编程达到把我们监控逻辑注入到流程中去,详情如下注释说明:

public class MiniProfilerStartUpModule : IHttpModule
    {
        private static bool enable = bool.Parse(System.Configuration.ConfigurationManager.AppSettings["MiniProfilerEnabled"]);
        public void Dispose()
        {
        }

        public void Init(HttpApplication context)
        {
            context.BeginRequest += Context_BeginRequest;
            context.EndRequest += Context_EndRequest;
        }

        private void Context_EndRequest(object sender, EventArgs e)
        {
            MiniProfiler.Stop();
        }

        private void Context_BeginRequest(object sender, EventArgs e)
        {
            if (enable)
            {
                // 启动miniprofiler监控
                MiniProfiler.Start();
            }
        }
    }

其二MiniProfilerActivator.cs,这里使用了 WebActivatorEx nuget包 来控制asp.net应用程序启动的横切面注入编程,以及动态注入asp.net管道使用上述的自定义类:DynamicModuleUtility.RegisterModule(typeof(MiniProfilerStartUpModule));实现拦截,详情如下注释说明:

using StackExchange.Profiling;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Microsoft.Web.Infrastructure.DynamicModuleHelper;

// 标记表示应用程序启动之前调用指定类的指定方法
[assembly: WebActivatorEx.PreApplicationStartMethod(typeof(DJLNET.WebMvc.App_Start.MiniProfilerActivator), "Start", Order = 0)] 
namespace DJLNET.WebMvc.App_Start
{
    public static class MiniProfilerActivator
    {
        public static void Start()
        {
            var enable = bool.Parse(System.Configuration.ConfigurationManager.AppSettings["MiniProfilerEnabled"]);
            if (enable)
            {
                // 注入自定义HttpModule
                DynamicModuleUtility.RegisterModule(typeof(MiniProfilerStartUpModule));
                // 添加监控的actionfiler
                GlobalFilters.Filters.Add(new StackExchange.Profiling.Mvc.ProfilingActionFilter());
                // 启动ef监控初始化
                StackExchange.Profiling.EntityFramework6.MiniProfilerEF6.Initialize();
                // 启用显示详情消耗时间信息
                MiniProfiler.Settings.PopupShowTimeWithChildren = true;
                // 清空原始自带的视图引擎
                var viewEngines = ViewEngines.Engines.ToList();
                ViewEngines.Engines.Clear();
                // 替换为miniprofiler的视图引擎包装器,这样就达到对view渲染时间监控
                foreach (var item in viewEngines)
                {
                    var wapper = new StackExchange.Profiling.Mvc.ProfilingViewEngine(item);
                    ViewEngines.Engines.Add(wapper);
                }
            }
        }
    }
}

接着在我们的_Layout.cshtml分别在文件头部添加

@using StackExchange.Profiling;

html body 底部添加如下代码:

@*MiniProfiler配置*@
@MiniProfiler.RenderIncludes()

这样边便可以能够通过miniprofiler的JS注入实现控制实现交互和样式,达到展示性能图表等等

第三步:展示我们的效果图,当然也包括我们的问题中提到的EF的SQL展示等等,这里需要注意一点就是miniprofiler会把它觉得有问题的请求会标记为红色,让开发者注意该请求发生的事情,如下图:




以上便是部分的展示图,请忽略博主的界面和网站设计,这只是用来试验和测试一些方法来用的哈,到这里边是对第八的问题有了一个稍微好的解释和回答了。等等,你以为到这里就算完了嘛,不,还没有,下面我们继续走进EF的一些小知识,请....


EF小知识(加戏篇)

时常在我们基于数据库的应用程序开发得时候,常常需要在SQL查询过程中需要nolock关键字对查询表允许脏读和幻读,那么你想过在EF怎么实现吗,且EF的查询默认是没有开启事务的哦...
其实呢,博主先前也是不知道的呐,但是有Bing、Google还怕什么呢,况且还有Stackoverflow,所以答案就在上面了,多说一句在国内你懂得的环境下面Bing搜索的改版挺赞的哈,先给出链接地址:https://stackoverflow.com/questions/926656/entity-framework-with-nolock,这里总的大部分做法来说是设置上下文环境的事务等级为:System.Transactions.IsolationLevel.ReadUncommitted,链接的方法各异,可以参考使用即可,主要看nolock查询的情况频繁程度和使用场景的多少等等看情况来设置。

例如:可以直接利用在某个特殊查询(什么鬼报表查询)中,直接使用如下方式:

//declare the transaction options
var transactionOptions = new System.Transactions.TransactionOptions();
//set it to read uncommited
transactionOptions.IsolationLevel = System.Transactions.IsolationLevel.ReadUncommitted;
//create the transaction scope, passing our options in
using (var transactionScope = new System.Transactions.TransactionScope(
    System.Transactions.TransactionScopeOption.Required, 
    transactionOptions)
)

//declare our context
using (var context = new MyEntityConnection())
{
    //any reads we do here will also read uncomitted data
    //...
    //...
    //don't forget to complete the transaction scope
    transactionScope.Complete();
}

又或者统一在所有EF查询中使用自定义DbCommandInterceptor来控制生成的Command达到with nolock的效果,参考如下:

public class NoLockInterceptor : DbCommandInterceptor
{
    private static readonly Regex _tableAliasRegex = 
        new Regex(@"(?<tableAlias>AS \[Extent\d+\](?! WITH \(NOLOCK\)))", 
            RegexOptions.Multiline | RegexOptions.IgnoreCase);

    [ThreadStatic]
    public static bool SuppressNoLock;

    public override void ScalarExecuting(DbCommand command, 
        DbCommandInterceptionContext<object> interceptionContext)
    {
        if (!SuppressNoLock)
        {
            command.CommandText = 
                _tableAliasRegex.Replace(command.CommandText, "${tableAlias} WITH (NOLOCK)");
        }
    }

    public override void ReaderExecuting(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
    {
        if (!SuppressNoLock)
        {
            command.CommandText = 
                _tableAliasRegex.Replace(command.CommandText, "${tableAlias} WITH (NOLOCK)");
        }
    }
}

然后添加到EF中去DbInterception.Add(new NoLockInterceptor());然后设置启用NoLockInterceptor.SuppressNoLock = true;
其次:这里链接中还提供了一中稍微灵活的方式 postsharp(收费)+ReadUncommitedTransactionScopeAttribute 来实现,想对那些查询应用nolock只需要打上标记就行了,参考如下:

[Serializable]
public class ReadUncommitedTransactionScopeAttribute : MethodInterceptionAspect
{
    public override void OnInvoke(MethodInterceptionArgs args)
    {
        //declare the transaction options
        var transactionOptions = new TransactionOptions();
        //set it to read uncommited
        transactionOptions.IsolationLevel = IsolationLevel.ReadUncommitted;
        //create the transaction scope, passing our options in
        using (var transactionScope = new TransactionScope(TransactionScopeOption.Required, transactionOptions))
        {
            //declare our context
            using (var scope = new TransactionScope())
            {
                args.Proceed();
                scope.Complete();
            }
        }
    }
}
[ReadUncommitedTransactionScope()]
    public static SomeEntities[] GetSomeEntities()
    {
        using (var context = new MyEntityConnection())
        {
            //any reads we do here will also read uncomitted data
            //...
            //...

        }
    }

注意这里的是国际友人提供的参考实现方法,博主本身并没有实践,所以酌情使用,自行定夺!!!

这里最后在说一句,Get Record ID in Entity Framework after insert ?回答如下(原文链接:https://stackoverflow.com/questions/16954767/get-record-id-in-entity-framework-after-insert):

总结

至此,我们通过了三篇文章来分析使用EF中遇到的问题和解决手段,也大致了解了EF本身好处和瑕疵吧,也正好这个系列文章也算是有了一个不错的结尾,那么接下博主将在继续研究大家所关心的话题,什么DDD、DI、AOP、IOC、MQ、微服务、分布式、数据一致性等等,以及最近最新的一些的新东西.netcore一系列,例如文中开头所说的一些东西,总之啦对我们来说学习的东西有很多,但是如何下手如何跟进如何凝聚为自身属性,这些都是需要一步一步的走才能长成的吧,但愿与君共勉!期待与你的下次论剑!!

posted @ 2017-08-19 19:36  DJLNET  阅读(1564)  评论(2编辑  收藏  举报