Don't miss out Virtual Happy Hour this Friday (April 26).

Try our conversational search powered by Generative AI!

enba
Feb 15, 2010
  5789
(3 votes)

Virtual Roles and Visitor Segmentation the diferent implementation

This post is inspired by very interesting series of posts by Allan Thraen about how to use virtual roles for personalization and segmentation.For reference you can (and should) read here and here. Also, recently I’ve got hooked on .NET Reactive Extensions so I decided to see if I can implement Alans ScoreRole and PageViewRole to “observe” pages they are interested into and do their work.

Code for the VirtualRolesSample pack is available on codeplex.

Virtual roles use provider model and have no knowledge of httpcontext so to be able to associate page visit to a certain role Alan implemented a plugin in a VirtualRolesInit class that basically subscribes on page.Load event and hadles all the logic for all profiles there. I had a goal to separate this event handling into single role provider so that each role provider would be responsible of it’s own event.

I have done a couple of changes to the VirtualRolesInit  class to be able to acomplish this.

First, I removed the PlugInAttribute interface and implemented instead the new IInitializableModule which is the new initialization API used in EPiServer CMS 6(Magnus Stråle has several posts about it). This means that my changes will not work with previous versinos of EPiServer but it is quite easy to port this code back to the plugin mechanism.

Second, I implemented IObservable<PageBase> on VirtualRolesInit so that role classes can observe on it. This allowes me to remove all the logic form the initialization and page.Load event handler.

using System;
using System.Collections.Generic;
using EPiServer.Framework;
using EPiServer.Framework.Initialization;


namespace EPiServer.Research.VirtualRoles
{
    [ModuleDependency(typeof(EPiServer.Web.InitializationModule))]
    public class VirtualRolesInit : IInitializableModule, IObservable<PageBase>
    {
        private bool _initialized;
        private static List<IObserver<PageBase>> _observers = new List<IObserver<PageBase>>();
        private static readonly object _syncRoot = new object();
       
        #region IInitializableModule Members

        public void Initialize(InitializationEngine context)
        {
            if (!_initialized)
            {
                PageBase.PageSetup += new PageSetupEventHandler(PageBase_PageSetup);
                _initialized = true;
            }
        }

        public void Uninitialize(InitializationEngine context){}

        public void Preload(string[] parameters){}

        #endregion

        #region IObservable<PageBase> Members

        public IDisposable Subscribe(IObserver<PageBase> observer)
        {
            lock (_syncRoot)
            {
                _observers.Add(observer);
            }
            return new ObserverReference(observer);
        }

        #endregion

        void PageBase_PageSetup(PageBase sender, PageSetupEventArgs e)
        {
            sender.Load += new EventHandler(Page_Load);
        }

        void Page_Load(object sender, EventArgs e)
        {
            if (sender is PageBase)
            {
                lock (_syncRoot)
                {
                    foreach (var observer in _observers)
                    {
                        observer.OnNext(sender as PageBase);
                    }
                }
            }
        }
    }
}
So now we are able to observe this class from a single role. In the initialize method of the ScoreRole I subscribe to the VirtualRolesInit and handle all the pages that it sends to me.
public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config)
{
      //.... initialization code for Score role here
       VirtualRolesInit vri = new VirtualRolesInit();
       vri.Subscribe((page) => HandleScore(page));            
 }

 private void HandleScore(PageBase page)
 { 
      if (page != null && page.CurrentPage != null && page.CurrentPage[ScoreName] != null && page.CurrentPage[ScoreName] is int)
      {
           int score = (int)page.CurrentPage[ScoreName];
            EPiServerProfile.Current.SetPropertyValue(ScoreName, ((int)EPiServerProfile.Current[ScoreName]) + score);
            EPiServerProfile.Current.Save();
       }
 }

The same happeds for PageViewRole:

public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config)
{
    // ...... initialization code for pageViewRole here
    VirtualRolesInit vri = new VirtualRolesInit();
    vri.Subscribe(page => HandlePage(page));
}

private void HandlePage(PageBase page)
{
    if (page.Session["PageViews"] == null)
    {
        page.Session.Add("PageViews", new List<string>());
    }

    var lst = page.Session["PageViews"] as List<string>;

    if (!lst.Contains(page.CurrentPageLink.ToString()))
    {
        lst.Add(page.CurrentPageLink.ToString());
    }
}
Thats it. The only clarification I still own to the careful readers of this post is that ObserverReference that subscribe method returns.

Since the IObservable<T> Subscribe method returns an IDisposable object I implemented a type that has one property of name observer and that will give you a referfence of your Observer object.You can of course create your own object as needed.  Here is the code:

class ObserverReference : IDisposable
{
    private IObserver<PageBase> _observer;
    public ObserverReference(IObserver<PageBase> observer)
    {
        _observer = observer;
    }

    public IObserver<PageBase> Observer
    {
        get
        {
            return _observer;
        }
    }
    #region IDisposable Members

    public void Dispose()
    {
        _observer = null;
    }
    #endregion
}

Try it. It does work on my machine. ;)

Feb 15, 2010

Comments

Sep 21, 2010 10:33 AM

Looking really good, Enes. Could you perhaps put your changes in a new branch on the codeplex project for virtual roles? That would be awesome!

Sep 21, 2010 10:33 AM

Thanks Allan I'll do it gladly, I guess I need some kind of invite to the project ... or do I?

Please login to comment.
Latest blogs
Optimizely Unit Testing Using CmsContentScaffolding Package

Introduction Unit tests shouldn't be created just for business logic, but also for the content and rules defined for content creation (available...

MilosR | Apr 26, 2024

Solving the mystery of high memory usage

Sometimes, my work is easy, the problem could be resolved with one look (when I’m lucky enough to look at where it needs to be looked, just like th...

Quan Mai | Apr 22, 2024 | Syndicated blog

Search & Navigation reporting improvements

From version 16.1.0 there are some updates on the statistics pages: Add pagination to search phrase list Allows choosing a custom date range to get...

Phong | Apr 22, 2024

Optimizely and the never-ending story of the missing globe!

I've worked with Optimizely CMS for 14 years, and there are two things I'm obsessed with: Link validation and the globe that keeps disappearing on...

Tomas Hensrud Gulla | Apr 18, 2024 | Syndicated blog

Visitor Groups Usage Report For Optimizely CMS 12

This add-on offers detailed information on how visitor groups are used and how effective they are within Optimizely CMS. Editors can monitor and...

Adnan Zameer | Apr 18, 2024 | Syndicated blog

Azure AI Language – Abstractive Summarisation in Optimizely CMS

In this article, I show how the abstraction summarisation feature provided by the Azure AI Language platform, can be used within Optimizely CMS to...

Anil Patel | Apr 18, 2024 | Syndicated blog