 |
|
 |
I add it on my project reference and create a function
Public Function DynamicIListComparer(ByVal orderBy As String, ByVal pages As List(Of PageIListCollection)) As List(Of PageIListCollection) Dim comparer As New DynamicComparer(Of PageIListCollection)(orderBy) pages.Sort(comparer) Return pages
End Function
It works well!..but if i changed the code to pages.Sort(comparer.Compare)
i got an error..
Error 2 Argument not specified for parameter 'x' of 'Public Function Compare(x As T, y As T) As Integer'.
any suggestions..
By the way thanks Johannes Hansen 
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
 |
ok. i just noticed that if i have null string intermixed in my List's it errors out. any help would be wonderful. I do love this solution that you've put out here. I hope you'll get a chance to look at this soon.
i tried looking into the "#region Generate IL for dynamic method," but i got lost too quickly to get anywhere with it. (at the bottom i have a separate question on templates)
public class Project{ string _entryCode; public string EntryCode{ get{return _entryCode;} set{_entryCode=value;} } public Project() {} public Project(string ec) {_entryCode = ec;} }
List projects = new List();
projects.Add(new Project()); projects.Add(new Project()); projects.Add(new Project("asf")); projects.Add(new Project("veev")); projects.Add(new Project()); projects.Add(new Project("asdfe")); projects.Add(new Project()); projects.Add(new Project());
projects.Sort(new DynamicComparer("EntryCode"));
gridview1.DataSource = projects; gridview1.DataBind();
========================================= ===========Error Message================= ========================================= Object reference not set to an instance of an object. Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.NullReferenceException: Object reference not set to an instance of an object.
Source Error:
An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.
Stack Trace:
[NullReferenceException: Object reference not set to an instance of an object.] DynamicComparison(Project , Project ) +12 DynamicComparer.DynamicComparer`1.Compare(T x, T y) in C:\Documents and Settings\colin.cole\My Documents\Visual Studio 2005\Projects\comparer\DynamicComparer\DynamicComparer.cs:65 System.Collections.Generic.ArraySortHelper`1.SwapIfGreaterWithItems(T[] keys, TValue[] values, IComparer`1 comparer, Int32 a, Int32 b) +125
[InvalidOperationException: Failed to compare two elements in the array.] System.Collections.Generic.ArraySortHelper`1.SwapIfGreaterWithItems(T[] keys, TValue[] values, IComparer`1 comparer, Int32 a, Int32 b) +433 System.Collections.Generic.ArraySortHelper`1.QuickSort(T[] keys, TValue[] values, Int32 left, Int32 right, IComparer`1 comparer) +107 System.Collections.Generic.ArraySortHelper`1.Sort(T[] keys, TValue[] values, Int32 index, Int32 length, IComparer`1 comparer) +129 System.Collections.Generic.ArraySortHelper`1.Sort(T[] items, Int32 index, Int32 length, IComparer`1 comparer) +125 System.Array.Sort(T[] array, Int32 index, Int32 length, IComparer`1 comparer) +193 System.Collections.Generic.List`1.Sort(Int32 index, Int32 count, IComparer`1 comparer) +93
================================== ================================== ==================================
is it possible to write the DynamicMethod piece in a similar way as to a generic template? (see below.) I'm assuming you can't, since it looks like it's generating assembly level code. Or is the generic implementation method just slower? I'm just curious, since i know so little about this. I am in no way trying to be critical.
(note the GenericTemplateImplementation can be found here, written by yet someone else who's smarter than me: http://couldbedone.blogspot.com/2007/07/implementing-itemplate-as-anonymous.html[^].
public delegate void InstantiateTemplateDelegate(Control container);
public class GenericTemplateImplementation : ITemplate { private InstantiateTemplateDelegate m_instantiateTemplate;
public void InstantiateIn(Control container) { m_instantiateTemplate(container); }
public GenericTemplateImplementation(InstantiateTemplateDelegate instantiateTemplate) { m_instantiateTemplate = instantiateTemplate; } }
rptCategory.ItemTemplate = new GenericTemplateImplementation( delegate(Control cont) { RepeaterItem Container = (RepeaterItem)cont;
if (Container.ItemIndex != 0) Container.Controls.Add(new LiteralControl(" > ")); LinkButton b = new LinkButton(); b.ID = "lnk"; b.CommandName = "lnk"; Container.Controls.Add(b); });
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Hi Colin, sorry for the late reply.
I'm not planning to make any fixes or changes to the current implementation of the DynamicComparer. But don't be sad, go take a look at Marc Brook's implementation of the DynamicComparer at CodePlex[^]. I'm pretty sure his implementation fixes most of the bugs in my initial prototype/example implementation.
As to your second question, I'm not really sure I understand it, but I'll try to answer it anyways. Early on in the project I did experiment with a generic comparer but it turned out to be dreadfully slow. This is mainly because you then have to use reflection to lookup field and property types at runtime as well as casting the values before comparison. So no-go for a generic only implementation.
If you wanted to implement a simpler DynamicComparer it could probably be done in .NET 3.5 using LINQ expression trees. However, they might be a little slower than Marc's implementation since expression trees probably has to be more generalized, and thus has to do more checks at runtime (I haven't tried it though so I might be completely wrong). Also, Marc's implementation is extremely streamlined and is faster than compiled code in some (maybe even most) cases.
modified on Thursday, October 2, 2008 11:57 PM
|
| Sign In·View Thread·PermaLink | 1.00/5 (1 vote) |
|
|
|
 |
|
|
 |
|
 |
can this order propertys from composite classes? like this class can:
/*------------------------------------------------------------------------ This class is adapted from: * http://www.rosscode.com/blog/index.php?title=generic_multiple_sorting_for_typed_colle&more=1&c=1&tb=1&pb=1 * and http://www.codeplex.com/NSFxSamples/SourceControl/FileView.aspx?itemId=212083&changeSetId=16449 (thanx Joel) Created: 2008.04.10 Author: Matthew Kocaj ------------------------------------------------------------------------*/
using System; using System.Collections.Generic; using System.Reflection;
public enum OrderDirection { Ascending, Descending }
/// <summary> /// used to sort generic collections. /// will accept n number of columns where /// those columns are in any n number /// of composite classes for each item /// </summary> /// <typeparam name="T"></typeparam> public class EntityComparer<T> : IComparer<T> { private List<OrderItem> _orderItems = new List<OrderItem>();
#region Constructors
public EntityComparer() { }
public EntityComparer(List<OrderItem> orderItems) { this._orderItems = orderItems; }
public EntityComparer(string sortColumn, OrderDirection orderDirection) { this._orderItems.Add(new OrderItem(sortColumn, orderDirection)); }
/// <summary> /// used for sort expressions in the form "Column1 DESC, Column2 ASC, ..." /// </summary> /// <param name="sortExpression"></param> public EntityComparer(string sortExpression) { if (sortExpression != String.Empty) { string[] fields = sortExpression.Split(','); foreach (string field in fields) { string[] parts = field.Trim().Split(' '); if (parts[1] == "ASC") this._orderItems.Add(new OrderItem(parts[0], OrderDirection.Ascending)); if (parts[1] == "DESC") this._orderItems.Add(new OrderItem(parts[0], OrderDirection.Descending)); } } }
#endregion
public List<OrderItem> OrderItems { get { return this._orderItems; } set { this._orderItems = value; } }
#region IComparer<T> Members
public int Compare(T x, T y) { if (this._orderItems.Count == 0) return 0; else return this.CheckSort(0, x, y); }
#endregion
/// <summary> /// Recursive function to do sorting for multiple columns /// </summary> /// <param name="sortIndex">The current index of the column we're sorting at</param> /// <param name="x"></param> /// <param name="y"></param> /// <returns></returns> private int CheckSort(int sortIndex, T x, T y) { int returnValue = 0;
if (this._orderItems.Count - 1 >= sortIndex) { if (x.GetType().ToString() == y.GetType().ToString()) { string[] propertyList = this._orderItems[sortIndex].SortColumn.Split('.');
object currentObject1 = x; object currentObject2 = y;
for (int i = 0; i < propertyList.Length; i++) { PropertyInfo property = currentObject1.GetType().GetProperty(propertyList.GetValue(i).ToString());
if (property != null && property.CanRead) { currentObject1 = this.GetPropertyValue(property, currentObject1); currentObject2 = this.GetPropertyValue(property, currentObject2); } }
if (currentObject1 != null && currentObject2 != null) { // Assume all property types used are // Comparable IComparable value1 = (IComparable)currentObject1; IComparable value2 = (IComparable)currentObject2;
returnValue = value1.CompareTo(value2); }
// apply Descending differences if (this._orderItems[sortIndex].OrderDirection == OrderDirection.Descending) returnValue = returnValue * -1; }
// call again for multiple columns if (returnValue == 0) returnValue = this.CheckSort(sortIndex + 1, x, y); }
return returnValue; }
/// <summary> /// Get Property value /// </summary> /// <param name="property">Property</param> /// <param name="entity">Entity</param> /// <returns>Value</returns> private object GetPropertyValue(PropertyInfo property, object entity) { return property.GetValue(entity, null); }
public class OrderItem { private string _sortColumn = String.Empty; private OrderDirection _orderDirection = 0;
public OrderItem(string sortColumn, OrderDirection orderDirection) { this._sortColumn = sortColumn; this._orderDirection = orderDirection; }
public string SortColumn { get { return this._sortColumn; } }
public OrderDirection OrderDirection { get { return this._orderDirection; } } } }
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Hi,
Yes, the comparer that I've suggested can order any type of object, please take a look at Marc Brooks implementation for an improved version of the DynamicComparer.
The problem with the EntityComparer you've sent is the it will probably perform very poorly because it's using reflection, type casting, recursion and other nasty stuff in the CheckSort method. Actually I think this will perform worse than the "GenericClassComparer" from the chart in my article.
Kind regards, Johannes Hansen frontAvenue A/S
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Hi everyone,
I have implemented the coding on my application. Everything is good except I got an error when adding 6 more sorting items:
==================================================================== Illegal one-byte branch at position: 4. Requested branch was: 207.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.NotSupportedException: Illegal one-byte branch at position: 4. Requested branch was: 207.
Source Error:
Line 51: CheckSortProperties(sortProperties); Line 52: method = CreateDynamicCompareMethod(sortProperties); Line 53: comparer = (CompareMethodInvoker)method.CreateDelegate(typeof(CompareMethodInvoker)); Line 54: } Line 55: =====================================================================
I didn't get the updated version yet. What I implemented is the verion attached on this article. Can anybody send me a email with the updated version, please? zhucalvin@yahoo.com I do appreciate it if anybody can help me out.
Thanks.
- Calvin
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
That's due to the emitted branch instructions being 'short' or relative branches. You can change the emitted opcodes to be regular branches instead of short branches. The other option is to grab my current version from CodePlex Dynamic Reflection Librarywhich avoids this issue.
http://musingmarc.blogspot.com
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
 |
Hi Johannes,
First of all, good work you did on the DynamicComparer. It's really fast indeed.
I made some changes to it to support sorting on properties of properties. Let's say I have an Customer class which has a CurrentAddress property of the type Address. The Address class has properties like City and Street. What I wanted to do, is sorting customers on their city and street. As far as I could see, this functionality is not supported by the comparer.
After doing some research on dynamic methods, I modified the DynamicComparer to meet my needs described above. Now I call the DynamicComparer like this:
DynamicComparer<Customer> comparer = new DynamicComparer<Customer>("Address.City, Address.Street"); List<Customer> list = (List<Customer> )dao.GetAll(); list.Sort(comparer.Compare);
Maybe you can take a look and test it.
Greets, Nils Gruson
Here is the code I changed:
protected DynamicMethod CreateDynamicCompareMethod(SortProperty[] sortProperties) { DynamicMethod dm = new DynamicMethod("DynamicComparison", typeof(int), new Type[] { typeof(T), typeof(T) }, typeof(DynamicComparer));
#region Generate IL for dynamic method ILGenerator ilGen = dm.GetILGenerator();
Label lbl = ilGen.DefineLabel(); ilGen.DeclareLocal(typeof(int)); Dictionary localVariables = new Dictionary();
ilGen.Emit(OpCodes.Ldc_I4_0); ilGen.Emit(OpCodes.Stloc_0); Type propertyType; PropertyInfo propertyInfo, childPropertyInfo = null; foreach (SortProperty property in sortProperties) { string[] properties = property.Name.Split('.');
ilGen.Emit(OpCodes.Ldloc_0); ilGen.Emit(OpCodes.Brtrue_S, lbl); ilGen.Emit(OpCodes.Ldarg_0); propertyType = typeof(T); foreach (string propertyName in properties) { propertyInfo = propertyType.GetProperty(propertyName); ilGen.EmitCall(OpCodes.Callvirt, propertyInfo.GetGetMethod(), null); propertyType = propertyType.GetProperty(propertyName).PropertyType; childPropertyInfo = propertyInfo; }
if (childPropertyInfo.PropertyType.IsValueType) { if (!localVariables.ContainsKey(childPropertyInfo.PropertyType)) localVariables.Add(childPropertyInfo.PropertyType, ilGen.DeclareLocal(childPropertyInfo.PropertyType)); int localIndex = localVariables[childPropertyInfo.PropertyType].LocalIndex; ilGen.Emit(OpCodes.Stloc, localIndex); ilGen.Emit(OpCodes.Ldloca_S, localIndex); }
propertyType = typeof(T); ilGen.Emit(OpCodes.Ldarg_1); foreach (string propertyName in properties) { propertyInfo = propertyType.GetProperty(propertyName); ilGen.EmitCall(OpCodes.Callvirt, propertyInfo.GetGetMethod(), null); propertyType = propertyType.GetProperty(propertyName).PropertyType; }
ilGen.EmitCall(OpCodes.Callvirt, childPropertyInfo.PropertyType.GetMethod("CompareTo", new Type[] { childPropertyInfo.PropertyType }), null);
if (property.Descending) ilGen.Emit(OpCodes.Neg); ilGen.Emit(OpCodes.Stloc_0); }
ilGen.MarkLabel(lbl); ilGen.Emit(OpCodes.Ldloc_0); ilGen.Emit(OpCodes.Ret); #endregion
return dm; }
protected void CheckSortProperties(SortProperty[] sortProperties) { if (sortProperties == null) sortProperties = new SortProperty[0];
Type instanceType = typeof(T); if (!instanceType.IsPublic) throw new ArgumentException(string.Format("Type \"{0}\" is not public.", typeof(T).FullName));
foreach (SortProperty sProp in sortProperties) { string[] properties = sProp.Name.Split('.'); PropertyInfo pInfo = null;
Type propertyType = instanceType; foreach (string propertyName in properties) { pInfo = propertyType.GetProperty(propertyName); propertyType = propertyType.GetProperty(propertyName).PropertyType; } if (pInfo == null) throw new ArgumentException(string.Format("No public property named \"{0}\" was found.", sProp.Name));
if (!pInfo.CanRead) throw new ArgumentException(string.Format("The property \"{0}\" is write-only.", sProp.Name));
if (!typeof(IComparable).IsAssignableFrom(pInfo.PropertyType)) throw new ArgumentException(string.Format("The type \"{1}\" of the property \"{0}\" does not implement IComparable.", sProp.Name, pInfo.PropertyType.FullName)); } }
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Hi Nils
Good work on the upgrade... As far as I can tell it should work just as well as the ordinary DynamicComparer. Unfortunately I don't have the time to test it any more than this at the moment. However, you should take a look at the implementation Marc did. I'm not sure if it has child property capabilities but his implementation is far more robust than mine is so it would be a better base for your upgrade. Also, If you're planning on switching to .NET 3.5 you should take a good look at expression trees which could probably help making the DynamicComparer much simpler.
Hope this helps.
Kind regards, Johannes Hansen frontAvenue A/S
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
It's able to compare and sort System Types but can it compare enum properties in a class?
Example:
public static class SampleEnumClassName { public enum SampleEnum { EnumV1, EnumV2 } }
public class SampleClass { private SampleEnumClassName.SampleEnum pTestEnum; public SampleEnumClassName.SampleEnum _TestEnum { get{return pTestEnum;} set{pTestEnum = value;} }
public SampleClass(){} }
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
I'm not sure if my implementation of the DynamicComparer works with enums but I'm pretty sure that the implementation that Marc Brooks made do sort enums. You can find a link to his implementation in one of the earlier post in this comments section by him. Please try his implementation out or alternatively have a look at Linq and expression trees in .net 3.5 which can provide similar functionality and that will definately work with enums.
Hope this helps...
Kind regards, Johannes Hansen frontAvenue A/S
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Hi Guys, has anyone made a filtering class using this method. I would be very interested in seeing it.
-Thanks
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
As much of the functionality of the DynamicComparer will be overlapped by the functionality of te various Linq implementations in .NET 3.5. Therefore I won't develop/support this project any further as the Linq project will get a go-live license in june. Thanks for all the support and help which made this project a very rich learning experience.
Kind regards, Johannes Hansen frontAvenue A/S
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Thank you very much for your contribution then. We'll see if LINQ is going to be as performant as your implementation!
Simone Busoli
|
| Sign In·View Thread·PermaLink | 3.00/5 (2 votes) |
|
|
|
 |
|
 |
The null check seems to be invalid when working with custom objects, the fix seems to be using OpCodes.Brfalse_S instead of OpCodes.Brtrue_S.
// CreateDynamicCompareMethod(...) -> ilGen.Emit(OpCodes.Brfalse_S, lbl); // Is the local variable in the evaluation stack equal to 0. If not jump to the label we just defined (fixed by replacing Bftrue_S).
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
I must be dense, but I am unable to determine how to use this with a GridView that is populated with a objectDataSource
I know I need to do the work on the Grid_Sorting event, but my attempt
protected void DGrid_Sorting(object sender, GridViewSortEventArgs e) { string orderBy = e.SortExpression + " " + e.SortDirection; DynamicComparer comparer = new DynamicComparer(orderBy); List<> Ds = objDSDocuments.Select(); Ds.Sort(new DynamicComparer(e.SortExpression, e.SortDirection)); DGrid.DataSource = Ds; DGrid.DataBind(); }
generates errors.
Any thoughts?
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Hi Johannes, I've downloaded your excellent project and am implementing it in my application.
I've got my generic list List where EventDTO has a string member called "Location". The "Location" column in my SQL database is an optional varchar. I populate this list using a SqlDataReader, knowing that if "Location" is null in the DB, the member will also come back null. So, I try to sort on this column and I get a NullReferenceException in the "Compare" method (line 65 in the original DynamicComparer class).
Also, did you ever update this class to handle Nullables? I saw on another thread that you had talked about it, but I couldn't download your updated code.
Many Thanks! Troye Stonich
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Hi Troye
Thanks for your bug-report... I recommend that you take a look at Marc Brook's more robust implementation of my technique into his very cool utility library called "Utilities" which you can download here[^]. If you still get the error after switching to his implementation please submit an bug-report to Marc.
The update to the library using nullables has turned out to be a dud. Early performance tests showed the code to be awfully slow. So even though the code were MUCH simpler I didn't think it should be released.
I hope this helps...
Kind regards and happy new year! Johannes Hansen frontAvenue A/S
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Hi - I realise this is a bit out of place - but I can't figure out where to find this info on the net.
I've been playing with ADO.NET - Datasets, etc for a couple of months now - and I can't seem to find one basic part of SQL - the unique keyword. I know how to make a column unique - but not a search query.
e.g. How would i perform the following query?
SELECT UNIQUE col1, col2 from table
where the table might include many columns of which col1, col2 are not primary keys - or are not the only primary keys.....
I'm sure this is really easy - but I've wasted hours without success. Can anyone help me out?
Thanks
Martin
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
I'm not entirely sure what you mean. Could you provide an usage scenario?
Kind regards, Johannes Hansen frontAvenue A/S
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Actually the SQL should read - Distinct (instead of Unique)
So an example.
E.g. You have a databank with multiple positions
OrderNr, OrderDate, OrderUser, Product Nr 1 , 1.1.2005 , martin , 123 1 , 1.1.2005 , martin , 456 2 , 1.1.2005 , martin , 123
and you desire the following information: ------------------------------------- OrderNr, OrderDate, OrderUser 1 , 1.1.2005 , martin 2 , 1.1.2005 , martin ------------------------------- using SQL - I'd use a query like
select distinct OrderNr, OrderDate, OrderUser from table
how do I achieve this in ADO.NET?
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |