# Management SDK - Models

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

`client.Models` reads and changes the models in an instance: the content models that content items use, and the component models that page components use.

> **Note:** Upgrading from 1.x? `modelMethods` is now `client.Models`, and every method takes the instance GUID first. See [Migrating to 2.0](https://agilitycms.com/docs/dotNet/management-sdk-dotnet-migrating-to-2).

A model defines fields. A **content model** is used by content items. A **component model** (some API and SDK names still say `Module`) is used by page components. To manage the containers that hold content items of a model, see [Containers](https://agilitycms.com/docs/dotNet/management-sdk-dotnet-containers).

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 model types are in the `Agility.Management.Sdk.Models` namespace:

```csharp
using Agility.Management.Sdk;
using Agility.Management.Sdk.Models;
```

Every method also takes an optional `CancellationToken cancellationToken` as its last parameter. The signatures below leave it out.

## Reading models

### Get a model by ID

```csharp
ContentModel model = await client.Models.GetModelAsync(guid, modelId);
Console.WriteLine($"{model.DisplayName}: {model.Fields?.Count} fields");
```

**Signature:** `Task<ContentModel> GetModelAsync(string instanceGuid, int modelId)`

---

### Get a model by reference name

```csharp
ContentModel model = await client.Models.GetModelByReferenceNameAsync(guid, "BlogPost");
Console.WriteLine($"{model.DisplayName} (ID: {model.Id})");
```

**Signature:** `Task<ContentModel> GetModelByReferenceNameAsync(string instanceGuid, string referenceName)`

> **Note:** In 1.x, these methods returned `null` when there was no model. In 2.0 they never return `null`. If the API returns an error or an empty response, they throw an `AgilityManagementException`.

---

### List content models

```csharp
List<ContentModel> contentModels = await client.Models.GetContentModelsAsync(guid, includeDefaults: false);

foreach (var model in contentModels)
{
    Console.WriteLine($"{model.ReferenceName} - {model.DisplayName}");
}
```

| Parameter | What it does |
|---|---|
| `includeDefaults` | Include Agility's built-in models. Default `false`. |
| `includeModules` | Include component models too. Leave it `null` to use the API's default, which leaves them out. |
| `updatedSince` | Return only models changed after this time. Leave it `null` to get every model. |

To get only the models changed in the last day:

```csharp
var changed = await client.Models.GetContentModelsAsync(guid, updatedSince: DateTime.UtcNow.AddDays(-1));
```

**Signature:** `Task<List<ContentModel>> GetContentModelsAsync(string instanceGuid, bool includeDefaults = false, bool? includeModules = null, DateTime? updatedSince = null)`

---

### List component models

Get the component models that page components use. This replaces `GetPageModules` from 1.x.

```csharp
List<ContentModel> componentModels = await client.Models.GetComponentModelsAsync(guid);

foreach (var model in componentModels)
{
    Console.WriteLine(model.ReferenceName);
}
```

Pass `includeDefault: true` to include Agility's built-in components.

**Signature:** `Task<List<ContentModel>> GetComponentModelsAsync(string instanceGuid, bool includeDefault = false)`

## Field types

### List the field types you can use

`GetFieldTypesAsync` returns the names of the field types a model can use. Use these names as `ContentModelField.Type`.

```csharp
List<string> fieldTypes = await client.Models.GetFieldTypesAsync(guid);
```

**Signature:** `Task<List<string>> GetFieldTypesAsync(string instanceGuid)`

---

### List the field types your models use

`GetUsedFieldTypesAsync` lists the field types that the instance's models use, including custom fields. The API doesn't describe the shape of this response, so the SDK returns it as JSON.

```csharp
using System.Text.Json.Nodes;

JsonNode? used = await client.Models.GetUsedFieldTypesAsync(guid);
Console.WriteLine(used?.ToJsonString());
```

`includeDefaults` and `includeModules` control whether built-in models and component models are included. Leave them `null` to use the API's defaults, which include both.

**Signature:** `Task<JsonNode?> GetUsedFieldTypesAsync(string instanceGuid, bool? includeDefaults = null, bool? includeModules = null)`

## Creating and updating models

### Save a model

`SaveModelAsync` creates or updates a model and returns the saved model. To create one, set `Id` to `0`.

```csharp
ContentModel saved = await client.Models.SaveModelAsync(guid, new ContentModel
{
    Id = 0,
    DisplayName = "Blog Post",
    ReferenceName = "BlogPost",
    Fields =
    [
        new ContentModelField
        {
            Name = "Title",
            Label = "Title",
            Type = "Text",
            IsDataField = true,
            Editable = true,
            Settings = new() { ["Required"] = "True" },
        },
    ],
});

Console.WriteLine($"Created model ID: {saved.Id}");
```

Field settings, such as whether a field is required, go in the field's `Settings` dictionary as strings.

**Signature:** `Task<ContentModel> SaveModelAsync(string instanceGuid, ContentModel model)`

---

### Add a field to an existing model

The field list you send is the model's **complete** field list. To add a field, read the model, change it, and save the whole model back.

```csharp
ContentModel model = await client.Models.GetModelByReferenceNameAsync(guid, "BlogPost");

model.Fields ??= [];
model.Fields.Add(new ContentModelField
{
    Name = "Summary",
    Label = "Summary",
    Type = "Text",
    IsDataField = true,
    Editable = true,
    Settings = new() { ["Required"] = "False" },
});

ContentModel updated = await client.Models.SaveModelAsync(guid, model);
```

> ⚠️ **Don't save a model with a partial field list.** A model you build from scratch with only the field you want to add replaces the model's fields with that one field.

## Deleting models

### Delete a model

```csharp
await client.Models.DeleteModelAsync(guid, modelId);
```

The method returns nothing. If the delete fails, it throws an `AgilityManagementException`.

**Signature:** `Task DeleteModelAsync(string instanceGuid, int modelId)`

## 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 model = await client.Models.GetModelByReferenceNameAsync(guid, "BlogPost");
}
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.modelMethods`) | 2.0 (`client.Models`) |
|---|---|
| `GetContentModel(id, guid)` | `GetModelAsync(guid, modelId)` |
| `GetModelByReferenceName(name, guid)` | `GetModelByReferenceNameAsync(guid, referenceName)` |
| `GetContentModules(includeDefaults, guid, includeModules)` | `GetContentModelsAsync(guid, includeDefaults, includeModules)` |
| `GetPageModules(guid, includeDefault)` | `GetComponentModelsAsync(guid, includeDefault)` |
| `SaveModel(model, guid)` | `SaveModelAsync(guid, model)` |
| `DeleteModel(id, guid)` | `DeleteModelAsync(guid, modelId)` |

The `Model` and `ModelField` classes are now `ContentModel` and `ContentModelField`, and `Model.ID` is now `ContentModel.Id`. `GetFieldTypesAsync`, `GetUsedFieldTypesAsync` and the `updatedSince` filter are new in 2.0.
