internal override void ProcessMappingsFromRowValues(RowValues values)
        {
            AdExtensionIdToEntityIdAssociation = new AdExtensionIdToEntityIdAssociation();

            values.ConvertToEntity(this, Mappings);

            PerformanceData = PerformanceData.ReadFromRowValuesOrNull(values);
        }
        public async override Task RunAsync(AuthorizationData authorizationData)
        {
            try
            {
                CampaignService = new ServiceClient<ICampaignManagementService>(authorizationData);
                CustomerService = new ServiceClient<ICustomerManagementService>(authorizationData);

                #region MigrationStatus

                // To prepare for the sitelink ad extensions migration in 2017, you will need to determine
                // whether the account has been migrated from SiteLinksAdExtension to Sitelink2AdExtension. 
                // All ad extension service operations available for both types of sitelinks; however you will 
                // need to determine which type to add, update, and retrieve.

                bool sitelinkMigrationIsCompleted = false;

                // Optionally you can find out which pilot features the customer is able to use. Even if the customer 
                // is in pilot for sitelink migrations, the accounts that it contains might not be migrated.
                var featurePilotFlags = await GetCustomerPilotFeaturesAsync(authorizationData.CustomerId);

                // The pilot flag value for Sitelink ad extension migration is 253.
                // Pilot flags apply to all accounts within a given customer; however,
                // each account goes through migration individually and has its own migration status.
                if (featurePilotFlags.Any(pilotFlag => pilotFlag == 253))
                {
                    // Account migration status below will be either NotStarted, InProgress, or Completed.
                    OutputStatusMessage("Customer is in pilot for Sitelink migration.\n");
                }
                else
                {
                    // Account migration status below will be NotInPilot.
                    OutputStatusMessage("Customer is not in pilot for Sitelink migration.\n");
                }

                // Even if you have multiple accounts per customer, each account will have its own
                // migration status. This example checks one account using the provided AuthorizationData.
                var accountMigrationStatusesInfos = (await GetAccountMigrationStatusesAsync(
                    new long[] { authorizationData.AccountId },
                    SITELINK_MIGRATION
                )).ToArray();

                foreach (var accountMigrationStatusesInfo in accountMigrationStatusesInfos)
                {
                    OutputAccountMigrationStatusesInfo(accountMigrationStatusesInfo);

                    if (accountMigrationStatusesInfo.MigrationStatusInfo.Any(
                        statusInfo =>
                        statusInfo.Status == MigrationStatus.Completed && SITELINK_MIGRATION.CompareTo(statusInfo.MigrationType) == 0))
                    {
                        sitelinkMigrationIsCompleted = true;
                    }
                }

                #endregion MigrationStatus

                // Add a campaign that will later be associated with ad extensions. 

                var campaigns = new[] {
                    new Campaign
                    {
                        Name = "Women's Shoes " + DateTime.UtcNow,
                        Description = "Red shoes line.",

                        // You must choose to set either the shared  budget ID or daily amount.
                        // You can set one or the other, but you may not set both.
                        BudgetId = null,
                        DailyBudget = 50,
                        BudgetType = BudgetLimitType.DailyBudgetStandard,
                        BiddingScheme = new EnhancedCpcBiddingScheme(),

                        TimeZone = "PacificTimeUSCanadaTijuana",
                        DaylightSaving = true,

                        // Used with FinalUrls shown in the sitelinks that we will add below.
                        TrackingUrlTemplate =
                            "http://tracker.example.com/?season={_season}&promocode={_promocode}&u={lpurl}"
                    }
                };

                AddCampaignsResponse addCampaignsResponse = await AddCampaignsAsync(authorizationData.AccountId, campaigns);
                long?[] campaignIds = addCampaignsResponse.CampaignIds.ToArray();

                // Specify the extensions.

                var adExtensions = new AdExtension[] {
                    //new AppAdExtension
                    //{
                    //    AppPlatform = "Windows",
                    //    AppStoreId = "AppStoreIdGoesHere",
                    //    DestinationUrl = "DestinationUrlGoesHere",
                    //    DisplayText = "Contoso",
                    //},
                    new CallAdExtension {
                        CountryCode = "US",
                        PhoneNumber = "2065550100",
                        IsCallOnly = false,
                        Scheduling = new Schedule {

                            // For this example assume the call center is open Monday - Friday from 9am - 9pm
                            // in the account's time zone.

                            UseSearcherTimeZone = false,
                            DayTimeRanges = new[]
                            {
                                new DayTime
                                {
                                    Day = Day.Monday,
                                    StartHour = 9,
                                    StartMinute = Minute.Zero,
                                    EndHour = 21,
                                    EndMinute = Minute.Zero,
                                },
                                new DayTime
                                {
                                    Day = Day.Tuesday,
                                    StartHour = 9,
                                    StartMinute = Minute.Zero,
                                    EndHour = 21,
                                    EndMinute = Minute.Zero,
                                },
                                new DayTime
                                {
                                    Day = Day.Wednesday,
                                    StartHour = 9,
                                    StartMinute = Minute.Zero,
                                    EndHour = 21,
                                    EndMinute = Minute.Zero,
                                },
                                new DayTime
                                {
                                    Day = Day.Thursday,
                                    StartHour = 9,
                                    StartMinute = Minute.Zero,
                                    EndHour = 21,
                                    EndMinute = Minute.Zero,
                                },
                                new DayTime
                                {
                                    Day = Day.Friday,
                                    StartHour = 9,
                                    StartMinute = Minute.Zero,
                                    EndHour = 21,
                                    EndMinute = Minute.Zero,
                                },
                            },
                            StartDate = null,
                            EndDate = new Microsoft.BingAds.V10.CampaignManagement.Date {
                                Month = 12,
                                Day = 31,
                                Year = DateTime.UtcNow.Year + 1
                            },
                        }
                    },
                    new CalloutAdExtension
                    {
                        Text = "Callout Text"
                    },
                    //new ImageAdExtension
                    //{
                    //    AlternativeText = "Image Extension Alt Text",
                    //    ImageMediaIds = new long[] { await AddImageAsync(authorizationData) }
                    //},
                    new LocationAdExtension {
                        PhoneNumber = "206-555-0100",
                        CompanyName = "Contoso Shoes",
                        IconMediaId = null,
                        ImageMediaId = null,
                        Address = new Microsoft.BingAds.V10.CampaignManagement.Address {
                            StreetAddress = "1234 Washington Place",
                            StreetAddress2 = "Suite 1210",
                            CityName = "Woodinville",
                            ProvinceName = "WA",
                            CountryCode = "US",
                            PostalCode = "98608"
                        },
                        Scheduling = new Schedule {

                            // For this example assume you want to drive traffic every Saturday morning
                            // in the search user's time zone.

                            UseSearcherTimeZone = true,
                            DayTimeRanges = new[]
                            {
                                new DayTime
                                {
                                    Day = Day.Saturday,
                                    StartHour = 9,
                                    StartMinute = Minute.Zero,
                                    EndHour = 12,
                                    EndMinute = Minute.Zero,
                                },
                            },
                            StartDate = null,
                            EndDate = new Microsoft.BingAds.V10.CampaignManagement.Date {
                                Month = 12,
                                Day = 31,
                                Year = DateTime.UtcNow.Year + 1
                            },
                        }
                    },
                    new ReviewAdExtension
                    {
                        IsExact = true,
                        Source = "Review Source Name",
                        Text = "Review Text",
                        Url = "http://review.contoso.com" // The Url of the third-party review. This is not your business Url.
                    },
                    new StructuredSnippetAdExtension
                    {
                        Header = "Brands",
                        Values = new [] { "Windows", "Xbox", "Skype"}
                    }
                };

                // Before migration only the deprecated SiteLinksAdExtension type can be added, 
                // and after migration only the new Sitelink2AdExtension type can be added.
                adExtensions = adExtensions.Concat(sitelinkMigrationIsCompleted ? (AdExtension[])
                    GetSampleSitelink2AdExtensions() : GetSampleSiteLinksAdExtensions()).ToArray();


                // Add all extensions to the account's ad extension library
                var adExtensionIdentities = await AddAdExtensionsAsync(
                    authorizationData.AccountId,
                    adExtensions
                );

                OutputStatusMessage("Added ad extensions.\n");

                // DeleteAdExtensionsAssociations, SetAdExtensionsAssociations, and GetAdExtensionsEditorialReasons 
                // operations each require a list of type AdExtensionIdToEntityIdAssociation.
                var adExtensionIdToEntityIdAssociations = new AdExtensionIdToEntityIdAssociation[adExtensionIdentities.Count];

                // GetAdExtensionsByIds requires a list of type long.
                var adExtensionIds = new long[adExtensionIdentities.Count];

                // Loop through the list of extension IDs and build any required data structures
                // for subsequent operations. 

                for (int i = 0; i < adExtensionIdentities.Count; i++)
                {
                    adExtensionIdToEntityIdAssociations[i] = new AdExtensionIdToEntityIdAssociation
                    {
                        AdExtensionId = adExtensionIdentities[i].Id,
                        EntityId = (long)campaignIds[0]
                    };

                    adExtensionIds[i] = adExtensionIdentities[i].Id;
                }

                // Associate the specified ad extensions with the respective campaigns or ad groups. 
                await SetAdExtensionsAssociationsAsync(
                    authorizationData.AccountId,
                    adExtensionIdToEntityIdAssociations,
                    AssociationType.Campaign
                );

                OutputStatusMessage("Set ad extension associations.\n");

                // Get editorial rejection reasons for the respective ad extension and entity associations.
                var adExtensionEditorialReasonCollection =
                    (AdExtensionEditorialReasonCollection[])await GetAdExtensionsEditorialReasons(
                        authorizationData.AccountId,
                        adExtensionIdToEntityIdAssociations,
                        AssociationType.Campaign
                    );

                // If migration has been completed, then you should request the Sitelink2AdExtension objects.
                // You can always request both types; however, before migration only the deprecated SiteLinksAdExtension
                // type will be returned, and after migration only the new Sitelink2AdExtension type will be returned.
                AdExtensionsTypeFilter adExtensionsTypeFilter = (sitelinkMigrationIsCompleted ?
                    AdExtensionsTypeFilter.Sitelink2AdExtension : AdExtensionsTypeFilter.SiteLinksAdExtension) |
                    AdExtensionsTypeFilter.AppAdExtension |
                    AdExtensionsTypeFilter.CallAdExtension |
                    AdExtensionsTypeFilter.CalloutAdExtension |
                    AdExtensionsTypeFilter.ImageAdExtension |
                    AdExtensionsTypeFilter.LocationAdExtension |
                    AdExtensionsTypeFilter.ReviewAdExtension |
                    AdExtensionsTypeFilter.StructuredSnippetAdExtension;

                // Get all ad extensions added above.
                adExtensions = (AdExtension[]) await GetAdExtensionsByIdsAsync(
                    authorizationData.AccountId,
                    adExtensionIds,
                    adExtensionsTypeFilter,
                    AdExtensionAdditionalField.Scheduling
                );

                OutputStatusMessage("List of ad extensions that were added above:\n");
                OutputAdExtensionsWithEditorialReasons(adExtensions, adExtensionEditorialReasonCollection);

                // Get only the location extensions and remove scheduling.

                adExtensionsTypeFilter = AdExtensionsTypeFilter.LocationAdExtension;

                adExtensions = (AdExtension[])await GetAdExtensionsByIdsAsync(
                    authorizationData.AccountId,
                    adExtensionIds,
                    adExtensionsTypeFilter,
                    AdExtensionAdditionalField.Scheduling
                    );

                var updateExtensions = new List<AdExtension>();
                var updateExtensionIds = new List<long>();

                foreach (var extension in adExtensions)
                {
                    // GetAdExtensionsByIds will return a nil element if the request filters / conditions were not met.
                    if(extension != null && extension.Id != null)
                    {
                        // Remove read-only elements that would otherwise cause the update operation to fail.
                        var updateExtension = SetReadOnlyAdExtensionElementsToNull(extension);

                        // If you set the Scheduling element null, any existing scheduling set for the ad extension will remain unchanged. 
                        // If you set this to any non-null Schedule object, you are effectively replacing existing scheduling 
                        // for the ad extension. In this example, we will remove any existing scheduling by setting this element  
                        // to an empty Schedule object.
                        updateExtension.Scheduling = new Schedule { };

                        updateExtensions.Add(updateExtension);
                        updateExtensionIds.Add((long)updateExtension.Id);
                    }
                }

                OutputStatusMessage("Removing scheduling from the location ad extensions..\n");
                await UpdateAdExtensionsAsync(authorizationData.AccountId, updateExtensions);

                // Get only the location extension to output the result.
                adExtensions = (AdExtension[])await GetAdExtensionsByIdsAsync(
                    authorizationData.AccountId,
                    updateExtensionIds,
                    adExtensionsTypeFilter,
                    AdExtensionAdditionalField.Scheduling
                );

                OutputStatusMessage("List of ad extensions that were updated above:\n");
                OutputAdExtensionsWithEditorialReasons(adExtensions, null);

                // Delete the ad extension associations, ad extensions, and campaign, that were previously added. 
                // You should remove these lines if you want to view the added entities in the 
                // Bing Ads web application or another tool.

                // Remove the specified associations from the respective campaigns or ad groups. 
                // At this point the ad extensions are still available in the account's ad extensions library. 
                await DeleteAdExtensionsAssociationsAsync(
                    authorizationData.AccountId,
                    adExtensionIdToEntityIdAssociations,
                    AssociationType.Campaign
                );
                OutputStatusMessage("Deleted ad extension associations.\n");

                // Deletes the ad extensions from the account’s ad extension library.
                await DeleteAdExtensionsAsync(
                    authorizationData.AccountId,
                    adExtensionIds
                );
                OutputStatusMessage("Deleted ad extensions.\n");
                
                await DeleteCampaignsAsync(authorizationData.AccountId, new[] { (long)campaignIds[0] });
                OutputStatusMessage(String.Format("Deleted CampaignId {0}\n", (long)campaignIds[0]));
            }
            // Catch authentication exceptions
            catch (OAuthTokenRequestException ex)
            {
                OutputStatusMessage(string.Format("Couldn't get OAuth tokens. Error: {0}. Description: {1}", ex.Details.Error, ex.Details.Description));
            }
            // Catch Campaign Management service exceptions
            catch (FaultException<Microsoft.BingAds.V10.CampaignManagement.AdApiFaultDetail> ex)
            {
                OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message))));
            }
            catch (FaultException<Microsoft.BingAds.V10.CampaignManagement.ApiFaultDetail> ex)
            {
                OutputStatusMessage(string.Join("; ", ex.Detail.OperationErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message))));
                OutputStatusMessage(string.Join("; ", ex.Detail.BatchErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message))));
            }
            catch (FaultException<Microsoft.BingAds.V10.CampaignManagement.EditorialApiFaultDetail> ex)
            {
                OutputStatusMessage(string.Join("; ", ex.Detail.OperationErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message))));
                OutputStatusMessage(string.Join("; ", ex.Detail.BatchErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message))));
            }
            // Catch Customer Management service exceptions
            catch (FaultException<Microsoft.BingAds.CustomerManagement.AdApiFaultDetail> ex)
            {
                OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message))));
            }
            catch (FaultException<Microsoft.BingAds.CustomerManagement.ApiFault> ex)
            {
                OutputStatusMessage(string.Join("; ", ex.Detail.OperationErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message))));
            }
            catch (Exception ex)
            {
                OutputStatusMessage(ex.Message);
            }
        }
        public async override Task RunAsync(AuthorizationData authorizationData)
        {
            try
            {
                CampaignService = new ServiceClient <ICampaignManagementService>(authorizationData);
                CustomerService = new ServiceClient <ICustomerManagementService>(authorizationData);

                #region MigrationStatus

                // To prepare for the sitelink ad extensions migration in 2017, you will need to determine
                // whether the account has been migrated from SiteLinksAdExtension to Sitelink2AdExtension.
                // All ad extension service operations available for both types of sitelinks; however you will
                // need to determine which type to add, update, and retrieve.

                bool sitelinkMigrationIsCompleted = false;

                // Optionally you can find out which pilot features the customer is able to use. Even if the customer
                // is in pilot for sitelink migrations, the accounts that it contains might not be migrated.
                var featurePilotFlags = await GetCustomerPilotFeaturesAsync(authorizationData.CustomerId);

                // The pilot flag value for Sitelink ad extension migration is 253.
                // Pilot flags apply to all accounts within a given customer; however,
                // each account goes through migration individually and has its own migration status.
                if (featurePilotFlags.Any(pilotFlag => pilotFlag == 253))
                {
                    // Account migration status below will be either NotStarted, InProgress, or Completed.
                    OutputStatusMessage("Customer is in pilot for Sitelink migration.\n");
                }
                else
                {
                    // Account migration status below will be NotInPilot.
                    OutputStatusMessage("Customer is not in pilot for Sitelink migration.\n");
                }

                // Even if you have multiple accounts per customer, each account will have its own
                // migration status. This example checks one account using the provided AuthorizationData.
                var accountMigrationStatusesInfos = (await GetAccountMigrationStatusesAsync(
                                                         new long[] { authorizationData.AccountId },
                                                         SITELINK_MIGRATION
                                                         )).ToArray();

                foreach (var accountMigrationStatusesInfo in accountMigrationStatusesInfos)
                {
                    OutputAccountMigrationStatusesInfo(accountMigrationStatusesInfo);

                    if (accountMigrationStatusesInfo.MigrationStatusInfo.Any(
                            statusInfo =>
                            statusInfo.Status == MigrationStatus.Completed && SITELINK_MIGRATION.CompareTo(statusInfo.MigrationType) == 0))
                    {
                        sitelinkMigrationIsCompleted = true;
                    }
                }

                #endregion MigrationStatus

                // Add a campaign that will later be associated with ad extensions.

                var campaigns = new[] {
                    new Campaign
                    {
                        Name        = "Women's Shoes " + DateTime.UtcNow,
                        Description = "Red shoes line.",

                        // You must choose to set either the shared  budget ID or daily amount.
                        // You can set one or the other, but you may not set both.
                        BudgetId      = null,
                        DailyBudget   = 50,
                        BudgetType    = BudgetLimitType.DailyBudgetStandard,
                        BiddingScheme = new EnhancedCpcBiddingScheme(),

                        TimeZone       = "PacificTimeUSCanadaTijuana",
                        DaylightSaving = true,

                        // Used with FinalUrls shown in the sitelinks that we will add below.
                        TrackingUrlTemplate =
                            "http://tracker.example.com/?season={_season}&promocode={_promocode}&u={lpurl}"
                    }
                };

                AddCampaignsResponse addCampaignsResponse = await AddCampaignsAsync(authorizationData.AccountId, campaigns);

                long?[] campaignIds = addCampaignsResponse.CampaignIds.ToArray();

                // Specify the extensions.

                var adExtensions = new AdExtension[] {
                    //new AppAdExtension
                    //{
                    //    AppPlatform = "Windows",
                    //    AppStoreId = "AppStoreIdGoesHere",
                    //    DestinationUrl = "DestinationUrlGoesHere",
                    //    DisplayText = "Contoso",
                    //},
                    new CallAdExtension {
                        CountryCode = "US",
                        PhoneNumber = "2065550100",
                        IsCallOnly  = false,
                        Scheduling  = new Schedule {
                            // For this example assume the call center is open Monday - Friday from 9am - 9pm
                            // in the account's time zone.

                            UseSearcherTimeZone = false,
                            DayTimeRanges       = new[]
                            {
                                new DayTime
                                {
                                    Day         = Day.Monday,
                                    StartHour   = 9,
                                    StartMinute = Minute.Zero,
                                    EndHour     = 21,
                                    EndMinute   = Minute.Zero,
                                },
                                new DayTime
                                {
                                    Day         = Day.Tuesday,
                                    StartHour   = 9,
                                    StartMinute = Minute.Zero,
                                    EndHour     = 21,
                                    EndMinute   = Minute.Zero,
                                },
                                new DayTime
                                {
                                    Day         = Day.Wednesday,
                                    StartHour   = 9,
                                    StartMinute = Minute.Zero,
                                    EndHour     = 21,
                                    EndMinute   = Minute.Zero,
                                },
                                new DayTime
                                {
                                    Day         = Day.Thursday,
                                    StartHour   = 9,
                                    StartMinute = Minute.Zero,
                                    EndHour     = 21,
                                    EndMinute   = Minute.Zero,
                                },
                                new DayTime
                                {
                                    Day         = Day.Friday,
                                    StartHour   = 9,
                                    StartMinute = Minute.Zero,
                                    EndHour     = 21,
                                    EndMinute   = Minute.Zero,
                                },
                            },
                            StartDate = null,
                            EndDate   = new Microsoft.BingAds.V10.CampaignManagement.Date {
                                Month = 12,
                                Day   = 31,
                                Year  = DateTime.UtcNow.Year + 1
                            },
                        }
                    },
                    new CalloutAdExtension
                    {
                        Text = "Callout Text"
                    },
                    //new ImageAdExtension
                    //{
                    //    AlternativeText = "Image Extension Alt Text",
                    //    ImageMediaIds = new long[] { await AddImageAsync(authorizationData) }
                    //},
                    new LocationAdExtension {
                        PhoneNumber  = "206-555-0100",
                        CompanyName  = "Contoso Shoes",
                        IconMediaId  = null,
                        ImageMediaId = null,
                        Address      = new Microsoft.BingAds.V10.CampaignManagement.Address {
                            StreetAddress  = "1234 Washington Place",
                            StreetAddress2 = "Suite 1210",
                            CityName       = "Woodinville",
                            ProvinceName   = "WA",
                            CountryCode    = "US",
                            PostalCode     = "98608"
                        },
                        Scheduling = new Schedule {
                            // For this example assume you want to drive traffic every Saturday morning
                            // in the search user's time zone.

                            UseSearcherTimeZone = true,
                            DayTimeRanges       = new[]
                            {
                                new DayTime
                                {
                                    Day         = Day.Saturday,
                                    StartHour   = 9,
                                    StartMinute = Minute.Zero,
                                    EndHour     = 12,
                                    EndMinute   = Minute.Zero,
                                },
                            },
                            StartDate = null,
                            EndDate   = new Microsoft.BingAds.V10.CampaignManagement.Date {
                                Month = 12,
                                Day   = 31,
                                Year  = DateTime.UtcNow.Year + 1
                            },
                        }
                    },
                    new ReviewAdExtension
                    {
                        IsExact = true,
                        Source  = "Review Source Name",
                        Text    = "Review Text",
                        Url     = "http://review.contoso.com" // The Url of the third-party review. This is not your business Url.
                    },
                    new StructuredSnippetAdExtension
                    {
                        Header = "Brands",
                        Values = new [] { "Windows", "Xbox", "Skype" }
                    }
                };

                // Before migration only the deprecated SiteLinksAdExtension type can be added,
                // and after migration only the new Sitelink2AdExtension type can be added.
                adExtensions = adExtensions.Concat(sitelinkMigrationIsCompleted ? (AdExtension[])
                                                   GetSampleSitelink2AdExtensions() : GetSampleSiteLinksAdExtensions()).ToArray();


                // Add all extensions to the account's ad extension library
                var adExtensionIdentities = await AddAdExtensionsAsync(
                    authorizationData.AccountId,
                    adExtensions
                    );

                OutputStatusMessage("Added ad extensions.\n");

                // DeleteAdExtensionsAssociations, SetAdExtensionsAssociations, and GetAdExtensionsEditorialReasons
                // operations each require a list of type AdExtensionIdToEntityIdAssociation.
                var adExtensionIdToEntityIdAssociations = new AdExtensionIdToEntityIdAssociation[adExtensionIdentities.Count];

                // GetAdExtensionsByIds requires a list of type long.
                var adExtensionIds = new long[adExtensionIdentities.Count];

                // Loop through the list of extension IDs and build any required data structures
                // for subsequent operations.

                for (int i = 0; i < adExtensionIdentities.Count; i++)
                {
                    adExtensionIdToEntityIdAssociations[i] = new AdExtensionIdToEntityIdAssociation
                    {
                        AdExtensionId = adExtensionIdentities[i].Id,
                        EntityId      = (long)campaignIds[0]
                    };

                    adExtensionIds[i] = adExtensionIdentities[i].Id;
                }

                // Associate the specified ad extensions with the respective campaigns or ad groups.
                await SetAdExtensionsAssociationsAsync(
                    authorizationData.AccountId,
                    adExtensionIdToEntityIdAssociations,
                    AssociationType.Campaign
                    );

                OutputStatusMessage("Set ad extension associations.\n");

                // Get editorial rejection reasons for the respective ad extension and entity associations.
                var adExtensionEditorialReasonCollection =
                    (AdExtensionEditorialReasonCollection[]) await GetAdExtensionsEditorialReasons(
                        authorizationData.AccountId,
                        adExtensionIdToEntityIdAssociations,
                        AssociationType.Campaign
                        );

                // If migration has been completed, then you should request the Sitelink2AdExtension objects.
                // You can always request both types; however, before migration only the deprecated SiteLinksAdExtension
                // type will be returned, and after migration only the new Sitelink2AdExtension type will be returned.
                AdExtensionsTypeFilter adExtensionsTypeFilter = (sitelinkMigrationIsCompleted ?
                                                                 AdExtensionsTypeFilter.Sitelink2AdExtension : AdExtensionsTypeFilter.SiteLinksAdExtension) |
                                                                AdExtensionsTypeFilter.AppAdExtension |
                                                                AdExtensionsTypeFilter.CallAdExtension |
                                                                AdExtensionsTypeFilter.CalloutAdExtension |
                                                                AdExtensionsTypeFilter.ImageAdExtension |
                                                                AdExtensionsTypeFilter.LocationAdExtension |
                                                                AdExtensionsTypeFilter.ReviewAdExtension |
                                                                AdExtensionsTypeFilter.StructuredSnippetAdExtension;

                // Get all ad extensions added above.
                adExtensions = (AdExtension[]) await GetAdExtensionsByIdsAsync(
                    authorizationData.AccountId,
                    adExtensionIds,
                    adExtensionsTypeFilter,
                    AdExtensionAdditionalField.Scheduling
                    );

                OutputStatusMessage("List of ad extensions that were added above:\n");
                OutputAdExtensionsWithEditorialReasons(adExtensions, adExtensionEditorialReasonCollection);

                // Get only the location extensions and remove scheduling.

                adExtensionsTypeFilter = AdExtensionsTypeFilter.LocationAdExtension;

                adExtensions = (AdExtension[]) await GetAdExtensionsByIdsAsync(
                    authorizationData.AccountId,
                    adExtensionIds,
                    adExtensionsTypeFilter,
                    AdExtensionAdditionalField.Scheduling
                    );

                var updateExtensions   = new List <AdExtension>();
                var updateExtensionIds = new List <long>();

                foreach (var extension in adExtensions)
                {
                    // GetAdExtensionsByIds will return a nil element if the request filters / conditions were not met.
                    if (extension != null && extension.Id != null)
                    {
                        // Remove read-only elements that would otherwise cause the update operation to fail.
                        var updateExtension = SetReadOnlyAdExtensionElementsToNull(extension);

                        // If you set the Scheduling element null, any existing scheduling set for the ad extension will remain unchanged.
                        // If you set this to any non-null Schedule object, you are effectively replacing existing scheduling
                        // for the ad extension. In this example, we will remove any existing scheduling by setting this element
                        // to an empty Schedule object.
                        updateExtension.Scheduling = new Schedule {
                        };

                        updateExtensions.Add(updateExtension);
                        updateExtensionIds.Add((long)updateExtension.Id);
                    }
                }

                OutputStatusMessage("Removing scheduling from the location ad extensions..\n");
                await UpdateAdExtensionsAsync(authorizationData.AccountId, updateExtensions);

                // Get only the location extension to output the result.
                adExtensions = (AdExtension[]) await GetAdExtensionsByIdsAsync(
                    authorizationData.AccountId,
                    updateExtensionIds,
                    adExtensionsTypeFilter,
                    AdExtensionAdditionalField.Scheduling
                    );

                OutputStatusMessage("List of ad extensions that were updated above:\n");
                OutputAdExtensionsWithEditorialReasons(adExtensions, null);

                // Delete the ad extension associations, ad extensions, and campaign, that were previously added.
                // You should remove these lines if you want to view the added entities in the
                // Bing Ads web application or another tool.

                // Remove the specified associations from the respective campaigns or ad groups.
                // At this point the ad extensions are still available in the account's ad extensions library.
                await DeleteAdExtensionsAssociationsAsync(
                    authorizationData.AccountId,
                    adExtensionIdToEntityIdAssociations,
                    AssociationType.Campaign
                    );

                OutputStatusMessage("Deleted ad extension associations.\n");

                // Deletes the ad extensions from the account’s ad extension library.
                await DeleteAdExtensionsAsync(
                    authorizationData.AccountId,
                    adExtensionIds
                    );

                OutputStatusMessage("Deleted ad extensions.\n");

                await DeleteCampaignsAsync(authorizationData.AccountId, new[] { (long)campaignIds[0] });

                OutputStatusMessage(string.Format("Deleted Campaign Id {0}\n", (long)campaignIds[0]));
            }
            // Catch authentication exceptions
            catch (OAuthTokenRequestException ex)
            {
                OutputStatusMessage(string.Format("Couldn't get OAuth tokens. Error: {0}. Description: {1}", ex.Details.Error, ex.Details.Description));
            }
            // Catch Campaign Management service exceptions
            catch (FaultException <Microsoft.BingAds.V10.CampaignManagement.AdApiFaultDetail> ex)
            {
                OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message))));
            }
            catch (FaultException <Microsoft.BingAds.V10.CampaignManagement.ApiFaultDetail> ex)
            {
                OutputStatusMessage(string.Join("; ", ex.Detail.OperationErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message))));
                OutputStatusMessage(string.Join("; ", ex.Detail.BatchErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message))));
            }
            catch (FaultException <Microsoft.BingAds.V10.CampaignManagement.EditorialApiFaultDetail> ex)
            {
                OutputStatusMessage(string.Join("; ", ex.Detail.OperationErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message))));
                OutputStatusMessage(string.Join("; ", ex.Detail.BatchErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message))));
            }
            // Catch Customer Management service exceptions
            catch (FaultException <Microsoft.BingAds.CustomerManagement.AdApiFaultDetail> ex)
            {
                OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message))));
            }
            catch (FaultException <Microsoft.BingAds.CustomerManagement.ApiFault> ex)
            {
                OutputStatusMessage(string.Join("; ", ex.Detail.OperationErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message))));
            }
            catch (Exception ex)
            {
                OutputStatusMessage(ex.Message);
            }
        }
        public async override Task RunAsync(AuthorizationData authorizationData)
        {
            try
            {
                Service = new ServiceClient<ICampaignManagementService>(authorizationData);

                // Add a campaign that will later be associated with ad extensions. 

                var campaign = GetExampleCampaign();

                var campaignIds = await AddCampaignsAsync(authorizationData.AccountId, new[] { campaign });

                // Specify the extensions.

                var adExtensions = new AdExtension[] {
                    new AppAdExtension
                    {
                        AppPlatform="Windows",
                        AppStoreId="AppStoreIdGoesHere",
                        DestinationUrl="DestinationUrlGoesHere",
                        DisplayText="Contoso",
                    },
                    new CallAdExtension {
                        CountryCode = "US",
                        PhoneNumber = "2065550100",
                        IsCallOnly = false
                    },
                    new LocationAdExtension {
                        PhoneNumber = "206-555-0100",
                        CompanyName = "Contoso Shoes",
                        IconMediaId = null,
                        ImageMediaId = null,
                        Address = new Address {
                            StreetAddress = "1234 Washington Place",
                            StreetAddress2 = "Suite 1210",
                            CityName = "Woodinville",
                            ProvinceName = "WA",
                            CountryCode = "US",
                            PostalCode = "98608"
                        }
                    },
                    new SiteLinksAdExtension {
                        SiteLinks = new [] {
                            new SiteLink {
                                DestinationUrl = "Contoso.com/WomenShoeSale",
                                DisplayText = "Women's Shoe Sale"
                            }
                        }
                    }
                };

                // Add all extensions to the account's ad extension library
                var adExtensionIdentities = await AddAdExtensionsAsync(
                    authorizationData.AccountId,
                    adExtensions
                    );

                OutputStatusMessage("Added ad extensions.\n\n");

                // DeleteAdExtensionsAssociations, SetAdExtensionsAssociations, and GetAdExtensionsEditorialReasons 
                // operations each require a list of type AdExtensionIdToEntityIdAssociation.
                var adExtensionIdToEntityIdAssociations = new AdExtensionIdToEntityIdAssociation[adExtensionIdentities.Count];

                // GetAdExtensionsByIds requires a list of type long.
                var adExtensionIds = new long[adExtensionIdentities.Count];

                // Loop through the list of extension IDs and build any required data structures
                // for subsequent operations. 

                for (int i = 0; i < adExtensionIdentities.Count; i++)
                {
                    adExtensionIdToEntityIdAssociations[i] = new AdExtensionIdToEntityIdAssociation
                    {
                        AdExtensionId = adExtensionIdentities[i].Id,
                        EntityId = campaignIds[0]
                    };

                    adExtensionIds[i] = adExtensionIdentities[i].Id;
                }

                // Associate the specified ad extensions with the respective campaigns or ad groups. 
                SetAdExtensionsAssociationsAsync(
                    authorizationData.AccountId,
                    adExtensionIdToEntityIdAssociations,
                    AssociationType.Campaign
                    );

                OutputStatusMessage("Set ad extension associations.\n\n");

                // Get editorial rejection reasons for the respective ad extension and entity associations.
                var adExtensionEditorialReasonCollection =
                    (AdExtensionEditorialReasonCollection[])await GetAdExtensionsEditorialReasons(
                        authorizationData.AccountId,
                        adExtensionIdToEntityIdAssociations,
                        AssociationType.Campaign
                        );

                const AdExtensionsTypeFilter adExtensionsTypeFilter = AdExtensionsTypeFilter.AppAdExtension |
                                                                      AdExtensionsTypeFilter.CallAdExtension |
                                                                      AdExtensionsTypeFilter.LocationAdExtension |
                                                                      AdExtensionsTypeFilter.SiteLinksAdExtension;

                // Get the specified ad extensions from the account’s ad extension library.
                adExtensions = (AdExtension[])await GetAdExtensionsByIdsAsync(
                    authorizationData.AccountId,
                    adExtensionIds,
                    adExtensionsTypeFilter
                    );

                PrintAdExtensions(adExtensions, adExtensionEditorialReasonCollection);

                // Remove the specified associations from the respective campaigns or ad groups. 
                // The extesions are still available in the account's extensions library. 
                DeleteAdExtensionsAssociationsAsync(
                    authorizationData.AccountId,
                    adExtensionIdToEntityIdAssociations,
                    AssociationType.Campaign
                    );

                OutputStatusMessage("Deleted ad extension associations.\n\n");

                // Deletes the ad extensions from the account’s ad extension library.
                DeleteAdExtensionsAsync(
                    authorizationData.AccountId,
                    adExtensionIds
                    );

                OutputStatusMessage("Deleted ad extensions.\n\n");
            }
            // Catch authentication exceptions
            catch (OAuthTokenRequestException ex)
            {
                OutputStatusMessage(string.Format("Couldn't get OAuth tokens. Error: {0}. Description: {1}", ex.Details.Error, ex.Details.Description));
            }
            // Catch Campaign Management service exceptions
            catch (FaultException<Microsoft.BingAds.CampaignManagement.AdApiFaultDetail> ex)
            {
                OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message))));
            }
            catch (FaultException<Microsoft.BingAds.CampaignManagement.ApiFaultDetail> ex)
            {
                OutputStatusMessage(string.Join("; ", ex.Detail.OperationErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message))));
                OutputStatusMessage(string.Join("; ", ex.Detail.BatchErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message))));
            }
            catch (FaultException<Microsoft.BingAds.CampaignManagement.EditorialApiFaultDetail> ex)
            {
                OutputStatusMessage(string.Join("; ", ex.Detail.OperationErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message))));
                OutputStatusMessage(string.Join("; ", ex.Detail.BatchErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message))));
            }
            catch (Exception ex)
            {
                OutputStatusMessage(ex.Message);
            }
        }
Example #5
0
        internal override void ProcessMappingsFromRowValues(RowValues values)
        {
            AdExtensionIdToEntityIdAssociation = new AdExtensionIdToEntityIdAssociation();

            values.ConvertToEntity(this, Mappings);
        }
        internal override void ProcessMappingsFromRowValues(RowValues values)
        {
            AdExtensionIdToEntityIdAssociation = new AdExtensionIdToEntityIdAssociation();            

            values.ConvertToEntity(this, Mappings);

            PerformanceData = PerformanceData.ReadFromRowValuesOrNull(values);
        }
        public async override Task RunAsync(AuthorizationData authorizationData)
        {
            try
            {
                Service = new ServiceClient<ICampaignManagementService>(authorizationData);

                // Add a campaign that will later be associated with ad extensions. 

                var campaigns = new[] {
                    new Campaign
                    {
                        Id = null,
                        Name = "Women's Shoes " + DateTime.UtcNow,
                        Description = "Red shoes line.",
                        BudgetType = BudgetLimitType.MonthlyBudgetSpendUntilDepleted,
                        MonthlyBudget = 1000.00,
                        TimeZone = "PacificTimeUSCanadaTijuana",
                        DaylightSaving = true,

                        // Used with FinalUrls shown in the sitelinks that we will add below.
                        TrackingUrlTemplate =
                            "http://tracker.example.com/?season={_season}&promocode={_promocode}&u={lpurl}"
                    }
                };

                AddCampaignsResponse addCampaignsResponse = await AddCampaignsAsync(authorizationData.AccountId, campaigns);
                long?[] campaignIds = addCampaignsResponse.CampaignIds.ToArray();

                // Specify the extensions.

                var adExtensions = new AdExtension[] {
                    //new AppAdExtension
                    //{
                    //    AppPlatform = "Windows",
                    //    AppStoreId = "AppStoreIdGoesHere",
                    //    DestinationUrl = "DestinationUrlGoesHere",
                    //    DisplayText = "Contoso",
                    //},
                    new CallAdExtension {
                        CountryCode = "US",
                        PhoneNumber = "2065550100",
                        IsCallOnly = false
                    },
                    //new ImageAdExtension
                    //{
                    //    AlternativeText = "Image Extension Alt Text",
                    //    ImageMediaIds = new long[] { await AddImageAsync(authorizationData) }
                    //},
                    new LocationAdExtension {
                        PhoneNumber = "206-555-0100",
                        CompanyName = "Contoso Shoes",
                        IconMediaId = null,
                        ImageMediaId = null,
                        Address = new Address {
                            StreetAddress = "1234 Washington Place",
                            StreetAddress2 = "Suite 1210",
                            CityName = "Woodinville",
                            ProvinceName = "WA",
                            CountryCode = "US",
                            PostalCode = "98608"
                        }
                    },
                    new SiteLinksAdExtension {
                        SiteLinks = new [] {
                            new SiteLink
                            {
                                DisplayText = "Women's Shoe Sale 1",

                                // If you are currently using Destination URLs, you must replace them with Final URLs. 
                                // Here is an example of a DestinationUrl you might have used previously. 
                                // DestinationUrl = "http://www.contoso.com/womenshoesale/?season=spring&promocode=PROMO123",

                                // To migrate from DestinationUrl to FinalUrls for existing sitelinks, you can set DestinationUrl
                                // to an empty string when updating the sitelink. If you are removing DestinationUrl,
                                // then FinalUrls is required.
                                // DestinationUrl = "",

                                // With FinalUrls you can separate the tracking template, custom parameters, and 
                                // landing page URLs. 
                                FinalUrls = new[] {
                                    "http://www.contoso.com/womenshoesale"
                                },
                                // Final Mobile URLs can also be used if you want to direct the user to a different page 
                                // for mobile devices.
                                FinalMobileUrls = new[] {
                                    "http://mobile.contoso.com/womenshoesale"
                                }, 
                                // You could use a tracking template which would override the campaign level
                                // tracking template. Tracking templates defined for lower level entities 
                                // override those set for higher level entities.
                                // In this example we are using the campaign level tracking template.
                                TrackingUrlTemplate = null,

                                // Set custom parameters that are specific to this sitelink, 
                                // and can be used by the sitelink, ad group, campaign, or account level tracking template. 
                                // In this example we are using the campaign level tracking template.
                                UrlCustomParameters = new CustomParameters {
                                    Parameters = new[] {
                                        new CustomParameter(){
                                            Key = "promoCode",
                                            Value = "PROMO1"
                                        },
                                        new CustomParameter(){
                                            Key = "season",
                                            Value = "summer"
                                        },
                                    }
                                },
                            },
                        new SiteLink
                            {
                                DisplayText = "Women's Shoe Sale 2",

                                // If you are currently using Destination URLs, you must replace them with Final URLs. 
                                // Here is an example of a DestinationUrl you might have used previously. 
                                // DestinationUrl = "http://www.contoso.com/womenshoesale/?season=spring&promocode=PROMO123",

                                // To migrate from DestinationUrl to FinalUrls for existing sitelinks, you can set DestinationUrl
                                // to an empty string when updating the sitelink. If you are removing DestinationUrl,
                                // then FinalUrls is required.
                                // DestinationUrl = "",

                                // With FinalUrls you can separate the tracking template, custom parameters, and 
                                // landing page URLs. 
                                FinalUrls = new[] {
                                    "http://www.contoso.com/womenshoesale"
                                },
                                // Final Mobile URLs can also be used if you want to direct the user to a different page 
                                // for mobile devices.
                                FinalMobileUrls = new[] {
                                    "http://mobile.contoso.com/womenshoesale"
                                }, 
                                // You could use a tracking template which would override the campaign level
                                // tracking template. Tracking templates defined for lower level entities 
                                // override those set for higher level entities.
                                // In this example we are using the campaign level tracking template.
                                TrackingUrlTemplate = null,

                                // Set custom parameters that are specific to this sitelink, 
                                // and can be used by the sitelink, ad group, campaign, or account level tracking template. 
                                // In this example we are using the campaign level tracking template.
                                UrlCustomParameters = new CustomParameters {
                                    Parameters = new[] {
                                        new CustomParameter(){
                                            Key = "promoCode",
                                            Value = "PROMO2"
                                        },
                                        new CustomParameter(){
                                            Key = "season",
                                            Value = "summer"
                                        },
                                    }
                                },
                            }
                        }
                    }
                };

                // Add all extensions to the account's ad extension library
                var adExtensionIdentities = await AddAdExtensionsAsync(
                    authorizationData.AccountId,
                    adExtensions
                    );

                OutputStatusMessage("Added ad extensions.\n\n");

                // DeleteAdExtensionsAssociations, SetAdExtensionsAssociations, and GetAdExtensionsEditorialReasons 
                // operations each require a list of type AdExtensionIdToEntityIdAssociation.
                var adExtensionIdToEntityIdAssociations = new AdExtensionIdToEntityIdAssociation[adExtensionIdentities.Count];

                // GetAdExtensionsByIds requires a list of type long.
                var adExtensionIds = new long[adExtensionIdentities.Count];

                // Loop through the list of extension IDs and build any required data structures
                // for subsequent operations. 

                for (int i = 0; i < adExtensionIdentities.Count; i++)
                {
                    adExtensionIdToEntityIdAssociations[i] = new AdExtensionIdToEntityIdAssociation
                    {
                        AdExtensionId = adExtensionIdentities[i].Id,
                        EntityId = (long)campaignIds[0]
                    };

                    adExtensionIds[i] = adExtensionIdentities[i].Id;
                }

                // Associate the specified ad extensions with the respective campaigns or ad groups. 
                await SetAdExtensionsAssociationsAsync(
                    authorizationData.AccountId,
                    adExtensionIdToEntityIdAssociations,
                    AssociationType.Campaign
                    );

                OutputStatusMessage("Set ad extension associations.\n\n");

                // Get editorial rejection reasons for the respective ad extension and entity associations.
                var adExtensionEditorialReasonCollection =
                    (AdExtensionEditorialReasonCollection[])await GetAdExtensionsEditorialReasons(
                        authorizationData.AccountId,
                        adExtensionIdToEntityIdAssociations,
                        AssociationType.Campaign
                        );

                const AdExtensionsTypeFilter adExtensionsTypeFilter = AdExtensionsTypeFilter.AppAdExtension |
                                                                      AdExtensionsTypeFilter.CallAdExtension |
                                                                      AdExtensionsTypeFilter.ImageAdExtension | 
                                                                      AdExtensionsTypeFilter.LocationAdExtension |
                                                                      AdExtensionsTypeFilter.SiteLinksAdExtension;

                // Get the specified ad extensions from the account’s ad extension library.
                adExtensions = (AdExtension[])await GetAdExtensionsByIdsAsync(
                    authorizationData.AccountId,
                    adExtensionIds,
                    adExtensionsTypeFilter
                    );

                OutputAdExtensionsWithEditorialReasons(adExtensions, adExtensionEditorialReasonCollection);

                // Remove the specified associations from the respective campaigns or ad groups. 
                // The extesions are still available in the account's extensions library. 
                await DeleteAdExtensionsAssociationsAsync(
                    authorizationData.AccountId,
                    adExtensionIdToEntityIdAssociations,
                    AssociationType.Campaign
                    );

                OutputStatusMessage("Deleted ad extension associations.\n\n");

                // Deletes the ad extensions from the account’s ad extension library.
                await DeleteAdExtensionsAsync(
                    authorizationData.AccountId,
                    adExtensionIds
                    );

                OutputStatusMessage("Deleted ad extensions.\n\n");
            }
            // Catch authentication exceptions
            catch (OAuthTokenRequestException ex)
            {
                OutputStatusMessage(string.Format("Couldn't get OAuth tokens. Error: {0}. Description: {1}", ex.Details.Error, ex.Details.Description));
            }
            // Catch Campaign Management service exceptions
            catch (FaultException<Microsoft.BingAds.V10.CampaignManagement.AdApiFaultDetail> ex)
            {
                OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message))));
            }
            catch (FaultException<Microsoft.BingAds.V10.CampaignManagement.ApiFaultDetail> ex)
            {
                OutputStatusMessage(string.Join("; ", ex.Detail.OperationErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message))));
                OutputStatusMessage(string.Join("; ", ex.Detail.BatchErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message))));
            }
            catch (FaultException<Microsoft.BingAds.V10.CampaignManagement.EditorialApiFaultDetail> ex)
            {
                OutputStatusMessage(string.Join("; ", ex.Detail.OperationErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message))));
                OutputStatusMessage(string.Join("; ", ex.Detail.BatchErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message))));
            }
            catch (Exception ex)
            {
                OutputStatusMessage(ex.Message);
            }
        }
Example #8
0
        public async override Task RunAsync(AuthorizationData authorizationData)
        {
            try
            {
                Service = new ServiceClient <ICampaignManagementService>(authorizationData);

                // Add a campaign that will later be associated with ad extensions.

                var campaign = GetExampleCampaign();

                var campaignIds = await AddCampaignsAsync(authorizationData.AccountId, new[] { campaign });

                // Specify the extensions.

                var adExtensions = new AdExtension[] {
                    new AppAdExtension
                    {
                        AppPlatform    = "Windows",
                        AppStoreId     = "AppStoreIdGoesHere",
                        DestinationUrl = "DestinationUrlGoesHere",
                        DisplayText    = "Contoso",
                    },
                    new CallAdExtension {
                        CountryCode = "US",
                        PhoneNumber = "2065550100",
                        IsCallOnly  = false
                    },
                    new LocationAdExtension {
                        PhoneNumber  = "206-555-0100",
                        CompanyName  = "Contoso Shoes",
                        IconMediaId  = null,
                        ImageMediaId = null,
                        Address      = new Address {
                            StreetAddress  = "1234 Washington Place",
                            StreetAddress2 = "Suite 1210",
                            CityName       = "Woodinville",
                            ProvinceName   = "WA",
                            CountryCode    = "US",
                            PostalCode     = "98608"
                        }
                    },
                    new SiteLinksAdExtension {
                        SiteLinks = new [] {
                            new SiteLink {
                                DestinationUrl = "Contoso.com/WomenShoeSale",
                                DisplayText    = "Women's Shoe Sale"
                            }
                        }
                    }
                };

                // Add all extensions to the account's ad extension library
                var adExtensionIdentities = await AddAdExtensionsAsync(
                    authorizationData.AccountId,
                    adExtensions
                    );

                OutputStatusMessage("Added ad extensions.\n\n");

                // DeleteAdExtensionsAssociations, SetAdExtensionsAssociations, and GetAdExtensionsEditorialReasons
                // operations each require a list of type AdExtensionIdToEntityIdAssociation.
                var adExtensionIdToEntityIdAssociations = new AdExtensionIdToEntityIdAssociation[adExtensionIdentities.Count];

                // GetAdExtensionsByIds requires a list of type long.
                var adExtensionIds = new long[adExtensionIdentities.Count];

                // Loop through the list of extension IDs and build any required data structures
                // for subsequent operations.

                for (int i = 0; i < adExtensionIdentities.Count; i++)
                {
                    adExtensionIdToEntityIdAssociations[i] = new AdExtensionIdToEntityIdAssociation
                    {
                        AdExtensionId = adExtensionIdentities[i].Id,
                        EntityId      = campaignIds[0]
                    };

                    adExtensionIds[i] = adExtensionIdentities[i].Id;
                }

                // Associate the specified ad extensions with the respective campaigns or ad groups.
                SetAdExtensionsAssociationsAsync(
                    authorizationData.AccountId,
                    adExtensionIdToEntityIdAssociations,
                    AssociationType.Campaign
                    );

                OutputStatusMessage("Set ad extension associations.\n\n");

                // Get editorial rejection reasons for the respective ad extension and entity associations.
                var adExtensionEditorialReasonCollection =
                    (AdExtensionEditorialReasonCollection[]) await GetAdExtensionsEditorialReasons(
                        authorizationData.AccountId,
                        adExtensionIdToEntityIdAssociations,
                        AssociationType.Campaign
                        );

                const AdExtensionsTypeFilter adExtensionsTypeFilter = AdExtensionsTypeFilter.AppAdExtension |
                                                                      AdExtensionsTypeFilter.CallAdExtension |
                                                                      AdExtensionsTypeFilter.LocationAdExtension |
                                                                      AdExtensionsTypeFilter.SiteLinksAdExtension;

                // Get the specified ad extensions from the account’s ad extension library.
                adExtensions = (AdExtension[]) await GetAdExtensionsByIdsAsync(
                    authorizationData.AccountId,
                    adExtensionIds,
                    adExtensionsTypeFilter
                    );

                PrintAdExtensions(adExtensions, adExtensionEditorialReasonCollection);

                // Remove the specified associations from the respective campaigns or ad groups.
                // The extesions are still available in the account's extensions library.
                DeleteAdExtensionsAssociationsAsync(
                    authorizationData.AccountId,
                    adExtensionIdToEntityIdAssociations,
                    AssociationType.Campaign
                    );

                OutputStatusMessage("Deleted ad extension associations.\n\n");

                // Deletes the ad extensions from the account’s ad extension library.
                DeleteAdExtensionsAsync(
                    authorizationData.AccountId,
                    adExtensionIds
                    );

                OutputStatusMessage("Deleted ad extensions.\n\n");
            }
            // Catch authentication exceptions
            catch (OAuthTokenRequestException ex)
            {
                OutputStatusMessage(string.Format("Couldn't get OAuth tokens. Error: {0}. Description: {1}", ex.Details.Error, ex.Details.Description));
            }
            // Catch Campaign Management service exceptions
            catch (FaultException <Microsoft.BingAds.CampaignManagement.AdApiFaultDetail> ex)
            {
                OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message))));
            }
            catch (FaultException <Microsoft.BingAds.CampaignManagement.ApiFaultDetail> ex)
            {
                OutputStatusMessage(string.Join("; ", ex.Detail.OperationErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message))));
                OutputStatusMessage(string.Join("; ", ex.Detail.BatchErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message))));
            }
            catch (FaultException <Microsoft.BingAds.CampaignManagement.EditorialApiFaultDetail> ex)
            {
                OutputStatusMessage(string.Join("; ", ex.Detail.OperationErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message))));
                OutputStatusMessage(string.Join("; ", ex.Detail.BatchErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message))));
            }
            catch (Exception ex)
            {
                OutputStatusMessage(ex.Message);
            }
        }
        public async override Task RunAsync(AuthorizationData authorizationData)
        {
            try
            {
                Service = new ServiceClient <ICampaignManagementService>(authorizationData);

                // Add a campaign that will later be associated with ad extensions.

                var campaigns = new[] {
                    new Campaign
                    {
                        Id             = null,
                        Name           = "Women's Shoes " + DateTime.UtcNow,
                        Description    = "Red shoes line.",
                        BudgetType     = BudgetLimitType.MonthlyBudgetSpendUntilDepleted,
                        MonthlyBudget  = 1000.00,
                        TimeZone       = "PacificTimeUSCanadaTijuana",
                        DaylightSaving = true,

                        // Used with FinalUrls shown in the sitelinks that we will add below.
                        TrackingUrlTemplate =
                            "http://tracker.example.com/?season={_season}&promocode={_promocode}&u={lpurl}"
                    }
                };

                AddCampaignsResponse addCampaignsResponse = await AddCampaignsAsync(authorizationData.AccountId, campaigns);

                long?[] campaignIds = addCampaignsResponse.CampaignIds.ToArray();

                // Specify the extensions.

                var adExtensions = new AdExtension[] {
                    //new AppAdExtension
                    //{
                    //    AppPlatform = "Windows",
                    //    AppStoreId = "AppStoreIdGoesHere",
                    //    DestinationUrl = "DestinationUrlGoesHere",
                    //    DisplayText = "Contoso",
                    //},
                    new CallAdExtension {
                        CountryCode = "US",
                        PhoneNumber = "2065550100",
                        IsCallOnly  = false
                    },
                    new CalloutAdExtension
                    {
                        Text = "Callout Text"
                    },
                    //new ImageAdExtension
                    //{
                    //    AlternativeText = "Image Extension Alt Text",
                    //    ImageMediaIds = new long[] { await AddImageAsync(authorizationData) }
                    //},
                    new LocationAdExtension {
                        PhoneNumber  = "206-555-0100",
                        CompanyName  = "Contoso Shoes",
                        IconMediaId  = null,
                        ImageMediaId = null,
                        Address      = new Address {
                            StreetAddress  = "1234 Washington Place",
                            StreetAddress2 = "Suite 1210",
                            CityName       = "Woodinville",
                            ProvinceName   = "WA",
                            CountryCode    = "US",
                            PostalCode     = "98608"
                        }
                    },
                    new ReviewAdExtension
                    {
                        IsExact = true,
                        Source  = "Review Source Name",
                        Text    = "Review Text",
                        Url     = "http://review.contoso.com" // The Url of the third-party review. This is not your business Url.
                    },
                    new SiteLinksAdExtension {
                        SiteLinks = new [] {
                            new SiteLink
                            {
                                DisplayText = "Women's Shoe Sale 1",

                                // If you are currently using Destination URLs, you must replace them with Final URLs.
                                // Here is an example of a DestinationUrl you might have used previously.
                                // DestinationUrl = "http://www.contoso.com/womenshoesale/?season=spring&promocode=PROMO123",

                                // To migrate from DestinationUrl to FinalUrls for existing sitelinks, you can set DestinationUrl
                                // to an empty string when updating the sitelink. If you are removing DestinationUrl,
                                // then FinalUrls is required.
                                // DestinationUrl = "",

                                // With FinalUrls you can separate the tracking template, custom parameters, and
                                // landing page URLs.
                                FinalUrls = new[] {
                                    "http://www.contoso.com/womenshoesale"
                                },
                                // Final Mobile URLs can also be used if you want to direct the user to a different page
                                // for mobile devices.
                                FinalMobileUrls = new[] {
                                    "http://mobile.contoso.com/womenshoesale"
                                },
                                // You could use a tracking template which would override the campaign level
                                // tracking template. Tracking templates defined for lower level entities
                                // override those set for higher level entities.
                                // In this example we are using the campaign level tracking template.
                                TrackingUrlTemplate = null,

                                // Set custom parameters that are specific to this sitelink,
                                // and can be used by the sitelink, ad group, campaign, or account level tracking template.
                                // In this example we are using the campaign level tracking template.
                                UrlCustomParameters = new CustomParameters {
                                    Parameters = new[] {
                                        new CustomParameter()
                                        {
                                            Key   = "promoCode",
                                            Value = "PROMO1"
                                        },
                                        new CustomParameter()
                                        {
                                            Key   = "season",
                                            Value = "summer"
                                        },
                                    }
                                },
                            },
                            new SiteLink
                            {
                                DisplayText = "Women's Shoe Sale 2",

                                // If you are currently using Destination URLs, you must replace them with Final URLs.
                                // Here is an example of a DestinationUrl you might have used previously.
                                // DestinationUrl = "http://www.contoso.com/womenshoesale/?season=spring&promocode=PROMO123",

                                // To migrate from DestinationUrl to FinalUrls for existing sitelinks, you can set DestinationUrl
                                // to an empty string when updating the sitelink. If you are removing DestinationUrl,
                                // then FinalUrls is required.
                                // DestinationUrl = "",

                                // With FinalUrls you can separate the tracking template, custom parameters, and
                                // landing page URLs.
                                FinalUrls = new[] {
                                    "http://www.contoso.com/womenshoesale"
                                },
                                // Final Mobile URLs can also be used if you want to direct the user to a different page
                                // for mobile devices.
                                FinalMobileUrls = new[] {
                                    "http://mobile.contoso.com/womenshoesale"
                                },
                                // You could use a tracking template which would override the campaign level
                                // tracking template. Tracking templates defined for lower level entities
                                // override those set for higher level entities.
                                // In this example we are using the campaign level tracking template.
                                TrackingUrlTemplate = null,

                                // Set custom parameters that are specific to this sitelink,
                                // and can be used by the sitelink, ad group, campaign, or account level tracking template.
                                // In this example we are using the campaign level tracking template.
                                UrlCustomParameters = new CustomParameters {
                                    Parameters = new[] {
                                        new CustomParameter()
                                        {
                                            Key   = "promoCode",
                                            Value = "PROMO2"
                                        },
                                        new CustomParameter()
                                        {
                                            Key   = "season",
                                            Value = "summer"
                                        },
                                    }
                                },
                            }
                        }
                    }
                };

                // Add all extensions to the account's ad extension library
                var adExtensionIdentities = await AddAdExtensionsAsync(
                    authorizationData.AccountId,
                    adExtensions
                    );

                OutputStatusMessage("Added ad extensions.\n\n");

                // DeleteAdExtensionsAssociations, SetAdExtensionsAssociations, and GetAdExtensionsEditorialReasons
                // operations each require a list of type AdExtensionIdToEntityIdAssociation.
                var adExtensionIdToEntityIdAssociations = new AdExtensionIdToEntityIdAssociation[adExtensionIdentities.Count];

                // GetAdExtensionsByIds requires a list of type long.
                var adExtensionIds = new long[adExtensionIdentities.Count];

                // Loop through the list of extension IDs and build any required data structures
                // for subsequent operations.

                for (int i = 0; i < adExtensionIdentities.Count; i++)
                {
                    adExtensionIdToEntityIdAssociations[i] = new AdExtensionIdToEntityIdAssociation
                    {
                        AdExtensionId = adExtensionIdentities[i].Id,
                        EntityId      = (long)campaignIds[0]
                    };

                    adExtensionIds[i] = adExtensionIdentities[i].Id;
                }

                // Associate the specified ad extensions with the respective campaigns or ad groups.
                await SetAdExtensionsAssociationsAsync(
                    authorizationData.AccountId,
                    adExtensionIdToEntityIdAssociations,
                    AssociationType.Campaign
                    );

                OutputStatusMessage("Set ad extension associations.\n\n");

                // Get editorial rejection reasons for the respective ad extension and entity associations.
                var adExtensionEditorialReasonCollection =
                    (AdExtensionEditorialReasonCollection[]) await GetAdExtensionsEditorialReasons(
                        authorizationData.AccountId,
                        adExtensionIdToEntityIdAssociations,
                        AssociationType.Campaign
                        );

                const AdExtensionsTypeFilter adExtensionsTypeFilter = AdExtensionsTypeFilter.AppAdExtension |
                                                                      AdExtensionsTypeFilter.CallAdExtension |
                                                                      AdExtensionsTypeFilter.CalloutAdExtension |
                                                                      AdExtensionsTypeFilter.ImageAdExtension |
                                                                      AdExtensionsTypeFilter.LocationAdExtension |
                                                                      AdExtensionsTypeFilter.ReviewAdExtension |
                                                                      AdExtensionsTypeFilter.SiteLinksAdExtension;

                // Get the specified ad extensions from the account’s ad extension library.
                adExtensions = (AdExtension[]) await GetAdExtensionsByIdsAsync(
                    authorizationData.AccountId,
                    adExtensionIds,
                    adExtensionsTypeFilter
                    );

                OutputAdExtensionsWithEditorialReasons(adExtensions, adExtensionEditorialReasonCollection);

                // Remove the specified associations from the respective campaigns or ad groups.
                // The extesions are still available in the account's extensions library.
                await DeleteAdExtensionsAssociationsAsync(
                    authorizationData.AccountId,
                    adExtensionIdToEntityIdAssociations,
                    AssociationType.Campaign
                    );

                OutputStatusMessage("Deleted ad extension associations.\n\n");

                // Deletes the ad extensions from the account’s ad extension library.
                await DeleteAdExtensionsAsync(
                    authorizationData.AccountId,
                    adExtensionIds
                    );

                OutputStatusMessage("Deleted ad extensions.\n\n");
            }
            // Catch authentication exceptions
            catch (OAuthTokenRequestException ex)
            {
                OutputStatusMessage(string.Format("Couldn't get OAuth tokens. Error: {0}. Description: {1}", ex.Details.Error, ex.Details.Description));
            }
            // Catch Campaign Management service exceptions
            catch (FaultException <Microsoft.BingAds.V10.CampaignManagement.AdApiFaultDetail> ex)
            {
                OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message))));
            }
            catch (FaultException <Microsoft.BingAds.V10.CampaignManagement.ApiFaultDetail> ex)
            {
                OutputStatusMessage(string.Join("; ", ex.Detail.OperationErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message))));
                OutputStatusMessage(string.Join("; ", ex.Detail.BatchErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message))));
            }
            catch (FaultException <Microsoft.BingAds.V10.CampaignManagement.EditorialApiFaultDetail> ex)
            {
                OutputStatusMessage(string.Join("; ", ex.Detail.OperationErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message))));
                OutputStatusMessage(string.Join("; ", ex.Detail.BatchErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message))));
            }
            catch (Exception ex)
            {
                OutputStatusMessage(ex.Message);
            }
        }