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.
- Set a content's
SetChangedOnPublish
property totrue
:(content as IChangeTrackable).SetChangedOnPublish = true;
- Publish the content.
From Episerver's support page Cannot programmatically set modified date:
[S]etting the
SetChangedOnPublish
property totrue
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:
- 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). - Set the content's
Saved
property to your desired datetime value. (Yes, theSaved
property is the correct one even though we are modifying the Modified date. There's noModified
property.) - Set the content's
SetChangedOnPublish
property totrue
. Otherwise the new value of theSaved
property won't take effect. (Yes, it'sSetChanged*
and not "SetSaved*
" which doesn't even exist. I guess naming things is difficult.) - 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.)
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!