CogniScale – virtual hosting made easy

We’ve not been talking much about it and that’s partially my fault as well (busy with other projects), but Cognifide has a really cool initiative called Cognifide Labs that we intend to grow over time. The plan is to devote up to 10% company time into side projects that help us grow expertise and allow our devs to dwell into interesting technologies, methodologies and languages and develop their skills.

One of the first projects (that I took part in) is CogniScale – an app that allows FlexiScale users to manage their servers. Here’s the story…

Being the agile company taking part in many EPiServer projects we never seem to have enough environments to test our web-apps and software in general in various scenarios. We find ourselves constantly reinstalling and trying to keep our servers in a state that can can at least remotely be called as stable. After all how many deployments and tearing down of various EPiServer, CruiseControl, TeamCity, SQL Server and other "I need to have" apps can a server take before slowing down to a crawl or collapsing all together (that said I bow before our faithful THOTH for taking all the abuse it does). We definitely needed more servers! And we needed them now!

Early this year we’ve started to talk to the guys at XCalibre that came up with a great idea. What if you could have an unlimited amount of servers available for you at any given time? I mean really what if you could have 0 servers one day and the next day have a rich farm of servers for literally no cost, paying only when you power them up and not paying a bit if you take them down.  This turned out to be quite a project for them that turned to materialize as FlexiScale. (you can read more about it here). Looking at all that I’ve mentioned before while eliminating the cost of maintaining the servers locally we decided to give FlexiScale a spin.

Read the rest of this article »

Is EpiServer a hard-shelled clam or what?

As much as I seem to be enjoying my trip with EpiServer there are some little things I don’t seem to appreciate all that much and I’m not quite sure how to work around some of them in an elegant way.

EpiServer has a fairly advanced way of dealing with properties but it also seems to be a bit tough on the developer whenever you try to do something more than just strictly using its API-s. One of the areas I don’t really enjoy is the dealing with the pages that are expired or generally unavailable for the user for various reasons.

In the project that we’ve been implementing recently we needed to store page ids for further reference in numerous places and although this generally works, accessing a page that’s been deleted, expired or not published yet, has proven to be a challenge and I can’t seem to be able to find an elegant solution around it.

For instance, we have a list of bloggers, that are stored in our faceted navigation with links to their pages, our system lists them and the links to their pages, should someone’s page be unpublished yet – we run into problems.

Another good sample of where this is needed is a list of pages (A multi-page property of sorts). There seems to be no implementation of a multi-page property in EpiServer and the only reasonable implementation that I’ve been able to find is available through EpiCode. The following is it deals with the pages going in or out of the system, which leads me to think that the only way of checking whether a page is available for me is to instantiate it with all the consequences of it:

// get the page with error handling for // access denied or deleted page try { PageData page = Global.EPDataFactory.GetPage(pageref); isExternalLink = (page.StaticLinkURL != multipageLinkItem.Url); if (page != null && isExternalLink == false) _selectedPages.Add(page); } catch (PageNotFoundException notFoundEx) { // We should not add the page if it // does not exist } catch (EPiServer.Core.AccessDeniedException accessDeniedEx) { // User is not allowed to see page, skip it } catch (Exception ex) { // The page could not be loaded, for some other // reason. System.Diagnostics.Debug.Write("Page could not be loaded: " + pageref.ToString(), "PropertyMultiPage"); }

What I do not like about this part (of an otherwise remarkable piece) of code is that exceptions are not supposed to be the driving force of the program flow. But in this case they seem to have been forced to do it. It’s like I had to open a file to check its size or whether it even exists.

I can see why the system will not let me visit the page if I’m not allowed to do it as a user, but the fact that the API frowns at me whenever I even try to instantiate it just to check its existence or my rights to it has proven to be quite problematic. After all a user is not supposed to see a login screen in the list of pages, but rather when he/she enters a page that he/she no no rights to. Better yet, give me a way to check whether I even can access it.

Is there one already? Has anyone heard? Did anyone see?

Common Language Runtime, now even more common

There seems to be a storm over at over at the Internet about Microsoft going Cross platform and “opening the common language runtime to a multitude of platforms”. What seems to be a false perception that even such respectable podcasts as Buzz Out Loud or even TWIT fail to realize and mislead people on is that this has NOTHING to do with portable .Net desktop applications. Someone even suggested at one of the BOL podcasts that it’s Microsoft’s attempt to put .Net Framework on Linux servers. (Huh? Beg your pardon?) You may have not noticed it but when they say about cross-platform capability of Silverlight it always says Windows, Mac.. and then on a single breath they start enumerating browsers. A casual listener just measures the quantity and the list has an impressive 5-6 bullets. heh… wait… erm… not really… it’s actually only 2 platforms. You cannot enumerate browsers as platforms! You share 99% implementation between them, the only cross-browser thing is the interaction between the plugin and the host browser!

As a .Net developer this pretty much makes me laugh through tears. What an excellent publicity stunt. First of all, Microsoft does not plan to release a Linux version. It’s cross-platformedness (is that a word?) refers strictly to the fact that there has been a runtime engine port made for a Mac. What was ported? The CLR and the DLR. Big deal! This has been there in a form of Mono years ago! And the CLR is available for Linux for a few years now. How is that suddenly an exciting thing?

If you’re interested in what exactly was done you may want to look at the interview with Scott Guthrie, GM of the Silverlight team. GAC was not ported. Wait! GAC WAS NOT PORTED?! Only a subset of classes that are needed inside a browser. Basic GC, no ASP.Net. It’s nothing like a cross platform version of a full .Net framework. this is also nothing really that new from the cross platform point of view – this kind of stunt was already pulled by Microsoft in form of the Compact version of the .Net framework. Something similar has already been done in terms of XNA – which is a form of .Net framework for XBox. In fact Silverlight is much more like XNA than it is like the full .Net framework.

Second of all the only open part is the DLR which is actually developed in an “embrace and devour” fashion. Ruby, Python and the likes developers – we love you! come join us under our Common Language runtime! Of course Microsoft will open it. Those developers are all about open and free this makes a lot of sense doing it this way. Give something, get a lot in return. Don’t get me wrong, I love the dynamic extensions, and I really like what’s being developed in the DLR, but make no mistake, the motives haven’t changed.

In the end CLR has been open for a long time and it’s not where the most exciting part of the development is. It’s the framework. I’ve not seen a word about Windows forms being ported. Or any of the ASP.Net namespaces on that mater. Heck even Mono has big ASP.Net 1.0 and chunks of 2.0. If you look at the image where do you see the CLR?

It’s the small middle circle inside the big one. That’s a far cry from releasing .Net as a standalone portable framework. And if you think about it, it does not make any sense from Microsoft’s point of view to go the whole way. What for? for it to make Windows expendable? Ridiculous!

My feeling is that (other than making another bucket of money), partially the motives behind is is to kick Adobe’s butt. If it did not occur to you yet, Microsoft and Adobe are full-out at war at this point. PDF is combated with XML Digital Paper, Flash has its Silverlight, Shockwave (& Macromedia Director) has Blend and XAML now and so on…

Adobe already takes shots back at Microsoft

While Adobe made PDF readily-available to other software companies such as Apple, Sun, Corel, and OpenOffice, when it came to deep-pocketed Microsoft, Adobe took a different stance, demanding that it remove the feature and charge a separate fee.

In response, Microsoft agreed to remove the feature, but refused to charge consumers separately for it. Adobe consequently sought negotiating leverage by threatening to sue before the EC, despite the fact that neither Adobe nor Microsoft are based in Europe, neither operates major production facilities there, and neither maintains primary business locations there.

Now that we have a few myths busted, I don’t want you to think that I am not excited by Silverlight.

The coolness ensues

It is still incredibly cool that they are doing it and not for the cross platform reasons (although it’s nice), and not for the multi browser compatibility (even nicer), but for it’s roots in the full framework. For us .Net developers it’s like one day we woke up and we knew how to write Action Script (Flash) applications. The Silverlight download is only 5 meg, so in the broadband world it’s going to be on almost every computer fairly quick. We will have a robust development environment for developing those applets in less than a year. It’s root in the .Net framework roots will make it talk seamlessly to our IIS embedded apps and services. You wanted web applications?

You asked for web applications? We will deliver…
… only they will be desktop applications running inside a browser we approve....
… and on any platform we choose for you…
the best of both worlds – no matter if you want to run them in Windows OR in Internet Explorer. :)

But honestly – should you use some artificial and clunky surrogates of instantly-responsive-interactivity in forms of AJAX when you can have the real thing running faster and delivering much wider functionality? Developing a browser apps was possible before, but .Net was never perceived as a platform specifically designed for that kind of activity, the quasi multi-platform/browser compatibility has a chance to change that perception. Perfect crime :)

I can’t wait for Silverlight to turn gold. It’s a brave new world.

Posted in .Net Framework, C#, Internet Information Services, Rants, Software Development, Web applications
1 Star2 Stars3 Stars4 Stars5 Stars (3 votes, average: 3.67 out of 5)
Loading...
| 18 Comments »

I’ve recently been asked by one of our new dotNet developers whether it’s possible to cast your regular everyday .Net 1.x System.Collections.ArrayList or the like onto a generic System.Collections.Generic.List<T> . I have to admit, I seem to suffer from some kind of Obsessive-Compulsive Disorder when it comes to programming problems. I mean my original reaction is usually “Of course not, what kind of frivolous idea is that?“, but then I cannot go about without solving the problem. So it was this time.

There seems to be no pre-coded framework solution for the problem, you should iterate over the items instead.  

At first I thought we could make use of the the framework supported conversion of  List<T> into List<Y>through a somewhat awkward method in the List<T>

public List<TOutput> ConvertAll<TOutput> (
	Converter<T,TOutput> converter
)

which requires you to implement a delegate that will do the conversion for every item. One problem with this solution though is that it does something exactly oposite to what we needed. It exports rather than importing and it does so between two generic types. However, there is an easy enough way to add this functionality in and you will only need to code it once and you will probably want to place it in some kind of static utility class.

 public static void copyToGenericList<T>(IEnumerable list, IList<T> genericList)
{
    foreach (object o in list)
    {
        genericList.Add((T) o);
    }
}

With the help of this method you will copy any enumerable type into whatever generic list you may find. Granted the method is not doing any type checking and will fail in case of any type mismatch it may encounter, but you would want to know about it nonetheles, wouldn’t you? Simple try/catch it around will do the trick, either that or a simple modification: 

public static void copyToGenericListChecked<T>
    (IEnumerable list, IList<T> genericList)
{
    foreach (object o in list)
    {
        if (o is T)
        {
            genericList.Add((T)o);
        }
    }
}

Which however will fail silently if the objects in the original list are of a wrong type, which in turn makes using it quite dangerous.

The copying now becomes quite effortless:

//Create a non generic list of type in with some data in it
IList list = new ArrayList();
list.Add(456);
list.Add(123);
 
//our generic target
IList<int> intList = new List<int>();
 
//and the copy routine - completely effortless
SomeUtilClass.copyToGenericList(list, intList);

But better yet, why not simply create the list of the type you expect and populate its contents in one go from the non-generic? Sure:

public static List<T> convertToGenericList<T>(IEnumerable list)
{
    List<T> result = new List<T>();
    foreach (object o in list)
    {
        result.Add((T) o);
    }
    return result;
}

And the sample usage becomes exactly what we hoped for:

//Create a non generic list of type in with some data in it
IList list = new ArrayList();
list.Add(456);
list.Add(123);
 
//and the one line conversion - nice!
IList<int> intList2 = SomeUtilClass.convertToGenericList<int>(list);

Generics rule!

Posted in .Net Framework, C#, Software Development
1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
Loading...
| 33 Comments »

To finalize my mini series on the object store I’d like to put a simple page comments library. The library takes care of everything that is required for you to post and retrieve a list of comments. It does not (so far) offer any moderation functionality or even facilitates any comments removal. It’s something that I will most probably be added in the process.

I am in the process of figuring out how I can contribute it through the Community EpiCode effort on CodeResort. As soon as I get some answers from Steve, I’ll get it uploaded there. In the mean time let me document how to start using it.

For the time being you can get the code here or the compiled library with the intellisense help form here.

Posting comments

the posting is somewhat manual in terms of not having a pre-made control for it. Which if you look at the code does not have much sense to have.

All you need to do is put two edit boxes on a page and a submit button, and then bind the action of the submit button to a code looking somewhat like:

protected void SubmitComment(object sender, System.EventArgs e)
{
    IPageComment newComment =
        PageCommentFactory.createInstance(Guid.Empty,
        string.Format("CommentForPage{0}", CurrentPage.PageLink.ID),
        CurrentPage.PageLink.ID,
        SubjectTextBox.Text, ContentTextBox.Text, DateTime.Now, false, true, false);
    newComment.Save();
}

I honestly don’t feel like making a custom control for creating those would be worthwhile since no one would end up using it anyway.

The listing of comments however…

The comments can be accessed in a number of ways.

Probably the easiest one would be by using the templated control I’ve written in the library, which is a simple descendant of the ASP.NET repeater. The page could would look something like:

...

<%@ Register TagPrefix="CognifideControls"
    Namespace="Cognifide.EPiServerControls.PageComments.Controls"
    Assembly="Cognifide.EPiServerControls.PageComments" %>

...


<CognifideControls:PageCommentsList ID="CommentControl" runat="server"
    PageLinkIdProperty="<%# CurrentPage.PageLink.ID %>">
    <ItemTemplate> 
        <b><%# CommentControl.CurrentComment.Title %></b> - 
        <%# CommentControl.CurrentComment.SubmitDate.ToString() %><br />
        <%# CommentControl.CurrentComment.Content%><br /><br />
    </ItemTemplate>
</CognifideControls:PageCommentsList>

I’ve chose this way since that’s pretty much the standard way of adding controls that are defined in Episerver and just generally ASP.Net.

But nothing stops you from accessing the comments directly,  and then filling in the data for the repeater yourself like:

<asp:Repeater ID="CurrentComments" runat="server" 
    OnItemDataBound="CurrentComments_ItemDataBound"> 
    <ItemTemplate>
        <b><asp:Label ID="CommentSubjectLabel" runat="server"></asp:Label></b><br />
        <asp:Label ID="CommentContentLabel" runat="server"></asp:Label><br /><br />
    </ItemTemplate>
</asp:Repeater>

And then in the code-behind

protected void Page_Load(object sender, EventArgs e)
{
    List<IPageComment> comments =
        PageCommentFactory.GetCommentsForPage(CurrentPage.PageLink.ID, 
        DateTime.MinValue, DateTime.MaxValue, true);
    CurrentComments.DataSource = comments;
    CurrentComments.DataBind();
}

protected void CurrentComments_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
    IPageComment comment = (e.Item.DataItem as IPageComment);
    Label commentSubjectLabel = (Label)e.Item.FindControl("CommentSubjectLabel");
    if (comment != null)
    {
        commentSubjectLabel.Text = comment.Title;
    }
 
    Label commentContentLabel = (Label)e.Item.FindControl("CommentContentLabel");
    if (comment != null)
    {
        commentContentLabel.Text = comment.Content;
    }
}

That’s pretty much what my implementation does anyway.

I hope to be able to put it up on CodeResort soon so that we can see what else could be done. Additionally my library allows for replacing the persistence provider, which we will probably have implemented using nHibernate to test its speed versus the ObjectStore. Should you be interested in providing some help with this, or adding come moderation code to the admin side of the site on top of the interface, it would definitely be greatly appreciated.

I have started implementing the Property based on the code so that it can be easily displayed on the editor’s page, but for now, I’ll have to delay it since we’ve got some other stuff to do related to the project I’m currently working on.

Posted in ASP.NET, C#, Downloadable, EPiServer, Software Development, Web applications
1 Star2 Stars3 Stars4 Stars5 Stars (3 votes, average: 3.67 out of 5)
Loading...
| 3 Comments »

EPiServer’s ObjectStore (Part 2)

I think we’re mostly finished investigating ObjectStore for now. In this article I’ll try to finish up on the apsects of using the Object store in a real life solution that is a basic Page comments. In my previous article concerning ObjectStore I have described a way of storing and retrieving an object from the Store, which is fine and dandy if we know exactly the object’s OD, for example if we reference it from a page. But what good is a store like that if we cannot search it for content? The problem we were trying to solve using ObjectStore was storing comments for ANY Episerver page without having to do anything to the page type. We might need that for the upcoming project so the discovery may prove useful since this is a really neat way of storing objects.

So the class that we are going to store needs to persists the following values:

namespace Cognifide.EPiServerTest.ObjectStore
{
    [Serializable, XmlInclude(typeof (PageCommentDO))]
    public class PageCommentDO : IItem, IPageComment
    {
        private object id;
        private string name;
        [Indexed(true)]
        private int pageId;
        private string title;
        private string content;
        [Indexed(true)]
        private DateTime submitDate;
        [Indexed(true)]
        private bool moderated;
        [Indexed(true)]
        private bool published = true;
        [Indexed(true)]
        private bool reported;
 
    }
}

 You might have noticed that contrary to what I did before this class has some of its fields tagged with [Indexed(true)] attribute. This is required by the store object if we ever want to query the store for the class using those fields as a filtering criteria. The page also implements the required IItem interface (although the properties required by the interface are not shown in the excerpt) as well as our internal IPageComment which is added there for the purpose of easy switching of implementations. We intend to implement it on both ObjectStore and using NHibernate to make sure we get the best performance possible out of our solution. This interface is to allow us to easily switch between the 2 implementations.

Back to the ObjectStore

 First we need to introduce a few new classes EPiServer uses for querying the Store. The namespace that scopes the querying consists of the following entities: 

namespace EPiServer.BaseLibrary.Search
{    
    public class BetweenExpression : IExpression;
    public class EqualExpression : IExpression;
    public sealed class Expression;
    public interface IExpression;
    public class Order;
    public class Query : IEnumerable;
}

The Query class is at the center of any search in the Store, let’s start with the samples right away. The saving is almost identical to the sample that I’ve shown in my previous article:

public void Save()
{
    ISession session = null;
    try
    {
        // check if there is a schema for the type, if not create it.
        if (Context.Repository.SchemaForType(GetType()) == null)
        {
            TypeSchemaBuilder.RegisterSchemaAndType("CognifidePageCommentsStorage", GetType());
        }
 
        // get a new session from the current context.
        session = Context.Repository.CreateSession();
 
        // wrap it in a transaction 
        session.BeginTransaction();
 
        // make sure the id has been defined.
        if (Id.Equals(Guid.Empty))
        {
            Id = Guid.NewGuid();
        }
 
        //persist
        session.Save(this);
        session.CommitTransaction();
    }
    catch (ElektroPostException exception)
    {
        // ... rollback the transaction and do some reporting
        if (session != null)
        {
            session.RollbackTransaction();
        }
        throw;
    }
    finally
    {
        if (session != null)
        {
            session.Close();
        }
    }
}

Really there is not much meat there. The interesting part comes in the object retrieval. Even though the Search namespace does not look like it offers much, you can still build a fairly fophisticated filter with it. following code retrieves comments for a specific PageLinkID. The comments can be filtered by date and since the call is used to retrieve the page for viewing, we probably only want the ones that are public (meaning they have not been removed by the comment moderator).

 

public static List<PageCommentDO> GetCommentsForPage(int pageId, 
    DateTime beforeDate, DateTime afterDate, bool includeOnlyPublished)
{
    ISession session = null;
 
    try
    {
        // make sure there is a schema to read from, otherwise there is no point
        if (Context.Repository.SchemaForType( typeof (PageCommentDO)) == null)
        {
            return null;
        }
 
        //acquire a session
        session = Context.Repository.CreateSession();
 
        //create the query 
        Query query = new Query(typeof (PageCommentDO));
 
        // we only want comments for a singler page 
        query.Add(Expression.Equal("pageId", pageId));
 
        // should we filter to only wshow comments from some set time period?
        // the following demonstrate how to request a value from within a range
        if ((beforeDate != DateTime.MinValue) ||
            ((afterDate != DateTime.MaxValue) && (afterDate != DateTime.MinValue)))
        {
            query.Add(
                Expression.Between("submitDate",
                                   (beforeDate != DateTime.MinValue) ? beforeDate : new DateTime(0x76c, 1, 1),
                                   ((afterDate != DateTime.MinValue) && (afterDate != DateTime.MaxValue))
                                       ? afterDate
                                       : new DateTime(0x834, 1, 1)));
        }
 
        // probably for a displayed page we will only want to show published comments
        if (includeOnlyPublished)
        {
            query.Add(Expression.Equal("published", true));
        }
 
        // order the comments by date/time so that we get the most recent first
        query.AddOrder(new Order("submitDate", true));
 
        IList list = session.ExecuteQueryObject(query);
        List<PageCommentDO> result = new List<PageCommentDO>(list.Count);
        foreach (object item in list)
        {
            result.Add((PageCommentDO) item);
        }
        return result;
    }
    catch (ElektroPostException ex)
    {
        // ... do some reporting
        throw;
    }
    finally
    {
        if (session != null)
        {
            session.Close();
        }
    }
}

You nahe to agree, this really IS neat. All you need to do now to place the comments on a page is a few lines of code to bind the data from the Store with a repeated of a kind, and a set of edit boxes with a submit button :)

This is basically how much code the submission and retrieval the comments comment take:

// Load the comments to a repeater
protected void Page_Load(object sender, EventArgs e)
{
        List<PageCommentDO> comments = 
            PageCommentDO.GetCommentsForPage(CurrentPage.PageLink.ID, DateTime.MinValue, DateTime.MaxValue);
        CurrentComments.DataSource = comments;
        CurrentComments.DataBind();
}
 
// Comment submission
protected void SubmitComment(object sender, System.EventArgs e)
{
    PageCommentDO newComment = new
        PageCommentDO(Guid.Empty,
        string.Format("CommentForPage{0}", CurrentPage.PageLink.ID),
        CurrentPage.PageLink.ID,
        SubjectTextBox.Text, ContentTextBox.Text, DateTime.Now, false, true, false);
    newComment.Save();
}
 
// the data binding for the labels
protected void CurrentComments_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
    PageCommentDO comment = (e.Item.DataItem as PageCommentDO);
 
    Label commentSubjectLabel = (Label)e.Item.FindControl("CommentSubjectLabel");
    if (comment != null)
    {
        commentSubjectLabel.Text = comment.Title;
    }
 
    Label commentContentLabel = (Label)e.Item.FindControl("CommentContentLabel");
    if (comment != null)
    {
        commentContentLabel.Text = comment.Content;
    }
}

Short of having a control pre=built for you I cannot think of it being able to make it easier.

I will look into packing it into a nice control that we would be looking into contributing some time in the future and of course if there is actually a demand for it.

EPiServer?s ObjectStore (Part 2)

I think we’re mostly finished investigating ObjectStore for now. In this article I’ll try to finish up on the apsects of using the Object store in a real life solution that is a basic Page comments. In my previous article concerning ObjectStore I have described a way of storing and retrieving an object from the Store, which is fine and dandy if we know exactly the object’s OD, for example if we reference it from a page. But what good is a store like that if we cannot search it for content? The problem we were trying to solve using ObjectStore was storing comments for ANY Episerver page without having to do anything to the page type. We might need that for the upcoming project so the discovery may prove useful since this is a really neat way of storing objects.

So the class that we are going to store needs to persists the following values:

namespace Cognifide.EPiServerTest.ObjectStore
{
    [Serializable, XmlInclude(typeof (PageCommentDO))]
    public class PageCommentDO : IItem, IPageComment
    {
        private object id;
        private string name;
        [Indexed(true)]
        private int pageId;
        private string title;
        private string content;
        [Indexed(true)]
        private DateTime submitDate;
        [Indexed(true)]
        private bool moderated;
        [Indexed(true)]
        private bool published = true;
        [Indexed(true)]
        private bool reported;
 
    }
}

 You might have noticed that contrary to what I did before this class has some of its fields tagged with [Indexed(true)] attribute. This is required by the store object if we ever want to query the store for the class using those fields as a filtering criteria. The page also implements the required IItem interface (although the properties required by the interface are not shown in the excerpt) as well as our internal IPageComment which is added there for the purpose of easy switching of implementations. We intend to implement it on both ObjectStore and using NHibernate to make sure we get the best performance possible out of our solution. This interface is to allow us to easily switch between the 2 implementations.

Back to the ObjectStore

 First we need to introduce a few new classes EPiServer uses for querying the Store. The namespace that scopes the querying consists of the following entities: 

namespace EPiServer.BaseLibrary.Search
{    
    public class BetweenExpression : IExpression;
    public class EqualExpression : IExpression;
    public sealed class Expression;
    public interface IExpression;
    public class Order;
    public class Query : IEnumerable;
}

The Query class is at the center of any search in the Store, let’s start with the samples right away. The saving is almost identical to the sample that I’ve shown in my previous article:

public void Save()
{
    ISession session = null;
    try
    {
        // check if there is a schema for the type, if not create it.
        if (Context.Repository.SchemaForType(GetType()) == null)
        {
            TypeSchemaBuilder.RegisterSchemaAndType("CognifidePageCommentsStorage", GetType());
        }
 
        // get a new session from the current context.
        session = Context.Repository.CreateSession();
 
        // wrap it in a transaction 
        session.BeginTransaction();
 
        // make sure the id has been defined.
        if (Id.Equals(Guid.Empty))
        {
            Id = Guid.NewGuid();
        }
 
        //persist
        session.Save(this);
        session.CommitTransaction();
    }
    catch (ElektroPostException exception)
    {
        // ... rollback the transaction and do some reporting
        if (session != null)
        {
            session.RollbackTransaction();
        }
        throw;
    }
    finally
    {
        if (session != null)
        {
            session.Close();
        }
    }
}

Really there is not much meat there. The interesting part comes in the object retrieval. Even though the Search namespace does not look like it offers much, you can still build a fairly fophisticated filter with it. following code retrieves comments for a specific PageLinkID. The comments can be filtered by date and since the call is used to retrieve the page for viewing, we probably only want the ones that are public (meaning they have not been removed by the comment moderator).

 

public static List<PageCommentDO> GetCommentsForPage(int pageId, 
    DateTime beforeDate, DateTime afterDate, bool includeOnlyPublished)
{
    ISession session = null;
 
    try
    {
        // make sure there is a schema to read from, otherwise there is no point
        if (Context.Repository.SchemaForType( typeof (PageCommentDO)) == null)
        {
            return null;
        }
 
        //acquire a session
        session = Context.Repository.CreateSession();
 
        //create the query 
        Query query = new Query(typeof (PageCommentDO));
 
        // we only want comments for a singler page 
        query.Add(Expression.Equal("pageId", pageId));
 
        // should we filter to only wshow comments from some set time period?
        // the following demonstrate how to request a value from within a range
        if ((beforeDate != DateTime.MinValue) ||
            ((afterDate != DateTime.MaxValue) && (afterDate != DateTime.MinValue)))
        {
            query.Add(
                Expression.Between("submitDate",
                                   (beforeDate != DateTime.MinValue) ? beforeDate : new DateTime(0x76c, 1, 1),
                                   ((afterDate != DateTime.MinValue) && (afterDate != DateTime.MaxValue))
                                       ? afterDate
                                       : new DateTime(0x834, 1, 1)));
        }
 
        // probably for a displayed page we will only want to show published comments
        if (includeOnlyPublished)
        {
            query.Add(Expression.Equal("published", true));
        }
 
        // order the comments by date/time so that we get the most recent first
        query.AddOrder(new Order("submitDate", true));
 
        IList list = session.ExecuteQueryObject(query);
        List<PageCommentDO> result = new List<PageCommentDO>(list.Count);
        foreach (object item in list)
        {
            result.Add((PageCommentDO) item);
        }
        return result;
    }
    catch (ElektroPostException ex)
    {
        // ... do some reporting
        throw;
    }
    finally
    {
        if (session != null)
        {
            session.Close();
        }
    }
}

You nahe to agree, this really IS neat. All you need to do now to place the comments on a page is a few lines of code to bind the data from the Store with a repeated of a kind, and a set of edit boxes with a submit button :)

This is basically how much code the submission and retrieval the comments comment take:

// Load the comments to a repeater
protected void Page_Load(object sender, EventArgs e)
{
        List<PageCommentDO> comments = 
            PageCommentDO.GetCommentsForPage(CurrentPage.PageLink.ID, DateTime.MinValue, DateTime.MaxValue);
        CurrentComments.DataSource = comments;
        CurrentComments.DataBind();
}
 
// Comment submission
protected void SubmitComment(object sender, System.EventArgs e)
{
    PageCommentDO newComment = new
        PageCommentDO(Guid.Empty,
        string.Format("CommentForPage{0}", CurrentPage.PageLink.ID),
        CurrentPage.PageLink.ID,
        SubjectTextBox.Text, ContentTextBox.Text, DateTime.Now, false, true, false);
    newComment.Save();
}
 
// the data binding for the labels
protected void CurrentComments_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
    PageCommentDO comment = (e.Item.DataItem as PageCommentDO);
 
    Label commentSubjectLabel = (Label)e.Item.FindControl("CommentSubjectLabel");
    if (comment != null)
    {
        commentSubjectLabel.Text = comment.Title;
    }
 
    Label commentContentLabel = (Label)e.Item.FindControl("CommentContentLabel");
    if (comment != null)
    {
        commentContentLabel.Text = comment.Content;
    }
}

Short of having a control pre=built for you I cannot think of it being able to make it easier.

I will look into packing it into a nice control that we would be looking into contributing some time in the future and of course if there is actually a demand for it.

EPiServer?s ObjectStore (Part 2)

I think we’re mostly finished investigating ObjectStore for now. In this article I’ll try to finish up on the apsects of using the Object store in a real life solution that is a basic Page comments. In my previous article concerning ObjectStore I have described a way of storing and retrieving an object from the Store, which is fine and dandy if we know exactly the object’s OD, for example if we reference it from a page. But what good is a store like that if we cannot search it for content? The problem we were trying to solve using ObjectStore was storing comments for ANY Episerver page without having to do anything to the page type. We might need that for the upcoming project so the discovery may prove useful since this is a really neat way of storing objects.

So the class that we are going to store needs to persists the following values:

namespace Cognifide.EPiServerTest.ObjectStore
{
    [Serializable, XmlInclude(typeof (PageCommentDO))]
    public class PageCommentDO : IItem, IPageComment
    {
        private object id;
        private string name;
        [Indexed(true)]
        private int pageId;
        private string title;
        private string content;
        [Indexed(true)]
        private DateTime submitDate;
        [Indexed(true)]
        private bool moderated;
        [Indexed(true)]
        private bool published = true;
        [Indexed(true)]
        private bool reported;
 
    }
}

 You might have noticed that contrary to what I did before this class has some of its fields tagged with [Indexed(true)] attribute. This is required by the store object if we ever want to query the store for the class using those fields as a filtering criteria. The page also implements the required IItem interface (although the properties required by the interface are not shown in the excerpt) as well as our internal IPageComment which is added there for the purpose of easy switching of implementations. We intend to implement it on both ObjectStore and using NHibernate to make sure we get the best performance possible out of our solution. This interface is to allow us to easily switch between the 2 implementations.

Back to the ObjectStore

 First we need to introduce a few new classes EPiServer uses for querying the Store. The namespace that scopes the querying consists of the following entities: 

namespace EPiServer.BaseLibrary.Search
{    
    public class BetweenExpression : IExpression;
    public class EqualExpression : IExpression;
    public sealed class Expression;
    public interface IExpression;
    public class Order;
    public class Query : IEnumerable;
}

The Query class is at the center of any search in the Store, let’s start with the samples right away. The saving is almost identical to the sample that I’ve shown in my previous article:

public void Save()
{
    ISession session = null;
    try
    {
        // check if there is a schema for the type, if not create it.
        if (Context.Repository.SchemaForType(GetType()) == null)
        {
            TypeSchemaBuilder.RegisterSchemaAndType("CognifidePageCommentsStorage", GetType());
        }
 
        // get a new session from the current context.
        session = Context.Repository.CreateSession();
 
        // wrap it in a transaction 
        session.BeginTransaction();
 
        // make sure the id has been defined.
        if (Id.Equals(Guid.Empty))
        {
            Id = Guid.NewGuid();
        }
 
        //persist
        session.Save(this);
        session.CommitTransaction();
    }
    catch (ElektroPostException exception)
    {
        // ... rollback the transaction and do some reporting
        if (session != null)
        {
            session.RollbackTransaction();
        }
        throw;
    }
    finally
    {
        if (session != null)
        {
            session.Close();
        }
    }
}

Really there is not much meat there. The interesting part comes in the object retrieval. Even though the Search namespace does not look like it offers much, you can still build a fairly fophisticated filter with it. following code retrieves comments for a specific PageLinkID. The comments can be filtered by date and since the call is used to retrieve the page for viewing, we probably only want the ones that are public (meaning they have not been removed by the comment moderator).

 

public static List<PageCommentDO> GetCommentsForPage(int pageId, 
    DateTime beforeDate, DateTime afterDate, bool includeOnlyPublished)
{
    ISession session = null;
 
    try
    {
        // make sure there is a schema to read from, otherwise there is no point
        if (Context.Repository.SchemaForType( typeof (PageCommentDO)) == null)
        {
            return null;
        }
 
        //acquire a session
        session = Context.Repository.CreateSession();
 
        //create the query 
        Query query = new Query(typeof (PageCommentDO));
 
        // we only want comments for a singler page 
        query.Add(Expression.Equal("pageId", pageId));
 
        // should we filter to only wshow comments from some set time period?
        // the following demonstrate how to request a value from within a range
        if ((beforeDate != DateTime.MinValue) ||
            ((afterDate != DateTime.MaxValue) && (afterDate != DateTime.MinValue)))
        {
            query.Add(
                Expression.Between("submitDate",
                                   (beforeDate != DateTime.MinValue) ? beforeDate : new DateTime(0x76c, 1, 1),
                                   ((afterDate != DateTime.MinValue) && (afterDate != DateTime.MaxValue))
                                       ? afterDate
                                       : new DateTime(0x834, 1, 1)));
        }
 
        // probably for a displayed page we will only want to show published comments
        if (includeOnlyPublished)
        {
            query.Add(Expression.Equal("published", true));
        }
 
        // order the comments by date/time so that we get the most recent first
        query.AddOrder(new Order("submitDate", true));
 
        IList list = session.ExecuteQueryObject(query);
        List<PageCommentDO> result = new List<PageCommentDO>(list.Count);
        foreach (object item in list)
        {
            result.Add((PageCommentDO) item);
        }
        return result;
    }
    catch (ElektroPostException ex)
    {
        // ... do some reporting
        throw;
    }
    finally
    {
        if (session != null)
        {
            session.Close();
        }
    }
}

You nahe to agree, this really IS neat. All you need to do now to place the comments on a page is a few lines of code to bind the data from the Store with a repeated of a kind, and a set of edit boxes with a submit button :)

This is basically how much code the submission and retrieval the comments comment take:

// Load the comments to a repeater
protected void Page_Load(object sender, EventArgs e)
{
        List<PageCommentDO> comments = 
            PageCommentDO.GetCommentsForPage(CurrentPage.PageLink.ID, DateTime.MinValue, DateTime.MaxValue);
        CurrentComments.DataSource = comments;
        CurrentComments.DataBind();
}
 
// Comment submission
protected void SubmitComment(object sender, System.EventArgs e)
{
    PageCommentDO newComment = new
        PageCommentDO(Guid.Empty,
        string.Format("CommentForPage{0}", CurrentPage.PageLink.ID),
        CurrentPage.PageLink.ID,
        SubjectTextBox.Text, ContentTextBox.Text, DateTime.Now, false, true, false);
    newComment.Save();
}
 
// the data binding for the labels
protected void CurrentComments_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
    PageCommentDO comment = (e.Item.DataItem as PageCommentDO);
 
    Label commentSubjectLabel = (Label)e.Item.FindControl("CommentSubjectLabel");
    if (comment != null)
    {
        commentSubjectLabel.Text = comment.Title;
    }
 
    Label commentContentLabel = (Label)e.Item.FindControl("CommentContentLabel");
    if (comment != null)
    {
        commentContentLabel.Text = comment.Content;
    }
}

Short of having a control pre=built for you I cannot think of it being able to make it easier.

I will look into packing it into a nice control that we would be looking into contributing some time in the future and of course if there is actually a demand for it.

EPiServer’s ObjectStore

We’ve been looking at the way to efficiently store a lis of quotes some time ago. And Steve suggested that if we’re to store a gian number of quotes, we may look into some misterious being called ObjectStore…

If there will be many qoutes, e.g. you buy a “100.000 quotes of the day” database, you might want to put them into a separate table in the database. Seasoned EPiServer developers tend to think that everything can be stored as pages in EPiServer, which is kind of true, but not necessarily wise.

Another option could be to store the quotes in the ObjectStore, the general EPiServer storage feature, which can hold about anything you’d like, efficiently, quickly restorable, searchable, indexable, highly available and environmentally safe. It might even solve the global warming problem while we’re at it. Ok, maybe not. Truthfully, only a few developers outside of EPiServer know how to use it, and quite alot of us inside have no clue whatsoever. But, the tales I’ve heard about it would nominate it as a prime candidate for a quote system like this. Right now, it is storing things like XForms definitions and data, WSRP stuff, Content Mirroring data and lots more. It’s been around since 4.50 (I think) and is said to be documented – eventually, until then I guess we’ll have to resort to other ways of doing things.

MmmMmm… tasty!

Research mode “ON”.

There is no documentation, allright, but there is Reflector. Between the reflector and the sample server, there can be no secrets that can hide form us :)

Basically the ObjectStore is a way of persisting any serializable object (with some minor adjustments).

  • Object store is divided into Schemas.
  • A schema seems to be way of grouping types of objects into easily manageabble pools
  • A class type has to be contained within a schema to be persisted.
  • A type needs to be registered within a schema before you can write to it.
  • A type needs to implement EPiServer.BaseLibrary.IItem interface and be marked as Serializable for the ObjectStore to be able to serve it.

The Research Field

The namespaces of your interest is EPiServer.BaseLibrary, namely the following parts of it:

namespace EPiServer.BaseLibrary
{
public sealed class Context
public interface IContext
public interface IItem
public interface IItemList : IEnumerable
public interface IObjectStore
public interface IRepository
public interface ISession
}

Then you may want to open EPiServer.XForms.XFormData and EPiServer.XForms.XForm especially the Save() method of the latter is very educating.

The code for the ObjectStore implementation lives in the EPiServer.Implementation namespace but having all the interfaces exposed its implementation details should not be of our concern. Suffice to say itseems to be flexible ane extensible. You can have it sitting on an Oracle & Microsoft SQL Server databases as well as having it stored In an XML file. I assume there would not be much trouble replacing the object store looking as the Logging service has it setup dynamically through settings in its config file.

The results

  • The class you want to persist should inherit IItem and be serializable.
  • The default repository is stored in the Context and is capable of creating more sessions for us
  • Bah, enough of the bulleted lists… let the code speak for itself…

using System;

using System.Collections.Generic;

using System.Text;

using EPiServer.Implementation;

using EPiServer.Implementation.Serialization;

using EPiServer.BaseLibrary;

using System.Xml.Serialization;

 

namespace Cognifide.EPiServerTest.ObjectStore

{

    [Serializable, XmlInclude(typeof(CogObjectStoreItem))]

    public class CogObjectStoreItem : IItem

    {

 

        string data;

        private object id;

        private string name;

 

        public object Id

        {

            get { return this.id; }

            set { this.id = value; }

        }

        public string Name

        {

            get { return this.name; }

            set { this.name = value; }

        }

 

        public CogObjectStoreItem(object id, string name, string data)

        {

            this.data = data;

            this.id = id;

            this.name = name;

        }

 

        public void Save()

        {

            ISession session = null;

            try

            {

                // check if there is a schema for the type, if not create it.

                if (Context.Repository.SchemaForType(this.GetType()) == null)

                {

                    TypeSchemaBuilder.RegisterSchemaAndType

                        (“CogObjectStoreItem”, this.GetType());

                }

 

                // get a new session from the current context.

                session = Context.Repository.CreateSession();

 

                // wrap it in a transaction

                session.BeginTransaction();

 

                // make sure the id has been defined.

                if (this.Id.Equals(Guid.Empty))

                {

                    this.Id = Guid.NewGuid();

                }

 

                //persist

                session.Save(this);

                session.CommitTransaction();

            }

            catch (ElektroPostException exception)

            {

                // … rollback the transaction and do some reporting

                session.RollbackTransaction();

                throw exception;

            }

            finally

            {

                if (session != null)

                {

                    session.Close();

                }

            }

        }

 

        public static CogObjectStoreItem Load(object Id)

        {

            ISession session = null;

            try

            {

                // make sure there is a schema to read from.

                if (Context.Repository.SchemaForType(typeof(CogObjectStoreItem))==null)

                {

                    return null;

                }

 

                //acquire a session

                session = Context.Repository.CreateSession();

 

                // … and load the object

                return (CogObjectStoreItem)session.Load(Id);

            }

            catch (ElektroPostException exception)

            {

                // … do some reporting

                throw exception;

            }

            finally

            {

                if (session != null)

                {

                    session.Close();

                }

            }

        }

        public override string ToString()

        {

            return this.id.ToString();

        }

 

    }

}

The Conclusion

ObjectStore seems like a kind of Hibernate surrogate, the cool thing is that you can create the stored data without modifying the database schema, the not so cool is that there may be a serious penalty for bigger structures and I suspect there might be issues with speed for large data sets since the searching technique implemented seems to require that the objects be de-serialized to be checked if the match the filter.

Mateusz is going to test its performance over the following days to decide if it’s going to perform with what we need it for and I’ll try to see if I can get some hints for solving our problem on the EPiServer developer forums. But then again, even if it’s nto going to be our solution it may still prove useful for other issues.

Posted in C#, EPiServer
1 Star2 Stars3 Stars4 Stars5 Stars (4 votes, average: 4.50 out of 5)
Loading...
| 27 Comments »

Google Map Control and why EPiServer is so cool!

I’ve recently had a chance to write a Google Maps control for EPiServer, it’s still somewhat buggy and I’m still considering how to release it since it still contains some java script that is potentially GPL infected and I would not like to contaminate someone’s code with it. I may end up rewriting it to some extent or make it more server side so that it’s completely ASP based.

Anyway…

We’ve started working on the rewrite of our site internally in a few CMS’es basically creating an internal competition on which of the engines/teams can do the best the easiest and the fastest site. I can say honestly, EPiServer has been a blast! Virtually any control we’ve decided to place there was almost completely effortless. The controls that are delivered (with sample usage on the demo site) just seem to cover everything. Well, almost everything. There is no map creation component as far as I can tell.

I’ve been wanting to write this control for quite a while and since I deployed a wiki for my family and started filling it in. I had a really nice experience with this Google Map extension to the MediaWiki. I wanted us to have the same on our site. And in the mean time we’ve started running into some limitations that required us to write some plugins for the editor’s site of the CMS. Striking two birds with one stone, here comes the Google Maps for EpiServer.

Anyone familiar with EpiServer knows that the CMS allows you to define the content on any given page through a set of properties defined for its page type. There is a handful of those, and each of them comes with a specific editor. Some of them even come with so called DOPE (Dynamic-on-page-editing). This feature is really so cool that by itself it’s probably one of the driving selling factor. I wanted it all!

To deliver it you need to inherit a property, (in my case I decided to go with a LongString as I can easily go over the 255 char limit if the user woudl decide to have more than a couple of flagpoints on his/her map) and define its editors.

I’ve found out that the property can be easily integrated with the CMS (virtually without any user intervention) by means of attributes/reflection. So here we go:

namespace Cognifide.EPiServerControls.GoogleMaps

{

  [global::EPiServer.PlugIn.PageDefinitionTypePlugIn(

    DisplayName = “GoogleMapData”, Description = “Google Map”)]

  public class GoogleMapProperty : EPiServer.Core.PropertyLongString

  {

   …

  }

}

Yay! one line of code and my class is a property and will show up in the system as one of the available data formats. Now how cool is that!?

The cool part of it is that now as it’s a property, it’s even easier to integrate it with the page.

<%@ Page language=”c#” Codebehind=”GoogleMapsPage.aspx.cs”

    AutoEventWireup=”True”

    Inherits=”development.Templates.GoogleMapsPage”

    MasterPageFile=”~/templates/MasterPages/MasterPage.master” %>

 

<%@ Register TagPrefix=”EPiServer”

    Namespace=”EPiServer.WebControls” Assembly=”EPiServer” %>

 

<asp:ContentContentPlaceHolderID=”MainRegion” runat=”server”>

  <EPiServer:Property ID=”MyGoogleMap” runat=”server”

  PropertyName=”GoogleMapData” />

</asp:Content>

That’s it! The CodeFile is practically empty, except for the autogenerated part. The property instance knows by itself that is should pull the data from the page property defined in “PropertyName”!

The control supports all 3 modes:

View Mode – obviously:

Google Map View

Edit mode – can’t do without it:

Google Map View

I initially planned to put the dope-like editing there but for some reason EPiServer scripts kept interfering with the JavaScript defined for the control. Didn’t give it too much thought though what I really wanted to work good is…

DOPE mode – this is probably the coolest thing in the whole deal:

Google Map View

The only problem I still have with the last mode is that most of the code for the DOPE mode I have is a modified version of what comes originally from the MediaWiki Google Map extension. Since JavaScript is not my core competence, I’ve only modified it to the extent that was needed for the code to work and therefore before save, you need to copy the dynamically generated code that’s just below the editing controls and into the edit box. Lame, I know. But I don’t really fancy learning Java Script further right now and it was not the point of this exercise. Perhaps if the control is released someone will be kind enough to fix and extend it so that it’s more streamlined.

Cognifide
The article is based on the knowledge I’ve gathered and work I’ve performed for Cognifide. Cognifide is an official partner EPiServer and the real contributor of the the control.

Posted in .Net Framework, ASP.NET, C#, EPiServer, Web applications
1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
Loading...
| 24 Comments »