Apa cara termudah untuk membuat kode terhadap properti di C # ketika saya memiliki nama properti sebagai string? Misalnya, saya ingin mengizinkan pengguna untuk memesan beberapa hasil pencarian berdasarkan properti pilihan mereka (menggunakan LINQ). Mereka akan memilih properti "urutkan menurut" di UI - tentu saja sebagai nilai string. Apakah ada cara untuk menggunakan string itu secara langsung sebagai properti kueri linq, tanpa harus menggunakan logika kondisional (if / else, switch) untuk memetakan string ke properti. Refleksi?
Logikanya, inilah yang ingin saya lakukan:
query = query.OrderBy(x => x."ProductId");
Pembaruan: Saya awalnya tidak menentukan bahwa saya menggunakan Linq untuk Entitas - tampaknya refleksi itu (setidaknya pendekatan GetProperty, GetValue) tidak diterjemahkan ke L2E.
sumber
Jawaban:
Saya akan menawarkan alternatif ini untuk apa yang orang lain telah posting.
System.Reflection.PropertyInfo prop = typeof(YourType).GetProperty("PropertyName"); query = query.OrderBy(x => prop.GetValue(x, null));
Ini menghindari panggilan berulang-ulang ke API refleksi untuk mendapatkan properti. Sekarang satu-satunya panggilan berulang adalah mendapatkan nilainya.
Namun
Saya akan menganjurkan menggunakan a
PropertyDescriptor
sebagai gantinya, karena ini akan memungkinkan kustomTypeDescriptor
untuk ditetapkan ke jenis Anda, sehingga memungkinkan untuk memiliki operasi ringan untuk mengambil properti dan nilai. Jika tidak ada deskriptor khusus, deskripsi akan kembali ke refleksi.PropertyDescriptor prop = TypeDescriptor.GetProperties(typeof(YourType)).Find("PropertyName"); query = query.OrderBy(x => prop.GetValue(x));
Sedangkan untuk mempercepatnya, lihat
HyperDescriptor
proyek Marc Gravel di CodeProject. Saya telah menggunakan ini dengan sukses besar; ini adalah penyelamat untuk pengikatan data berkinerja tinggi dan operasi properti dinamis pada objek bisnis.sumber
PropertyDescriptor
toh (untuk memperhitungkan deskriptor tipe kustom, yang dapat membuat pengambilan nilai menjadi operasi yang ringan).Saya agak terlambat ke pesta, namun, saya harap ini bisa membantu.
Masalah dengan menggunakan refleksi adalah bahwa Pohon Ekspresi yang dihasilkan hampir pasti tidak akan didukung oleh penyedia Linq selain penyedia .Net internal. Ini bagus untuk koleksi internal, namun ini tidak akan berfungsi di mana penyortiran dilakukan pada sumber (baik itu SQL, MongoDb, dll.) Sebelum penomoran halaman.
Contoh kode di bawah ini menyediakan metode ekstensi yang dapat disesuaikan untuk OrderBy dan OrderByDescending, dan dapat digunakan seperti ini:
query = query.OrderBy("ProductId");
Metode Ekstensi:
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); } }
Salam, Mark.
sumber
Expression.Convert
untuk pindahproperty
keobject
? Saya mendapatkanUnable to cast the type 'System.String' to type 'System.Object'. LINQ to Entities only supports casting EDM primitive or enumeration types.
kesalahan, dan menghapusnya sepertinya berhasil.var propAsObject = Expression.Convert(property, typeof(object));
dan hanya digunakanproperty
di tempatpropAsObject
LINQ to Entities only supports casting EDM primitive or enumeration types
Saya menyukai jawaban dari @Mark Powell , tetapi seperti yang dikatakan @ShuberFu , itu memberikan kesalahan
LINQ to Entities only supports casting EDM primitive or enumeration types
.Penghapusan
var propAsObject = Expression.Convert(property, typeof(object));
tidak berfungsi dengan properti yang merupakan tipe nilai, seperti integer, karena tidak secara implisit mengotakkan int ke objek.Menggunakan Ide dari Kristofer Andersson dan Marc Gravell Saya menemukan cara untuk membangun fungsi Querable menggunakan nama properti dan masih berfungsi dengan Entity Framework. Saya juga menyertakan parameter IComparer opsional. Perhatian: Parameter IComparer tidak bekerja dengan Entity Framework dan harus ditinggalkan jika menggunakan Linq ke Sql.
Berikut ini bekerja dengan Entity Framework dan Linq to Sql:
query = query.OrderBy("ProductId");
Dan @Simon Scheurer ini juga berfungsi:
query = query.OrderBy("ProductCategory.CategoryId");
Dan jika Anda tidak menggunakan Entity Framework atau Linq ke Sql, ini berfungsi:
query = query.OrderBy("ProductCategory", comparer);
Ini kodenya:
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) ) ); } }
sumber
Aggregate
Fragmen itu luar biasa! Ini menangani tampilan virtual yang dibuat dari model EF Core denganJoin
, karena saya menggunakan properti seperti "T.Property". Jika tidak, memesan setelahnyaJoin
tidak mungkin menghasilkan salah satuInvalidOperationException
atauNullReferenceException
. Dan saya perlu memesan SETELAHJoin
, karena sebagian besar kueri konstan, urutan dalam tampilan tidak.Aggregate
potongan itu. Saya percaya itu adalah kombinasi dari kode Marc Gravell dan rekomendasi yang masuk akal. :)products.OrderBy(x => x.ProductId)
, Anda dapat menggunakanproducts.OrderBy("ProductId")
Ya, saya rasa tidak ada cara lain selain Refleksi.
Contoh:
query = query.OrderBy(x => x.GetType().GetProperty("ProductId").GetValue(x, null));
sumber
"LINQ to Entities does not recognize the method 'System.Object GetValue(System.Object)' method, and this method cannot be translated into a store expression."
Ada pemikiran atau saran, tolong?query = query.OrderBy(x => x.GetType().GetProperty("ProductId").GetValue(x, null));
Mencoba mengingat sintaks yang tepat dari atas kepala saya tetapi saya pikir itu benar.
sumber
Refleksi adalah jawabannya!
typeof(YourType).GetProperty("ProductId").GetValue(theInstance);
Ada banyak hal yang dapat Anda lakukan untuk meng-cache PropertyInfo yang direfleksikan, memeriksa string yang buruk, menulis fungsi perbandingan kueri Anda, dll., Tetapi intinya, inilah yang Anda lakukan.
sumber
Anda dapat menggunakan Linq dinamis - lihat blog ini .
Lihat juga pos StackOverFlow ini ...
sumber
Lebih produktif daripada ekstensi refleksi untuk item pesanan dinamis:
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); } }
Contoh:
var ordered = items.OrderBy(x => x.GetPropertyDynamic("ProductId"));
Anda juga mungkin perlu menyimpan lambas yang sesuai dengan cache (misalnya dalam Kamus <>)
sumber
Ekspresi Dinamis juga dapat mengatasi masalah ini. Anda dapat menggunakan kueri berbasis string melalui ekspresi LINQ yang dapat dibuat secara dinamis pada waktu proses.
var query = query .Where("Category.CategoryName == @0 and Orders.Count >= @1", "Book", 10) .OrderBy("ProductId") .Select("new(ProductName as Name, Price)");
sumber
Saya pikir kita dapat menggunakan alat yang kuat bernama Expression an dalam hal ini menggunakannya sebagai metode ekstensi sebagai berikut:
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); }
sumber