【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一系列,例如文中開頭所說的一些東西,總之啦對我們來說學習的東西有很多,但是如何下手如何跟進如何凝聚為自身屬性,這些都是需要一步一步的走才能長成的吧,但願與君共勉!期待與你的下次論劍!!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM