In this post which
is part 4 of my mini-series Learning Razor with an RSS-App we'll enable
DNN-Search support for the new Lucene-based search built into DNN in Version
What we'll Do and Add In Part 4
- Learn a tiny bit about the DNN
- Learn a bit about preparing
data before the view gets to show it
- …and why you can't really do
this in Razor
- …and how we will do it even
though we can't
- Because the code will get a bit long, we'll move most of it to another file
- And then benefit from a much simpler data-binding syntax (the default dynamic objects instead of xml-notations)
The search topic is
very complex and I'll write more about it some other day - so let's just focus
on some minimal parts for now.
Preparation: Install Everything
Again, install 2sxc
and everything just like in Post #2, but in this case:
- Make sure you're using at least the version 6.3.3 of 2sxc because it fixes a search-bug
- Also install the V4-App for this lesson from Codeplex or the forge.
If you need help, just watch the video of post #2 in this series.
How does the DNN-Search work?
There are basically
two ways to create a web-search.
- One is the spider-approach
used by Google, which will index all the pages visible to the outside
world and use this index when people look for something. This has many
benefits and drawbacks. It's super simple and it works in many cases - but
then again it will have a lot of problems with security (can't spider
protected content), with security again (can't decide what to show when
people search) and with data that doesn't appear as text in the output
- The other is a more
data-oriented approach, where the indexer looks at the data (directly in
the DB or through the object-model) and says "give me everything and
tell me how to use it". This is harder to implement, more error prone
(these are the drawbacks) but more precise and controllable. It also
allows us to index things that "don't exist" or add metadata
which is never displayed but important for search.
DNN actually has both of these systems built in, but only in the pro version Evoq Content. The DNN-Platform only has a data-oriented, non-spider search. So if we want to
create something useful, we must support that search interface.
Why this can't work in Razor…
Razor is a
View-Engine. So any code we write will be executed when the module is supposed
to be viewed. When the search-index is running, it won't execute view code but
will actually execute this command in a module:
I hope to write more about the search, but let me point out for now that a few things look wrong to me in the current implementation:
- To support search, you don't support an interface, you must inherit a class ModuleSearchBase. This is bad, but it's the way it is. IMHO it should be a ISearchable or something (similar to the previous version). I don't know why they went from an interface-design to a class design.
- The signature of the the GetModified... command also uses "real" classes instead of Interfaces. IMHO it should be a IList<ISearchDocument> instead of IList<SearchDocument>. Yes, the "I" does make a big difference...
So: I believe these interfaces will change again in a near future - for now, they work.
…but we can make it work
In a perfect
MVC-situation, there must be a controller that delivers data for the search,
while the view should have no role in this at all.
So in reality, when
you create a module in DNN, you will have to support these search commands. If
you want to give the end-designer (the web-designer or view programmer) the
power to modify this, you must do something in addition to the View engine.
Now 2sxc has a
powerful data management system handling versions, languages and search. If we could add the RSS-data to
the modules data-pipeline, it would be indexed
So here's what we
- The Razor-Views in 2sxc
always have a Data object containing streams of .... data (surprise!). 2sxc
prepares this data before it starts up the View - and the data contains
things like the Content (stuff the editor added in this module, this is in
the default-stream), but it can also contain other things…
- The Razor-view might need to
add data which is not accessible in 2sxc - like this RSS-feed. But there
are also other possibilities, like SQL-queries or even 2sxc-data with
special filters applied. Because of this, there is a CustomizeData method that is always called
it exists in the template.
- This CustomizeData is used to prepare
data for the view…
- …and it also called when the
Search-Crawler indexes the module. So in this method you have the ability
to add data to the Data-Object - which would then end up in the search
- Note: the code got a bit long (about 60 lines of code) - so we outsourced it.
This is the code for the preparation work:
Note that there is
more to it - like there are status variables so that your code knows you're in
index-mode, and there is another command CustomizeSearch for advanced search
customization like alternate URLs (see documentation). But for now, let's keep it simple.
The Search Integration way 2sxc does it
This is how our
Views can customize the search. Note that there are other ways - but if your
module ever supports custom Razor-Views, you might want to plan with something
like this as well.
Here's the inline code (calling up the external functions and attaching it to the pipeline:
This code does the following
- Internal code: open the external library, call the helper function
- External code: Retrieve the data as always
- External code: Convert it to 2sxc entities
- creating a data-table
- putting the feed in it (with thumbnail and everything)
- and then using the data table as a DataSource.
This has to
do with the Data-Pipeline concept of 2sxc, allowing you to chain
data-functionality. So once something is in a DataSource, it can benefit
from other data-sources (like URL-Filters, advanced caching, etc.).
- Internal code: Then it attaches this new
data-source to the main Data, and that's it!
And search works automatically!
Since 2sxc automatically adds everything in the Data["..."] object to the search results, it all works now - see this example:
The Template Code can now be Simplified
Remember that the
placeholders in the HTML were long and ugly? Since the RSS-feed is now a
standardized entity, we can loop through the Data-stream instead of the
XML-object.So instead of this
We now get the nicer code like this:
See the Code and Live Result
...here. But it's even better to just download the App and work with it.
Now Try a bit yourself…
Go ahead and mess
about with the code. You can do it in Visual Studio or with the edit-template
functionality. And don't worry: you can always uninstall the App and install it
again when you mess it up :).
Wrapping up Phase 4 - Good and Bad
So now we have a
much improved Razor-based App. A lot of the last shortcomings are fixed:
- DNN-Search Support now works
- ...but doesn't cache - so the RSS-feed will be requested every single minute because the search asks for it
- Template looks much simpler, because the placeholders got nicer
- Long data preparation-code is outsourced (and can be shared across templates)
…but it still has
many shortcomings like the following:
- It's not multi-language
capable - so if you needed a different feed in another language, again you
would have to create a new script - or additional fields for each language
- The data is not cached - so the RSS-feed will be requested
- every single minute because the search asks for it
- and for every page view
- The admin can't control the
presentation (like decide to show/hide images or reduce the amount of
- The admin couldn't easily
switch to another view like a 2-column look instead of a table
shortcomings will be handled in the following lessons :). Stay tuned!
With love from Switzerland
Daniel Mettler grew up in the jungles of Indonesia and is founder and CEO of 2sic internet solutions in
Switzerland and Liechtenstein, an 20-head web specialist with over 600
DNN projects since 1999. He is also chief architect of 2sxc (2SexyContent - see forge), an open source module for creating attractive content and DNN Apps.