2016-01-17 6 views
7

जबकि Entity Framework performance पर एक लेख पढ़ रहा है, मैं जानकारी के इस टुकड़े भर में आया था:एक LINQ अभिव्यक्ति क्वेरी नए सिरे से लिखना कैशिंग एसक्यूएल निष्पादन योजना को सक्षम करने के

दूसरे, समस्या [एसक्यूएल सर्वर कार्य योजना लागू करके पुन: उपयोग नहीं होगा ] पहली जगह में होता है क्योंकि (छोड़ने के लिए) (int) के लिए एक int गुजरते समय (कार्यान्वयन विस्तार के कारण), इकाई फ्रेमवर्क यह नहीं देख सकता कि क्या वे पूर्ण मूल्यों को लेते हैं जैसे कि टेक (100), या चर (परिणामPerPage) जैसे परिवर्तनीय, इसलिए यह नहीं पता कि मान पैरामीटरकृत किया जाना चाहिए या नहीं।

var schools = db.Schools 
    .OrderBy(s => s.PostalZipCode) 
    .Skip(model.Page * model.ResultsPerPage) 
    .Take(model.ResultsPerPage) 
    .ToList(); 

इस शैली करने के लिए:

प्रस्तावित समाधान कोड के इस शैली बदलने के लिए है

int resultsToSkip = model.Page * model.ResultsPerPage; 
var schools = db.Schools 
    .OrderBy(s => s.PostalZipCode) 
    .Skip(() => resultsToSkip) //must pre-calculate this value 
    .Take(() => model.ResultsPerPage) 
    .ToList(); 

कौन सा इकाई की रूपरेखा पता चला है कि इन चरों कर रहे हैं और कि उत्पन्न की अनुमति देता है एसक्यूएल को parametrized किया जाना चाहिए, जो बदले में निष्पादन योजना का पुन: उपयोग करने की अनुमति देता है।

हमारे पास हमारे एप्लिकेशन में कुछ कोड है जो वैरिएबल का उपयोग उसी तरह करता है, लेकिन हमें रनटाइम पर अभिव्यक्ति बनाना होगा क्योंकि प्रकार पहले से ज्ञात नहीं है।

var convertedId = typeof(T).GetConvertedIdValue(id); 
var prop = GetIdProperty(typeof(T)); 

var itemParameter = Expression.Parameter(typeof(T), "item"); 
var whereExpression = Expression.Lambda<Func<T, bool>> 
    (
    Expression.Equal(
     Expression.Property(
      itemParameter, 
      prop.Name 
      ), 
     Expression.Constant(convertedId) 
     ), 
    new[] { itemParameter } 
    ); 

return Get<T>().Where(whereExpression); 

समस्या यह है कि Expression.Constant(convertedId) का उपयोग कर एक निरंतर उत्पन्न एसक्यूएल करने के लिए सम्मिलित करने के लिए कारण बनता है:

यहाँ क्या यह की तरह लग रहे करने के लिए प्रयोग किया जाता है।

WHERE [Extent1].[Id] = 1234 

और:

WHERE [Extent1].[Id] = 1235 

और: यह एसक्यूएल हर नया आइटम जिसे आप खोजना है, जो किसी भी कार्य योजना लागू करके कैशिंग बंद हो जाता है के लिए बदलने के लिए कारण बनता है

WHERE [Extent1].[Id] = 1236 

सवाल तो, जेनरेट एसक्यूएल के पैरामीट्रिजेशन को मजबूर करने के लिए आप इस तरह अभिव्यक्ति भवन का उपयोग कैसे कर सकते हैं?() => convertedId वाक्यविन्यास काम नहीं करेगा। मैंने इसका उत्तर दिया है।

+0

मुझे यह नहीं मिला, यहां सवाल क्या है? –

+0

प्रश्न था कि Expression.Constant का उपयोग करते समय पैरामीट्रिज्ड एसक्यूएल उत्पन्न करने के लिए ऊपर दिए गए कोड को कैसे परिवर्तित किया जाए, क्योंकि '() => convertId' वाक्यविन्यास काम नहीं करता है। –

+0

मैंने मुख्य पोस्ट को स्पष्ट रूप से प्रश्न शामिल करने के लिए अपडेट किया है। –

उत्तर

5

परीक्षण और त्रुटि के एक बहुत बाद, हमने पाया आप अभी भी थोड़ा बदल रहा है कि हम इसे कैसे पास से एक पैरामीटर के रूप convertedId पहचान करने के लिए इकाई की रूपरेखा के लिए मजबूर कर सकते हैं:

.... 

var convObj = new 
{ 
    id = convertedId 
}; 
var rightExp = Expression.Convert(Expression.Property(Expression.Constant(convObj), "id"), convertedId.GetType()); 

var whereExpression = Expression.Lambda<Func<T, bool>> 
    (
    Expression.Equal(
     Expression.Property(
      itemParameter, 
      prop.Name 
      ), 
     rightExp 
     ), 
    new[] { itemParameter } 
    ); 

return Get<T>().Where(whereExpression); 

उत्पन्न एसक्यूएल का उपयोग करने का कारण बनता है कौन सा एक ही पैरामीटर (और कोड) किसी भी आईडी के लिए:

WHERE [Extent1].[Id] = @p__linq__0 

सवाल है कि हम साथ काम कर रहे थे में क्वेरी निष्पादन योजना उत्पन्न करने के लिए एक लंबा समय लगता है, इसलिए हम नई ID तक पहुँचने के लिए निष्पादन समय में एक महत्वपूर्ण कमी देखा (से 3 ~ 4 सेकंड ~ 300 मिलीसेकंड तक)

+0

यह सही है, वगैरह उदाहरण के बजाय निरंतर मानों का उपयोग करके जेनरेट किए गए एसक्यूएल की अंतर्निहित समस्या को हाइलाइट करने का एक आसान तरीका है। –

2

मुझे दोबारा दोबारा दोहराएं।

आप इस

var item = Expression.Parameter(typeof(T), "item"); 
var left = Expression.Property(item, idPropertyName); 
Expression right = ...; 
var body = Expression.Equal(left, right); 
var predicate = Expression.Lambda<Func<T, bool>>(body, item); 

तरह Expression<Func<T, bool>> निर्माण कर रहे हैं और प्रश्न किस क्रम में right के लिए इस्तेमाल किया जाना चाहिए एफई एक निरंतर रूप में यह इलाज नहीं बनाना है।

तरह

var right = Expression.Convert(Expression.Constant(convertedId), left.Type); 

जाहिर आदिम मूल्य काम नहीं करता है, तो समाधान कुछ वर्ग उदाहरण के संपत्ति प्रदान करना है। आपने अनाम प्रकार का उपयोग करके इसे हल किया है, लेकिन निश्चित रूप से ऐसा करने के कई अन्य तरीके हैं।

उदाहरण के लिए

, को बंद करने का उपयोग कर

Expression<Func<object>> closure =() => convertedId; 
var right = Expresion.Convert(closure.Body, left.Type); 

या Tuple<T> उदाहरण (जैसे कि यह हो गया होता यदि आप अभिव्यक्ति मैन्युअल बनाने नहीं कर रहे थे) (थोड़ा वर्बोज़, लेकिन समाप्त Expression.Convert)

var tuple = Activator.CreateInstance(
    typeof(Tuple<>).MakeGenericType(left.Type), convertedId); 
var right = Expression.Property(Expression.Constant(tuple), "Item1"); 

आदि

+0

अच्छा! मुझे क्लोजर समाधान बहुत पसंद है –

संबंधित मुद्दे