i new to MVC and LINQ and i am learning how to use AngularJs and MVC for a new project that i have been assigned. So i am learning these technologies through an online video.The tutor uses code first approach, so Visual Studio Creates all the necessary files and a database that matches the tutors models, i on the other hand already have a database and i created the Api's to perform REST operations against the DB. So when i run this code below
public IEnumerable<Todo> GetTodos()
{
return Models.DbStoredProcModel.GetAllTodos.GetAllTodoEntryStoredProcedure();
}
it return a Json output like this
So up to this point, my json output matches with the video tutorial even though our code approach is different. So the tutor, using this piece of code he wrote
Tutors Code:
public IEnumerable<Todo> GetTodos(string q= null,string sort = null, bool desc = false, int ? limit = null, int offset =0)
{
var result = ((IObjectContextAdapter)db).ObjectContext.CreateObjectSet<Todo>();
IQueryable<Todo> items = string.IsNullOrEmpty(sort)
? result.OrderBy(o => o.Priority)
: result.OrderBy(String.Format("it. {0} {1}", sort, desc ? "DESC" : "ASC"));
if (!string.IsNullOrEmpty(q) && q != "undefined")
items = items.Where(t => t.Text.Contains(q));
if (offset > 0) items = items.Skip(offset);
if (limit.HasValue) items = items.Take(limit.Value);
return items;
}
it will allow him to perform Search and Sort operations on the data returned.
now here is my own code trying to code trying to do the same.
public IEnumerable<Todo> GetTodos(string q= null,string sort = null, bool desc = false, int ? limit = null, int offset =0)
{
var result = Models.DbStoredProcModel.GetAllTodos.GetAllTodoEntryStoredProcedure( );
IQueryable<Todo> items = string.IsNullOrEmpty(sort)
? result.OrderBy(o => o.Priority)
// i get the error on the line below
: result.OrderBy(String.Format("it. {0} {1}", sort, desc ? "DESC" : "ASC"));
if (!string.IsNullOrEmpty(q) && q != "undefined")
items = items.Where(t => t.Text.Contains(q));
if (offset > 0) items = items.Skip(offset);
if (limit.HasValue) items = items.Take(limit.Value);
return items;
}
however, i get an error saying that
Error:The type arguments for method 'System.Linq.Enumerable.OrderBy<TSource,TKey>(System.Collections.Generic.IEnumerable<TSource>, System.Func<TSource,TKey>)' cannot be inferred from the usage. Try specifying the type arguments explicitly.
How do i fix this?
Change
result.OrderBy(String.Format("it. {0} {1}", sort, desc ? "DESC" : "ASC"))
to
result.OrderBy<Todo,string>(String.Format("it. {0} {1}", sort, desc ? "DESC" : "ASC"))
for a more clear error message.
The underlying issue is perhaps that
result.OrderBy(String.Format("it. {0} {1}", sort, desc ? "DESC" : "ASC"))
doesn't have a meaningful interpretation in the context of your application. It looks like dynamic linq to me, requiring references that pull in alternative OrderBy definitions than the ones you will have referenced.
My suggestion would be to check the namespaces referenced by the tutorial (in the project references and at the top of this file), in case you have missed any.
Related
My result set is not sorting. How do I set up OrderBy for type System.Linq.GroupedEnumerable
I've converted an application from Core 1.1 to 2.2. Everything ported over fine except one piece of logic that:
1) takes a response from a service call maps it to a GroupedEnumerable
2) takes the grouped set and passes it to a function that maps it to an object of type System.Linq.Enumerable.SelectEnumerableIterator.
The resulting object is properly populated but not sorted. I have tried the order by in the function parameter call and as a separate process afterwards.
//response = {myService.myClient.SearchNominationsResponse}
//groupedSet = {System.Linq.GroupedEnumerable<ServiceClients.myClient.NominationObject, long>}
//result = {System.Linq.Enumerable.SelectEnumerableIterator<System.Linq.IGrouping<long, ServiceClients.myClient.NominationObject>, app.ViewModels.EvaluationSummary>}
public IEnumerable<EvaluationSummary> GetEvaluationSummaries(string sortBy, string sortOrder, Filter filter = null)
{
var request = Mapper.MapSearchNominationRequest(filter);
request.IsDetailed = false;
var response = myService.SearchNominationsAsync(request).GetAwaiter().GetResult();
var groupedSet = response.mySet.GroupBy(n => n.SetId);
// I get a proper result but it is not sorted
var result = groupedSet.Select(
g => Mapper.MapEvaluationSummary(
g.OrderBy(g2 => sortBy + " " + sortOrder)
.Last()));
// Still not sorting
result = result.OrderBy(r => sortBy + sortOrder);
return result;
}
public EvaluationSummary MapEvaluationSummary(SetObject setIn)
{
var eval = new EvaluationSummary
{
setDate = setIn.Date,
setId = setIn.Id,
setTypeId = setIn.TypeId,
setTypeDescription = setIn.TypeDescription,
setStatusId = setIn.StatusId,
setStatusDescription = setIn.StatusDescription,
setBy = setIn.Manager,
setEmployee = setIn.employee
};
}
So in my view I have columns that list Date, Id, setEmployee. I can click on these values to issue a sort pattern and I can see that the sortBy and sortOrder variables are being passed in with proper values but the sorting is not happening.
I expect 'William' to appear before 'Bill' and then Bill to appear before 'William' when toggling the employee column header in my view.
Based off of the previous answers, I'm still not sure if I can substitute a property name in the LINQ with a variable. To fix my problem and move on I've implemented JS logic to sort my table headers. We had a custom JS that we use to format tables in our apps but it seems the sort functionality never worked. Anyway not an answer to my question but this is how I solved the problem:
Logic can be found at:
http://www.kryogenix.org/code/browser/sorttable/
-HaYen
I have a list which can contain multiple records of people:
result =people.Select(p => new PersonDetail
{
Involvement = p._Discriminator,
FullName = p.BuildFullName(),
DateOfBirth = p.DateOfBirth != null ? p.DateOfBirth.Value.ToShortDateString() : string.Empty,
Race = MappingExtensions.MergeCodeAndText(p.RaceCode, p.RaceText),
Sex = MappingExtensions.MergeCodeAndText(p.SexCode, p.SexText),
Height = p.Height,
Weight = p.Weight != null ? p.Weight.ToString() : string.Empty,
EyeColor = MappingExtensions.MergeCodeAndText(p.EyeColorCode, p.EyeColorText),
HairColor = MappingExtensions.MergeCodeAndText(p.HairColor1Code, p.HairColor1Text),
//...
}).ToList();
I want to order this list by Involvement type (victim, suspect, witness).
I've tried the following using Remove and Insert:
foreach (var i in result.Where(i => i.Involvement.ToLower() =="suspect"))
{
result.Remove(i);
result.Insert(0, i);
}
return result;
On the first loop it works as I would expect however on the second loop I get an exception thrown. I suspect there is some recursion going on or the exception is thrown because it keeps finding the record I promoted on the first pass and can't get by it.
I wanted to perform a loop vs just one pass as there might be multiple records that are marked as suspect. I need to promote all of these to the top above witness or victims. The other involvements are not relevant in ordering.
example:
Bob "suspect"
Jane"suspect"
Kenny "witness"
Joe "victim"
Any suggestions on how to select multiple records and ensure they are placed at the top of the list above others?
Thanks for any ideas or suggestions
First of all your can't change result collection in foreach. The collection used in foreach is immutable and here is MSDN link with explanation: foreach
You can use OrderBy to reorder your collection:
result = result.OrderBy(r => r.Involvement.ToLower() =="suspect" ? 0 : 1).ToList();
Expression in OrderBy will promote "suspect" items to the top of the result.
The currently accepted answer will only succeed if the list being ordered only cares about the precedence of Suspect being at the top of the list, i.e.,
Kenny, Witness
Bob, Suspect
Joe, Victim
Jane, Suspect
In other words, if the ordering precedence also includes Witness and Victim, the the accepted answer will be correct because Witness is already taking precedence in the order over Victim; when using the accepted answer, the result will correctly be:
Bob, Suspect
Jane, Suspect
Kenny, Witness
Joe, Victim
However, if the ordering precedence must include strings other than "Suspect", then the accepted answer will fail, i.e. if the list comes in as,
Jill, Victim
Kenny, Witness
Bob, Suspect
Joe, Victim
Jane, Suspect
Then the result will be:
Bob, Suspect
Jane, Suspect
Jill, Victim
Kenny, Witness
Joe, Victim
But, the correct result should be (assuming Witness takes precedence over Victim):
Bob, Suspect
Jane, Suspect
Kenny, Witness
Jill, Victim
Joe, Victim
To custom sort based on a non-alpha sort, you'll need to provide some type of IComparer or similar:
// Sample class with a name and involvement:
public class Detail
{
public string Name { get; set; }
public string Involvement { get; set; }
public Detail( string name, string involvement )
{
Name = name;
Involvement = involvement;
}
}
// implementation of IComparer that uses a custom alpha sort:
public class DetailComparer : IComparer<Detail>
{
static readonly List<string> Ordered = new List<string> { "suspect", "witness", "victim" };
public int Compare( Detail x, Detail y )
{
int i = Ordered.FindIndex( str => str.Equals( x.Involvement, StringComparison.OrdinalIgnoreCase ) );
int j = Ordered.FindIndex( str => str.Equals( y.Involvement, StringComparison.OrdinalIgnoreCase ) );
if( i > j ) return 1;
if( i < j ) return -1;
return 0;
}
}
The list can then be sorted by providing the comparer:
var details = new List<Detail>
{
new Detail("Jill", "Victim"),
new Detail("Kenny", "Witness"),
new Detail("Bob", "Suspect"),
new Detail("Joe", "Victim"),
new Detail("Jane", "Suspect"),
};
details.Sort( new DetailComparer() );
By providing the custom IComparer, any precedence can be declared for every "Involvement".
I will attempt to be as specific as possible. So we are using Sitefinity 8.1.5800, I have a couple dynamic content modules named ReleaseNotes and ReleaseNoteItems. ReleaseNotes has some fields but no reference to ReleaseNoteItems.
Release Note Items has fields and a related data field to ReleaseNotes.
So I can query all ReleaseNoteItems as dynamic content pretty quickly less than a second.
I then use these objects provided by sitefinity and map them to a C# object so I can use strong type. This mapping process is taking almost a minute and using over 600 queries for only 322 items (N+1).
In Short: I need to get all sitefinity objects and Map them to a usable c# object quicker than I currently am.
The method for fetching the dynamic content items (takes milliseconds):
private IList<DynamicContent> GetAllLiveReleaseNoteItemsByReleaseNoteParentId(Guid releaseNoteParentId)
{
DynamicModuleManager dynamicModuleManager = DynamicModuleManager.GetManager(String.Empty);
Type releasenoteitemType = TypeResolutionService.ResolveType("Telerik.Sitefinity.DynamicTypes.Model.ReleaseNoteItems.Releasenoteitem");
string releaseNoteParentTypeString = "Telerik.Sitefinity.DynamicTypes.Model.ReleaseNotes.Releasenote";
var provider = dynamicModuleManager.Provider as OpenAccessDynamicModuleProvider;
int? totalCount = 0;
var cultureName = "en";
Thread.CurrentThread.CurrentUICulture = new CultureInfo(cultureName);
Type releasenoteType = TypeResolutionService.ResolveType("Telerik.Sitefinity.DynamicTypes.Model.ReleaseNotes.Releasenote");
// This is how we get the releasenote items through filtering
DynamicContent myCurrentItem = dynamicModuleManager.GetDataItem(releasenoteType, releaseNoteParentId);
var myMasterParent =
dynamicModuleManager.Lifecycle.GetMaster(myCurrentItem) as DynamicContent;
var relatingItems = provider.GetRelatedItems(
releaseNoteParentTypeString,
"OpenAccessProvider",
myMasterParent.Id,
string.Empty,
releasenoteitemType,
ContentLifecycleStatus.Live,
string.Empty,
string.Empty,
null,
null,
ref totalCount,
RelationDirection.Parent).OfType<DynamicContent>();
IList<DynamicContent> allReleaseNoteItems = relatingItems.ToList();
return allReleaseNoteItems;
}
This is the method that takes almost a minute that is mapping sitefinity object to C# object:
public IList<ReleaseNoteItemModel> GetReleaseNoteItemsByReleaseNoteParent(ReleaseNoteModel releaseNoteItemParent)
{
return GetAllLiveReleaseNoteItemsByReleaseNoteParentId(releaseNoteItemParent.Id).Select(rn => new ReleaseNoteItemModel
{
Id = rn.Id,
Added = rn.GetValue("Added") is bool ? (bool)rn.GetValue("Added") : false,
BugId = rn.GetValue<string>("bug_id"),
BugStatus = rn.GetValue<Lstring>("bugStatus"),
Category = rn.GetValue<Lstring>("category"),
Component = rn.GetValue<Lstring>("component"),
#Content = rn.GetValue<Lstring>("content"),
Criticality = rn.GetValue<Lstring>("criticality"),
Customer = rn.GetValue<string>("customer"),
Date = rn.GetValue<DateTime?>("date"),
Grouped = rn.GetValue<string>("grouped"),
Override = rn.GetValue<string>("override"),
Patch_Num = rn.GetValue<string>("patch_num"),
PublishedDate = rn.PublicationDate,
Risk = rn.GetValue<Lstring>("risk"),
Title = rn.GetValue<string>("Title"),
Summary = rn.GetValue<Lstring>("summary"),
Prod_Name = rn.GetValue<Lstring>("prod_name"),
ReleaseNoteParent = releaseNoteItemParent,
McProductId = GetMcProductId(rn.GetRelatedItems("McProducts").Cast<DynamicContent>()),
}).ToList();
}
Is there any way to optimize this all into one query or a better way of doing this? Taking almost a minute to map this objects is too long for what we need to do with them.
If there is no way we will have to cache the items or make a SQL query. I would rather not do caching or SQL query if I do not have to.
Thank you in advance for any and all help you can provide, I am new to posting questions on stackoverflow so if you need any additional data please let me know.
Is there a reason why you are doing a .ToList() for the items? Is it possible for you to avoid that. In my opinion, most of the time(of the 1 minute) is taken to convert all your items into a list. Conversion from Sitefinity object to C# object is not the culprit here.
Look Arno's answer here: https://plus.google.com/u/0/112295105425490148444/posts/QrsVtxj1sCB?cfem=1
You can use the "Content links manager" to query dynamic modules relationships (both by parent -ParentItemId- or by child -ChildItemId-) much faster:
var providerName = String.Empty;
var parentTitle = "Parent";
var relatedTitle = "RelatedItem3";
DynamicModuleManager dynamicModuleManager = DynamicModuleManager.GetManager(providerName);
Type parentType = TypeResolutionService.ResolveType("Telerik.Sitefinity.DynamicTypes.Model.ParentModules.ParentModule");
Type relatedType = TypeResolutionService.ResolveType("Telerik.Sitefinity.DynamicTypes.Model.RelatedModules.RelatedModule");
ContentLinksManager contentLinksManager = ContentLinksManager.GetManager();
// get the live version of all parent items
var parentItems = dynamicModuleManager.GetDataItems(parentType).Where(i => i.GetValue<string>("Title").Contains(parentTitle) && i.Status == ContentLifecycleStatus.Live && i.Visible);
// get the ids of the related items.
// We use the OriginalContentId property since we work with the live vesrions of the dynamic modules
var parentItemIds = parentItems.Select(i => i.OriginalContentId).ToList();
// get the live versions of all the schedules items
var relatedItems = dynamicModuleManager.GetDataItems(relatedType).Where(i => i.Status == ContentLifecycleStatus.Live && i.Visible && i.GetValue<string>("Title").Contains(relatedTitle));
// get the content links
var contentLinks = contentLinksManager.GetContentLinks().Where(cl => cl.ParentItemType == parentType.FullName && cl.ComponentPropertyName == "RelatedField" && parentItemIds.Contains(cl.ParentItemId) && cl.AvailableForLive);
// get the IDs of the desired parent items
var filteredParentItemIds = contentLinks.Join<ContentLink, DynamicContent, Guid, Guid>(relatedItems, (cl) => cl.ChildItemId, (i) => i.OriginalContentId, (cl, i) => cl.ParentItemId).Distinct();
// get the desired parent items by the filtered IDs
var filteredParentItems = parentItems.Where(i => filteredParentItemIds.Contains(i.OriginalContentId)).ToList();
I would imagine that every release note item under a single release note would be related to the same product wouldn't it?
If so, do you need to do the GetMcProductId method for every item?
I'm trying to optimize the following method by having it return only the data my program actually needs for its calculations. In the past the dataset hasn't been that large so I haven't needed to optimize this too much. More recently, my dataset grew from about 30k records to 700k records, so I'm trying to optimize this further.
public void readRawThresholdsInList(int inputtedESourceID, DateTime maxDateTimeVal, List<int> hashKey)
{
log.Info("Reading in raw thresholds from db");
using (FAI db= new FAI ())
{
rawThresholds = (from thr in db.Thresholds
where thr.CalculatedDate == maxDateTimeVal
where thr.ErrorSourceId == inputtedESourceID
where hashKey.Contains(thr.HashKey)
select thr).ToList();
}
log.Info("Read in " + rawThresholds.Count() + " threshold records for error source id: " + inputtedESourceID + ".");
}
I got the method to return about 200k rows from 700k by adding the hashKey.Contains(thr.HashKey), but I want to take this one step further by doing the contains on a combination of 2 fields instead of just 1. The catch is that this has to happen on the DB for it to improve my runtimes. There is already post processing that only acts on the rows the program will need.
I want to be able to give the method the following as an input and have the contains (or something similar) act on the new HashKeyHostId object:
public HashKeyHostId{public int hashKey; public int hostId;}
public void readRawThresholdsInList(int inputtedESourceID, DateTime maxDateTimeVal, List<HashKeyHostId> hashKeyHostIdPairs){
}
There's probably a very simple way of doing this that I'm not thinking of.
Edit:
In response to James's comment - I don't believe this would work. The hashkey variable would now be an object containing pairs of integers instead of just 1 integer. This alone would prevent you from doing a .contains because it's no longer a primitive. It'd be the "public HashKeyHostId{public int hashKey; public int hostId;}" object I posted above. The new requirement is that a hashkey/hostId combo have to match up to 1 record in the DB.
If you're suggesting I do what Janne said (give it a list of hash keys and a list of host ids), I'm fairly certain this would return all results where the hashkey belongs to any of the hostids in the second list (aka, any combination of the two lists). I need it to only return rows with the specified combinations, not combinations of anything in the two lists.
Edit2: Sample dataset:
Hashkeys = 100,101,102,103
HostIds = 1,2,3,4,5,6,...,10000
I'd give it a list like
List<HashKeyHostId> data = new List<HashKeyHostId>()
{new HashKeyHostId(100,1),new HashKeyHostId(101,5)}
I believe the query Janne/James are suggesting would return records for any of those combinations (100,1; 100,5; 101,1; 101,5;). I need it to only return records for 100,1 and 101,5.
edit3:
I tried doing a where hashkeyHostIdpair.Any(o=> o.hashkey==thr.HashKey && o.hostid==thr.HostId)", but that errored out with the same "Unable to create a constant value of type 'RTM_DB.HashKeyHostId'. Only primitive types are supported in this context." message. It doesnt look like you can do a .Any or a .contains on a list of non-primitive types. I even tried making my own .Any with a where clause and that threw the same exception. (where hashkeyHostIdpair.Where(o=>o.hostid==thr.HostId && o.hashkey==thr.HashKey).Count()>0))
edit4: Per Josh's suggestion I tried this:
rawThresholds=fai.Thresholds.Where(o=>o.CalculatedDate==maxDateTimeVal)
.Where(o=>o.ErrorSourceId==inputtedESourceID)
.Where(o=> hashkeyHostIdpair.Contains(new HashKeyHostId(){ hashkey=o.HashKey, hostid = o.HostId})).ToList();
but it errored out with {System.NotSupportedException: Unable to create a constant value of type 'RTM_DB.HashKeyHostId'. Only primitive types ('such as Int32, String, and Guid') are supported in this context
There's probably a very simple way of doing this that I'm not thinking of.
Yeah, there is
where hashKey.Contains(someValue) && hashKey.Contains(someOtherValue)
Something along this would maybe be what you want to do?
public void readRawThresholdsInList(int inputtedESourceID, DateTime maxDateTimeVal, List<int> hashKeys, List<int> hostIds)
{
log.Info("Reading in raw thresholds from db");
using (var db = new FAI())
{
var rths = (from thr in db.Thresholds
where thr.CalculatedDate == maxDateTimeVal
&& thr.ErrorSourceId == inputtedESourceID
select thr);
if (hashKeys != null && hashKeys.Count() > 0)
rths = rths.Where(rth => hashKeys.Contains(rth.HashKey))
if (hostIds != null && hostIds.Count() > 0)
rths = rths.Where(rth => hostIds.Contains(rth.HostId)) // FieldName?
rawThresholds = rths.ToList();
}
log.Info("Read in " + rawThresholds.Count() + " threshold records for error source id: " + inputtedESourceID + ".");
}
-- edit --
You could do something like this, but I wouldnt recommend it. Figure out a value which you can multiply the HashKey safely so HostId will always be in the last digits
var filters = new int[] { 100 * 100 + 1 , 101 * 100 + 5 }; // 10001 & 10105
var rths = (from rth in db.Thresholds
where rth.CalculatedDate == maxDateTimeVal
&& rth.ErrorSourceId == inputtedESourceID
&& filters.Contains(rth.HashKey * 100 + rth.HostId)
select rth).ToList();
If you have something like
List<HashKeyHostId> data = new List<HashKeyHostId>() {
new HashKeyHostId { hashKey = 100, hostId = 1 },
new HashKeyHostId { hashKey = 101, hostId = 5 }
}
You can use it in a contains like this:
<somequery>.Where(x => data.Contains(new HashKeyHostId { hashKey = x.HashKey, hostId = x.HostId }))
Note the use of the object initializer syntax instead of a constructor.
This should get translated to SQL with each item in the list being appended to the WHERE clause like:
WHERE ([t0].[HashKey] = #p0 AND [t0].[HostId] = #p1) OR
([t0].[HashKey] = #p2 AND [t0].[HostId] = #p3) OR ...
I have a class which has approx 50 properties, instances of this class is added to a list. This list is then added to a Velocity context. Now, I would like to sort this data. Which field, or if it is ascending or descending is not known until the template is being parsed.
Resources I've looked into:
Better way to use Velocity's GenericTools in a Standalone app?
Velocity foreach sort list
http://velocity.apache.org/tools/devel/generic/
Based on the resources listed here I can't figure out how to solve this. Is the GenericTools available for the Castle's Nvelocity? If not, how may I implement such a generic sort I'm asking for here?
My solution was to write my own sort-class and add this as a context to nvelocity. I'm passing the field to sort on as string and accessing it as reflection. I'm also setting sort ascending or descending by string value. I'm also passing in the name of the comparer and accessing this with reflection as well. I'm then using the List method OrderBy or OrderByDescending with the choosen field and comparer.
I did find parts of the code here: http://zootfroot.blogspot.co.uk/2009/10/dynamic-linq-orderby.html
public class NvelocitySort
{
public List<MyObject> Sort(List<MyObject> list, string fieldAndMode, string comparerName)
{
fieldAndMode = fieldAndMode.Trim();
// Split the incoming string to get the field name and sort ascending or descending
string[] split = fieldAndMode.Split(' ');
// Set default sort mode
string mode = "asc";
// If sort mode not specified, this will be the field name
string field = fieldAndMode;
// If sort mode added split length shall be 2
if (split.Length == 2)
{
field = split[0];
if (split[1].ToLower() == "asc" || split[1].ToLower() == "ascending") mode = "asc";
if (split[1].ToLower() == "desc" || split[1].ToLower() == "descending") mode = "desc";
}
// If length is more than 2 or 0, return same list as passed in
else if (split.Length > 2 || split.Length == 0)
{
return list;
}
// Get comparer based on comparer name
IComparer<string> comparer = (IComparer<string>)Activator.CreateInstance(Type.GetType(string.Format("Namespace.{0}", comparerName)));
// Choose the sort order
if (mode == "asc")
return list.OrderBy(item => item.GetReflectedPropertyValue(field), comparer).ToList();
if (mode == "desc")
return list.OrderByDescending(item => item.GetReflectedPropertyValue(field), comparer).ToList();
// If sort order not asc/desc return same list as passed in
return list;
}
}
This is the reflection method for retrieving the field.
public static string GetReflectedPropertyValue(this object subject, string field)
{
object reflectedValue = subject.GetType().GetProperty(field).GetValue(subject, null);
return reflectedValue != null ? reflectedValue.ToString() : "";
}
Simple comparer example:
public class TextComparer : IComparer<string>
{
public int Compare(string x, string y)
{
return string.Compare(x, y);
}
}
Added to Nvelocity context like this:
this.velocityContext.Put("sorter", new NvelocitySort());
Accessed from Nvelocity template like this:
#foreach($item in $sorter.Sort($listObject, "Name desc", "TextComparer"))
$item.Name
#end
Hope it helps someone else...
EDIT:
Found an even better way to do it (implements multiple fields sorting):
http://www.codeproject.com/Articles/280952/Multiple-Column-Sorting-by-Field-Names-Using-Linq