[C#][EF] CompiledQuery

  • 4872
  • 0
  • C#
  • 2010-09-22

[C#][EF] CompiledQuery

昨天收到點部落格所送的Entity Framework與LINQ開發實戰(點部落真是佛心來的),

第一篇就來整理一下CompiledQuery。

 

DAL採用ORM技術可以減少前端程式與後端資料庫緊密耦合,例如在開發階段客戶使用Sybase DB,

但客戶告知系統正式上線後,DB將抽換成MS SQL或 Oracle,這意味者,你不能寫特定資料庫的SQL語法,

而這時就可以感受到ORM資料存取技術的好處(當後端資料庫更換時,前端AP將不用整個翻修改寫相對應SQL),

但相對的就得犧牲部分效能(雖然EF4中針對查詢效能已有大幅度改善)。

 

提高EF查詢效能方法:

一、使用已編譯的 LINQ 查詢

當我們要使用不同參數執行查詢相同實體多次時,CompiledQuery類別會對查詢進行編譯及快取(LINQ to Entities ),

以達到重複使用並提高查詢效能。

二、考慮使用 NoTracking

如果沒有更新或刪除物件的計畫,則執行查詢時,

請考慮使用 NoTracking 合併選項,以減少追蹤物件所花費成本。

(關於提高EF查詢效能MSDN有更詳細的說明)

 

Using CompiledQuery and NoTracking:

image_thumb[39]

BL專案新增TestControl.cs。

DAL專案新增Demo實體資料模型,實體資料模型中包含一個nopt_qtpls實體資料表,

以及TABLENopt_Qtpls.cs。

MyEF新增BL和DAL參考。

 

TestControl.cs

 

 

 

 

 

 

 

 public enum QryMethod
        {
            Select,
            CompiledQuery       
        }          
       
        public TestControl()
        {
         
        }
 
        public string TestInit( QryMethod QryType, string Bill_No )
        {
            TableNopt_Qtpls tabqtpls = new TableNopt_Qtpls();
            string QryResult = string.Empty;
            string FinallResult = string.Empty;            
            Stopwatch sw = new Stopwatch();
            sw.Reset();
            sw.Start();
            switch( QryType )
            {               
                case QryMethod.CompiledQuery:
                    QryResult = tabqtpls.ExecCompiledQuery( Bill_No );
                    break;
                case QryMethod.Select:
                    QryResult = tabqtpls.ExecSelect( Bill_No );
                    break;
                default:
                    QryResult = "未執行任何查詢!";
                    break;            
            }
            sw.Stop(); 
            FinallResult = string.Format( "查詢結果:{0}", QryResult )+Environment.NewLine
                + string.Format( "花費時間(ms):{0}", sw.ElapsedMilliseconds.ToString() + Environment.NewLine );            
            return FinallResult;
        }

 

 

 

 

 

 

TestControl類別中,提供TestInit方法。

 

TableNopt_Qtpls.cs

   private static readonly Func<demoEntities, string, IQueryable<nopt_qtpls>> _CompiledQuery =
       CompiledQuery.Compile<demoEntities, string, IQueryable<nopt_qtpls>>(
      ( db, Bill_No ) => from qtpls in db.nopt_qtpls
                         where qtpls.BILL_NO == Bill_No
                         select qtpls );
 
        public string ExecCompiledQuery( string Bill_No )
        {
            using( demoEntities db = new demoEntities() )
            {
                try
                {
                    db.nopt_qtpls.MergeOption = MergeOption.NoTracking;
                    var qry = _CompiledQuery.Invoke( db, Bill_No );
                    StringBuilder sb = new StringBuilder();
                    foreach( var s in qry )
                    {
                        sb.AppendLine( string.Format( "單號:{0},類型{1}", s.BILL_NO, s.BILL_NO_TYPE ) );
                    }
                    return sb.ToString();
                }
                catch( Exception ex )
                {
                    return ex.Message;
                }
            }
        }
 
  public string ExecSelect( string Bill_No )
        {
            string ConnectionString = ConfigurationManager.ConnectionStrings[ "SqlConStr" ].ConnectionString;
            using( SqlConnection conn = new SqlConnection( ConnectionString ) )
            {
                try
                {
                    string select = "SELECT BILL_NO,BILL_NO_TYPE FROM dbo.nopt_qtpls where BILL_NO=@Bill_No";
                    string Result = string.Empty;
                    SqlDataReader dr = null;
                    using( SqlCommand cmd = new SqlCommand( select, conn ) )
                    {
                        conn.Open();
                        cmd.Parameters.Clear();
                        cmd.Parameters.Add( new SqlParameter( "@Bill_No", Bill_No ) );
                        dr = cmd.ExecuteReader();
                        StringBuilder sb = new StringBuilder();
                        while( dr.Read() )
                        {
                            sb.AppendLine( string.Format( "單號:{0},類型{1}", dr[ "BILL_NO" ].ToString(), dr[ "BILL_NO_TYPE" ].ToString() ) );
                        }
                        return sb.ToString();
                    }
                }
                catch( SqlException sqlex )
                {
                    return sqlex.Message;
                }
                catch( Exception ex )
                {
                    return ex.Message;
                }
            }
        }

 

 

 

 

 

 

TableNopt_Qtpls類別中,提供兩種存取資料方法。

 

Default.aspx.cs

  protected void Button1_Click( object sender, EventArgs e )
        {             
            TestControl testcontrol = new TestControl();
            //查詢5次不同的Bill_No參數,Using EF(CompiledQuery,NoTracking)
            StringBuilder sb = new StringBuilder();
            for( int i = 0; i < 5; i++ )
            {
                sb.AppendLine( testcontrol.TestInit( TestControl.QryMethod.CompiledQuery, Bill_No[ i ] ) );
            }
            TextBox1.Text = sb.ToString();
            sb.Clear();
            //查詢5次不同的Bill_No參數,Using Ado.Net 
            for( int i = 0; i < 5; i++ )
            {
                sb.AppendLine( testcontrol.TestInit( TestControl.QryMethod.Select, Bill_No[ i ] ) );
            }
            TextBox2.Text = sb.ToString();
        }

 

 

 

 

 

 

 

結果(這裡順便和ADO.NET比較一下):

第一次

image_thumb[28]

可以看到只有第一次花費較多時間,後續相關查詢時間幾乎都差距不大。

 

第二次

image_thumb[29]

 

第三次

image_thumb[30]

 

可以看到CompiledQuery對EF效能果然影響很大CompiledQuery讓後續查詢花費時間可以很接近使用ADO.NET查詢花費時間。

 

參考

已編譯的查詢 (LINQ to Entities)

效能考量因素 (Entity Framework)