सी # - एक स्ट्रिंग के रूप में संपत्ति के नाम का उपयोग करके एक संपत्ति द्वारा ऑर्डर करने के लिए कोड


92

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

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

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

अपडेट: मैंने मूल रूप से निर्दिष्ट नहीं किया था कि मैं Linq से Entities का उपयोग कर रहा हूं - यह प्रतीत होता है कि प्रतिबिंब (कम से कम गेटप्रॉपी, गेटवैल्यू दृष्टिकोण) L2E में अनुवाद नहीं करता है।


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

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

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

1
बस उसी समस्या को दूर करना था .. नीचे मेरा जवाब देखें। stackoverflow.com/a/21936366/775114
मार्क पॉवेल

जवाबों:


129

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

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));

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


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

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

ASP.NET GridView को क्रमबद्ध रूप से संभालने के लिए इस तरह से कुछ घंटों के लिए खोजा गया: PropertyDescriptor Prop = TypeDescriptor.GetProperties (टाइपोफॉरशिप (ScholarshipRequest))। Find (e .ortExpression, true)।
बैक्सटर

1
stackoverflow.com/questions/61635636/… परावर्तन के साथ एक समस्या थी जो EfCore 3.1.3 में काम नहीं करती थी। यह EfCore 2 में त्रुटि फेंकने के लिए लगता है जिसे चेतावनी के लिए सक्रिय करने की आवश्यकता है। नीचे
@Mark

1
मुझे निम्न प्राप्त होता है: InvalidOperationException: LINQ एक्सप्रेशन 'DbSet <MyObject> .Where (t => t.IsMasterData) .OrderBy (t => t.GGDType) (। GetProperty ("पता")। GetValue (obj: t)। सूचकांक: null) .GetType ()) 'का अनुवाद नहीं किया जा सका। या तो क्वेरी को किसी ऐसे रूप में फिर से लिखें, जिसका अनुवाद किया जा सकता है, या ग्राहक के मूल्यांकन पर स्विच करके या तो AsEnumerable (), AsAsyncEnumerable (), ToList (), या toListAsync () को कॉल करके सम्मिलित किया जा सकता है।
रात 12:56

67

मुझे पार्टी में थोड़ी देर हो गई है, हालांकि, मुझे उम्मीद है कि यह कुछ मदद कर सकता है।

प्रतिबिंब का उपयोग करने के साथ समस्या यह है कि परिणामी अभिव्यक्ति ट्री लगभग निश्चित रूप से आंतरिक नेट प्रदाता के अलावा किसी भी Linq प्रदाताओं द्वारा समर्थित नहीं किया जाएगा। यह आंतरिक संग्रहों के लिए ठीक है, हालाँकि यह काम नहीं करेगा जहाँ पेजिंग से पहले स्रोत (जैसे कि SQL, MongoDb, आदि) पर छँटाई करना है।

नीचे दिए गए कोड का नमूना ऑर्डरबाय और ऑर्डरबायस्केंडिंग के लिए IQueryable extention विधियां प्रदान करता है, और इसका उपयोग इस तरह किया जा सकता है:

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);            
    }
}

सादर, मार्क


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

4
क्यों हम क्या ज़रूरत है Expression.Convertपरिवर्तित करने के लिए propertyकरने के लिए object? मुझे एक Unable to cast the type 'System.String' to type 'System.Object'. LINQ to Entities only supports casting EDM primitive or enumeration types.त्रुटि मिल रही है , और इसे हटाने से काम लगता है।
शुबेरफ्यू

@Demodave अगर मुझे सही से याद है। var propAsObject = Expression.Convert(property, typeof(object));और बस का उपयोग propertyके स्थान परpropAsObject
ShuberFu

सोना। .Net कोर 2.0.5 के लिए अनुकूलित।
क्रिस एमेलिंक्स

2
मिला त्रुटिLINQ to Entities only supports casting EDM primitive or enumeration types
Mateusz Puwałowski

35

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

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

से उपयोग करना विचार Kristofer एंडरसन और मार्क Gravell मैं संपत्ति नाम का उपयोग Queryable समारोह का निर्माण और यह अभी भी इकाई की रूपरेखा के साथ काम करने के लिए एक रास्ता मिल गया। मैंने एक वैकल्पिक IComparer पैरामीटर भी शामिल किया है। सावधानी: IComparer पैरामीटर Entity फ्रेमवर्क के साथ काम नहीं करता है और Linq से Sql का उपयोग करते समय इसे छोड़ दिया जाना चाहिए।

निम्नलिखित इकाई फ्रेमवर्क और Linq Sql के साथ काम करता है:

query = query.OrderBy("ProductId");

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

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

और अगर आप Sql में Entity Framework या 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)
            )
        );
}
}

Geez, यार, क्या आप Microsoft हैं? :) यह Aggregateटुकड़ा बहुत बढ़िया है! यह EF Core मॉडल से बनाए गए आभासी विचारों का ध्यान रखता है Join, क्योंकि मैं "T.Property" जैसे गुणों का उपयोग करता हूं। अन्यथा आदेश देने के बाद या Joinतो उत्पादन असंभव होगा । और मुझे AFTER को ऑर्डर करने की आवश्यकता है , क्योंकि अधिकांश प्रश्न स्थिर हैं, विचारों में आदेश नहीं हैं। InvalidOperationExceptionNullReferenceExceptionJoin
हैरी

@Harry। धन्यवाद, लेकिन मैं वास्तव में Aggregateटुकड़े के लिए बहुत अधिक क्रेडिट नहीं ले सकता । मेरा मानना ​​है कि यह मार्क ग्रेवेल के कोड का संयोजन और एक अंतर्मुखी सिफारिश था। :)
डेविड स्पेक्ट

@DavidSpecht मैं सिर्फ अभिव्यक्ति पेड़ सीख रहा हूं, इसलिए उनके बारे में सब कुछ अब तक मेरे लिए काला जादू है। लेकिन मैं जल्दी से सीखता हूं, वीएस में सी # इंटरैक्टिव विंडो बहुत मदद करती है।
हैरी

इसका उपयोग कैसे करें?
दत्त गुयेन

@ डैट गुयेन के बजाय products.OrderBy(x => x.ProductId), आप का उपयोग कर सकते हैंproducts.OrderBy("ProductId")
डेविड

12

हां, मुझे नहीं लगता कि प्रतिबिंब के अलावा कोई और तरीका है।

उदाहरण:

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

मैं त्रुटि प्राप्त करता हूं "LINQ to Entities does not recognize the method 'System.Object GetValue(System.Object)' method, and this method cannot be translated into a store expression."कोई विचार या सलाह, कृपया?
फ्लोरिन वोरडोल

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

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


2

प्रतिबिंब उत्तर है!

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

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


2

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

इसके अलावा इस StackOverFlow पोस्ट की जाँच करें ...


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

2

डायनेमिक ऑर्डर आइटम के प्रतिबिंब विस्तार से अधिक उत्पादक:

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"));

इसके अलावा, आपको संकलित लैम्ब्स को कैश करने की आवश्यकता हो सकती है (उदाहरण के लिए शब्दकोश <>)


1

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

var query = query
          .Where("Category.CategoryName == @0 and Orders.Count >= @1", "Book", 10)
          .OrderBy("ProductId")
          .Select("new(ProductName as Name, Price)");

0

मुझे लगता है कि हम एक शक्तिशाली उपकरण नाम अभिव्यक्ति का उपयोग कर सकते हैं इस मामले में एक विस्तार विधि के रूप में इसका उपयोग इस प्रकार है:

public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> source, string ordering, bool descending)
{
    var type = typeof(T);
    var property = type.GetProperty(ordering);
    var parameter = Expression.Parameter(type, "p");
    var propertyAccess = Expression.MakeMemberAccess(parameter, property);
    var orderByExp = Expression.Lambda(propertyAccess, parameter);
    MethodCallExpression resultExp = 
        Expression.Call(typeof(Queryable), (descending ? "OrderByDescending" : "OrderBy"), 
            new Type[] { type, property.PropertyType }, source.Expression, Expression.Quote(orderByExp));
    return (IOrderedQueryable<T>)source.Provider.CreateQuery<T>(resultExp);
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.