As I’ve been somewhat involved in the development of the new DynamicDataStore in EPiServer CMS 6, and especially its LINQ-support, I started to think that much of the same concepts should be able to apply on EPiServer Pages as well to provide a nice LINQ-interface to EPiServer PageData.
So, I sat down and ripped out parts of the DDS LINQ-support and rewrote some stuff needed for querying pages/properties, and found out that I could actually quite easily support a bunch of LINQ-features like sorting, skip&take, projection with select new, grouping et.c.
This project has been my little “pet” for some time now, but after Allan Thraen showed it in his talk “Codemania” at the EPiServer Partner Summit 2010, I believe its time to present the module for a more public review.
Note that this is an experimental technical preview of some functionality that may or may not be adopted into the core product sometime in the future.
It is provided “as is” for you to play around with and use as you may seem appropriate.
Suggestions and comments are of course more than welcome!!
Also, please note the following precautions:
- The module is completely unsupported and provided “as is”.
You are more than welcome to email me any questions/suggestions, but I cannot make any guarantees on fixing/adding functionality. |
- LINQ support is far from complete, and you will most likely stumble upon unsupported functionality as you evaluate.
|
- The only database engine supported so far is MS SQL Server 2008 (this also includes the “free” express version). MS SQL Server 2005 might work, but I haven’t confirmed this.
|
- The module will work with EPiServer versions 5R2 and CMS6. Future releases are not guaranteed to work.
|
- The module only work towards the EPiServerDB database.
It will not work for any Custom Page Provider.
|
|
Ok, having said that,
How do i get the stuff installed?
First, download the zip from http://labs.episerver.com/PageFiles/111555/EPiServer.Research.Linq2Pages.zip (21Kb) and unzip them to some tempfolder (it only contains two files).
Then copy the EPiServer.Research.Linq2Pages.dll to your EPiServer’s /bin folder.
The module requires some additions to the database, a table, a few functions and a new datatype, but these objects should be added automatically as you execute your first query.
The zip also contains the dbscript.sql if the automatic registration should fail, or if you decide to remove the objects in the future (just select all “drop”-statements in the beginning of the file and hit execute).
All added database objects have a name starting with “EPiServer.Research.Linq2Pages” to be easily found and removed if needed.
No modifications on any existing database objects are made.
How do I use it?
You work with it by creating an instance of the PageQuery type. The type tries to mimic the functionality of the familiar FindPagesWithCriteria API function, so most of the arguments would be the same.
The first argument is a PageReference to a page which acts as the root for the query, only pages “below” this root page are returned.
The second argument is a LanguageBranch stating what language branch the query should operate on.
The third/fourth arguments are optional and dictates how permission checks are to be performed.
Let me show a small sample in code illustrating how it may look like:
1: using EPiServer.Research.Linq2Pages;
2:
3: ...
4:
5: var myQuery = new PageQuery(PageReference.StartPage, "en");
6:
7: var myResults = from page in myQuery
8: where page.PageTypeID == 3
9: orderby page.StartPublish descending
10: select page;
11:
12: foreach( PageData page in myResults.Take(5) )
13: { 14: // do something
15: }
This sample would fetch the five most recently published pages with a PageTypeID = 3.
You may query on Property data as well, the trick is to cast the property into its “native” dataformat (string, int, float, datetime, bool et.c.)
An example on querying on properties lools like so:
1: var myQuery = new PageQuery(PageReference.StartPage, "en");
2:
3: var myResults = from page in myQuery
4: where (string)page["MainIntro"] == "Some intro text"
5: select page;
6:
7: foreach( PageData page in myResults )
8: { 9: // do something
10: }
Ok, so far we’ve only fetched complete pagedata objects delivered through the DataFactory (we do a select page in our query). If you only need some properties, for example to present a list of pages, you might consider using select new to only fetch the needed properties. An example might be:
1: var myQuery = new PageQuery(PageReference.StartPage, "en");
2:
3: var myResults = from page in myQuery
4: where page.PageTypeID == 3
5: select new { 6: PageLink = page.PageLink,
7: PageName = page.PageName,
8: MainBody = (string)page["MainBody"]
9: };
10:
11: foreach( var subpage in myResults )
12: { 13: // do something
14: }
In this query, the actual property values are retrieved directly in the query and DataFactory is not invovled at all.
An important note here: you may only be able to return properties in their “native” dataformat, i.e. the MainBody property above would be of type string, and not PropertyXhtmlString.
Permissions
All queries are executed with a access permissions check (unless the AccessLevel.NoAccess is specified when creating the PageQuery).
This adds some overhead to the queries, and if you only work with public data, you may want to use the below constructor instead:
1: var myQuery = new PageQuery(PageReference.StartPage, "en", AccessLevel.NoAccess);
Some more advanced query examples
1: var myQuery = new PageQuery(PageReference.StartPage, "en");
2:
3: var myResults = from page in myQuery
4: group page by page.PageTypeID into g
5: orderby g.Count() descending
6: select new { 7: PageTypeID = g.Key,
8: Count = g.Count()
9: };
10:
11: foreach( var subpage in myResults )
12: { 13: // do something
14: }
Here we use a group by to get a list of all pagetypeids and the count of pages created using that pagetype.
1: var myQuery = new PageQuery(PageReference.StartPage, "en");
2:
3: var myResults = from page in myQuery
4: group page by page.StartPublish.Month into g
5: orderby g.Count() descending
6: select new { 7: Month = g.Key,
8: Count = g.Count()
9: };
10:
11: foreach( var subpage in myResults )
12: { 13: // do something
14: }
Here we run a query to list the months when pages were published, ordered descendingly by the psge count.
Viewing the query
The generated sql-query is logged through log4net, and may be viewed by adding a logger with the name EPiServer.Research.Linq2Pages”.
1: <logger name="EPiServer.Research.Linq2Pages">
2: <level value="ALL"/>
3: <appender-ref ref="ConsoleAppender" />
4: </logger>
This would be invaluable to look at when trying to understand why your most ingenious LINQ-construct fails to execute, as I am pretty confident will happen sooner or later ;-)
Ok, thats all for now, I look forward to your comments & suggestions!
Regards,
Johan Olofsson