# Management SDK - Containers

> Source: https://agilitycms.com/docs/dotNet/management-sdk-dotnet-containers

`client.Containers` reads and changes the containers in an instance. A container holds content items of one content model, either as a single item or as a list.

> **Note:** Upgrading from 1.x? `containerMethods` is now `client.Containers`, and every method takes the instance GUID first. See [Migrating to 2.0](https://agilitycms.com/docs/dotNet/management-sdk-dotnet-migrating-to-2).

Every container is based on a content model. To read or create the model first, see [Models](https://agilitycms.com/docs/dotNet/management-sdk-dotnet-models). To read and save the content items inside a container, see [Content](https://agilitycms.com/docs/dotNet/management-sdk-dotnet-content).

The examples assume a `client` created as shown in the [Intro](https://agilitycms.com/docs/dotNet/management-sdk-dotnet-intro), and `guid` set to your instance GUID. The container types are in `Agility.Management.Sdk.Models`, and `ContainerListOptions` is in `Agility.Management.Sdk.Clients`:

```csharp
using Agility.Management.Sdk;
using Agility.Management.Sdk.Clients;
using Agility.Management.Sdk.Models;
```

A container's ID is its `ContentViewID`. Every method also takes an optional `CancellationToken cancellationToken` as its last parameter. The signatures below leave it out.

## Reading containers

### Get all containers

```csharp
List<ContentContainer> containers = await client.Containers.GetContainerListAsync(guid);

foreach (var container in containers)
{
    Console.WriteLine($"{container.ReferenceName} (ID: {container.ContentViewID})");
}
```

Pass `updatedSince` to get only the containers changed after a given time:

```csharp
var changed = await client.Containers.GetContainerListAsync(guid, updatedSince: DateTime.UtcNow.AddDays(-7));
```

**Signature:** `Task<List<ContentContainer>> GetContainerListAsync(string instanceGuid, DateTime? updatedSince = null)`

---

### Get containers a page at a time

For large instances, page through the containers. The result has the total count and one page of containers.

```csharp
ContentContainerPagedResult page = await client.Containers.GetContainerListPagedAsync(guid,
    new ContainerListOptions
    {
        PageSize = 100,
        RecordOffset = 0,
        ContentType = ContentViewType.All,
        IncludeModules = true,
        UpdatedSince = DateTime.UtcNow.AddDays(-7),   // optional
    });

Console.WriteLine($"Total containers: {page.TotalCount}");
foreach (var container in page.Items ?? [])
{
    Console.WriteLine(container.ReferenceName);
}
```

| `ContainerListOptions` | What it does |
|---|---|
| `PageSize` | Containers per page. The API's default is 20. |
| `RecordOffset` | How many containers to skip. The API's default is 0. |
| `ContentType` | Which kind of container to list: `ContentViewType.All`, `Shared`, `Linked` or `DynamicPageList`. |
| `IncludeModules` | Include component containers. The API's default is `true`. |
| `UpdatedSince` | Only containers changed after this time. |

Settings you leave `null` use the API's defaults. You can also pass no options at all.

**Signature:** `Task<ContentContainerPagedResult> GetContainerListPagedAsync(string instanceGuid, ContainerListOptions? options = null)`

---

### Get a container by ID

```csharp
ContentContainer container = await client.Containers.GetContainerAsync(guid, containerId);
Console.WriteLine($"Container: {container.ReferenceName}");
```

**Signature:** `Task<ContentContainer> GetContainerAsync(string instanceGuid, int containerId)`

---

### Get a container by reference name

```csharp
ContentContainer posts = await client.Containers.GetContainerByReferenceNameAsync(guid, "blogposts");
Console.WriteLine($"Found: {posts.ReferenceName} (ID: {posts.ContentViewID})");
```

**Signature:** `Task<ContentContainer> GetContainerByReferenceNameAsync(string instanceGuid, string referenceName)`

> **Note:** In 1.x, the get methods returned `null` when there was no container. In 2.0 they never return `null`. If the API returns an error or an empty response, they throw an `AgilityManagementException`.

---

### Get the containers that use a model

Find every container based on a content model.

```csharp
List<ContentContainer> forModel = await client.Containers.GetContainersByModelAsync(guid, modelId);

foreach (var container in forModel)
{
    Console.WriteLine(container.ReferenceName);
}
```

**Signature:** `Task<List<ContentContainer>> GetContainersByModelAsync(string instanceGuid, int modelId)`

---

### Get a container's security settings

`GetContainerSecurityAsync` returns the container with its user and team permissions, in `Users` and `Teams`.

```csharp
ContentContainer secured = await client.Containers.GetContainerSecurityAsync(guid, containerId);

foreach (var user in secured.Users ?? [])
{
    Console.WriteLine(user.EmailAddress);
}

foreach (var team in secured.Teams ?? [])
{
    Console.WriteLine(team.TeamName);
}
```

**Signature:** `Task<ContentContainer> GetContainerSecurityAsync(string instanceGuid, int containerId)`

---

### Get a container's notification settings

```csharp
List<Notification> notifications = await client.Containers.GetNotificationsAsync(guid, containerId);

foreach (var notification in notifications)
{
    Console.WriteLine($"{notification.EmailAddress}: {notification.NotificationType}");
}
```

`NotificationType` is a `NotificationTypes` value, such as `Publish` or `RequestApproval`.

**Signature:** `Task<List<Notification>> GetNotificationsAsync(string instanceGuid, int containerId)`

## Creating and updating containers

### Save a container

`SaveContainerAsync` creates or updates a container and returns the saved container. To create one, set `ContentViewID` to `0`. To update one, pass its existing `ContentViewID`.

This example creates a list container for a content model:

```csharp
ContentModel model = await client.Models.GetModelByReferenceNameAsync(guid, "BlogPost");

ContentContainer container = await client.Containers.SaveContainerAsync(guid, new ContentContainer
{
    ContentViewID = 0,
    ContentDefinitionID = model.Id,
    ContentDefinitionTypeID = 1,
    ContentViewName = "Blog Posts",
    ReferenceName = "blogposts",
    IsShared = false,
    IsDynamicPageList = true,
});

Console.WriteLine($"Created container: {container.ReferenceName} (ID: {container.ContentViewID})");
```

`ContentDefinitionID` is the ID of the content model the container is based on.

The API may change the reference name you send to keep it unique. To keep it exactly as given, pass `forceReferenceName: true`:

```csharp
var container = await client.Containers.SaveContainerAsync(guid, newContainer, forceReferenceName: true);
```

**Signature:** `Task<ContentContainer> SaveContainerAsync(string instanceGuid, ContentContainer container, bool? forceReferenceName = null)`

> **Tip:** Use the reference name the save returns, not the one you sent, when you save content items into the new container.

## Deleting containers

### Delete a container

```csharp
await client.Containers.DeleteContainerAsync(guid, containerId);
```

The method returns nothing. If the delete fails, it throws an `AgilityManagementException`.

**Signature:** `Task DeleteContainerAsync(string instanceGuid, int containerId)`

## Error handling

Every error from the API or the network throws an `AgilityManagementException`. It carries the HTTP status code, the API's own message and a request ID.

```csharp
try
{
    var container = await client.Containers.GetContainerByReferenceNameAsync(guid, "blogposts");
}
catch (AgilityManagementException ex)
{
    Console.Error.WriteLine($"{ex.StatusCode}: {ex.ApiMessage} (request {ex.RequestId})");
}
```

Reads are retried automatically on temporary failures. Saves and deletes are never retried. See the [Intro](https://agilitycms.com/docs/dotNet/management-sdk-dotnet-intro) for the full list of exceptions and the retry rules.

## Coming from 1.x

| 1.x (`client.containerMethods`) | 2.0 (`client.Containers`) |
|---|---|
| `GetContainerById(id, guid)` | `GetContainerAsync(guid, containerId)` |
| `GetContainerByReferenceName(name, guid)` | `GetContainerByReferenceNameAsync(guid, referenceName)` |
| `GetContainersByModel(modelId, guid)` | `GetContainersByModelAsync(guid, modelId)` |
| `GetContainerSecurity(id, guid)` | `GetContainerSecurityAsync(guid, containerId)` |
| `GetContainerList(guid)` | `GetContainerListAsync(guid)` |
| `GetContainerListPaged(guid, pageSize, recordOffset, ...)` | `GetContainerListPagedAsync(guid, new ContainerListOptions { ... })` |
| `GetNotificationList(id, guid)` | `GetNotificationsAsync(guid, containerId)` |
| `SaveContainer(container, guid)` | `SaveContainerAsync(guid, container)` |
| `DeleteContainer(id, guid)` | `DeleteContainerAsync(guid, containerId)` |

The `Container` class is now `ContentContainer`, and `PagedResult<Container>` is now `ContentContainerPagedResult`. The `updatedSince` filter on `GetContainerListAsync` and the `forceReferenceName` option are new in 2.0.
