|
|
Comments and Discussions
|
|
 |
|

|
This saved me alot of time... I needed a dynamic sort on some lists and this code fit the bill.
Thanks again,
Jon Cunningham
Jon Cunningham
|
|
|
|
|
|

|
Hi Oslec
First of all, thank you for getting involved.
Secondly, I'm not sure why you are experiencing that particular problem, but I do know that my original code had some bugs in it and that the implementation Marc Brooks did is much more robust. So I recommend you try his implementation out and see if that works better (which I'm sure it will). You can grab his implementation at the Dynamic Reflection Library's project site on Codeplex[^]. If you have any comments/bug reports/suggestions to Marc's implementation please post them on the Codeplex site, thanks.
Kind regards,
Johannes Hansen
frontAvenue A/S
|
|
|
|

|
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<project> projects = new List<project>();
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<project>("EntryCode"));
gridview1.DataSource = projects;
gridview1.DataBind();
</project></project></project>
=========================================
===========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);
});
|
|
|
|

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

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

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

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

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

|
Thanks!!
Great article! Help me a lot!
Valeria
|
|
|
|

|
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(); // Declare and define a label that we can jump to.
ilGen.DeclareLocal(typeof(int)); // Declare a local variable for storing result.
Dictionary localVariables = new Dictionary();
ilGen.Emit(OpCodes.Ldc_I4_0); // Push 0 onto the eval stack.
ilGen.Emit(OpCodes.Stloc_0); // Store the eval stack item in the local variable @ position 0.
Type propertyType;
PropertyInfo propertyInfo, childPropertyInfo = null;
foreach (SortProperty property in sortProperties) // For each of the properties we want to check inject the following il.
{
string[] properties = property.Name.Split('.');
ilGen.Emit(OpCodes.Ldloc_0); // Load local variable at position 0.
ilGen.Emit(OpCodes.Brtrue_S, lbl); // Is the local variable in the evaluation stack equal to 0. If not jump to the label we just defined.
ilGen.Emit(OpCodes.Ldarg_0); // Load argument at position 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 the type is a valuetype then we need to inject the following IL.
{
if (!localVariables.ContainsKey(childPropertyInfo.PropertyType)) // Do we have a local variable for this type? If not, add one.
localVariables.Add(childPropertyInfo.PropertyType, ilGen.DeclareLocal(childPropertyInfo.PropertyType)); // Adds a local variable of type x.
int localIndex = localVariables[childPropertyInfo.PropertyType].LocalIndex; // This local variable is for handling value types of type x.
ilGen.Emit(OpCodes.Stloc, localIndex); // Store the value in the local var at position x.
ilGen.Emit(OpCodes.Ldloca_S, localIndex); // Load the address of the value into the stack.
}
propertyType = typeof(T);
ilGen.Emit(OpCodes.Ldarg_1); // Load argument at position 0.
foreach (string propertyName in properties)
{
propertyInfo = propertyType.GetProperty(propertyName);
ilGen.EmitCall(OpCodes.Callvirt, propertyInfo.GetGetMethod(), null);
propertyType = propertyType.GetProperty(propertyName).PropertyType;
}
// Compare the top 2 items in the evaluation stack and push the return value into the eval stack.
ilGen.EmitCall(OpCodes.Callvirt, childPropertyInfo.PropertyType.GetMethod("CompareTo", new Type[] { childPropertyInfo.PropertyType }), null);
if (property.Descending) // If the sort should be descending we need to flip the result of the comparison.
ilGen.Emit(OpCodes.Neg); // Negates the item in the eval stack.
ilGen.Emit(OpCodes.Stloc_0); // Store the result in the local variable.
}
ilGen.MarkLabel(lbl); // This is the spot where the label we created earlier should be added.
ilGen.Emit(OpCodes.Ldloc_0); // Load the local var into the eval stack.
ilGen.Emit(OpCodes.Ret); // Return the value.
#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));
}
}
|
|
|
|

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

|
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(){}
}
|
|
|
|

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

|
Hi Guys, has anyone made a filtering class using this method. I would be very interested in seeing it.
-Thanks
|
|
|
|

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

|
Thank you very much for your contribution then. We'll see if LINQ is going to be as performant as your implementation!
Simone Busoli
|
|
|
|

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

|
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?
|
|
|
|

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

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

|
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
|
|
|
|
 |
|
|
General News Suggestion Question Bug Answer Joke Rant Admin
Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.
|
Dynamically sorting a list by using dynamic methods and delegates.
| Type | Article |
| Licence | CPOL |
| First Posted | 18 Dec 2005 |
| Views | 187,869 |
| Bookmarked | 83 times |
|
|