मैं Linq OrderBy तर्क को गतिशील रूप से कैसे निर्दिष्ट करूं?


94

orderbyएक पैरामीटर के रूप में मेरे द्वारा लिए गए मूल्य का उपयोग करने के लिए पारित तर्क को मैं कैसे निर्दिष्ट करूं ?

उदाहरण के लिए:

List<Student> existingStudends = new List<Student>{ new Student {...}, new Student {...}}

वर्तमान में कार्यान्वयन:

List<Student> orderbyAddress = existingStudends.OrderBy(c => c.Address).ToList();

इसके बजाय c.Address, मैं इसे एक पैरामीटर के रूप में कैसे ले सकता हूं?

उदाहरण

 string param = "City";
 List<Student> orderbyAddress = existingStudends.OrderByDescending(c => param).ToList();

4
आप गतिशील Linq की तलाश में हो सकते हैं: weblogs.asp.net/scottgu/archive/2008/01/07/…
BrokenGlass

@Nev_Rahd: सवाल को थोड़ा स्पष्ट करने की कोशिश की। इसके अलावा, OrderByएक Linq सुविधा है, और पर है IEnumerable, के लिए विशिष्ट नहीं एक सुविधा है List। बेझिझक वापस संपादित करें या इसे आगे बदलें :) :)
मर्लिन मॉर्गन-ग्राहम

जवाबों:


129

यहाँ प्रतिबिंब का उपयोग कर एक possiblity है ...

var param = "Address";    
var propertyInfo = typeof(Student).GetProperty(param);    
var orderByAddress = items.OrderBy(x => propertyInfo.GetValue(x, null));

3
लेकिन क्या यह सच है जब प्रदाताओं द्वारा व्याख्या की गई Linq अभिव्यक्तियों की तरह, Entity Framework (sql server, or other) ??
a.boussema

2
@ संजय - ThenByविधि का उपयोग करें ।
कोडकॉन्क्सन

7
जब मैं यह कोशिश करता हूं तो मुझे यह त्रुटि मिलती है: LINQ से एंटिटीज विधि 'System.Object GetValue (System.Object, System.Object [])' विधि को मान्यता नहीं देता है, और इस पद्धति को स्टोर अभिव्यक्ति में अनुवाद नहीं किया जा सकता है। क्या यह जवाब केवल Linq To SQL पर लागू होता है?
दीक्षा

4
.AsEnumerable (): var orderByAddress = items.AsEnumableable () के साथ कोई त्रुटि नहीं। OrderBy (x => propertyInfo.GetValue (x, null));
सीजर

1
मैं गतिशील रूप से asc या desc द्वारा ऑर्डर करने का निर्णय कैसे ले सकता हूं
हितेश मोधा

123

आप अभिव्यक्ति वृक्ष के निर्माण के लिए प्रतिबिंब का थोड़ा सा उपयोग इस प्रकार कर सकते हैं (यह एक विस्तार विधि है):

public static IQueryable<TEntity> OrderBy<TEntity>(this IQueryable<TEntity> source, string orderByProperty,
                          bool desc) 
{
     string command = desc ? "OrderByDescending" : "OrderBy";
     var type = typeof(TEntity);
     var property = type.GetProperty(orderByProperty);
     var parameter = Expression.Parameter(type, "p");
     var propertyAccess = Expression.MakeMemberAccess(parameter, property);
     var orderByExpression = Expression.Lambda(propertyAccess, parameter);
     var resultExpression = Expression.Call(typeof(Queryable), command, new Type[] { type, property.PropertyType },
                                   source.Expression, Expression.Quote(orderByExpression));
     return source.Provider.CreateQuery<TEntity>(resultExpression);
}

orderByPropertyवह प्रॉपर्टी का नाम जिसे आप ऑर्डर करना चाहते हैं और यदि इसके लिए पैरामीटर के रूप में सही है desc, तो अवरोही क्रम में सॉर्ट करेगा; अन्यथा, बढ़ते क्रम में क्रमबद्ध हो जाएगा।

अब आपको ऐसा करने में सक्षम होना चाहिए existingStudents.OrderBy("City",true);याexistingStudents.OrderBy("City",false);


10
यह उत्तर भयानक है, और प्रतिबिंब उत्तर से बहुत बेहतर है। यह वास्तव में इकाई ढांचे जैसे अन्य प्रदाताओं के साथ काम करता है।
सैम

2
अगर मैं कर सकता तो मैं इस दस बार मतदान करता !!! आप इस तरह से विस्तार विधि लिखना कहाँ से सीखते हैं ?? !!
जाच

3
क्या यह बिल्ट-इन ऑर्डरबाय की तरह एक IOrderedQueryable लौटना चाहिए? इस तरह, आप इस पर कॉल कर सकते हैं।
पैट्रिक सज्जापस्की

4
ऐसा लगता है कि EFCore 3.0 का उपयोग करते समय यह काम नहीं करता है, मुझे एक रनटाइम त्रुटि मिल रही है जहां यह क्वेरी का अनुवाद नहीं कर सकता है।
मिल्डन

3
हां, @ माइल्डन, यह मेरे लिए 3.0 और 3.1 में भी टूटता है। त्रुटि के साथ ~ "खिचड़ी भाषा का अनुवाद"। अगर यह प्रासंगिक है तो मैं MySQl के लिए पोमेलो का उपयोग करता हूं। समस्या एक्सप्रेशन है। यदि आप उस अभिव्यक्ति को कोड करते हैं जो यह काम करती है। तो इसके बजाय Lambda.Expression () बस कुछ प्रदान करें: LambdaExpression orderByExp1 = (अभिव्यक्ति <Func <AgencySystemBiz, string >>) (x => x.Name);
मेनेस

10

@Icarus द्वारा उत्तर पर विस्तार करने के लिए : यदि आप चाहते हैं कि विस्तार प्रकार की वापसी पद्धति IQuery के बजाय एक IOrderedQueryable हो, तो आप केवल परिणाम निम्नानुसार डाल सकते हैं:

public static IOrderedQueryable<TEntity> OrderBy<TEntity>(this IQueryable<TEntity> source, string orderByProperty, bool desc)
{
    string command = desc ? "OrderByDescending" : "OrderBy";
    var type = typeof(TEntity);
    var property = type.GetProperty(orderByProperty);
    var parameter = Expression.Parameter(type, "p");
    var propertyAccess = Expression.MakeMemberAccess(parameter, property);
    var orderByExpression = Expression.Lambda(propertyAccess, parameter);
    var resultExpression = Expression.Call(typeof(Queryable), command, new Type[] { type, property.PropertyType },
        source.Expression, Expression.Quote(orderByExpression));
    return (IOrderedQueryable<TEntity>)source.Provider.CreateQuery<TEntity>(resultExpression);
}

2
ऐसा लगता है कि एंटिटी फ्रेमवर्क के लिए अन्य उत्तर उपयुक्त नहीं थे। यह EF के लिए एक सही समाधान है क्योंकि Linq से लेकर Entities GetProperty, GetValue का समर्थन नहीं करता है
बिल

1
यह विधि मेरे लिए 3.0 और 3.1 में विफल रही है (यह 2.2 में काम किया)। मैं MySql के लिए पोमेलो का उपयोग करता हूं ताकि प्रासंगिक हो सके। चारों ओर एक काम है लेकिन उसके बदसूरत। ऊपर मेरी टिप्पणी देखें।
मेनेस

यह मेरे लिए ईएफ 3.0 में काम किया। हालाँकि, आपको निम्न पंक्ति बदलनी चाहिए ताकि सामने वाले को केस-सेंसिटिविटी से मिलान करने की आवश्यकता न हो: var property = type.GetProperty (OrderByProperty, BindingFlags.IgnoreCase | BindingFlags.IB। BindingFlags.Instance);
राजा आर्थर तीसरा

क्या यह अभी भी कोर 3.1 के लिए अनुकूलित है?
क्रिस गो

8

1) System.Linq.Dynamic स्थापित करें

2) निम्नलिखित कोड जोड़ें

public static class OrderUtils
{
    public static string ToStringForOrdering<T, TKey>(this Expression<Func<T, TKey>> expression, bool isDesc = false)
    {
        var str = expression.Body.ToString();
        var param = expression.Parameters.First().Name;
        str = str.Replace("Convert(", "(").Replace(param + ".", "");
        return str + (isDesc ? " descending" : "");
    }
}

3) लैम्ब्डा फ़ंक्शन के चयन के लिए अपना स्विच लिखें

public static class SortHelper
{
    public static Expression<Func<UserApp, object>> UserApp(string orderProperty)
    {
        orderProperty = orderProperty?.ToLowerInvariant();
        switch (orderProperty)
        {
            case "firstname":
                return x => x.PersonalInfo.FirstName;
            case "lastname":
                return x => x.PersonalInfo.LastName;
            case "fullname":
                return x => x.PersonalInfo.FirstName + x.PersonalInfo.LastName;
            case "email":
                return x => x.Email;

        }
    }
}

4) अपने सहायकों का उपयोग करें

Dbset.OrderBy(SortHelper.UserApp("firstname").ToStringForOrdering())

5) आप इसका उपयोग पेजिंग ( PagedList ) के साथ कर सकते हैं

public virtual  IPagedList<T> GetPage<TOrder>(Page page, Expression<Func<T, bool>> where, Expression<Func<T, TOrder>> order, bool isDesc = false,
      params Expression<Func<T, object>>[] includes)
    {
        var orderedQueryable = Dbset.OrderBy(order.ToStringForOrdering(isDesc));
        var query = orderedQueryable.Where(where).GetPage(page);
        query = AppendIncludes(query, includes);

        var results = query.ToList();
        var total =  Dbset.Count(where);

        return new StaticPagedList<T>(results, page.PageNumber, page.PageSize, total);
    }

व्याख्या

System.Linq.Dynamic हमें OrderBy विधि में स्ट्रिंग मान सेट करने की अनुमति देता है। लेकिन इस विस्तार के अंदर स्ट्रिंग को लैम्ब्डा में पार्स किया जाएगा। इसलिए मैंने सोचा कि अगर हम लैंबडा को स्ट्रिंग करेंगे और ऑर्डरबी विधि को देंगे तो यह काम करेगा। और यह काम करता है!


6
   private Func<T, object> GetOrderByExpression<T>(string sortColumn)
    {
        Func<T, object> orderByExpr = null;
        if (!String.IsNullOrEmpty(sortColumn))
        {
            Type sponsorResultType = typeof(T);

            if (sponsorResultType.GetProperties().Any(prop => prop.Name == sortColumn))
            {
                System.Reflection.PropertyInfo pinfo = sponsorResultType.GetProperty(sortColumn);
                orderByExpr = (data => pinfo.GetValue(data, null));
            }
        }
        return orderByExpr;
    }

    public List<T> OrderByDir<T>(IEnumerable<T> source, string dir, Func<T, object> OrderByColumn)
    {
        return dir.ToUpper() == "ASC" ? source.OrderBy(OrderByColumn).ToList() : source.OrderByDescending(OrderByColumn).ToList();``
    }

 // Call the code like below
        var orderByExpression= GetOrderByExpression<SearchResultsType>(sort);

    var data = OrderByDir<SponsorSearchResults>(resultRecords, SortDirectionString, orderByExpression);    

प्रतिभाशाली! वास्तव में मुझे जो चाहिए था।
ब्रैंडन ग्रिफिन

5

यहाँ मैं एक सशर्त उतरने से निपटने के लिए आया हूँ। आप इसे keySelectorगतिशील रूप से फंक उत्पन्न करने के अन्य तरीकों के साथ जोड़ सकते हैं ।

    public static IOrderedQueryable<TSource> OrderBy<TSource, TKey>(this IQueryable<TSource> source,
            System.Linq.Expressions.Expression<Func<TSource, TKey>> keySelector,
            System.ComponentModel.ListSortDirection sortOrder
            )
    {
        if (sortOrder == System.ComponentModel.ListSortDirection.Ascending)
            return source.OrderBy(keySelector);
        else
            return source.OrderByDescending(keySelector);
    }

उपयोग:

//imagine this is some parameter
var direction = System.ComponentModel.ListSortDirection.Ascending;
query = query.OrderBy(ec => ec.MyColumnName, direction);

ध्यान दें कि आप .OrderByकिसी भी IQueryable पर एक नए पैरामीटर के साथ इस एक्सटेंशन को चेन कर सकते हैं ।

// perhaps passed in as a request of user to change sort order
// var direction = System.ComponentModel.ListSortDirection.Ascending;
query = context.Orders
        .Where(o => o.Status == OrderStatus.Paid)
        .OrderBy(ec => ec.OrderPaidUtc, direction);

3

यह आपको पास नहीं होने देता है stringजैसा कि आपने अपने प्रश्न में पूछा है, , लेकिन यह अभी भी आपके लिए काम कर सकता है।

OrderByDescendingविधि एक लेता है Func<TSource, TKey>, तो आप अपने समारोह इस तरह से फिर से लिखने कर सकते हैं:

List<Student> QueryStudents<TKey>(Func<Student, TKey> orderBy)
{
    return existingStudents.OrderByDescending(orderBy).ToList();
}

वहाँ के OrderByDescendingरूप में अच्छी तरह से लेने के लिए अन्य अधिभार हैं Expression<Func<TSource, TKey>>, और / या एक IComparer<TKey>। आप उन पर भी गौर कर सकते हैं और देख सकते हैं कि क्या वे आपको कुछ भी प्रदान करते हैं।


यह काम नहीं करता है क्योंकि आप TKey के प्रकार को परिभाषित नहीं करते हैं। आपको इसके स्थान पर <TKey> के लिए अपना <T> बदलना होगा।
पैट्रिक डेसजार्डिन्स

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

LINQ in Action: IEnumerable <Book> CustomSort <TKey> (Func <Book, TKey> चयनकर्ता, बूलियन आरोही) {IEnumerable <Book> books = SampleData.Books; आरोही वापस? books.OrderBy (चयनकर्ता): books.OrderByDescending (चयनकर्ता); }
लेसज़ेक पी।

1

मेरे लिए काम करने वाला एकमात्र समाधान यहां पोस्ट किया गया था https://gist.github.com/neoGeneva/1878868 neoGeneva द्वारा।

मैं उसके कोड को फिर से पोस्ट करूंगा क्योंकि यह अच्छी तरह से काम करता है और मैं नहीं चाहूंगा कि यह इंटरव्यू में खो जाए!

    public static IQueryable<T> OrderBy<T>(this IQueryable<T> source, string sortExpression)
    {
        if (source == null)
            throw new ArgumentNullException("source", "source is null.");

        if (string.IsNullOrEmpty(sortExpression))
            throw new ArgumentException("sortExpression is null or empty.", "sortExpression");

        var parts = sortExpression.Split(' ');
        var isDescending = false;
        var propertyName = "";
        var tType = typeof(T);

        if (parts.Length > 0 && parts[0] != "")
        {
            propertyName = parts[0];

            if (parts.Length > 1)
            {
                isDescending = parts[1].ToLower().Contains("esc");
            }

            PropertyInfo prop = tType.GetProperty(propertyName);

            if (prop == null)
            {
                throw new ArgumentException(string.Format("No property '{0}' on type '{1}'", propertyName, tType.Name));
            }

            var funcType = typeof(Func<,>)
                .MakeGenericType(tType, prop.PropertyType);

            var lambdaBuilder = typeof(Expression)
                .GetMethods()
                .First(x => x.Name == "Lambda" && x.ContainsGenericParameters && x.GetParameters().Length == 2)
                .MakeGenericMethod(funcType);

            var parameter = Expression.Parameter(tType);
            var propExpress = Expression.Property(parameter, prop);

            var sortLambda = lambdaBuilder
                .Invoke(null, new object[] { propExpress, new ParameterExpression[] { parameter } });

            var sorter = typeof(Queryable)
                .GetMethods()
                .FirstOrDefault(x => x.Name == (isDescending ? "OrderByDescending" : "OrderBy") && x.GetParameters().Length == 2)
                .MakeGenericMethod(new[] { tType, prop.PropertyType });

            return (IQueryable<T>)sorter
                .Invoke(null, new object[] { source, sortLambda });
        }

        return source;
    }

1
  • अपने कोड में डला पैकेज डायनामाइट जोड़ें

  • नेमस्पेस डायनामाइट जोड़ें। एक्सटेंशन्स एग: डायनामाइट का उपयोग कर। एक्सटेन्शन;

  • किसी भी SQL क्वेरी की तरह क्वेरी द्वारा आदेश दें जैसे: Students.OrderBy ("सिटी DESC, पता")। ToList ();


1

@Icarus की प्रतिक्रिया का विस्तार करने के लिए: यदि आप दो क्षेत्रों द्वारा क्रमबद्ध करना चाहते हैं तो मैं निम्नलिखित कार्य कर सकता हूं (एक क्षेत्र के लिए इकारिस की प्रतिक्रिया बहुत अच्छी तरह से काम करती है)।

public static IQueryable<T> OrderByDynamic<T>(this IQueryable<T> q, string SortField1, string SortField2, bool Ascending)
        {
            var param = Expression.Parameter(typeof(T), "p");
            var body = GetBodyExp(SortField1, SortField2, param);
            var exp = Expression.Lambda(body, param);

            string method = Ascending ? "OrderBy" : "OrderByDescending";
            Type[] types = new Type[] { q.ElementType, exp.Body.Type };
            var mce = Expression.Call(typeof(Queryable), method, types, q.Expression, exp);
            return q.Provider.CreateQuery<T>(mce);
        }

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

public static NewExpression GetBodyExp(string field1, string field2, ParameterExpression Parametro)
        {    
            // SE OBTIENE LOS NOMBRES DE LOS TIPOS DE VARIABLE 
            string TypeName1 = Expression.Property(Parametro, field1).Type.Name;
            string TypeName2 = Expression.Property(Parametro, field2).Type.Name;

            // SE DECLARA EL TIPO ANONIMO SEGUN LOS TIPOS DE VARIABLES
            Type TypeAnonymous = null;
            if (TypeName1 == "String")
            {
                string var1 = "0";
                if (TypeName2 == "Int32")
                {
                    int var2 = 0;
                    var example = new { var1, var2 };
                    TypeAnonymous = example.GetType();
                }

                if (TypeName2 == "String")
                {
                    string var2 = "0";
                    var example = new { var1, var2 };
                    TypeAnonymous = example.GetType();
                }    
            }    

            if (TypeName1 == "Int32")
            {
                int var1 = 0;
                if (TypeName2 == "Int32")
                {
                    string var2 = "0";
                    var example = new { var1, var2 };
                    TypeAnonymous = example.GetType();
                }

                if (TypeName2 == "String")
                {
                    string var2 = "0";
                    var example = new { var1, var2 };
                    TypeAnonymous = example.GetType();
                }    
            }

            //se declaran los TIPOS NECESARIOS PARA GENERAR EL BODY DE LA EXPRESION LAMBDA
            MemberExpression[] args = new[] { Expression.PropertyOrField(Parametro, field1), Expression.PropertyOrField(Parametro, field2) };
            ConstructorInfo CInfo = TypeAnonymous.GetConstructors()[0];
            IEnumerable<MemberInfo> a = TypeAnonymous.GetMembers().Where(m => m.MemberType == MemberTypes.Property);

            //BODY 
            NewExpression body = Expression.New(CInfo, args, TypeAnonymous.GetMembers().Where(m => m.MemberType == MemberTypes.Property));

            return body;
        }

इसका उपयोग करने के लिए निम्नलिखित किया जाता है

IQueryable<MyClass> IqMyClass= context.MyClass.AsQueryable();
List<MyClass> ListMyClass= IqMyClass.OrderByDynamic("UserName", "IdMyClass", true).ToList();

अगर ऐसा करने का कोई बेहतर तरीका है, तो वे इसे साझा करेंगे तो बहुत अच्छा होगा

मैं इसे धन्यवाद करने के लिए हल करने में कामयाब रहा: मैं Linq के साथ एक एकाधिक संपत्ति लैंबडा अभिव्यक्ति कैसे बना सकता हूं


-1

मैं पार्टी के लिए देर से आया हूं लेकिन इनमें से किसी भी समाधान ने मेरे लिए काम नहीं किया। मैं System.Linq.Dynamic की कोशिश करने के लिए उत्सुक था, लेकिन मुझे नहीं मिला कि नुगेट पर, शायद मूल्यह्रास किया गया था? किसी भी तरह से...

यहाँ एक समाधान है जिसके साथ मैं आया था। मैं गतिशील के मिश्रण का उपयोग करने के लिए आवश्यक OrderBy , OrderByDescending और OrderBy> ThenBy

मैंने अपनी सूची ऑब्जेक्ट के लिए एक विस्तार विधि बनाई, थोड़ा सा हैक मुझे पता है ... मैं यह सिफारिश नहीं करता अगर यह कुछ ऐसा था जो मैं बहुत कुछ कर रहा था, लेकिन यह एक बंद के लिए अच्छा है।

List<Employee> Employees = GetAllEmployees();

foreach(Employee oEmployee in Employees.ApplyDynamicSort(eEmployeeSort))
{
    //do stuff
}

public static IOrderedEnumerable<Employee> ApplyDynamicSort(this List<Employee> lEmployees, Enums.EmployeeSort eEmployeeSort)
{
    switch (eEmployeeSort)
    {
        case Enums.EmployeeSort.Name_ASC:
            return lEmployees.OrderBy(x => x.Name);
        case Enums.EmployeeSort.Name_DESC:
            return lEmployees.OrderByDescending(x => x.Name);
        case Enums.EmployeeSort.Department_ASC_Salary_DESC:
            return lEmployees.OrderBy(x => x.Department).ThenByDescending(y => y.Salary);
        default:
            return lEmployees.OrderBy(x => x.Name);
    }
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.