2008-10-22 16 views
6

में ऑर्डरबी की उपस्थिति की जांच कैसे करें मैं LINQ से Entities इकाइयों के लिए भंडार उत्पन्न करने के लिए T4 का उपयोग कर रहा हूं।ऑब्जेक्टिव <T> अभिव्यक्ति वृक्ष

भंडार में (अन्य चीजों के साथ) पेजिंग के लिए उपयुक्त एक सूची विधि शामिल है। Supported and Unsupported Methods के लिए प्रलेखन इसका उल्लेख नहीं करता है, लेकिन आप Skip को एक अज्ञात IQueryable पर "कॉल" नहीं कर सकते हैं। यह निम्न अपवाद बढ़ा देंगे:

System.NotSupportedException: विधि 'छोड़ें' केवल संस्थाओं के लिए LINQ में अनुसार क्रमबद्ध इनपुट के लिए समर्थित है। विधि 'OrderBy' विधि से पहले बुलाया जाना चाहिए 'छोड़ें' ..

मैं एक डिफ़ॉल्ट एक आंशिक विधि के माध्यम से छँटाई परिभाषित करने की अनुमति देकर इसे हल। लेकिन मुझे समस्याएं आ रही हैं कि क्या अभिव्यक्ति वृक्ष में वास्तव में OrderBy है।

मैं संभव के रूप में करने के लिए समस्या कम कर दिया है कम कोड के रूप में:

public partial class Repository 
{ 
    partial void ProvideDefaultSorting(ref IQueryable<Category> currentQuery); 

    public IQueryable<Category> List(int startIndex, int count) 
    { 
     IQueryable<Category> query = List(); 
     ProvideDefaultSorting(ref query); 
     if (!IsSorted(query)) 
     { 
      query = query.OrderBy(c => c.CategoryID); 
     } 
     return query.Skip(startIndex).Take(count); 
    } 
    public IQueryable<Category> List(string sortExpression, int startIndex, int count) 
    { 
      return List(sortExpression).Skip(startIndex).Take(count); 
    } 
    public IQueryable<Category> List(string sortExpression) 
    { 
     return AddSortingToTheExpressionTree(List(), sortExpression); 
    } 
    public IQueryable<Category> List() 
    { 
      NorthwindEntities ent = new NorthwindEntities(); 
      return ent.Categories; 
    } 

    private Boolean IsSorted(IQueryable<Category> query) 
    { 
     return query is IOrderedQueryable<Category>; 
    } 
} 

public partial class Repository 
{ 
    partial void ProvideDefaultSorting(ref IQueryable<Category> currentQuery) 
    { 
     currentQuery = currentQuery.Where(c => c.CategoryName.Contains(" ")); // no sorting.. 
    } 
} 

यह मेरा वास्तविक कार्यान्वयन नहीं है!

लेकिन मेरा प्रश्न है, मैं IsSorted विधि को कैसे कार्यान्वित कर सकता हूं? समस्या यह है कि LINQ से Entities क्वेरी हमेशा ObjectQuery प्रकार के होते हैं, जो IOrderedQueryable लागू करता है।

तो मुझे यह सुनिश्चित करना चाहिए कि अभिव्यक्ति वृक्ष में OrderBy विधि मौजूद है? पेड़ को पार्स करने का एकमात्र विकल्प क्या है?

अद्यतन
मैं स्पष्ट करना है कि यह कैसे भंडार करने के लिए छँटाई समर्थन जोड़ने, लेकिन इसकी जांच करने का करने के लिए करता है, तो ProvideDefaultSorting आंशिक विधि वास्तव में अभिव्यक्ति पेड़ के लिए एक OrderBy जोड़ा गया है के बारे में नहीं है दो अन्य भार के जोड़ दिया है ।

समस्या यह है कि पहली आंशिक कक्षा एक टेम्पलेट द्वारा उत्पन्न होती है और आंशिक वर्ग के दूसरे भाग के कार्यान्वयन को किसी अन्य समय टीम के सदस्य द्वारा बनाया जाता है। आप .NET Entity Framework को EntityContext जेनरेट करने के तरीके से इसकी तुलना कर सकते हैं, यह अन्य डेवलपर्स के लिए एक्सटेंशन पॉइंट की अनुमति देता है। तो मैं ProvideDefaultSorting सही ढंग से लागू नहीं होने पर इसे मजबूत बनाने की कोशिश करना चाहता हूं और क्रैश नहीं करना चाहता हूं।

तो शायद सवाल अधिक है, मैं कैसे पुष्टि कर सकता हूं कि ProvideDefaultSorting वास्तव में अभिव्यक्ति वृक्ष में सॉर्टिंग जोड़ता था।

अद्यतन 2
नए सवाल का जवाब दे रहा था, और स्वीकार किए जाते हैं, मुझे लगता है कि मैं और अधिक प्रश्न मिलान करने के लिए शीर्षक बदलना चाहिए। या क्या मुझे वर्तमान शीर्षक छोड़ना चाहिए क्योंकि इससे लोगों को इस समाधान के लिए एक ही समस्या का सामना करना पड़ेगा?

+0

आपको यह जवाब देखना चाहिए http://stackoverflow.com/questions/36923850/how-to-know-if-orderby-was-applied-to-query – yosbel

उत्तर

1

आप ProvideDefaultSorting की वापसी प्रकार में इस का समाधान कर सकते

मैं LinqToSql में इस समाधान का परीक्षण किया। यह कोड निर्माण नहीं करता है:

public IOrderedQueryable<int> GetOrderedQueryable() 
    { 
     IQueryable<int> myInts = new List<int>() { 3, 4, 1, 2 }.AsQueryable<int>(); 
     return myInts.Where(i => i == 2); 
    } 

यह कोड बनाता है, लेकिन कपटी है और कोडर को वह लायक है जो वे लायक हैं। (इस का निर्माण नहीं करता है) रेफरी के साथ

public IOrderedQueryable<int> GetOrderedQueryable() 
    { 
     IQueryable<int> myInts = new List<int>() { 3, 4, 1, 2 }.AsQueryable<int>(); 
     return myInts.Where(i => i == 2) as IOrderedQueryable<int>; 
    } 

एक ही कहानी:

public void GetOrderedQueryable(ref IOrderedQueryable<int> query) 
    { 
     query = query.Where(i => i == 2); 
    } 
+0

आपके समय के लिए धन्यवाद, इसने एक समस्या हल की है, * अगर * आंशिक विधि लागू की गई है, तो मैं कम से कम सुनिश्चित कर सकता हूं कि यह क्रमबद्ध है। फिर एकमात्र मामला बनी हुई है, यह देखने के लिए कि आंशिक विधि लागू की गई है या नहीं। –

+0

की आवश्यकता नहीं है - केवल आईडी सॉर्टिंग करें, और फिर आंशिक विधि में IOrderedQueryable को सौंपें। –

+0

यह एक संभावना हो सकती है, लेकिन मैं क्वेरी को यथासंभव स्वच्छ रखना चाहता हूं। –

1

मुझे डर है कि इससे थोड़ा कठिन है। आप देखते हैं, एंटीटी फ्रेमवर्क, कुछ परिस्थितियों में, silently ignore an OrderBy. तो अभिव्यक्ति वृक्ष में ऑर्डरबी की तलाश करने के लिए पर्याप्त नहीं है। ऑर्डरबी को "दाएं" स्थान पर होना चाहिए, और "दाएं" स्थान की परिभाषा इकाई फ्रेमवर्क का कार्यान्वयन विवरण है।

जैसा कि आपने अभी अनुमान लगाया है, मैं वैसे ही हूं जैसा आप हैं; मैं इकाई भंडार पैटर्न का उपयोग कर रहा हूं और प्रस्तुति परत पर एक टेक/छोड़ कर रहा हूं। जिस समाधान का मैंने उपयोग किया है, जो शायद आदर्श नहीं है, लेकिन जो भी मैं कर रहा हूं उसके लिए काफी अच्छा है, यह सुनिश्चित करने के लिए कि ऑर्डरबी हमेशा अभिव्यक्ति के पेड़ में आखिरी चीज है, यह सुनिश्चित करने के लिए कि आखिरी संभव पल तक कोई ऑर्डर नहीं करना है। तो कोई भी कार्रवाई जो टेक/छोड़ने जा रही है (सीधे या अप्रत्यक्ष रूप से) पहले ऑर्डरबी को सम्मिलित करती है। कोड संरचित किया गया है कि यह केवल एक बार हो सकता है।

+0

ठीक है , वह एक लफंगा है। तो अगर मैं सही ढंग से समझता हूं तो आप इस तर्क को हल करते हैं कि यह प्रस्तुति परत को सॉर्ट किया गया है या नहीं? मैं यह नहीं कह सकता कि एक आदर्श समाधान की तरह लगता है। –

+0

क्योंकि हम उपयोगकर्ताओं को गतिशील रूप से डेटा का सहारा लेते हैं, प्रस्तुति परत पर सॉर्टिंग करने से हमें समझ में आता है। लेकिन मैं दावा नहीं करूंगा कि यह सभी अनुप्रयोगों के लिए उपयुक्त है। ऐसा करें जहां यह आपको समझ में आता है, लेकिन सुनिश्चित करें कि यह आखिरी चीज है जो आप ले/छोड़ने से पहले करते हैं, और यह केवल एक बार किया जाता है। –

+0

मैं समझता हूं कि आप उपयोगकर्ताओं को गतिशील रूप से अपने डेटा का सहारा ले सकते हैं (उदाहरण के लिए एक ग्रिडव्यू)। मेरी ओवरलोडेड सूची विधि में से एक स्ट्रिंग सॉर्टएक्सप्रेस पैरामीटर है जिसे पार्स किया गया है और LINQ अभिव्यक्ति में परिवर्तित किया गया है। शायद समस्या वास्तव में डिफ़ॉल्ट सॉर्टिंग को कैसे लागू करें। –

2

पेजिंग एक मजबूत तरीके से आदेश पर निर्भर करता है। संचालन को कसकर दोबारा क्यों नहीं? यहाँ एक तरह से करना है कि:

समर्थन वस्तुओं

public interface IOrderByExpression<T> 
{ 
    ApplyOrdering(ref IQueryable<T> query); 
} 

public class OrderByExpression<T, U> : IOrderByExpression<T> 
{ 
    public IQueryable<T> ApplyOrderBy(ref IQueryable<T> query) 
    { 
    query = query.OrderBy(exp); 
    } 
    //TODO OrderByDescending, ThenBy, ThenByDescending methods. 

    private Expression<Func<T, U>> exp = null; 

    //TODO bool descending? 
    public OrderByExpression (Expression<Func<T, U>> myExpression) 
    { 
    exp = myExpression; 
    } 
} 

चर्चा के अंतर्गत विधि:

public IQueryable<Category> List(int startIndex, int count, IOrderByExpression<Category> ordering) 
{ 
    NorthwindEntities ent = new NorthwindEntities(); 
    IQueryable<Category> query = ent.Categories; 
    if (ordering == null) 
    { 
     ordering = new OrderByExpression<Category, int>(c => c.CategoryID) 
    } 
    ordering.ApplyOrdering(ref query); 

    return query.Skip(startIndex).Take(count); 
} 

कुछ समय बाद, विधि बुला:

var query = List(20, 20, new OrderByExpression<Category, string>(c => c.CategoryName)); 
+0

हालांकि यह एक सॉर्टिंग पैरामीटर प्रदान करने का एक अच्छा तरीका है, मेरे इंटरफ़ेस में पहले से ही एक अधिभार शामिल है जो सॉर्टिंग प्रदान करता है। मैं इसे और अधिक स्पष्ट करने के लिए अपना प्रश्न बदल दूंगा। –

0
ProvideDefaultSorting(ref query); 
    if (!IsSorted(query)) 
    { 
      query = query.OrderBy(c => c.CategoryID); 
    } 

इसमें बदलें:

//apply a default ordering 
    query = query.OrderBy(c => c.CategoryID); 
    //add to the ordering 
    ProvideDefaultSorting(ref query); 

यह एक सही समाधान नहीं है।

यह आपके द्वारा बताई गई "ऑर्डरिंग फ़ंक्शन में फ़िल्टर" समस्या को हल नहीं करता है। यह हल करता है "मैं ऑर्डरिंग को लागू करना भूल गया" या "मैं ऑर्डर नहीं करना चुनता"।

public void OrderManyTimes() 
    { 
     DataClasses1DataContext myDC = new DataClasses1DataContext(); 
     var query = myDC.Customers.OrderBy(c => c.Field3); 
     query = query.OrderBy(c => c.Field2); 
     query = query.OrderBy(c => c.Field1); 

     Console.WriteLine(myDC.GetCommand(query).CommandText); 

    } 

उत्पन्न करता है (ध्यान दें orderings के विपरीत क्रम):

SELECT Field1, Field2, Field3 
FROM [dbo].[Customers] AS [t0] 
ORDER BY [t0].[Field1], [t0].[Field2], [t0].[Field3] 
+0

यह वास्तव में काम करेगा, और फिलहाल मेरे पास यह अस्थायी समाधान (निर्माण को तोड़ने के लिए नहीं) के रूप में है। लेकिन यह वह समाधान नहीं है जिसे मैं ढूंढ रहा था। –

1

डेविड B के लिए धन्यवाद मैं एक निम्न समाधान मिल गया है। (मुझे उस स्थिति के लिए पहचान जोड़नी पड़ी जहां आंशिक विधि निष्पादित नहीं की गई थी या बस इसे पैरामीटर वापस कर दिया गया था)।

public partial class Repository 
{ 
    partial void ProvideDefaultSorting(ref IOrderedQueryable<Category> currentQuery); 

    public IQueryable<Category> List(int startIndex, int count) 
    { 
     NorthwindEntities ent = new NorthwindEntities(); 
     IOrderedQueryable<Category> query = ent.CategorySet; 
     var oldQuery = query; 
     ProvideDefaultSorting(ref query); 
     if (oldQuery.Equals(query)) // the partial method did nothing with the query, or just didn't exist 
     { 
      query = query.OrderBy(c => c.CategoryID); 
     } 
     return query.Skip(startIndex).Take(count); 
    } 
    // the rest..   
} 

public partial class Repository 
{ 
    partial void ProvideDefaultSorting(ref IOrderedQueryable<Category> currentQuery) 
    { 
     currentQuery = currentQuery.Where(c => c.CategoryName.Contains(" ")).OrderBy(c => c.CategoryName); // compile time forced sotring 
    } 
} 

यह संकलन समय पर सुनिश्चित करता है कि अगर आंशिक विधि कार्यान्वित किया जाता है, यह कम से कम यह रखना चाहिए एक IOrderdQueryable।

और जब आंशिक विधि लागू नहीं की जाती है या केवल इसके पैरामीटर को लौटाती है, तो क्वेरी बदली नहीं जाएगी, और वह फ़ॉलबैक सॉर्ट का उपयोग करेगी।

0

मैंने एक समाधान लागू किया है जो डिफ़ॉल्ट प्राथमिक क्रम के रूप में अपनी प्राथमिक कुंजी द्वारा जो भी संग्रह टाइप करता है, निर्दिष्ट नहीं है। शायद यह आपके लिए काम करेगा।

चर्चा और सामान्य उद्देश्य कोड के लिए http://johnkaster.wordpress.com/2011/05/19/a-bug-fix-for-system-linq-dynamic-and-a-solution-for-the-entity-framework-4-skip-problem/ देखें। (और गतिशील LINQ के लिए एक आकस्मिक बग फिक्स।)

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