---
title: Content associations
slug: cms-12/api/groups-content-associations
docTags: 
createdAt: 2026-08-07T16:45:43.000Z
---

A group is a named aggregation of users and content. You associate resources with a group by adding them as an association.

In the Optimizely Community API, an association is represented with the `Association` class.

Associations are managed through a service implementing the interface `IAssociationService`. This service provides the ability to persist, retrieve, and remove Associations you define.

For Optimizely Community Async API information, see *Async API* in [Discover the platform](docId\:NKmGS-fLtRQIYLEWZfb69).

## Access an IAssociationService

When the `Groups` feature is installed on an Optimizely Content Management System (CMS) site with the feature's site integration package, you can get an instance of this service from the inversion of control (IoC) container.

Example:

```csharp
var associationService = EPiServer.ServiceLocation.ServiceLocator.Current.GetInstance<IAssociationService>();
```

When the feature is installed on a non-Optimizely CMS site, you can get an instance of a service from the default `factory` class provided within the package.

Example:

```csharp
var factory = new EPiServer.Social.Groups.DefaultAssociationServiceFactory();
var associationService = factory.Create();
```

## Add an association

To add an association, use the `Add(Association)` method of the `IAssociationService`. This method accepts an instance of the `Association` class and returns a reference to an instance of that class populated with any additional, system-generated data (for example, a unique ID).

```csharp
IAssociationService associationService;
GroupId idOfExistingGroup;
Reference resource;

// ...

var association = new Association(resource, idOfExistingGroup);
associationService.Add(association);
```

If the identified group cannot be found, a `GroupDoesNotExistException` is thrown.

If the association already exists, a `DuplicateAssociationException` is thrown.

The above example invokes the request to add an association synchronously. An example of adding an association asynchronously using the asynchronous overload with C#'s async and await keywords is described below.

```csharp
private async Task<Association> AddAssociationAsync(IAssociationService associationService) {
  GroupId idOfExistingGroup;
  Reference resource;

  // ...

  var association = new Association(resource, idOfExistingGroup);

  var addAssociationTask = associationService.AddAsync(association);

  //Do other application specific work in parallel while the task executes.
  //....

  //Wait until the task runs to completion.
  var addedAssociation = await addAssociationTask;
  return addedAssociation;
}
```

## Validate an association

To validate that a resource is an association of a group, use the `IsAssociated(GroupId,Reference)` of the `IAssociationService`. This method accepts an instance of the `GroupId` class, identifying the target group, and an instance of the `Reference` class, identifying the target resource. The method returns a Boolean, indicating whether or not the identified resource is an association of the identified group.

```csharp
IAssociationService associationService;
GroupId idOfExistingGroup;
Reference resource;

// ...

bool isAssociation = associationService.IsAssociated(idOfExistingGroup, resource);
```

The above example invokes the request to validate an association synchronously. An example of validating an association asynchronously using the asynchronous overload with C#'s async and await keywords is described below.

```csharp
private async Task<bool> ValidateAssociationAsync(IAssociationService associationService) {
  // ...

  GroupId idOfExistingGroup;
  Reference resource;

  // ...

  var isAssociationTask = associationService.IsAssociatedAsync(idOfExistingGroup, resource);

  //Do other application specific work in parallel while the task executes.
  //....

  //Wait until the task runs to completion.
  var isValid = await isAssociationTask;
  return isValid;
}
```

## Remove an association

To remove a specific instance of Association, which has previously been added through the platform, use the `Remove(AssociationId)` method. This method accepts an instance of the `AssociationId` class, which identifies the `AssociationId` to be removed. The result is the deletion of the `Association` corresponding to that ID.

```csharp
IAssociationService associationService;

//...

// Construct a AssociationId corresponding to the desired association
var associationId = AssociationId.Create("...");
var association = associationService.Get(associationId);
```

The above example invokes the request to remove an association synchronously. An example of removing an association asynchronously using the asynchronous overload with C#'s async and await keywords is described below.

```csharp
private async Task RemoveAssociationAsync(IAssociationService associationService) {
  //...

  // Construct a AssociationId corresponding to the desired association
  var associationId = AssociationId.Create("...");
  var removeAssociationTask = associationService.RemoveAsync(associationId);

  //Do other application specific work in parallel while the task executes.
  //....

  //Wait until the task runs to completion.
  await removeAssociationTask;
}
```

## Retrieve an association

To retrieve a specific instance of an `Association`, which has previously been added through the platform, use the `Get(AssociationId)` method of `IAssociationService`. This method accepts an instance of the `AssociationId` class, which identifies the `Association` to retrieve. It returns the instance of the `Association` class corresponding to that identifier.

```csharp
IAssociationService associationService;

//...

// Construct a AssociationId corresponding to the desired association
var associationId = AssociationId.Create("...");
var association = associationService.Get(associationId);
```

If the requested `Association` cannot be found, an `AssociationDoesNotExistException` is thrown.

The above example invokes the request to retrieve an association synchronously. An example of retrieving an association asynchronously using the asynchronous overload with C#'s async and await keywords is described below.

```csharp
private async Task<Association> GetAssociationAsync(IAssociationService associationService) {
  //...

  // Construct a AssociationId corresponding to the desired association
  var associationId = AssociationId.Create("...");

  var getAssociationTask = associationService.GetAsync(associationId);

  //Do other application specific work in parallel while the task executes.
  //....

  //Wait until the task runs to completion.
  var association = await getAssociationTask;
  return association;
}
```

You can retrieve a collection of associations which have previously been added through the platform by using the `Get(Criteria<AssociationFilter>)` method. This method accepts an instance of `Criteria<AssociationFilter>`, which contains the specifications necessary to retrieve the desired `Associations`.

The `Filter` property of the `Criteria<AssociationFilter>` class accepts an instance of the `AssociationFilter` class. This class contains the specifications that let you refine the result set of `Associations` that you want to retrieve.

The properties of the `AssociationFilter` include:

- `Group` – Assigning a value (`GroupId`) to this property refines a result set to Associations of the identified group
- `Resource` – Assigning a value (`Reference`) to this property refines a result set to Associations associated with the identified resource.

The specifications of the `AssociationFilter` may be applied in conjunction with one another. Each specification, assigned a value in the filter, further refines the result set (a logical AND).

The example below demonstrates retrieving a result page of Associations assigned to a group.

```csharp
IAssociationService associationService;
Group existingGroup;

// ...

var criteria = new Criteria<AssociationFilter> {
  Filter = new AssociationFilter {
    Group = existingGroup.Id
  }
};
var pageOfAssociations = associationService.Get(criteria);
```

In the above examples, the request to retrieve associations is invoked synchronously. An example of retrieving associations asynchronously using the asynchronous overload with C#'s async and await keywords is described below.

```csharp
private async Task<ResultPage<Association>> GetAssociationsAsync(IAssociationService associationService) {
  Group existingGroup;

  // ...
  var criteria = new Criteria<AssociationFilter> {
    Filter = new AssociationFilter {
      Group = existingGroup.Id
    }
  };
  var getAssociationTask = associationService.GetAsync(criteria);

  //Do other application specific work in parallel while the task executes.
  //....

  //Wait until the task runs to completion.
  var pageOfAssociations = await getAssociationTask;
  return pageOfAssociations;
}
```

The `AssociationSortFields` class exposes a set of fields upon which a result set of associations may be ordered. You can apply these fields to construct sorting rules associated with your criteria. These fields include:

- `AssociationSortFields.Group`
- `AssociationSortFields.Id`
- `AssociationSortFields.Resource`

The example below demonstrates how to apply sorting rules to order your result set by group ID in ascending order.

```csharp
var criteria = new Criteria<AssociationFilter> {
  // ...

  OrderBy = new List<SortInfo> {
    new SortInfo(AssociationSortFields.Group, true)
  }
}
```

For more details about criteria, including information on paging and sorting, see *Criteria* in [Discover the platform](docId\:NKmGS-fLtRQIYLEWZfb69).

## Extend associations with composites

An `Association` is a relationship between a resource and a group. You may need to associate additional information with associations to describe and shape them further. Continuing an earlier example, where a group represents an athletic team, an association may be leveraged to associate relevant documents (roster, scorecards, and so on) with the team. You might want to capture information describing or categorizing those documents.

Just as you can extend a `Group`With data from your design, an association may be extended by creating a composite. see *Composites* in [Discover the platform](docId\:NKmGS-fLtRQIYLEWZfb69).

### Add a composite association

To save a Composite Association, which you have defined, use the `Add<TExtension>(Association, TExtension)` method of the `IAssociationService`. This method accepts an instance of the Association class and an instance of `TExtension`. It returns an instance of `Composite\<Association,TExtension>`.

Consider the following class, which represents a sample of extension data:

```csharp
public class TeamDocument {
  public string Category {
    get;
    set;
  }
  public DateTime Modified {
    get;
    set;
  }
}
```

In the example below, an `Association` is composed with an instance of this extension class:

```csharp
IAssociationService associationService;
GroupId existingGroup;
Reference resource;

// ...
var association = new Association(resource, existingGroup);
var extension = new TeamDocument {
  Category = "Lineups",
    Modified = new DateTime(2016, 4, 1)
};
var compositeAssociation = associationService.Add(association, extension);
```

The above example invokes the request to add an association synchronously. An example of adding an association asynchronously using the asynchronous overload with C#'s async and await keywords is described below.

```csharp
private async Task<Composite<Association, AthleticTeam>> AddAssociationAsync(IAssociationService associationService) {
  // ...
  GroupId existingGroup;
  Reference resource;

  // ...
  var association = new Association(resource, existingGroup);
  var extension = new TeamDocument {
    Category = "Lineups",
      Modified = new DateTime(2016, 4, 1)
  };
  var addAssociationTask = associationService.AddAsync(association, extension);

  //Do other application specific work in parallel while the task executes.
  //....

  //Wait until the task runs to completion.
  var compositeAssociation = await addAssociationTask;
  return compositeAssociation;
}
```

### Update a composite association

To update a specific instance of a Composite Association, which was previously added through the platform, use the `Update<TExtension>(AssociationId, TExtension)` method of the `IAssociationService`. This method accepts an instance of the `AssociationId` class and an instance of `TExtension`. It returns an instance of `Composite\<Association,TExtension>`.

Consider the following class, which represents a sample of extension data:

```csharp
public class TeamDocument {
  public string Category {
    get;
    set;
  }
  public DateTime Modified {
    get;
    set;
  }
}
```

For this example, the `IAssociationService` must be properly implemented.

```csharp
IAssociationService associationService;

// ...

// Construct a associationId corresponding to the desired association
AssociationId existingAssociationId;

var newExtension = new TeamDocument {
  Category = "Roster Changes",
    Modified = DateTime.Now;
};
associationService.Update(existingAssociationId, newExtension);
```

The above example invokes the request to update an association synchronously. An example of updating an association asynchronously using the asynchronous overload with C#'s async and await keywords is described below.

```csharp
private async Task<Composite<Association, TeamDocument>> UpdateAssociationAsync(IAssociationService associationService) {
  // ...

  // Construct a associationId corresponding to the desired association
  AssociationId existingAssociationId;

  var newExtension = new TeamDocument {
    Category = "Roster Changes",
      Modified = DateTime.Now;
  };
  var updateGroupTask = associationService.UpdateAsync(existingAssociationId, newExtension);

  //Do other application specific work in parallel while the task executes.
  //....

  //Wait until the task runs to completion.
  var updatedAssociation = await updateAssociationTask;
  return updatedAssociation;
}
```

### Retrieve a composite association

To retrieve a specific instance of a Composite Association, which has previously been added through the platform, use the `Get<TExtension>(AssociationId)` method. This method accepts an instance of the `AssociationId` class, which identifies the `Association` to retrieve. It returns the instance of the `Composite\<Association,TExtension>` class corresponding to that identifier.

```csharp
IAssociationService associationService;

//...

// Construct a AssociationId corresponding to the desired association
var associationId = AssociationId.Create("...");
var compositeAssociation = associationService.Get<MyAssociationExtension>(associationId);
```

If a Composite Association with the specified ID and extension type cannot be found, an `AssociationDoesNotExistException` is thrown.

The above example invokes the request to retrieve an association synchronously. An example of retrieving an association asynchronously using the asynchronous overload with C#'s async and await keywords is described below.

```csharp
private async Task<Composite<Association, MyAssociationExtension>> GetAssociationAsync(IAssociationService associationService) {
  //...

  // Construct a AssociationId corresponding to the desired group
  var associationId = AssociationId.Create("...");
  var getAssociationTask = associationService.GetAsync<MyAssociationExtension>(associationId);

  //Do other application specific work in parallel while the task executes.
  //....

  //Wait until the task runs to completion.
  var compositeAssociation = await getAssociationTask;
  return compositeAssociation;
}
```

To retrieve a collection of Composite Associations, which were previously added through the platform, use the `Get<TExtension>(CompositeCriteria\<AssociationFilter,TExtension>)` method. This method accepts an instance of `CompositeCriteria\<AssociationFilter,TExtension>`, which contains the specifications necessary to retrieve the desired Associations.

The Filter property of the `CompositeCriteria\<AssociationFilter,TExtension>` class accepts an instance of the `AssociationFilter` class. This class contains the specifications that let you refine the result set of Associations you want to retrieve.

The `ExtensionFilter` property of the `CompositeCriteria\<AssociationFilter,TExtension>` class accepts a `FilterExpression` that lets you specify a Boolean expression to refine the result set further by values represented in your extension data. For information on this type of filter, see *Composite criteria* and *Filtering composites* in [Discover the platform](docId\:NKmGS-fLtRQIYLEWZfb69).

Consider the following class, which represents a sample of extension data:

```csharp
public class TeamDocument {
  public string Category {
    get;
    set;
  }
  public DateTime Modified {
    get;
    set;
  }
}
```

In the example below, a page of Associations composed with TeamDocument is retrieved, where the document is categorized as a lineup:

```csharp
IAssociationService associationService;

// ...
var criteria = new CompositeCriteria<AssociationFilter,
  TeamDocument> {
    ExtensionFilter = FilterExpressionBuilder<TeamDocument> .Field(e => e.Category).EqualTo("Lineups")
  };
var pageOfAssociations = associationService.Get(criteria);
```

In the above example, the request to retrieve associations is invoked synchronously. An example of retrieving associations asynchronously using the asynchronous overload with C#'s async and await keywords is described below.

```csharp
private async Task<ResultPage<Composite<Association, TeamDocument>>> GetAssociationsAsync(IAssociationService associationService) {
  // ...
  var criteria = new CompositeCriteria<AssociationFilter,
    TeamDocument> {
      ExtensionFilter = FilterExpressionBuilder<TeamDocument> .Field(e => e.Category).EqualTo("Lineups")
    };
  var getAssociationTask = associationService.GetAsync(criteria);

  //Do other application specific work in parallel while the task executes.
  //....

  //Wait until the task runs to completion.
  var pageOfAssociations = await getAssociationTask;
  return pageOfAssociations;
}
```

The fields of your extension data may also be applied to sort your result set. The example below demonstrates the use of an extension data field in defining a sorting rule.

```csharp
var criteria = new CompositeCriteria<AssociationFilter, TeamDocument>
  {
    // ...
    OrderBy = new List<SortInfo>
      {
        new SortInfo(new SortField(FilterExpressionBuilder<TeamDocument>.Field(e => e.Modified)), false)
      }
  };
```

For details about criteria, including information on paging and sorting, see *Criteria* in [Discover the platform](docId\:NKmGS-fLtRQIYLEWZfb69).
