How to change Modified date programmatically in Episerver

Published on in C# and Episerver

Last updated on

When migrating content from another platform to Episerver, I wanted to set a specific datetime for each content's Modified date (Saved property). Sounds simple, but it was only possible by using Episerver's internal, undocumented API.

Table of contents

How to do it

Setting the current date

This should be straightforward.

  1. Set a content's SetChangedOnPublish property to true:
    (content as IChangeTrackable).SetChangedOnPublish = true;
    
  2. Publish the content.

From Episerver's support page Cannot programmatically set modified date:

[S]etting the SetChangedOnPublish property to true and then [publishing] the page [will] trigger an update of the modified date in the database. So it will set the date to the same one as when you publish the page.

Setting an arbitrary date

This is more complicated and this is what this blog post is about.

First, here's the code:

using EPiServer.Framework;
using EPiServer.DataAccess.Internal;

// ...

// Update the "Modified" datetime.
// A hacky solution that uses Epi's internal, undocumented, possibly unstable API.
// Tested with EPiServer.CMS 11.14.0 and 11.19.0.
ContextCache.Current[ContentSaveDB.UseIChangeTrackingSavedKey] = new object();
(content as IChangeTrackable).Saved = data.PublishedAt;
(content as IChangeTrackable).SetChangedOnPublish = true;

And here's a rough explanation:

  1. Use Episerver's internal ContentSaveDB API to change an internal setting. (Or something like that – I don't really know what's going on since the internal API is internal and undocumented).
  2. Set the content's Saved property to your desired datetime value. (Yes, the Saved property is the correct one even though we are modifying the Modified date. There's no Modified property.)
  3. Set the content's SetChangedOnPublish property to true. Otherwise the new value of the Saved property won't take effect. (Yes, it's SetChanged* and not "SetSaved*" which doesn't even exist. I guess naming things is difficult.)
  4. That's it! Now publish the content or do whatever you were doing.

But before you go, remember this:

The code might stop working when you do any NuGet package updates. That's because the API you are using is internal and thus unsupported. From the ContentSaveDB class's documentation:

Unsupported INTERNAL API! Not covered by semantic versioning; might change without notice.

Is this a good idea?

Not really, but do you have any better ideas?

In my case, I was migrating content from another platform (Contentful, but that's irrelevant) to Episerver. After I was done, I didn't need the code anymore, so it didn't matter that I was using an internal, undocumented, possibly unstable API.

You too shouldn't use this hacky method in any long-living code.

Should this be even possible?

Well, that's a funny question! According to Episerver's support page Cannot programmatically set modified date from October 2017, this shouldn't be possible. From the page:

It is not possible to set the modified date to a specific date programmatically.

The date can only be changed by setting the SetChangedOnPublish property to true and then publish the page. This will in turn trigger an update of the modified date in the database. So it will set the date to the same one as when you publish the page.

But like I explained above, it is possible to set the Modified date to a specific date programmatically.

(Fun fact: Google decided to choose the misleading quote above as a Featured snipped (screenshot below). Thanks Google. 😂 I have since wrapped the <blockquote> in a <div data-nosnippet> element.)

Screenshot of Google's Featured snippet from this blog post, displaying the above quote from Episerver's support page saying that it's not possible to set the modified date programmatically.

How I found the solution

"Oh, damn," I thought to myself after reading that Episerver's support page. But fortunately I didn't give up just yet.

After googling a bit more, I found a GitHub gist by Kim Gunnarsson from April 2016. (Funnily, the gist was created 1.5 years before that Episerver's support page.)

The following lines of code from the gist are relevant, the first line being the most important:

EPiServer.Framework.ContextCache.Current[EPiServer.DataAccess.ContentSaveDB.UseIChangeTrackingSavedKey] = new object();

var clone = page.CreateWritableClone();
clone.Saved = clone.StartPublish;
clone.SetChangedOnPublish = true;

The gist is currently 4.5 years old (as of October 2020) and thus didn't work out of the box for me – I was using EPiServer.CMS version 11.14.0 and soon after version 11.19.0. But this did work:

EPiServer.Framework.ContextCache.Current[EPiServer.DataAccess.Internal.ContentSaveDB.UseIChangeTrackingSavedKey] = new object();
(content as IChangeTrackable).Saved = data.PublishedAt;
(content as IChangeTrackable).SetChangedOnPublish = true;

I.e. the ContentSaveDB class has been changed into an internal class at some point.

The first line can be simplified into this with proper using statements:

using EPiServer.Framework;
using EPiServer.DataAccess.Internal;

ContextCache.Current[ContentSaveDB.UseIChangeTrackingSavedKey] = new object();

Finally, you may want to slap a comment saying that this is a hacky solution. Behold, we ended up with the code that I already presented to you at the very beginning:

using EPiServer.Framework;
using EPiServer.DataAccess.Internal;

// ...

// Update the "Modified" datetime.
// A hacky solution that uses Epi's internal, undocumented, possibly unstable API.
// Tested with EPiServer.CMS 11.14.0 and 11.19.0.
ContextCache.Current[ContentSaveDB.UseIChangeTrackingSavedKey] = new object();
(content as IChangeTrackable).Saved = data.PublishedAt;
(content as IChangeTrackable).SetChangedOnPublish = true;

Hmm, did someone say this isn't possible? Ha, checkmate!