---
title: Changes to the Category API in CMS 8
slug: cms-11/dev-docs/changes-to-the-category-api-in-cms-8
docTags: 
createdAt: 2026-07-22T19:45:22.987Z
---

To improve the performance of categories in Optimizely Content Management System (CMS 8), the Category API was updated to use the read-only instance pattern commonly used in other areas of the API. This means that requested categories are returned as read-only instances by default, and modification can be done only after a writable clone has been requested. As a part of the change, a new `CategoryRepository` class is introduced to improve the testability of the API. This repository replaces the now-obsolete methods directly on the `Category` class, such as `GetRoot()` and `Save`.

Example of modifying a `Category`:

```csharp
var categoryRepository = ServiceLocator.Current.GetInstance<CategoryRepository>();
    var myCategory = categoryRepository.Get("MyCategory"); // Returns a read-only instance
    myCategory = myCategory.CreateWritableClone();
    myCategory.Description = "My category is really nice";
    categoryRepository.Save(myCategory);
```

## Changes to the parent collection

Another smaller change is that the parent `Category` instance is no longer modified beyond the current when adding or deleting a category. This means you must reload the parent category to get an updated version with/without the added/deleted category. For example:

```csharp
var parent = categoryRepository.Get("Parent");
    var childCategory = new Category(parent, "Child");
    categoryRepository.Save(childCategory);
    
    // parent.Categories will not contain a "Child" category here.
    
    parent = categoryRepository.Get("ParentCategory");
    
    // parent.Categories will now contain the "Child" category
```

## Support for moving categories

The `Save` method now respects changes to the `Parent` property. So, it's possible to move a category from one parent to another without having to recreate it. This also means that content items associated with the moved category maintain their association after the move.

## Changes to CategoryCollection

To make the category tree easier to use, we changed the `CategoryCollection` class to no longer inherit from `CollectionBase`. Now, it implements `IList<Category>` instead of just IList.

## Full list of breaking Category API changes

While we tried to keep the API as backward-compatible as possible, some behavioral changes were introduced with version 8. The following changes made to the API are considered breaking:

- `Category` now implements `IReadOnly`. `Save()` requires that the category is writable when called.
- `Category.GetRoot()` now returns a read-only `Category` instance.
- `Category.Delete()` no longer removes itself from its parent's categories.
- `Category.Delete()` no longer clears the `Categories` property when called.
- `Category.Save()` with a new category no longer adds itself to its parent's categories.
- `Category.Save()` with new category now respects the sort order if given.
- `Category.Save()` now requires the name to be unique among siblings.
- `Category.Save()` now respects a change of parents.
- `Category.Save()` now throws `ArgumentException` instead of `DataAbstractionException` when the `Category` is in an invalid state.
- `Category.GetList()` implementation has changed to returns a list of type `List<Category>` instead of a `CategoryCollection`.
- `CategoryCollection` no longer inherits from `CollectionBase`, it now implements `IList<Category>`.
- `CategoryCollection` now implements `IReadOnly`.
- `CategoryCollection.AddRange()` now validates and sets the `Parent` property of the inserted `Categories` the same way as `Add()` does.
- `CategoryCollection` now throws `InvalidOperationException` instead of `DataAbstractionException` if the `Parent` value of the inserted `Category` doesn't match.
- `CategoryList.MemberOfAny()` overloads were combined into one.
- `CategoryList.MemberOfAll()` overloads were combined into one.
- `CategoryList.GetHashCode()` now returns the same hash for two instances that match according to the `Equals()` method.

Note that many parts of the old API were marked obsolete. These items have compile-time information describing the change and where to find the equivalent method in the updated API.
