Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Cross session personalisation with EPiServer
public abstract class BaseRepository : IDisposable
{
protected IDbConnection Connection { get; set; }
private bool _disposed;
protected ILog Log
{
get { return LogManager.GetLogger(GetType()); }
}
protected BaseRepository()
{
Initialize();
}
private void Initialize()
{
var episerver = ConfigurationManager.ConnectionStrings["EPiServerDB"];
if (!episerver.ConnectionString.Contains("AsynchronousProcessing"))
{
Connection = new SqlConnection(string.Format("{0};Asynchronous Processing=true;", episerver.ConnectionString));
}
else
{
Connection = new SqlConnection(episerver.ConnectionString);
}
SqlInsightDbProvider.RegisterProvider();
EnsureDatabase();
}
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
ReleaseMangedResources();
}
ReleaseUnmangedResources();
_disposed = true;
}
}
public virtual void ReleaseMangedResources()
{
}
public virtual void ReleaseUnmangedResources()
{
}
public void Dispose() // Implement IDisposable
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Ensure that the database has all objects available to use the repository.
/// </summary>
public abstract void EnsureDatabase();
}
public class XCriterionModel
{
public Guid AnonymousUserId { get; set; }
public string SessionKey { get; set; }
public string Data { get; set; }
public DateTime Modified { get; set; }
}
public interface IXCriterionRepository
{
/// <summary>
/// Get an exsiting or new Xcriterion record
/// </summary>
/// <param name="userId"></param>
/// <param name="key"></param>
/// <returns></returns>
XCriterionModel GetXCriterion(Guid userId, string key);
/// <summary>
/// async update an existing Xcriterion
/// </summary>
/// <param name="criterion"></param>
void UpdateXCriterion(XCriterionModel criterion);
/// <summary>
/// Delete all records which are not modified since x days
/// </summary>
/// <param name="expirationDays"></param>
void Clean(int expirationDays);
}
public class XCriterionRepository : BaseRepository, IXCriterionRepository
{
public XCriterionModel GetXCriterion(Guid userId, string key)
{
XCriterionModel result = null;
var results = Connection.QuerySql<XCriterionModel>(
"SELECT * FROM XCriterionModel WHERE AnonymousUserId = @AnonymousUserId AND SessionKey = @SessionKey",
new { AnonymousUserId = userId, SessionKey = key });
if (!results.Any())
{
var newObj = new XCriterionModel()
{
AnonymousUserId = userId,
SessionKey = key,
Data = string.Empty,
Modified = DateTime.Now
};
try
{
Connection.ExecuteSql(
"INSERT INTO XCriterionModel SELECT AnonymousUserId, SessionKey, Data, Modified FROM @Criterion",
new {Criterion = new List<XCriterionModel> {newObj}});
result = newObj;
}
catch (Exception ex)
{
Log.Error(ex);
throw ex;
}
}
else
{
result = results.FirstOrDefault();
}
return result;
}
public void UpdateXCriterion(XCriterionModel criterion)
{
Connection.ExecuteSqlAsync("UPDATE XCriterionModel SET Data = @Data, Modified = @Modified WHERE AnonymousUserId = @AnonymousUserId AND SessionKey = @SessionKey", criterion);
}
public void Clean(int expirationDays)
{
Connection.ExecuteSql(
"DELETE FROM XCriterionModel WHERE Modified < DATEADD(DAY, DATEDIFF(DAY, 0, GETDATE()), @NumberOfDays)",
new {NumberOfDays = 0 - expirationDays});
}
public override void EnsureDatabase()
{
Connection.ExecuteSql(
"IF NOT exists(SELECT 1 FROM sys.Tables WHERE Name = N'XCriterionModel' AND Type = N'U')" +
"BEGIN" +
" CREATE TYPE XCriterionModelTable AS TABLE (AnonymousUserId [uniqueidentifier], SessionKey [nvarchar](50), Data [nvarchar](max), Modified [date]); " +
" CREATE TABLE XCriterionModel (" +
" AnonymousUserId uniqueidentifier," +
" SessionKey nvarchar(50)," +
" Data nvarchar(max)," +
" Modified date" +
" primary key (AnonymousUserId, SessionKey)" +
" );" +
"END");
}
}
public abstract class XCriterionBase<T> : CriterionBase<T> where T : class, ICriterionModel, new()
{
protected IXCriterionRepository Repository { get; set; }
protected abstract string SessionKey { get; }
protected XCriterionBase(IXCriterionRepository repository)
{
Repository = repository;
}
private const string AnonymousUserCookieKey = "EPi:AnonymousUserId";
protected Guid AnonymousUserId
{
get
{
return Guid.Parse(GetCookie(new HttpContextWrapper(HttpContext.Current), AnonymousUserCookieKey, Guid.NewGuid().ToString(), false).Value);
}
}
protected static DateTime CookieExpires
{
get { return DateTime.Today.AddDays(int.Parse(ConfigurationManager.AppSettings["XCriterionBase.CookieExpirationDays"])); }
}
public override void Subscribe(ICriterionEvents criterionEvents)
{
criterionEvents.StartSession += Handle_StartSession;
}
public override void Unsubscribe(ICriterionEvents criterionEvents)
{
criterionEvents.StartSession -= Handle_StartSession;
}
protected static HttpCookie GetCookie(HttpContextBase httpContext, string cookieName, string cookieValue=null, bool overrideExistingValue=false)
{
var cookie = httpContext.Request.Cookies[cookieName];
if (cookie == null)
{
cookie = new HttpCookie(cookieName)
{
Value = cookieValue,
Expires = CookieExpires
};
httpContext.Response.Cookies.Add(cookie);
httpContext.Request.Cookies.Add(cookie);
}
else //update exsiting
{
if(overrideExistingValue) cookie.Value = cookieValue;
cookie.Expires = CookieExpires;
httpContext.Response.Cookies.Add(cookie);
httpContext.Request.Cookies.Set(cookie);
}
return cookie;
}
private static void Handle_StartSession(object sender, CriterionEventArgs e)
{
//Set the cookie but do not override the existing value.
GetCookie(e.HttpContext, AnonymousUserCookieKey, Guid.NewGuid().ToString(), false);
}
}
//based on: EPiServer.Personalization.VisitorGroups.Criteria.ViewedPagesCriterion;
[VisitorGroupCriterion(Category = "Cross session Criteria", Description = "Matches after visiting a page", DisplayName = "Visited Page X")]
public class ViewedPagesXCriterion : XCriterionBase<ViewedPagesModel>
{
protected override string SessionKey
{
get
{
return "EPi:ViewedPagesX";
}
}
public ViewedPagesXCriterion(IXCriterionRepository repository)
: base(repository)
{
}
public ViewedPagesXCriterion()
: this(ServiceLocator.Current.GetInstance<IXCriterionRepository>())
{
}
public override bool IsMatch(IPrincipal principal, HttpContextBase httpContext)
{
var viewedPages = GetViewedPages(httpContext);
return viewedPages != null && viewedPages.Contains(Model.GetViewedPageLink());
}
public override void Subscribe(ICriterionEvents criterionEvents)
{
base.Subscribe(criterionEvents);
criterionEvents.VisitedPage += VisitedPage;
}
public override void Unsubscribe(ICriterionEvents criterionEvents)
{
base.Unsubscribe(criterionEvents);
criterionEvents.VisitedPage -= VisitedPage;
}
private void VisitedPage(object sender, CriterionEventArgs e)
{
var pageLink = e.GetPageLink();
if (PageReference.IsNullOrEmpty(pageLink))
return;
AddViewedPage(e.HttpContext, pageLink);
}
private void AddViewedPage(HttpContextBase httpContext, PageReference pageLink)
{
var hashSet = GetViewedPages(httpContext);
if (hashSet == null)
{
hashSet = new HashSet<PageReference>();
if (httpContext.Session != null)
httpContext.Session[SessionKey] = hashSet;
}
if (!hashSet.Contains(pageLink))
hashSet.Add(pageLink); //add to current set when not exists
//update in database
Repository.UpdateXCriterion(
new XCriterionModel() {
AnonymousUserId = AnonymousUserId,
SessionKey = SessionKey,
Data = JsonConvert.SerializeObject(hashSet),
Modified = DateTime.Now
});
}
private HashSet<PageReference> GetViewedPages(HttpContextBase httpContext)
{
if (httpContext.Session == null)
return null;
var hashSet = httpContext.Session[SessionKey] as HashSet<PageReference>;
if (hashSet == null)
{
var criterion = Repository.GetXCriterion(AnonymousUserId, SessionKey);
if (criterion.Data.ValidateJSON())
{
hashSet = JsonConvert.DeserializeObject<HashSet<PageReference>>(criterion.Data);
httpContext.Session[SessionKey] = hashSet;
}
}
return hashSet;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.