Introduction
I imagine most of us having used Tridion’s Core Service for performing various tasks at one point or at least having heard of it. For those that have not, as a short introduction, the Core Service is a SOAP based webservice that is used to programmatically interact with the CM (externally). The Core Service is very versatile and every action that can be done in the CME can also be done via code, or to be more precise, using code you can do even more (actions which are unavailable from the UI). It’s a mature part of the system, the API is easy to understand + there are a lot of resources around to get you started. I advise you to embrace it as soon as possible in order to have a very powerful tool at your disposal.
History
Tridion’s Core Service first appeared in the 2011 version, but the general idea of an external integration point was present far longer. The predecessor of Core Service was the Business Connector and the oldest reference of it I could dig up was is the Tridion R5 release. The mechanism was somewhat different as stated in the official documentation:
Replacing the Business Connector as an integration API is the Core Service. The underlying technologies between these two interfaces are quite different: the Business Connector uses SOAP to interact with an XMLResponder, whereas the Core Service exposes Content Manager functionality through a Web service
Core Service methods
As stated in the introduction, there are a lot of resources out there related to Core Service, but those that deal with async are almost impossible to find. It’s not just the resources, 99% of all the implementations out there use the “regular” (synchronous) methods. Because of this, I have decided to write this article to shed some light on how to use them.
You might have noticed that there are async variants of all the methods (as hinted by their names). See the following image for example, Intellisense lists additional members for the Create()
method. The client is an instance of the SessionAwareCoreServiceClient
class from the pre-built Tridion.ContentManager.CoreService.Client.dll
.
What’s confusing (or frightening) for people is this list of 5 members (4 methods and 1 event) for a single action, so they end up sticking to the synchronous one, even though it would make sense to use an async variant in some cases. The confusing part is that this list actually contains two sets of members for async operations. The image below illustrates which of them are related.
Create members grouped together
-
The red rectangle represents the synchronous action, which is a single method
-
The green rectangle represents an asynchronous action, which consists of 2 members, a method and an event
-
The blue rectangle represents an asynchronous action, which consists of 2 methods
Below are snippets of code for creating a component with each of these 3 approaches (the full code can be found at the end of this article). In each of them an existing component is read and a new component is created based off of it. I’ve done this so I can recycle the component’s content, locational info, etc., I only need to change the tcm to Tcm null (new items don’t have an id yet) and give it a unique name (because it is being created in the same folder).
Synchronous approach
No explanation is really needed. The created component is available as the return value of the method:
public IdentifiableObjectData Create(IdentifiableObjectData data, ReadOptions readBackOptions)
Example:
private void CreateComponentSynchronously() { ComponentData componentData = (ComponentData)Client.Read("tcm:1047-5502", readOptions); componentData.Id = "tcm:0-0-0"; componentData.Title = DateTime.Now.Ticks.ToString(); IdentifiableObjectData createdComponent = (ComponentData)Client.Create(componentData, readOptions); }Asynchronous approach #1
This approach uses the combination of the following members:
- public void CreateAsync(IdentifiableObjectData data, ReadOptions readBackOptions, object userState)
- public event EventHandler<CreateCompletedEventArgs> CreateCompleted
This approach is known as the Event-based Asynchronous Pattern (EAP), you can read more about it here. The created component is available in the event handler which is executed after the component creation is finished.
Example:
private void CreateComponentASynchronously_EAP() { ComponentData componentData = (ComponentData)Client.Read("tcm:1047-5502", readOptions); componentData.Id = "tcm:0-0-0"; componentData.Title = DateTime.Now.Ticks.ToString(); Client.CreateCompleted += OnComponentCreated; Client.CreateAsync(componentData, readOptions); } private void OnComponentCreated(object sender, CreateCompletedEventArgs e) { IdentifiableObjectData createdComponent = (ComponentData)e.Result; }
Asynchronous approach #2
This approach uses the combination of the following members:
public IAsyncResult BeginCreate(IdentifiableObjectData data, ReadOptions readBackOptions, AsyncCallback callback, object asyncState)
IdentifiableObjectData EndCreate(IAsyncResult result)
This approach is known as the Asynchronous Programming Model (APM) pattern (also called the IAsyncResult pattern) and you can read more about it here. The AsyncCallback callback
is automatically called once the action has finished so it’s the perfect place to call the EndCreate()
method whose return value is the created component. The object asyncState
is used to differentiate between multiple instances of the callback. This object is available as a property of the IAsyncResult result
.
Note that this is one of the approaches, you could also take a different one called polling (also described in the link above).
Example:
private void CreateComponentASynchronously_APM() { ComponentData componentData = (ComponentData)Client.Read("tcm:1047-5502", readOptions); componentData.Id = "tcm:0-0-0"; componentData.Title = DateTime.Now.Ticks.ToString(); Client.BeginCreate(componentData, readOptions, new AsyncCallback(ComponentCreatedCallback), Guid.NewGuid()); } private void ComponentCreatedCallback(IAsyncResult ar) { IdentifiableObjectData createdComponent = Client.EndCreate(ar); }Final words
So, there you have it, as you can see using the async methods is not at all that difficult, the potentially confusing part is the presence of 2 async approaches. Do note, however, that both of these approaches (APM and EAP) are deprecated in favor of the Task-based Asynchronous Pattern (TAP), which uses a single method to represent the initiation and completion of an asynchronous operation. You have surely seen async
and await
which are part of TAP introduced in .NET Framework 4. Unfortunately, TAP is not yet available in the pre-built Core Service client dll, but hopefully it will be added at one point.
If you have any question, please feel free to get in touch.
Appendix
Full example:
public void CreateComponents() { CreateComponentSynchronously(); CreateComponentASynchronously_APM(); CreateComponentASynchronously_EAP(); } private void CreateComponentSynchronously() { ComponentData componentData = (ComponentData)Client.Read("tcm:1047-5502", readOptions); componentData.Id = "tcm:0-0-0"; componentData.Title = DateTime.Now.Ticks.ToString(); IdentifiableObjectData createdComponent = (ComponentData)Client.Create(componentData, readOptions); } //Asynchronous Programming Model (APM) pattern (also called the IAsyncResult pattern) private void CreateComponentASynchronously_APM() { ComponentData componentData = (ComponentData)Client.Read("tcm:1047-5502", readOptions); componentData.Id = "tcm:0-0-0"; componentData.Title = DateTime.Now.Ticks.ToString(); Client.BeginCreate(componentData, readOptions, new AsyncCallback(ComponentCreatedCallback), Guid.NewGuid()); } private void ComponentCreatedCallback(IAsyncResult ar) { IdentifiableObjectData createdComponent = Client.EndCreate(ar); } //Event-based Asynchronous Pattern (EAP) private void CreateComponentASynchronously_EAP() { ComponentData componentData = (ComponentData)Client.Read("tcm:1047-5502", readOptions); componentData.Id = "tcm:0-0-0"; componentData.Title = DateTime.Now.Ticks.ToString(); Client.CreateCompleted += OnComponentCreated; Client.CreateAsync(componentData, readOptions); } private void OnComponentCreated(object sender, CreateCompletedEventArgs e) { IdentifiableObjectData createdComponent = (ComponentData)e.Result; }