2014-11-05 19 views
16

Tôi đang gọi hàm Bảng giá trị từ khung thực thể và cần thêm option (recompile) vào nó vì kế hoạch thực hiện mà nó chọn không tối ưu. Chạy truy vấn trong SQL Server Management Studio, nó sẽ trông giống như sau:Thêm gợi ý truy vấn khi gọi hàm Table-Valued

select 
     * 
from dbo.fDE_myquery(0, 0, 3309, '7/1/2013', '7/1/2014', 0, 0) 
option (recompile) 

Từ EF, không có cách nào để thêm gợi ý đó, AFAIK. Phần EF trông giống như sau:

var query = from f in ctx.fDE_myQuery(aBool, anotherBool, StartDate, 
      EndDate, someInt, moreBool) 
      select f; 

tôi thấy câu hỏi này:

How do I control parameter sniffing and/or query hints in entity framework?

Nhưng đó là cũ, và là giải pháp chấp nhận không thực sự cung cấp đủ thông tin về cách thực thực hiện giải pháp được đề xuất (sử dụng hướng dẫn kế hoạch) với khung thực thể. Nếu đó là giải pháp duy nhất, làm cách nào để bạn có được khung thực thể để sử dụng hướng dẫn lập kế hoạch?

Trả lời

34

Tôi đã xem qua này:

https://entityframework.codeplex.com/wikipage?title=Interception

Và có vẻ như bạn có thể làm một cái gì đó như thế này:

public class HintInterceptor : DbCommandInterceptor 
{ 
    public override void ReaderExecuting(System.Data.Common.DbCommand command, DbCommandInterceptionContext<System.Data.Common.DbDataReader> interceptionContext) 
    { 
     command.CommandText += " option (recompile)"; 
     base.ReaderExecuting(command, interceptionContext); 
    } 
} 

Và đăng ký nó như thế này (tôi đã làm nó trong Application_Start của global.asax.cs) :

DbInterception.Add(new HintInterceptor()); 

Và nó sẽ wil l cho phép bạn thay đổi CommandText. Vấn đề duy nhất là nó hiện được đính kèm cho mỗi truy vấn đọc có thể là vấn đề vì một số trong số đó có thể bị tác động tiêu cực bởi gợi ý đó. Tôi đoán tôi có thể làm một cái gì đó với bối cảnh để tìm ra nếu gợi ý là thích hợp hay không, hoặc tệ hơn trường hợp tôi có thể kiểm tra các CommandText chính nó.

Không hoàn toàn có vẻ là giải pháp trang nhã hoặc tinh tế nhất.

Sửa: Từ interceptorContext, bạn có thể lấy DbContexts, vì vậy tôi định nghĩa một giao diện trông như thế này:

public interface IQueryHintContext 
{ 
    string QueryHint { get; set; } 
    bool ApplyHint { get; set; } 
} 

Và sau đó tạo ra một lớp dẫn xuất từ ​​DbContext ban đầu của tôi (được tạo ra bởi EF) và triển khai giao diện trên. Sau đó, tôi đã thay đổi đánh chặn của tôi trông như thế này:

public class HintInterceptor : DbCommandInterceptor 
{ 
    public override void ReaderExecuting(System.Data.Common.DbCommand command, DbCommandInterceptionContext<System.Data.Common.DbDataReader> interceptionContext) 
    { 
     if (interceptionContext.DbContexts.Any(db => db is Dal.IQueryHintContext)) 
     { 
      var ctx = interceptionContext.DbContexts.First(db => db is Dal.IQueryHintContext) as Dal.IQueryHintContext; 
      if (ctx.ApplyHint) 
      { 
       command.CommandText += string.Format(" option ({0})", ctx.QueryHint); 
      } 
     } 
     base.ReaderExecuting(command, interceptionContext); 
    } 
} 

Bây giờ sử dụng nó, tôi tạo ra một bối cảnh sử dụng lớp được thừa kế của tôi thay vì bản gốc, thiết QueryHint để bất cứ điều gì tôi muốn nó được (recompile trong trường hợp này) và đặt ApplyHint ngay trước khi tôi thực thi lệnh và đặt lại thành false sau đó.

Để làm hơn tất cả điều này một chút khép kín, tôi đã kết thúc việc xác định một giao diện như thế này:

public interface IQueryHintContext 
{ 
    string QueryHint { get; set; } 
    bool ApplyHint { get; set; } 
} 

Và mở rộng bối cảnh db của tôi như thế này (bạn có thể, tất nhiên, chỉ cần sử dụng một lớp học phần để mở rộng các lớp EF tạo cũng):

public class MyEntities_Ext : MyEntities, IQueryHintContext 
{ 
    public string QueryHint { get; set; } 
    public bool ApplyHint { get; set; } 
} 

Và sau đó, để làm cho turn-on, turn-off phần một chút dễ dàng hơn để xử lý, tôi định nghĩa này:

public class HintScope : IDisposable 
{ 
    public IQueryHintContext Context { get; private set; } 
    public void Dispose() 
    { 
     Context.ApplyHint = false; 
    } 

    public HintScope(IQueryHintContext context, string hint) 
    { 
     Context = context; 
     Context.ApplyHint = true; 
     Context.QueryHint = hint; 
    } 
} 

Bây giờ sử dụng nó, tôi có thể làm chỉ này:

using (var ctx = new MyEntities_Ext()) 
{ 
    // any code that didn't need the query hint 
    // .... 
    // Now we want the query hint 
    using (var qh = new HintScope(ctx, "recompile")) 
    { 
     // query that needs the recompile hint 
    } 
    // back to non-hint code 
} 

này có lẽ hơi quá mức cần thiết và có thể phát triển hơn nữa (ví dụ, sử dụng một enum cho gợi ý có sẵn thay vì một chuỗi - hoặc subclassing một truy vấn recompile gợi ý, do đó bạn không cần phải chỉ định chuỗi recompile mỗi lần và có nguy cơ lỗi đánh máy), nhưng nó đã giải quyết được vấn đề trước mắt của tôi.

+0

Bạn có thể giải thích thêm về cách bạn gọi nó không? –

+1

Thêm 'tùy chọn (biên dịch lại)' mất> 10 giây truy vấn LINQ mà tôi đã xử lý trong vài ngày tới <1 giây. Ý tưởng tuyệt vời bằng cách sử dụng một kẻ đánh chặn để thêm nó. –

+0

@drexdrex: Tôi mở rộng thêm một chút. Hy vọng nó giúp. –

1

Có những người gọi khác là fDE_myquery ngoài mức sử dụng cụ thể của bạn không? Và bao lâu thì điều này được gọi? Vấn đề không phải là SELECT * FROM dbo.fDE_myquery(); của bạn đang nhận được gói tối ưu phụ, đó là một hoặc nhiều truy vấn bên trong của fDE_myquery đang nhận được gói tối ưu phụ. Do đó, bạn có thể chỉ cần thêm OPTION(RECOMPILE) vào một hoặc nhiều truy vấn bên trong TVF đó.

Nếu TVF này được gọi là rất nhiều thì điều này sẽ có tác động tiêu cực đến hiệu suất. Đó là lý do tại sao tôi hỏi về các ứng dụng khác của TVF này: nếu đây là chỉ, hoặc cho đến nay chính, sử dụng TVF này, thì có thể sẽ rất đáng giá nếu các kế hoạch xấu được chọn thường xuyên.

Nhưng nếu có một số người gọi khác của TVF này không gặp sự cố thì việc đặt RECOMPILE trong TVF có thể không phải là cách để đi. Mặc dù, trong trường hợp đó, bạn có thể tạo một TVF bao bọc gói gọn SELECT * FROM dbo.fDE_myquery() OPTION (RECOMPILE);. Điều này có vẻ là một giải pháp linh hoạt hơn :). Nó sẽ phải là một TVF Multistatment thay vì TVF Inline thường tốt hơn như tôi vừa thử nó và TV nội tuyến dường như không đánh giá cao điều khoản OPTION, nhưng TVF đa tầng là tốt với nó.

EDIT:
Hoặc, nếu bạn muốn xử lý này hoàn toàn trong EF, bạn chỉ có thể đưa ra một yêu cầu biên dịch lại với một dòng mã:

ctx.context.ExecuteStoreCommand("EXEC sp_recompile 'dbo.fDE_myquery';"); 

sau đó làm của bạn:

var query = from f in ctx.fDE_myQuery(aBool, anotherBool, StartDate, 
      EndDate, someInt, moreBool) 
      select f; 
+0

Tôi chắc chắn rằng mình có thể đi vào TVF (tôi không viết nó) hoặc thậm chí quấn TVF trong một TVF khác mà chỉ gọi nó bằng gợi ý truy vấn bắt buộc, nhưng tôi cho rằng đó là phương pháp cuối cùng. Dường như có * nên là * một cách để chỉ định gợi ý truy vấn trong EF không liên quan đến việc phải thay đổi mọi thứ trên cơ sở dữ liệu. –

+0

@MattBurland: đủ công bằng. Tôi chỉ có một ý tưởng khác là tôi đã cập nhật câu trả lời của mình. Có nhiều hơn 1 cách để có được một 'biên dịch lại ':). –

Các vấn đề liên quan