Why are Pages and Blocks Returned From The Episerver API Read-Only?
Wed 21 October, 2015 / By Jon D Jones
When you use the Get<>() or GetChildren<>() methods from IContentLoader or IContentRepository, the page data that will get returned will always be a read-only version. If you try and update a property and save it you will get
Why?Why would Episerver make everything read-only? One reason is to simplify the API for everyone. In 95% of the situations when you will be working with pages or blocks, 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. This type of architecture significantly reduces the amount of short-lived objects being created and consequently reduce the amount of memory that your website will use.
IReadOnlyThe interface Read-only handling has been added for the EPiServer.Core.PageData class and all contained classes. Behind the scenes when you call Get<>() or GetChildren<>() Episerver first creates a writable PageData object. The object is then populated with it's corresponding data. A call is made to IReadOnly.MakeReadOnly(), which in turn makes the page/blocks properties immutable. The object is added to the cache and any subsequent calls from that page or block from the API will return that object.
How Do I Update A Page?If you try and load a page from the API and save it directly you will receive a System.NotSupportedException telling you that the 'The property **** is read-only.' When you work with pages and blocks you may notice the CreateWritableClone() method. This as the name suggests makes a mutable copy of the page. With this new writable copy, you can update as you want.
var contentRepository = ServiceLocator.Current.GetInstance<IContentRepository>(); var contentReference = new ContentReference(5); var variant = contentRepository.Get<ContentPage>(contentReference); var clone = variant.CreateWritableClone<ContentPage>(); clone.name = "Update"; contentRepository.Save(clone,SaveAction.Publish, AccessLevel.NoAccess);In the code above, we use IContentRepository instead of IContentLocator as it gives us access to the Save method. We retrieve the page/block from the database using the IContentRepository.Get<>() method, which returns an immutable copy of the page/block. To make the object writable, we create a clone/copy of it using the CreateWritableClone() method defined on the IReadOnly interface that every block and page has to inherit from. When we have a copy of the clone, you can update it as how you want. You can then use the IContentRepository.Save() to persist the data back to the database. 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.