Skip navigation

My last post in this series consisted of a code snippet to Eloqua API How To: Query a Contact by Email Address.

 

In this post, I will be showing how to retrieve Metadata for an Entity, in this case, a Contact.

 

Once again, I will be assuming that the code found Connecting to the Eloqua API - Generic Setup is being used to establish a connection to the Eloqua API.

 

For this example, we will once again be using the EloquaService API.  For more information on which API to use for specific functions, see my post Eloqua SOAP API - Which one do I use?.

 

Now for the code...

 

    class MetaDataRequest
    {

         static void Main()
          {


            String entity = "contact";

 

            try
            {
               

               //Create the service instance using your credentials

               EloquaInstance service = new EloquaInstance("instance", "userid", "password");

               

               // Execute a request to list all Entity Types
                ListEntityTypesResult entityTypesResults = service.ServiceProxy.ListEntityTypes();

               

               // Extract the name of each Entity Type
                foreach (String type in entityTypesResults.EntityTypes)
                {
                    Console.WriteLine(type);
                }

               

                // Contact is a member of the Base Entity Type
                // Execute a request to describe the Base Entity Type
                DescribeEntityTypeResult typeResult = service.ServiceProxy.DescribeEntityType("Base");

               

               // Instantiate a new Entity Type object
                EntityType entityType = new EntityType();

              

                // Extract the ID, Name and Type of each Entity Type
                foreach (EntityType entityTypeResult in typeResult.EntityTypes)
                {
                    if (String.Compare(entity, entityTypeResult.Name, true) == 0)
                    {
                        entityType.ID = entityTypeResult.ID;
                        entityType.Name = entityTypeResult.Name;
                        entityType.Type = entityTypeResult.Type;
                        break;
                    }
                }

                // Execute a request to describe the Contact Entity
                DescribeEntityResult result = service.ServiceProxy.DescribeEntity(entityType);

              

                // Extract the Entity's Properties
                Console.WriteLine(String.Format("Is Createable: {0}", result.IsCreateable));
                Console.WriteLine(String.Format("Is Deletable: {0}", result.IsDeletable));
                Console.WriteLine(String.Format("Is Queryable: {0}", result.IsQueryable));
                Console.WriteLine(String.Format("Is Retrievable: {0}", result.IsRetrievable));
                Console.WriteLine(String.Format("Is Updateable: {0}", result.IsUpdateable));

              

                // Extract the Entity's Field Metadata
                foreach (DynamicEntityFieldDefinition fieldDef in result.Fields)
                {
                    Console.WriteLine(String.Format("Data Type: {0}", fieldDef.DataType));
                    Console.WriteLine(String.Format("Default Value: {0}", fieldDef.DefaultValue));
                    Console.WriteLine(String.Format("Display Name: {0}", fieldDef.DisplayName));
                    Console.WriteLine(String.Format("Internal Name: {0}", fieldDef.InternalName));
                    Console.WriteLine(String.Format("Is Custom: {0}", fieldDef.IsCustom));
                    Console.WriteLine(String.Format("Is Required: {0}", fieldDef.IsRequired));
                    Console.WriteLine(String.Format("Is Writeable: {0}", fieldDef.IsWriteable));
                    Console.WriteLine(String.Format("Length: {0}", fieldDef.Length));
                }

            

            }
           

            // Customize your own error handling code
            catch (System.ServiceModel.FaultException ex)
            {
                // Catch Service Model Fault Exceptions
                Console.WriteLine(String.Format("Reason: {0}", ex.Reason));
                Console.WriteLine(String.Format("Fault Type: {0}", ex.GetType()));
                Console.WriteLine(String.Format("Fault Code: {0}", ex.Code.Name));
            }
            catch (Exception ex)
            {
                // Catch System Exceptions
                Console.WriteLine(String.Format("Exception Message: {0}", ex.Message));
            }

                 // Wait for user input before stepping out.
                 Console.WriteLine("Press any key to continue.");
                 Console.ReadKey();
             }
         } 

    }

 

 

 

When the program is run, a console window will pop up showing the results of the query something like below.

 

Capture.PNG

Have you been looking into building an application that uses the Eloqua API?

 

If so, have you ever asked yourself:

 

I was told that Eloqua has several different APIs, which one do I use?

 

or

 

Which WSDL do I need to reference in my code to accomplish what I am trying to do?

 

Well, the table below outlines the 4 Eloqua APIs, along with their WSDL location, and general uses.

 

Eloqua API NameWSDL LocationUses
EloquaService

https://secure.eloqua.com/API/1.2/Service.svc?wsdl

This is the Standard API. It can be used to:

 

  • Perform CRUD operations on an Asset.
  • Perform CRUD operations on Entity.
  • Perform Asset Metadata operations.
  • Perform Entity Metadata operations
  • Perform Group Membership operations (Add, Remove, or List members) for Contact, Prospect, or Company Groups.
EloquaDataTransferService

https://secure.eloqua.com/API/1.2/DataTransferService.svc?wsdl

  • Updating of large numbers of records within Eloqua in a scalable fashion.
  • Bulk transfer of data from Eloqua to a streamlined file for download and manipulation (the data transfer service is the preferred method of transferring records if the number of records will regularly exceed 50000 records).
  • Export any saved report in the system that returns a list of Contact, Prospect or Company entity records.
  • Export of static reports that contain contact activity data.
  • Bulk transfer of flat files (this particular API endpoint has been optimized for transmitting binary files).

 

NOTE: The Bulk API has now replaced the DTS.  See here Eloqua Bulk API 1.0: Importing and Exporting Data for details.

 

Click Here for more details

EloquaEmailService

https://secure.eloqua.com/API/1.2/EmailService.svc?wsdl

  • Create HTML and Text email assets and related settings, such as the Subject line and the From and Reply-To display names and addresses.
  • Find existing associated email objects in the application for a given email. This includes redirect links, email field merges, email headers and footers, Activity Driven Content (ADC) rules, Signature Layouts, Email Groups, and encodings used.
  • Search for, upload, or create images or redirect links,
  • Create and schedule email batches. This allows third-party applications to use Eloqua's email engine to send email blasts to recipients. Eloqua will still enforce CAN-SPAM compliance and observe subscriber preferences, as set in the tool, even when the email send is initiated in another application. You can create an email batch (with a specified schedule and signature), send to a single recipient, or check on the status of an email batch deployment.

 

Click Here for more details

ExternalActionServicehttps://secure.eloqua.com/api/1.2/ExternalActionService.svc?wsdl

Building Cloud Connectors

 

This is just a general outline, but it should at least get you on the right track.

 

For more information, go here and read the detailed API documentation.

So, you've read the Where can I find the documentation for the Eloqua API, have been granted access, and gottenConnecting to the Eloqua API - Generic Setup to the Eloqua Web Services API via code. 

 

Now it's time to play!

 

This series of How To posts will focus on code snippits that show examples of what can be done using the API. 

 

Let's start with something basic...

 

The code below will illustrate how to query a Contact by e-mail address, and list some details of the returned record.

 

Note: we will assume that the code from Connecting to the Eloqua API - Generic Setup is also being used.

 

 

    class RetrieveContactByEmail
    {
        static void Main()
        {
            const string searchQuery = "C_EmailAddress='tj.fields@eloqua.com'";

            try
            {
               //Create the service instance using your credentials

               EloquaInstance service = new EloquaInstance("instance", "userid", "password");
               
                // Instantiate a Contact Entity Type
                EntityType contactEntityType = new EntityType
                                            {
                                                ID = 0,
                                                Name = "Contact",
                                                Type = "Base"
                                            };

                // Create a new list containing the fields you want populated
                List<string> fieldList = new List<string> { "C_EmailAddress", "C_FirstName", "C_LastName" };
             
                // Define a container for the Query results
                DynamicEntityQueryResults queryResult;

 

                // Set the page number and size for the results
                const int currentPage = 1;
                const int pageSize = 20;

 

                // If the field list is empty - the request will return all Entity Fields
                // Otherwise, only fields defined in the field list are returned
                if (fieldList.Count == 0)
                {
                    // Execute the request and return all of the Entity's fields
                    queryResult = service.ServiceProxy.Query(contactEntityType, searchQuery, null, currentPage, pageSize);
                }
                else
                {
                    // Execute the request and return only the selected fields
                    queryResult = service.ServiceProxy.Query(contactEntityType, searchQuery, fieldList.ToArray(), currentPage, pageSize);
                }

 

                if (queryResult.Entities.Length > 0)
                {
                    //Extract the total number of pages and records
                    Console.WriteLine(String.Format("Total number of pages: {0}", queryResult.TotalPages));
                    Console.WriteLine(String.Format("Total number of records: {0}", queryResult.TotalRecords));

                   

                    // Extract each Dynamic Entity in the result
                    foreach (DynamicEntity dynamicEntity in queryResult.Entities)
                    {
                       
                        // Extract the field name and value of each field in the collection
                        foreach (KeyValuePair<string, string> field in dynamicEntity.FieldValueCollection)
                        {
                            Console.WriteLine(String.Format("Field Name: {0}: {1}", field.Key, field.Value));
                        }
                    }
                }
            }

 

            // Customize your own error handling code
            catch (System.ServiceModel.FaultException ex)
            {
                // Catch Service Model Fault Exceptions
                Console.WriteLine(String.Format("Reason: {0}", ex.Reason));
                Console.WriteLine(String.Format("Fault Type: {0}", ex.GetType()));
                Console.WriteLine(String.Format("Fault Code: {0}", ex.Code.Name));
            }
            catch (Exception ex)
            {
                // Catch System Exceptions
                Console.WriteLine(String.Format("Exception Message: {0}", ex.Message));
            }

            // Wait for user input before stepping out.
            Console.WriteLine("Press any key to continue.");
            Console.ReadKey();
        }
    }

 

When the program is run, a console window will pop up showing the results of the query as seen below.

 

Capture.PNG

This is of course a very simple example, but it shows how easy it is to get going with the Eloqua API.

 

A great intro to using the WS API with Java can be found Using Eloqua WS API with Java - Part 1.

Whether your application creates new contacts or just manipulates existing ones, contacts and groups are a core part of Eloqua’s marketing automation capabilities, and you will likely find yourself adding contacts to groups regularly.  This is quite a simple process using the Eloqua API.  To do so, you’ll need the ContactGroupID, which you can retrieve with a DescribeAssetType call.  We'll use the code for the standard setup for the Eloqua API that we looked at earlier.

 

Once you have this ID, to add members to the group is simple. For each contact, create a new DynamicEntity, of type Contact, with the ID of the contact you want to add.  Then, create a DynamicAsset of type ContactGroup, with the ID of the contact group you would like to add the contact to.  Then, call AddGroupMember to add the DynamicEntity to the DynamicAsset, and your contact is added to the group.

 

 

        public List<EloquaContact> ImportContactsIntoGroup(List<EloquaContact> tmpContacts, int intContactGroupID)

        {

            if (intContactGroupID > 0)

            {

                foreach (EloquaContact tmpContact in tmpContacts)

                {

                    if (tmpContact.ContactID != 0)

                    {

                        try

                        {

 

                            // Execute the request

                            EloquaServiceNew.EntityType entityType = new EloquaServiceNew.EntityType();

 

                            entityType.ID = 0;

                            entityType.Name = "Contact";

                            entityType.Type = "Base";

 

                            // Instantiate a new Dynamic Entity of type Contact and supply the EntityID

                            EloquaServiceNew.DynamicEntity entity = new EloquaServiceNew.DynamicEntity { EntityType = entityType, Id = tmpContact.ContactID };

 

                            // Build a Contact Group Asset Type object

                            EloquaServiceNew.AssetType assetType = new EloquaServiceNew.AssetType();

 

                            assetType.ID = 0;

                            assetType.Type = "ContactGroup";

                            assetType.Name = "ContactGroupName";

 

                            // Instantiate a new Dynamic Asset of type ContactGroup and supply the AssetID

                            EloquaServiceNew.DynamicAsset asset = new EloquaServiceNew.DynamicAsset { AssetType = assetType, Id = intContactGroupID };

 

                            // Execute the request

                            EloquaServiceNew.GroupMemberResult result = serviceProxy.AddGroupMember(entity, asset);

 

                            if (result.Success)

                            {

                            }

                            else

                            {

                                foreach (EloquaServiceNew.Error error in result.Errors)

                                {

                                    //Console.WriteLine(error.ErrorCode);

                                    //Console.WriteLine(error.Message);

                                }

                            }

                        }

                        catch (Exception ex)

                        {

                            Trace.TraceWarning(String.Format("Exception in EloquaInstance:ImportContactsIntoGroup {0} :: {1}", ex.Message, ex.InnerException), "Exception");

                            throw;

                        }

                    }

                }

            }

            return tmpContacts;

        }

In building any application against the Eloqua API, whether for a Cloud Connector, or for another application type, one of the most common things you’ll need to do is to map the fields which you’re going to store data in or read data from.  Whether for contact fields, prospect fields, company fields, or data card fields, you’ll find yourself performing this task frequently.

 

In this sample code snippet, which uses the same general setup for the API as we have used for many other code snippets, we’ll look at populating a dropdown list box with the fields on a contact record.

 

The first function that we’ll use is ListContactFields, which queries the API and retrieves a list of the fields defined for the Base type of “Contact” using the DescribeEntity call.  The field definition returns the following values:

 

DisplayName – the name presented to the Eloqua user (ie “First Name”)

InternalName – a system name, that is used for field querying or updating (ie “C_FirstName”)

IsRequired – a Boolean value for whether the field is required and a create or update will fail if it is left blank

IsWriteable – a Boolean value for whether the field can be written to, or is a system field

DataType – the type (Number, Date, String, etc) of the field

 

 

        public List<string[]> ListContactFields()

        {

 

            if (this.ContactFieldList.Count() > 0)

            {

                return this.ContactFieldList;

            }

            else

            {

                string[] tmpFields;

                List<string[]> FieldList = new List<string[]>();

                try

                {

                    // Execute the request

                    EloquaServiceNew.EntityType entityType = new EloquaServiceNew.EntityType();

 

                    entityType.ID = 0;

                    entityType.Name = "Contact";

                    entityType.Type = "Base";

 

                    // Execute the request

 

                    if (this.OkayForNextEloquaAPICall(APICallType.DescribeEntity))

                    {   //pauses thread until 1 second has elapsed

                    }

                    EloquaServiceNew.DescribeEntityResult result = serviceProxy.DescribeEntity(entityType);

 

                    // Extract the ID, Name and Type of each Asset Type

 

                    foreach (EloquaServiceNew.DynamicEntityFieldDefinition fieldDef in result.Fields)

                    {

                        tmpFields = new string[5];

                        tmpFields[0] = fieldDef.DisplayName;

                        tmpFields[1] = fieldDef.InternalName;

                        tmpFields[2] = Convert.ToString(fieldDef.IsRequired);

                        tmpFields[3] = Convert.ToString(fieldDef.IsWriteable);

                        tmpFields[4] = Convert.ToString(fieldDef.DataType);

 

                        FieldList.Add(tmpFields);

                    }

                    CPMFieldListComparer flc = new CPMFieldListComparer();

 

                    FieldList.Sort(18, FieldList.Count - 18, flc);

 

                }

                catch (Exception ex)

                {

                    Trace.TraceWarning(String.Format("Exception in EloquaInstance:ListContactFields {0} :: {1}", ex.Message, ex.InnerException), "Exception");

 

                }

                this.ContactFieldList = FieldList;

                return FieldList;

            }

        }

 

Two things to note about this.  First, the EloquaInstance object definition has a ContactFieldList property added so that the retrieved set of fields can be saved if multiple fields are being mapped in one interface.  This is a very common usage pattern, and you may want to consider this.

 

Second, the fields are sorted using a specific Compare function that allows them to be sorted only from the 18th value forward.  If you notice, the typical Eloqua Contact Field dropdown has standard fields like email address, first name, last name, and address at the top, and then an alphabetically sorted list of fields below that.  To do this, we use:

 

    CPMFieldListComparer flc = new CPMFieldListComparer();

 

    FieldList.Sort(18, FieldList.Count - 18, flc);

 

 

    public class CPMFieldListComparer : IComparer<string[]>

    {

        public int Compare(string[] xField, string[] yField)

        {

            return string.Compare(xField[0], yField[0]);

        }

    }

 

With this List of string[] values returned, we can then begin to populate the dropdown list for our application.   This follows a normal pattern, but looks carefully at whether the field is read only or not, whether the field will be used to read from or write to, and what data type the field is.  Eloqua fields will return the following as data types:

 

Text: up to 100 chars

LargeText: 4000 chars

Number: whole number

Numeric: number with a decimal place

Date: date and time value

 

Looking at these values, the dropdown list can be populated with contact field values and the selected value chosen, allowing the user to quickly select which fields they would like to read contact data from or write contact data to.  Note that this is not always a simple one-to-one map, as date values can be written into string fields, and numeric values can be read into string variables, but not vice versa.

 

        public void PopulateContactFieldDropdown(DropDownList ddlTarget, string strValue, string strFieldType, bool blnReadOnly)

        {

            string strEloquaDataType = "";

            if (ddlTarget != null)

            {

                List<string[]> tmpContactFields = this.ListContactFields();

                if (tmpContactFields == null || tmpContactFields.Count == 0)

                {

                    ddlTarget.Items.Clear();

 

                    ddlTarget.Items.Add(new ListItem("(input Eloqua credentials and save)", "-1"));

 

                }

                else

                {

                    ddlTarget.Items.Clear();

 

                    ddlTarget.Items.Add(new ListItem("(please select field)", "-1"));

                    foreach (string[] tmpField in tmpContactFields)

                    {

                        strEloquaDataType = tmpField[4];

                        if ((tmpField[3] == "True") || blnReadOnly) //is writeable or field is read only

                        {

                            if (blnReadOnly)

                            {

                                switch (strFieldType)

                                {

                                    case "string":

                                        ddlTarget.Items.Add(new ListItem(tmpField[0], tmpField[1]));

                                        break;

                                    case "int":

                                        if ((strEloquaDataType == "Number") || (strEloquaDataType == "Numeric"))

                                            ddlTarget.Items.Add(new ListItem(tmpField[0], tmpField[1]));

                                        break;

                                    case "date":

                                        if ((strEloquaDataType == "Date"))

                                            ddlTarget.Items.Add(new ListItem(tmpField[0], tmpField[1]));

                                        break;

                                }

                            }

                            else

                            {

                                switch (strFieldType)

                                {

                                    case "string":

                                        if ((strEloquaDataType == "Text") || (strEloquaDataType == "LargeText"))

                                            ddlTarget.Items.Add(new ListItem(tmpField[0], tmpField[1]));

                                        break;

                                    case "int":

                                        if ((strEloquaDataType == "Numeric") || (strEloquaDataType == "Number") || (strEloquaDataType == "Text"))

                                            ddlTarget.Items.Add(new ListItem(tmpField[0], tmpField[1]));

                                        break;

                                    case "date":

                                        if ((strEloquaDataType == "Date") || (strEloquaDataType == "Text"))

                                            ddlTarget.Items.Add(new ListItem(tmpField[0], tmpField[1]));

                                        break;

                                }

                            }

                        }

                    }

                   if (ddlTarget.Items.FindByValue(strValue) != null)

                        ddlTarget.Items.FindByValue(strValue).Selected = true;

                }

            }

        }

The ExternalActionService that is used for Cloud Connectors to allow access to the contacts in a marketing automation program does a great job of retrieving and managing the status on contacts, companies, and prospects in a program step.  However, as it does that, it is only referencing those members by an id such as a contact id.  To do anything of interest with those Contacts, you will likely want to flesh them out with the rest of the data that is known on them as contacts.

 

To do this, the code snippet we’ll look at queries the standard Eloqua API, using the generic setup code we looked at earlier, passes in a set of fields to populate, and returns with a set of values for those fields.

 

 

        public List<EloquaContact> GetContactsFullDetail(List<EloquaContact> tmpContacts)

        {

            int intTotalContacts;

            int intCurrentContactCount;

            int intCurrentBatchSize;

            int intRetrieveBatchSize = 50;

            int intCurrentContactIndex = 0;

            int intCurrentArrayIndex = 0;

 

            int internalErrorCount = 0;

 

            bool blnResetNullContacts = false;

 

            List<EloquaContact> tmpFullContactList = new List<EloquaContact>();

 

            EloquaContact tmpContact;

 

            try

            {

                // Build a Contact Entity Type object

                EloquaServiceNew.EntityType entityType = new EloquaServiceNew.EntityType();

 

                entityType.ID = 0;

                entityType.Name = "Contact";

                entityType.Type = "Base";

 

 

                // Create a new list containing the fields you want populated

 

                List<string> fieldList = new List<string>();

                try

                {

                    fieldList = PopulateFieldList(AutomatedStep.EloquaBaseType.Contact);

                }

                catch (Exception ex)

                {

                    Trace.TraceWarning(String.Format("Exception in EloquaInstance:GetContactsFullDetail - populate field list {0} :: {1} :: {2} ", ex.Message, ex.InnerException, strInstanceName), "Exception");

                    throw;

                }

                // Build a Dynamic Entity array to store the results

 

                EloquaServiceNew.DynamicEntity[] retrievedEntities;

 

                // Only fields defined in the field list are returned

                strErrorDebug += "Beginning batch ";

 

                if (fieldList.Count > 0)

                {

 

                    // Execute the request and return only the selected Entity fields

                    // Set the ID of the Contact Entity

                    intTotalContacts = tmpContacts.Count();

                    intCurrentContactCount = 0;

 

 

                    int[] ids;

                    intCurrentContactCount = 0;

                    intCurrentBatchSize = Math.Min((intTotalContacts - intCurrentContactCount), intRetrieveBatchSize);

                    strErrorDebug += "While loop ";

                    while (intCurrentBatchSize > 0)

                    {

 

                        ids = new int[intCurrentBatchSize];

                        intCurrentArrayIndex = 0;

                        try{

                            for (intCurrentContactIndex = intCurrentContactCount; intCurrentContactIndex < (intCurrentContactCount + intCurrentBatchSize); intCurrentContactIndex++)

                            {

                                ids[intCurrentArrayIndex] = tmpContacts.ElementAt(intCurrentContactIndex).ContactID;

                                intCurrentArrayIndex++;

                            }

                        }

                        catch (Exception ex)

                        {

                            Trace.TraceWarning(String.Format("Exception in EloquaInstance:GetContactsFullDetail populate array {0} :: {1} :: {2}  ", ex.Message, ex.InnerException, strInstanceName), "Exception");

                            throw;

                        }

                        try

                        {

                            retrievedEntities = serviceProxy.Retrieve(entityType, ids, fieldList.ToArray());

                        }

                        catch (Exception ex)

                        {

                            Trace.TraceWarning(String.Format("Exception in EloquaInstance:GetContactsFullDetail retrieve {0} :: {1} :: {2}  ", ex.Message, ex.InnerException, strInstanceName), "Exception");

                            throw;

                        }

 

                        if (retrievedEntities.Length > 0)

                        {

                            int intIDCount = 0;

                            foreach (EloquaServiceNew.DynamicEntity dynamicEntity in retrievedEntities)

                            {

                                if (dynamicEntity != null)

                                {

                                    try

                                    {

                                        tmpContact = new EloquaContact();

                                        tmpContact.ContactID = dynamicEntity.Id;

                                        PopulateContactFromResult(tmpContact, dynamicEntity.FieldValueCollection);

                                        tmpContact.ExternalActionID = tmpContacts.Find(delegate(EloquaContact ec) { return ec.ContactID == tmpContact.ContactID; }).ExternalActionID;

                                        tmpFullContactList.Add(tmpContact);

                                    }

                                    catch (Exception ex)

                                    {

                                        Trace.TraceWarning(String.Format("Exception in EloquaInstance:GetContactsFullDetail populate contact {0} :: {1} :: {2}  ", ex.Message, ex.InnerException, strInstanceName), "Exception");

                                        internalErrorCount++;

                                        if (internalErrorCount > 4)

                                            throw;

                                    }

                                }

                                else

                                {

                                    blnResetNullContacts = true;

                                }

                                intIDCount++;

                            }

                        }

 

                        intCurrentContactCount += intCurrentBatchSize;

                        intCurrentBatchSize = Math.Min((intTotalContacts - intCurrentContactCount), intRetrieveBatchSize);

                    }//end batch retrieve while loop

 

 

                }

 

            }

            catch (Exception ex)

            {

                Trace.TraceWarning(String.Format("Exception in EloquaInstance:GetContactsFullDetail {0} :: {1} :: {2} ", ex.Message, ex.InnerException, strInstanceName), "Exception");

                throw;

            }

 

            if (blnResetNullContacts)

            {

                //handle null contacts in the step

            }

 

            return tmpFullContactList;

        }

 

 

 

This code snippet calls PopulateFieldList to create a list of fields (which will ultimately be passed to the API as an array of strings). You’ll want to implement your own logic here to define the set of fields (by their internal name – you’ll see this when you list the contact fields via the API, but it is not displayed to the user) that you want to retrieve.  The values for the fields will generally look like “C_EmailAddress”, or “C_Custom_Field1”.  Based on the field mappings that have been provided by the users of your application, you will have this list of fields available and can return it here in the PopulateFieldList function you implement.

 

The other function that is seen here that you’ll want to implement your own version of is:

PopulateContactFromResult(tmpContact, dynamicEntity.FieldValueCollection);

 

This takes a contact object that we have defined for our own use, and a dynamicEntity.FieldValueCollection, and parses through those to populate the Contact.  In this example, we have fields for FirstName, LastName, and Company on the EloquaContact object definition:

 

    public class EloquaContact

    {

        public int ContactID { get; set; }

 

        public string EmailAddress { get; set; }

        public int ExternalActionID { get; set; }

       public string FirstName { get; set; }

       public string LastName { get; set; }

       public string Company { get; set; }

 

 

        public EloquaContact()

        {

            ContactID = 0;

            EmailAddress = "";

            ExternalActionID = 0;

        }

   }

 

 

We pass these into the PopulateContactFromResult function to parse the FieldValueCollection to look for the right fields to grab values from.  In this example, we use a custom function called GetFieldNameValues to return a set of values and fields to populate.  This would return a dictionary that looked like the following:

 

“FirstName”, “C_FirstName”

“LastName”, “C_LastName”

“Company”, “C_CompanyName1”

 

 

    private void PopulateContactFromResult(EloquaContact tmpContact, IEnumerable<KeyValuePair<string, string>> FieldValueCollection)

        {

            string strCurrentField = "";

            string strEmailAddressField = "";

 

            try

            {

                Dictionary<string, string> dctFieldNameValues = GetFieldNameValues();

 

                foreach (KeyValuePair<string, string> keyValPair in FieldValueCollection)

                {

                    if (dctFieldNameValues.ContainsValue(keyValPair.Key))

                    {

                        strCurrentField = dctFieldNameValues.First(delegate(KeyValuePair<string, string> x) { return x.Value == keyValPair.Key; }).Key;

                        tmpContact.SetFieldValue(strCurrentField, keyValPair.Value);

                    }

                    if (keyValPair.Key == strEmailAddressField)

                        tmpContact.EmailAddress = keyValPair.Value;

                }

            }

            catch (Exception ex)

            {

                Trace.TraceWarning(String.Format("Exception in EloquaInstance:PopulateContactFromResult {0} :: {1} :: {2}", ex.Message, ex.InnerException, strCurrentField), "Exception");

                throw;

            }

 

        }

 

 

With this value retrieved, we will then call a function on the Contact object to set this value:

 

tmpContact.SetFieldValue(“FirstName”, “Bob”);

 

This approach can be implemented in many ways, depending on your application’s approach to managing data on contacts.  The way in which data is retrieved from the Eloqua API, however, will remain common.

The Eloqua API is carefully managed and throttled, and returns reponse codes with excellent information about the success or failure of your call.  Because of this, it can be very useful to wrap your calls to Eloqua in a way that elegantly responds to, and acts upon, the information returned.

 

Some calls to the API have throttling that only allows a certain number of calls per time period.  Given that many applications may be using the API for a given client at the same time, or many threads of the same application, it is often wise to look for the error message from this throttling function, implement a slight pause, and retry.

 

In the following code snippet, we perform a simple action against the API (listing members in a program builder step), but wrap it in a way that detects the error codes, responds accordingly, and allows your application to implement specific logic for password expiry, system maintenance, or other response codes.

 

This code snippet uses the typical SOAP service proxy that we set up in another article.

 

        public Member[] PerformListMembersInStepByStatusWithRetries(ExternalActionServiceClient programServiceProxy, int intPBStepID, ExternalActionStatus status, int intPageNumber, int intPageSize)

        {

            bool blnOkayToRetry = true;

            bool blnPasswordsExpired = false;

            bool blnSystemMaintenance = false;

            bool blnMaxConnections = false;

 

            int intRetryCount = 0;

            Member[] retrievedEntities = null;

 

 

            if (this.OkayForNextEloquaAPICall(APICallType.ListMembersInStepByStatus))

            { 

            while (blnOkayToRetry)

            {

                blnOkayToRetry = false;

                try

                {

                    intRetryCount++;

                    retrievedEntities = programServiceProxy.ListMembersInStepByStatus(intPBStepID, status, intPageNumber, intPageSize);

 

 

                }

                catch (Exception ex)

                {

                    if (ex.GetBaseException().GetType().FullName.IndexOf("OperationTimeIntervalFault") > 0)

                    {

                        blnOkayToRetry = true;

                    }

                    if (ex.GetBaseException().GetType().FullName.IndexOf("UnexpectedErrorFault") > 0)

                    {

                        if (intRetryCount < 5)

                        {

                            blnOkayToRetry = true;

                        }

                        else

                        {

                            blnOkayToRetry = false;

                            Trace.TraceWarning(String.Format("More than 5 Exceptions in EloquaInstance:PerformListMembersInStepByStatusWithRetries {0} :: {1}", ex.Message, ex.InnerException), "Exception");

                        }

                    }

                    if (ex.InnerException != null)

                    {

                        if ((ex.InnerException.ToString().IndexOf("Invalid Company, Username and Password Combination") > 0) || (ex.InnerException.ToString().IndexOf("Account is disabled") > 0) || (ex.InnerException.ToString().IndexOf("Account has been locked out") > 0))

                        {

                            //passwords have expired

                            blnPasswordsExpired = true;

                            blnOkayToRetry = false;

 

                        }

                        if (ex.InnerException.ToString().IndexOf("servers are currently undergoing maintenance") > 0)

                        {

                            //system maintenance

                            blnSystemMaintenance = true;

                            blnOkayToRetry = false;

 

                        }

 

                        if (ex.InnerException.ToString().IndexOf("exceeded the maximum number of concurrent connections") > 0)

                        {

                            //max connections

                            blnMaxConnections = true;

                            blnOkayToRetry = false;

 

                        }

 

 

                    }

                    if (blnOkayToRetry)

                    {

                        Trace.WriteLine(String.Format("Retry after 300ms"), "Information");

                        Thread.Sleep(300);

                    }

                    else

                    {

                        Trace.TraceWarning(String.Format("Exception in EloquaInstance:PerformListMembersInStepByStatusWithRetries {0} :: {1}", ex.Message, ex.InnerException), "Exception");

                        if(!blnPasswordsExpired && !blnSystemMaintenance && !blnMaxConnections)

                            throw;

                    }

                }

            }

            }

 

            if (retrievedEntities == null)

            {

                //error has occured, return a blank array

                retrievedEntities = new Member[0];

            }

 

            if (blnPasswordsExpired)

            {

              //specific handling code

            }

 

            if (blnSystemMaintenance)

            {

              //specific handling code

            }

 

            if (blnMaxConnections)

            {

              //specific handling code

            }

 

            return retrievedEntities;

        }

 

 

This code snippet uses a function of OkayForNextEloquaAPICall to implement an appropriate pause mechanism.  By passing in a calling type (which we have defined), we can implement pauses uniquely by type.  Query calls are generally the heaviest, and are therefore throttled the most.

 

 

        private bool OkayForNextEloquaAPICall(APICallType callingType)

        {

            int intPause = 0;

            System.TimeSpan diffResult;

            diffResult= DateTime.Now.ToUniversalTime().Subtract(dttLastEloquaAPICall);

 

            int intRequiredPause = 0;

            bool blnResetClock = false;

            switch(callingType)

            {

                case APICallType.Query:

                    intRequiredPause = 1000;

                    blnResetClock = true;

                    break;

                case APICallType.Update:

                    intRequiredPause = 0;

                    blnResetClock = false;

                    break;

                case APICallType.Retrieve:

                    intRequiredPause = 0;

                    blnResetClock = false;

                    break;

                case APICallType.ListEntityTypes:

                    intRequiredPause = 0;

                    blnResetClock = false;

                    break;

                case APICallType.ListMembersInStepByStatus:

                    intRequiredPause = 0;

                    blnResetClock = false;

                    break;

                default:

                    break;

            }

 

            while (diffResult < TimeSpan.FromMilliseconds(intRequiredPause))

            {

                intPause++;

                Thread.Sleep(100);

                diffResult = DateTime.Now.ToUniversalTime().Subtract(dttLastEloquaAPICall);

            }

            if(blnResetClock)

            {

                dttLastEloquaAPICall = DateTime.Now.ToUniversalTime();

            }

            return true;

        }

Recently, we looked at counting the members of a program builder step in order to trigger the actions of a Cloud Connector step.  Once you’ve counted the members, the next step is to retrieve the details of who is in the step.

 

The following code snippet shows you how this can be accomplished.  We start of with a copy of the general API setup instructions, which, if you've followed already, are common between code snippets.

 

First, the following SOAP Endpoints are defined:

 

EloquaProgramService  as: https://secure.eloqua.com/api/1.2/ExternalActionService.svc?wsdl

 

and EloquaServiceNew as: https://secure.eloqua.com/API/1.2/Service.svc?wsdl

 

 

With that, let’s create an object to manage our communication with Eloqua.  In creating an instance of this class, we’ll pass in three variables – the Eloqua Instance  name, the User ID, and the Password.

 

The Eloqua Instance name is the company name that you use on your login page.  Your user id and password represent the user account for an account that has API access.  To have API access, two things must be true – the API must be enabled for your instance, and the account you are using must have the right to use the API.  To turn on the API for your install, just call the Eloqua support desk and ask them to enable it for you.  Your system administrator will be able to grant access to the API security rights under the Admin console.

 

With that set up, our constructor just creates a serviceProxy for each endpoint and applies the Client Credentials to it.  The Eloqua Instance Name and the User ID are concatenated into a string that looks like InstanceName\UserID.

 

    public class EloquaInstance

    {

 

        private EloquaServiceNew.EloquaServiceClient serviceProxy;

        private EloquaProgramService.ExternalActionServiceClient programServiceProxy;

 

        private DateTime dttLastEloquaAPICall;

 

 

        public EloquaInstance(string InstanceName, string UserID, string UserPassword)

        {

            strInstanceName = InstanceName;

            strUserID = UserID;

            strUserPassword = UserPassword;

 

            serviceProxy = new EloquaServiceNew.EloquaServiceClient();

            serviceProxy.ClientCredentials.UserName.UserName = strInstanceName + "\\" + strUserID;

            serviceProxy.ClientCredentials.UserName.Password = strUserPassword;

 

 

            programServiceProxy = new EloquaProgramService.ExternalActionServiceClient();

            programServiceProxy.ClientCredentials.UserName.UserName = strInstanceName + "\\" + strUserID;

            programServiceProxy.ClientCredentials.UserName.Password = strUserPassword;

 

            ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };

 

            dttLastEloquaAPICall = DateTime.Now.ToUniversalTime().Subtract(TimeSpan.FromMilliseconds(1000));

 

        }

 

       }

 

 

With that as a constructor, our Eloqua instance object is ready to get the members in the program builder step.  This is done iteratively, getting up to 100 contacts, companies, or prospects with each call. You query for a specific ExternalActionStatus, and get all members in the step with that status.

 

The ExternalActionStatus is one of three values:

 

0 – Awaiting Action

1 – In Progress

2 - Completed

 

Calling ListMembersInStepByStatus retrieves, by page, the set of members in the step.  Often, for a Cloud Connector, you will be processing 100 at a time, and them marking them as complete, so you may want to just start from a page number of 0.  A batch size of 100 is typical.

 

        public List<EloquaContact> ListContactsInStep(int intPBStepID, int intStepStatus, int intBatchSize)

        {

            List<EloquaContact> tmpContactsInStep = new List<EloquaContact>();

            EloquaContact tmpContact;

            int intPageNumber = 0;

 

            try

            {

                EloquaProgramService.ExternalActionStatus status;

                status = (EloquaProgramService.ExternalActionStatus)intStepStatus;

 

                Member[] result = null;

                try

                {

                    result = programServiceProxy.ListMembersInStepByStatus(intPBStepID, status, intPageNumber, intBatchSize);

                }

                catch (Exception ex)

                {

                    Trace.TraceWarning(String.Format("Exception in EloquaInstance:ListContactsInStep:GettingResult {0} :: {1}", ex.Message, ex.InnerException), "Exception");

                    throw;

                }

                if (result != null)

                {

                    foreach (var eam in result)

                    {

                        try

                        {

                            if (eam != null)

                            {

                                if ((int)eam.EntityType == 1)

                                {

                                    tmpContact = new EloquaContact();

                                    tmpContact.ContactID = eam.EntityId;

                                    tmpContact.ExternalActionID = eam.Id;

                                    tmpContactsInStep.Add(tmpContact);

                                }

                            }

                        }

                        catch (Exception ex)

                        {

                            Trace.TraceWarning(String.Format("Exception in EloquaInstance:ListContactsInStep:Individual {0} :: {1}", ex.Message, ex.InnerException), "Exception");

                        }

                    }

                }

            }

            catch (Exception ex)

            {

                Trace.TraceWarning(String.Format("Exception in EloquaInstance:ListContactsInStep {0} :: {1} :: {2} :: {3}", ex.Message, ex.InnerException, strInstanceName, intPBStepID.ToString()), "Exception");

                throw;

            }

            return tmpContactsInStep;

        }

 

 

To manage the contacts that are being returned, I’ve defined a simple class called EloquaContact.  You could manage them as a DynamicEntity, but often it’s easier to manage them directly as Contacts.

 

 

    public class EloquaContact

    {

        public int ContactID { get; set; }

 

        public string EmailAddress { get; set; }

        public int ExternalActionID { get; set; }

 

        public EloquaContact()

        {

            ContactID = 0;

            EmailAddress = "";

            ExternalActionID = 0;

        }

   }

 

 

The program step members that are returned by the query will have an EntityType of 1 (Contact).  The will also have an EntityID (ContactID) and and ExternalActionID that reflects their status in this particular step.  You’ll need this value later as we work with step members, so keep it handy.  I’ve added that as a property of my EloquaContact object.

 

The next thing we need to do, if we are going to begin working with these contacts is to set their status to InProgress to make sure that other instances of the same Cloud Connector, or other threads that are operating at the same time, do not retrieve the same members more than once.

 

To do this, we pass in to the SetMemberStatus call, an array of the members we would like to update.  These members are identified by their EntityType (1 for Contacts), old status, EntityID (Contact ID) and ExternalActionID.  If a contact has been previously moved to a new status, the results returned will not include this contact’s ID.

 

 

      public List<EloquaContact> SetStatusOfContactsInStep(int intProgramStepID, int intOldStepStatus, int intNewStepStatus, List<EloquaContact> tmpContactsInStep)

        {

 

            List<EloquaContact> tmpUpdatedContacts = new List<EloquaContact>();

            EloquaContact tmpContact;

            EloquaProgramService.ExternalActionStatus OldStatus;

            EloquaProgramService.ExternalActionStatus NewStatus;

            EloquaProgramService.Member[] stepMembers;

            OldStatus = (EloquaProgramService.ExternalActionStatus)intOldStepStatus;

            NewStatus = (EloquaProgramService.ExternalActionStatus)intNewStepStatus;

            EloquaProgramService.Member tmpMember;

 

            stepMembers = new EloquaProgramService.Member[tmpContactsInStep.Count()];

 

            try

            {

                for (int index = 0; index < tmpContactsInStep.Count(); index++)

                {

                    tmpMember = new EloquaProgramService.Member();

 

                    tmpMember.EntityId = tmpContactsInStep.ElementAt(index).ContactID;

                    tmpMember.EntityType = (EloquaProgramService.EntityType)1;

                    tmpMember.StepId = intProgramStepID;

                    tmpMember.Status = OldStatus;

                    tmpMember.Id = tmpContactsInStep.ElementAt(index).ExternalActionID;

                    stepMembers[index] = tmpMember;

                }

 

                var results = programServiceProxy.SetMemberStatus(stepMembers, NewStatus);

 

                foreach (Member tmpUpdatedMember in results)

                {

                    tmpContact = new EloquaContact();

                    tmpContact.ContactID = tmpUpdatedMember.EntityId;

                    tmpContact.ExternalActionID = tmpUpdatedMember.Id;

                    tmpUpdatedContacts.Add(tmpContact);

                }

            }

            catch (Exception ex)

            {

                Trace.TraceWarning(String.Format("Exception in EloquaInstance:SetStatusOfContactsInStep {0} :: {1} :: Step {2}", ex.Message, ex.InnerException, intProgramStepID.ToString()), "Exception");

 

                throw;

            }

            return tmpUpdatedContacts;

 

        }

 

Now we have a set of Contact IDs (follow an identical process for Prospects or Companies), and have set their status to In Progress so we can begin our work on these contacts according to the logic of the Cloud Connector we are building.

When building a Cloud Connector, one of the core things that  every developer needs to do is to count the members (contacts,  companies, or prospects) in a program step.  If there are members in the  step, you can then query the step directly to find out the contact IDs  of each member of the step, but first, its best to see if any members  exist in the step.

 

As a marketing automation program can  be run in one of many modes, and members can be added to the program in  many ways, you won’t know that members exist in the step until you query  it to retrieve a count.

 

The following code snippet shows you how this can be accomplished.

 

First, you will need some general setup for API use.  Many of these code snippets use a similar setup, so the general API setup instructions can be shared.  You will need the SOAP endpoints for the calls defined.  In this  example, we will only use the External Action Service, but this code  snippet shows both that and the standard Eloqua SOAP API configured as  they will usually be used in tandem by most programs that leverage the  Eloqua API.

 

The following SOAP Endpoints are defined:

 

EloquaProgramService  as: https://secure.eloqua.com/api/1.2/ExternalActionService.svc?wsdl

 

and EloquaServiceNew as: https://secure.eloqua.com/API/1.2/Service.svc?wsdl

 

 

With  that, let’s create an object to manage our communication with Eloqua.   In creating an instance of this class, we’ll pass in three variables –  the Eloqua Instance  name, the User ID, and the Password.

 

The  Eloqua Instance name is the company name that you use on your login  page.  Your user id and password represent the user account for an  account that has API access.  To have API access, two things must be  true – the API must be enabled for your instance, and the account you  are using must have the right to use the API.  To turn on the API for  your install, just call the Eloqua support desk and ask them to enable  it for you.  Your system administrator will be able to grant access to  the API security rights under the Admin console.

 

With that  set up, our constructor just creates a serviceProxy for each endpoint  and applies the Client Credentials to it.  The Eloqua Instance Name and  the User ID are concatenated into a string that looks like  InstanceName\UserID.

 

    public class EloquaInstance

    {

 

        private EloquaServiceNew.EloquaServiceClient serviceProxy;

        private EloquaProgramService.ExternalActionServiceClient programServiceProxy;

 

        private DateTime dttLastEloquaAPICall;

 

 

        public EloquaInstance(string InstanceName, string UserID, string UserPassword)

        {

            strInstanceName = InstanceName;

            strUserID = UserID;

            strUserPassword = UserPassword;

 

            serviceProxy = new EloquaServiceNew.EloquaServiceClient();

            serviceProxy.ClientCredentials.UserName.UserName = strInstanceName + "\\" + strUserID;

            serviceProxy.ClientCredentials.UserName.Password = strUserPassword;

 

 

            programServiceProxy = new EloquaProgramService.ExternalActionServiceClient();

            programServiceProxy.ClientCredentials.UserName.UserName = strInstanceName + "\\" + strUserID;

            programServiceProxy.ClientCredentials.UserName.Password = strUserPassword;

 

            ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };

 

            //without prior knowledge, set the last API call 1s ago to allow new object instances to invoke call

            dttLastEloquaAPICall = DateTime.Now.ToUniversalTime().Subtract(TimeSpan.FromMilliseconds(1000));

 

        }

 

       }

 

 

With  that as a constructor, our Eloqua instance object is ready to count the  members in the program builder step.  Each count call takes in as  parameters the Program Builder Step ID (this is the number that you see  in the Program Step configuration window when you set it as a Cloud  Connector) and an ExternalActionStatus.  The ExternalActionStatus is one  of three values:

 

0 – Awaiting Action

1 – In Progress

2 - Completed

 

Note  that the contacts (or companies, or prospects) in the step will not  show up to the API until program builder has executed.  You may see them  in that step in the UI, but they will not be seen by the API until the  next cycle.

 

To count the members in the step, just call GetMemberCountInStepByStatus with the step ID and status you are interested in.

 

 

 

        public int CountMembersInStepByStatus(int intPBStepID, int intStepStatus)

        {

            int intMemberCount = 0;

            try

            {

                EloquaProgramService.ExternalActionStatus status;

                status = (EloquaProgramService.ExternalActionStatus)intStepStatus;

 

                try

                {

                    intMemberCount = programServiceProxy.GetMemberCountInStepByStatus(intPBStepID, status);

                }

                catch (Exception ex)

                {

                     Trace.TraceWarning(String.Format("Exception in  EloquaInstance:CountMembersInStepByStatus:GettingResult {0} :: {1}",  ex.Message, ex.InnerException), "Exception");

                    throw;

                }

 

            }

            catch (Exception ex)

            {

                 Trace.TraceWarning(String.Format("Exception in  EloquaInstance:CountMembersInStepByStatus {0} :: {1} :: {2} :: {3}",  ex.Message, ex.InnerException, strInstanceName, intPBStepID.ToString()),  "Exception");

                throw;

            }

            return intMemberCount;

        }

 

From  there, you will know how many members are in each step, at each  status.  If there are members in the Awaiting Action status, you can  then retrieve the members of the step and begin to execute your Cloud  Connector logic against them.

Filter Blog

By date: By tag: