Is there a better/more efficient way to query ContentTypes than how I am doing it here?

Topics: Writing modules
Jun 13, 2011 at 7:07 PM

Hi there,

I have defined a ContentType named: 'Event'. It contains a custom field named 'EventDate' of type DateField (custom class).
I need to list events grouped by year and month. I cannot use the standard list containers as they give me no possibility to sort by EventDate nor can I have contained items grouped.

I am obtaining my grouped/sorted results with the code pasted below. It is working, but I wonder though, if there is a better way to do this?
It would be very nice if I could get rid of the SortEntry helper class!!


Helper class:

        class SortEntry
            public SortEntry(ContentItem ci, DateTime ed)
                ContentItem = ci;
                EventDate = ed;

            public ContentItem ContentItem { get; set; }
            public DateTime EventDate { get; set; }
Code that retrieves Events and sorts/groups them:
           // in case I make a module out of this
            string contentType = "Event";
            string sortBy = "EventDate";

            // make sure the the required type actually exists!
            var contentTypeDefinition = _contentDefinitionManager.GetTypeDefinition(contentType);
            if (contentTypeDefinition == null)
                return HttpNotFound();
            var events = _orchardServices.ContentManager.Query().ForType(contentType).List();
            var sortEntries = new List<SortEntry>();
            foreach(var myEvent in events)
                var contentPart  = myEvent.Parts.FirstOrDefault(part => part.GetType() == typeof(ContentPart));
                if (contentPart != null)
                    var myfield = contentPart.Fields.Where(f => f.Name == sortBy).SingleOrDefault() as DateField;
                    if (myfield != null)
                        sortEntries.Add(new SortEntry(myEvent, myfield.DateTime.Value));

            // sort and group
            var results = from f in sortEntries
                          orderby f.EventDate descending
                          group f by new { f.EventDate.Year, f.EventDate.Month } into g 
                          select new { Year = g.Key.Year, Month = g.Key.Month, Events = (from c in g orderby c.EventDate.Day descending select c.ContentItem)};

            // process results (fill view model)
            foreach (var result in results)
                var year = result.Year;
                var month = result.Month;
                foreach (var ee in result.Events)
                    // grab body
                    var bodyPart = ee.Get<BodyPart>();
                    if (bodyPart != null)
                        string eventInfo = bodyPart.Text;


Jun 13, 2011 at 10:40 PM

Hi Santiago,

To achieve this properly you need to implement a content part instead of a field.

But before you go any further - did I already point you towards smeyer's RoutableByDatePart? See:

You can add it to your Event content type and then it'll get displayed on URLs like /yyyy/mm/.

Once you have a part available, you can join and sort on the fly from ContentQuery like so:

 var events = _orchardServices.ContentManager.Query(contentType).OrderBy<RoutableByDatePartRecord>(date=>date.EventStartDate).List();
Jun 13, 2011 at 11:51 PM

Thanks Pete!
I see now that the ContentManager is pretty much Part oriented. Indeed it would be much simpler if my EventDate would be a Part and not a Field.
BTW, I could not find the RoutableByDatePart in the gallery, but I see the link in the thread was still valid.

I'll change my code so that EventDate will be a custom Part.

Just for curiosity, do you know why the ContentManager does not support Fields in the same extent as it supports Parts? I was under the impression the difference between them was on a conceptual level and not so much functionality-wise. But I realize now I was completely wrong there.

Thanks again!


Jun 14, 2011 at 12:42 AM

There was a discussion about this elsewhere. There is a possibility that fields will be queryable in a future version. But they are implemented completely differently - a part is backed by an actual database record, and hence is queryable via Linq - whereas fields are just bolted onto a part and all stored in an XML field, hence not Linq queryable. Other than that, you can do most things with a field that you can with a part, including participating in Indexing and therefore getting standard Search functionality. But for custom business logic and querying you need the more advanced scenarios that parts are capable of.

I guess Steve hasn't posted that module on the gallery yet, but you can still install it using that download. Haven't got around to testing it myself yet, although I'll be looking at it soon.

Jun 14, 2011 at 2:09 AM

Thx for the insight!
Hopefully Orchard will be extended with queryable Fields in the future.