2009-11-06 12 views
53

के रूप में संपत्ति नाम का उपयोग कर किसी संपत्ति द्वारा आदेश देने के लिए कोड C# में किसी संपत्ति के विरुद्ध कोड करने का सबसे आसान तरीका क्या है जब मेरे पास स्ट्रिंग के रूप में संपत्ति का नाम हो? उदाहरण के लिए, मैं उपयोगकर्ता को अपनी पसंद की संपत्ति (LINQ का उपयोग करके) कुछ खोज परिणामों को ऑर्डर करने की अनुमति देना चाहता हूं। वे यूआई में "ऑर्डर बाय" प्रॉपर्टी का चयन करेंगे - पाठ्यक्रम के स्ट्रिंग वैल्यू के रूप में। गुणों के तारों को मैप करने के लिए सशर्त तर्क (यदि/else, स्विच) का उपयोग किए बिना, linq क्वेरी की एक संपत्ति के रूप में सीधे उस स्ट्रिंग का उपयोग करने का कोई तरीका है। प्रतिबिंब?सी # - स्ट्रिंग

तार्किक रूप से, यह मैं क्या करना चाहते हैं क्या है:

query = query.OrderBy(x => x."ProductId"); 

अद्यतन: मैं मूल रूप से है कि मैं संस्थाओं को LINQ का उपयोग कर रहा हूँ निर्दिष्ट नहीं किया है - यह है कि प्रतिबिंब दिखाई देता है (कम से कम getProperty, GetValue दृष्टिकोण) L2E में अनुवाद नहीं करता है।

+0

मुझे लगता है कि आप प्रतिबिंब का उपयोग करना चाहते हैं, और मुझे यकीन है कि आप एक लैम्ब्डा अभिव्यक्ति में प्रतिबिंब का उपयोग कर सकते है जब एक सूची के खिलाफ LINQ का उपयोग नहीं कर रहा हूँ ... अच्छी तरह से, Linq में एसक्यूएल करने के लिए लगभग निश्चित रूप से नहीं लेकिन शायद या कुछ और। – CodeRedick

+0

@ टेलोस: कोई कारण नहीं है कि आप लैम्ब्डा में प्रतिबिंब (या कोई अन्य एपीआई) का उपयोग नहीं कर सकते हैं। यदि कोड अभिव्यक्ति के रूप में मूल्यांकन किया जाता है और किसी अन्य चीज़ में अनुवाद किया जाता है (जैसे LINQ-to-SQL, जैसा कि आप सुझाव देते हैं) पूरी तरह से एक और सवाल है, तो यह काम करेगा या नहीं। –

+0

यही कारण है कि मैंने एक उत्तर के बजाय एक टिप्पणी पोस्ट की। ;) ज्यादातर Linq2SQL के लिए उपयोग किया जाता है ... – CodeRedick

उत्तर

77

मैं इस विकल्प को हर किसी के पोस्ट के लिए पेश करता हूं।

System.Reflection.PropertyInfo prop = typeof(YourType).GetProperty("PropertyName"); 

query = query.OrderBy(x => prop.GetValue(x, null)); 

यह गुण प्राप्त करने के लिए प्रतिबिंब एपीआई के लिए कॉल दोहराया टाल। अब एकमात्र बार-बार कॉल मूल्य प्राप्त कर रहा है।

हालांकि

मैं बजाय, उन्हें PropertyDescriptor का उपयोग कर की वकालत के रूप में इस कस्टम के लिए TypeDescriptor अपने प्रकार को सौंपा जा करने की अनुमति देगा होगा यह संभव गुणों और मूल्यों को पुन: प्राप्त करने के लिए हल्के संचालन के लिए बना रही है। एक कस्टम डिस्क्रिप्टर की अनुपस्थिति में यह किसी भी तरह प्रतिबिंब पर वापस आ जाएगा।

PropertyDescriptor prop = TypeDescriptor.GetProperties(typeof(YourType)).Find("PropertyName"); 

query = query.OrderBy(x => prop.GetValue(x)); 

यह तेजी से ऊपर के रूप में, मार्क बजरी के CodeProject पर HyperDescriptor परियोजना की जाँच करें। मैंने इसे बड़ी सफलता के साथ उपयोग किया है; यह व्यापार वस्तुओं पर उच्च प्रदर्शन डेटा बाध्यकारी और गतिशील संपत्ति संचालन के लिए एक जीवन बचतकर्ता है।

+0

अच्छी बात। उन lambdas – dkackman

+0

के साथ सावधान रहना चाहिए ध्यान दें कि परिलक्षित आमंत्रण (यानी GetValue) प्रतिबिंब का सबसे महंगा हिस्सा है। मेटाडाटा पुनर्प्राप्ति (यानी।GetProperty) वास्तव में कम महंगा है (परिमाण के क्रम से), इसलिए उस हिस्से को कैशिंग करके, आप वास्तव में खुद को इतना बचत नहीं कर रहे हैं। यह किसी भी तरह से बहुत अधिक लागत जा रहा है, और वह लागत भारी होने जा रही है। बस कुछ ध्यान दें। – jrista

+1

@jrista: निश्चित होने के लिए आमंत्रण सबसे महंगा है। हालांकि, "कम महंगा" का मतलब "मुक्त" नहीं है, या इसके करीब भी है। मेटाडाटा पुनर्प्राप्ति समय की एक छोटी-छोटी अवधि लेती है, इसलिए इसे कैशिंग करने के लिए कोई फायदा नहीं होता है और कोई * नुकसान नहीं होता है (जब तक कि मुझे यहां कुछ याद नहीं आ रहा है)। सच में यह वास्तव में 'प्रॉपर्टी डिस्क्रिप्टर' का उपयोग करना चाहिए (कस्टम टाइप डिस्क्रिप्टर के लिए खाते में, जो * लाइटवेट ऑपरेशन को मूल्य पुनर्प्राप्त कर सकता है)। –

11

हाँ, मुझे नहीं लगता कि प्रतिबिंब से एक और तरीका है।

उदाहरण:

query = query.OrderBy(x => x.GetType().GetProperty("ProductId").GetValue(x, null)); 
2

प्रतिबिंब जवाब है!

typeof(YourType).GetProperty("ProductId").GetValue(theInstance); 

चीजें आप परिलक्षित PropertyInfo कैश करने के लिए, बुरा तार के लिए जाँच, आपकी क्वेरी तुलना समारोह, आदि लिखना कर सकते हैं के बहुत सारे है, लेकिन इसके दिल में, यह आप क्या करते हैं।

5
query = query.OrderBy(x => x.GetType().GetProperty("ProductId").GetValue(x, null)); 

मेरे सिर के ऊपर से सटीक वाक्यविन्यास को याद करने की कोशिश कर रहा है लेकिन मुझे लगता है कि यह सही है।

3

आप गतिशील लिंक का उपयोग कर सकते हैं - this ब्लॉग देखें।

इसके अलावा this Stackoverflow पद की जाँच ...

+0

यह मेरे लिए सबसे अच्छा जवाब है – Demodave

37

मैं एक छोटे से पार्टी के लिए देर हो रही है, फिर भी, मुझे आशा है कि यह कुछ मदद की हो सकता है।

प्रतिबिंब का उपयोग करने में समस्या यह है कि परिणामी अभिव्यक्ति वृक्ष लगभग निश्चित रूप से आंतरिक .NET प्रदाता के अलावा किसी भी लिंक प्रदाता द्वारा समर्थित नहीं होगा।यह आंतरिक संग्रह के लिए ठीक है, हालांकि यह काम नहीं करेगा जहां अंकन से पहले सॉर्टिंग (एसक्यूएल, मोंगो डीबी, आदि) पर सॉर्टिंग किया जाना है। नीचे

कोड नमूना OrderBy और OrderByDescending के लिए IQueryable विस्तार तरीके प्रदान, और इसलिए की तरह इस्तेमाल किया जा सकता है:

query = query.OrderBy("ProductId"); 

एक्सटेंशन विधि:

public static class IQueryableExtensions 
{ 
    public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> source, string propertyName) 
    { 
     return source.OrderBy(ToLambda<T>(propertyName)); 
    } 

    public static IOrderedQueryable<T> OrderByDescending<T>(this IQueryable<T> source, string propertyName) 
    { 
     return source.OrderByDescending(ToLambda<T>(propertyName)); 
    } 

    private static Expression<Func<T, object>> ToLambda<T>(string propertyName) 
    { 
     var parameter = Expression.Parameter(typeof(T)); 
     var property = Expression.Property(parameter, propertyName); 
     var propAsObject = Expression.Convert(property, typeof(object)); 

     return Expression.Lambda<Func<T, object>>(propAsObject, parameter);    
    } 
} 

सादर, मार्क।

+6

एक समाधान के लिए वोट जो EF – icesar

+0

उत्कृष्ट समाधान के लिए काम करता है - मैं बिल्कुल यही ढूंढ रहा था। मुझे वास्तव में अभिव्यक्ति के पेड़ों में खोदने की जरूरत है। उस पर अभी भी बहुत रूकी है। @ मार्क, नेस्टेड अभिव्यक्ति करने के लिए कोई समाधान? कहें कि मेरे पास एक प्रकार टी है जिसमें एक संपत्ति "उप" प्रकार TSub है जिसमें स्वयं की संपत्ति "मान" है। अब मैं "Sub.Value" स्ट्रिंग के लिए अभिव्यक्ति अभिव्यक्ति > अभिव्यक्ति प्राप्त करना चाहता हूं। –

+4

'संपत्ति' को 'ऑब्जेक्ट' में बदलने के लिए हमें 'Expression.Convert' क्यों चाहिए? मुझे 'System.Object' टाइप करने के लिए 'System.String' टाइप करने में असमर्थता मिल रही है। LINQ से संस्थाएं केवल ईडीएम आदिम या गणना प्रकार कास्टिंग का समर्थन करती हैं। 'त्रुटि, और इसे हटाने का काम लगता है। – ShuberFu

9

मैं @Mark Powell से जवाब पसंद आया, लेकिन जैसा कि @ShuberFu ने कहा, यह त्रुटि LINQ to Entities only supports casting EDM primitive or enumeration types देता है।

var propAsObject = Expression.Convert(property, typeof(object)); को हटाकर उन गुणों के साथ काम नहीं किया जो मूल्य प्रकार थे, जैसे पूर्णांक, क्योंकि यह स्पष्ट रूप से ऑब्जेक्ट करने के लिए int को बॉक्स नहीं करेगा।

Kristofer Andersson और Marc Gravell से विचारों का उपयोग करके मुझे संपत्ति के नाम का उपयोग करके क्वेरी करने योग्य फ़ंक्शन बनाने का एक तरीका मिला और यह अभी भी इकाई फ्रेमवर्क के साथ काम करता है। मैंने एक वैकल्पिक आईसीओएमपेयर पैरामीटर भी शामिल किया। सावधानी: आईसीओएमपेयर पैरामीटर एंटिटी फ्रेमवर्क के साथ काम नहीं करता है और लिंक से एसक्यूएल का उपयोग करते समय बाहर छोड़ा जाना चाहिए।

Sql को इकाई की रूपरेखा और Linq के साथ निम्न काम करता है:

query = query.OrderBy("ProductId"); 

और @Simon Scheurer यह भी काम करता है:

query = query.OrderBy("ProductCategory.CategoryId"); 

और तुम Sql को इकाई की रूपरेखा या Linq का उपयोग नहीं कर रहे हैं, यह काम करता है :

query = query.OrderBy("ProductCategory", comparer); 

यहां कोड है:

public static class IQueryableExtensions 
{  
public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> query, string propertyName, IComparer<object> comparer = null) 
{ 
    return CallOrderedQueryable(query, "OrderBy", propertyName, comparer); 
} 

public static IOrderedQueryable<T> OrderByDescending<T>(this IQueryable<T> query, string propertyName, IComparer<object> comparer = null) 
{ 
    return CallOrderedQueryable(query, "OrderByDescending", propertyName, comparer); 
} 

public static IOrderedQueryable<T> ThenBy<T>(this IOrderedQueryable<T> query, string propertyName, IComparer<object> comparer = null) 
{ 
    return CallOrderedQueryable(query, "ThenBy", propertyName, comparer); 
} 

public static IOrderedQueryable<T> ThenByDescending<T>(this IOrderedQueryable<T> query, string propertyName, IComparer<object> comparer = null) 
{ 
    return CallOrderedQueryable(query, "ThenByDescending", propertyName, comparer); 
} 

/// <summary> 
/// Builds the Queryable functions using a TSource property name. 
/// </summary> 
public static IOrderedQueryable<T> CallOrderedQueryable<T>(this IQueryable<T> query, string methodName, string propertyName, 
     IComparer<object> comparer = null) 
{ 
    var param = Expression.Parameter(typeof(T), "x"); 

    var body = propertyName.Split('.').Aggregate<string, Expression>(param, Expression.PropertyOrField); 

    return comparer != null 
     ? (IOrderedQueryable<T>)query.Provider.CreateQuery(
      Expression.Call(
       typeof(Queryable), 
       methodName, 
       new[] { typeof(T), body.Type }, 
       query.Expression, 
       Expression.Lambda(body, param), 
       Expression.Constant(comparer) 
      ) 
     ) 
     : (IOrderedQueryable<T>)query.Provider.CreateQuery(
      Expression.Call(
       typeof(Queryable), 
       methodName, 
       new[] { typeof(T), body.Type }, 
       query.Expression, 
       Expression.Lambda(body, param) 
      ) 
     ); 
} 
} 
+0

उत्कृष्ट जवाब, इच्छा है कि मैं आपको दो बार ऊपर उठा सकता हूं – Anduril

1

गतिशील आदेश आइटम के लिए प्रतिबिंब विस्तार की तुलना में अधिक उत्पादक:

public static class DynamicExtentions 
{ 
    public static object GetPropertyDynamic<Tobj>(this Tobj self, string propertyName) where Tobj : class 
    { 
     var param = Expression.Parameter(typeof(Tobj), "value"); 
     var getter = Expression.Property(param, propertyName); 
     var boxer = Expression.TypeAs(getter, typeof(object)); 
     var getPropValue = Expression.Lambda<Func<Tobj, object>>(boxer, param).Compile();    
     return getPropValue(self); 
    } 
} 

उदाहरण:

var ordered = items.OrderBy(x => x.GetPropertyDynamic("ProductId")); 
0

इसके अलावा Dynamic Expressions इस समस्या को हल कर सकते हैं। आप LINQ अभिव्यक्तियों के माध्यम से स्ट्रिंग-आधारित क्वेरी का उपयोग कर सकते हैं जिन्हें रन-टाइम पर गतिशील रूप से बनाया जा सकता था।

var query = query 
      .Where("Category.CategoryName == @0 and Orders.Count >= @1", "Book", 10) 
      .OrderBy("ProductId") 
      .Select("new(ProductName as Name, Price)"); 
संबंधित मुद्दे