When you use either the Get<T>() or GetChildren<T>() methods from ContentLoader or ContentRepository, the object that is returned will always be a read-only version. If you try and update any property on these objects and try to persist that data back to the CMS using the Save() method you will encounter an exception ❌. Read on to learn why 🔥🔥🔥

Why does Episerver make everything read-only?

The first reason is to simplify the API for everyone. When you work with pages or blocks in code, 95% of the time you will only need to access them in a read-only context, like displaying data on the screen. By making all the objects returned from the API immutable (read-only) when you work with an object, you know that it won't change. This allows the API to share the same object instance between several different threads. You can access the same object in different controllers at the same time and you will get exactly the same behaviour each time. This type of architecture significantly reduces the number of short-lived objects being created and consequently reduces the amount of memory that your website will need to consume.

IReadOnly

If you use a reflection tool, like DotPeek to look at the underlining code of PageData, you will notice it implements the IReadOnly interface (look in EPiServer.Core.PageData). Behind the scenes when you call eitherGet<T>() or GetChildren<T>(), Episerver first creates a writable PageData object. The object is then populated with its corresponding data. A call is made to IReadOnly.MakeReadOnly(), which in turn converts the object to an immutable object. This object is then added to the cache and any subsequent calls from that page or block from the API will return in a read-only state.

How Do I Update A Page?

If you try to update an object returned from the API and save it you will encounter a System.NotSupportedException exception stating that the The property ***** is read-only. When you work with pages and blocks, you may notice they both expose a method called CreateWritableClone(). CreateWritableClone() makes a mutable copy of the page. With this new writable copy, you can then update the content and persist the values back into Episerver.

Note the codes uses IContentRepository rather than IContentLocator. IContentRepository will give you access to the Save() method, while IContentLocator only gives read-only CMS content access. We retrieve the page/block from the database using the Get<T>() method. On Line6, the CreateWritableClone() method is used to create an ibject that is not reead-only. On Line8, Save() is used to store the data. If you need to know if the page/block you are currently working with is read-only or a clone you can use the IsReadOnly property.

Happy Coding 🤘