public static string ToAdGroupCriterionFixedBidBulkString(this FixedBid bid) { if (bid == null) { return(null); } return(bid.Amount.ToBulkString()); }
public async override Task RunAsync(AuthorizationData authorizationData) { try { ApiEnvironment environment = ((OAuthDesktopMobileAuthCodeGrant)authorizationData.Authentication).Environment; // You will need to use the Campaign Management service to get the Bing Merchant Center Store Id. // This will be used when creating a new Bing Shopping Campaign. // For other operations such as adding product conditions, // you can manage Bing Shopping Campaigns solely with the Bulk Service. CampaignManagementExampleHelper = new CampaignManagementExampleHelper(this.OutputStatusMessage); CampaignManagementExampleHelper.CampaignManagementService = new ServiceClient <ICampaignManagementService>(authorizationData, environment); BulkServiceManager = new BulkServiceManager(authorizationData, environment); var progress = new Progress <BulkOperationProgressInfo>(x => OutputStatusMessage(string.Format("{0} % Complete", x.PercentComplete.ToString(CultureInfo.InvariantCulture)))); #region CampaignThroughAdGroupSetup // Get a list of all Bing Merchant Center stores associated with your CustomerId IList <BMCStore> stores = (await CampaignManagementExampleHelper.GetBMCStoresByCustomerIdAsync())?.BMCStores; if (stores == null) { OutputStatusMessage( string.Format("You do not have any BMC stores registered for CustomerId {0}.\n", authorizationData.CustomerId) ); return; } var uploadEntities = new List <BulkEntity>(); /* Add a new Bing Shopping campaign that will be associated with a ProductScope criterion. * - Set the CampaignType element of the Campaign to Shopping. * - Create a ShoppingSetting instance and set its Priority (0, 1, or 2), SalesCountryCode, and StoreId elements. * Add this shopping setting to the Settings list of the Campaign. */ var bulkCampaign = new BulkCampaign { // ClientId may be used to associate records in the bulk upload file with records in the results file. The value of this field // is not used or stored by the server; it is simply copied from the uploaded record to the corresponding result record. // Note: This bulk file Client Id is not related to an application Client Id for OAuth. ClientId = "YourClientIdGoesHere", Campaign = new Campaign { // When using the Campaign Management service, the Id cannot be set. In the context of a BulkCampaign, the Id is optional // and may be used as a negative reference key during bulk upload. For example the same negative value set for the campaign Id // will be used when associating this new campaign with a new campaign product scope in the BulkCampaignProductScope object below. Id = campaignIdKey, CampaignType = CampaignType.Shopping, Settings = new[] { new ShoppingSetting() { Priority = 0, SalesCountryCode = "US", StoreId = (int)stores[0].Id } }, Name = "Bing Shopping Campaign " + DateTime.UtcNow, Description = "Bing Shopping Campaign Example.", // 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, TimeZone = "PacificTimeUSCanadaTijuana", // Used with CustomParameters defined in lower level entities such as ad group criterion. TrackingUrlTemplate = "http://tracker.example.com/?season={_season}&promocode={_promocode}&u={lpurl}" } }; /* Optionally, you can create a ProductScope criterion that will be associated with your Bing Shopping campaign. * Use the product scope criterion to include a subset of your product catalog, for example a specific brand, * category, or product type. A campaign can only be associated with one ProductScope, which contains a list * of up to 7 ProductCondition. You'll also be able to specify more specific product conditions for each ad group. */ var bulkCampaignProductScope = new BulkCampaignProductScope { BiddableCampaignCriterion = new BiddableCampaignCriterion() { CampaignId = campaignIdKey, CriterionBid = null, // Not applicable for product scope Criterion = new ProductScope() { Conditions = new ProductCondition[] { new ProductCondition { Operand = "Condition", Attribute = "New" }, new ProductCondition { Operand = "CustomLabel0", Attribute = "MerchantDefinedCustomLabel" }, } }, }, Status = Status.Active, }; // Specify one or more ad groups. var bulkAdGroup = new BulkAdGroup { CampaignId = campaignIdKey, AdGroup = new AdGroup { Id = adGroupIdKey, Name = "Product Categories", StartDate = null, EndDate = new Microsoft.BingAds.V12.CampaignManagement.Date { Month = 12, Day = 31, Year = DateTime.UtcNow.Year + 1 }, Language = "English", Status = AdGroupStatus.Active }, }; /* * Create a product ad. You must add at least one product ad to the ad group. * The product ad identifier can be used for reporting analytics. * Use Merchant Promotions if you want tags to appear at the bottom of your product ad * as "special offer" links, helping to increase customer engagement. For details * on Merchant Promotions see https://help.bingads.microsoft.com/#apex/3/en/56805/0. */ var bulkProductAd = new BulkProductAd { AdGroupId = adGroupIdKey, ProductAd = new ProductAd { } }; uploadEntities.Add(bulkCampaign); uploadEntities.Add(bulkAdGroup); uploadEntities.Add(bulkCampaignProductScope); uploadEntities.Add(bulkProductAd); // Upload and write the output var Reader = await WriteEntitiesAndUploadFileAsync(uploadEntities); var downloadEntities = Reader.ReadEntities().ToList(); var campaignResults = downloadEntities.OfType <BulkCampaign>().ToList(); OutputBulkCampaigns(campaignResults); var adGroupResults = downloadEntities.OfType <BulkAdGroup>().ToList(); OutputBulkAdGroups(adGroupResults); var productAdResults = downloadEntities.OfType <BulkProductAd>().ToList(); OutputBulkProductAds(productAdResults); var campaignProductScopeResults = downloadEntities.OfType <BulkCampaignProductScope>().ToList(); OutputBulkCampaignProductScopes(campaignProductScopeResults); Reader.Dispose(); #endregion CampaignThroughAdGroupSetup #region BidAllProducts var adGroupId = (long)adGroupResults[0].AdGroup.Id; var helper = new ProductPartitionHelper(adGroupId); var root = helper.AddUnit( null, new ProductCondition { Operand = "All", Attribute = null }, 0.35, false, "root" ); OutputStatusMessage("Applying only the root as a Unit with a bid . . . \n"); var applyBulkProductPartitionActionsResults = await ApplyBulkProductPartitionActions(helper.PartitionActions); var productPartitions = await GetBulkAdGroupProductPartitionTree(adGroupId); OutputStatusMessage("The ad group's product partition only has a tree root node: \n"); OutputProductPartitions(productPartitions); /* * Let's update the bid of the root Unit we just added. */ var updatedRoot = GetNodeByClientId(applyBulkProductPartitionActionsResults, "root"); var bid = new FixedBid { Amount = 0.45 }; ((BiddableAdGroupCriterion)(updatedRoot.AdGroupCriterion)).CriterionBid = bid; helper = new ProductPartitionHelper(adGroupId); helper.UpdatePartition(updatedRoot); OutputStatusMessage("Updating the bid for the tree root node . . . \n"); await ApplyBulkProductPartitionActions(helper.PartitionActions); productPartitions = await GetBulkAdGroupProductPartitionTree(adGroupId); OutputStatusMessage("Updated the bid for the tree root node: \n"); OutputProductPartitions(productPartitions); #endregion BidAllProducts #region InitializeTree /* * Now we will overwrite any existing tree root, and build a product partition group tree structure in multiple steps. * You could build the entire tree in a single call since there are less than 20,000 nodes; however, * we will build it in steps to demonstrate how to use the results from bulk upload to update the tree. * * For a list of validation rules, see the Product Ads technical guide: * https://docs.microsoft.com/en-us/bingads/guides/product-ads */ helper = new ProductPartitionHelper(adGroupId); /* * Check whether a root node exists already. */ var existingRoot = GetNodeByClientId(applyBulkProductPartitionActionsResults, "root"); if (existingRoot != null) { existingRoot.ClientId = "deletedroot"; helper.DeletePartition(existingRoot); } root = helper.AddSubdivision( null, new ProductCondition { Operand = "All", Attribute = null }, "root" ); /* * The direct children of any node must have the same Operand. * For this example we will use CategoryL1 nodes as children of the root. * For a list of valid CategoryL1 through CategoryL5 values, see the Bing Category Taxonomy: * http://go.microsoft.com/fwlink?LinkId=507666 */ var animalsSubdivision = helper.AddSubdivision( root, new ProductCondition { Operand = "CategoryL1", Attribute = "Animals & Pet Supplies" }, "animalsSubdivision" ); /* * If you use a CategoryL2 node, it must be a descendant (child or later) of a CategoryL1 node. * In other words you cannot have a CategoryL2 node as parent of a CategoryL1 node. * For this example we will a CategoryL2 node as child of the CategoryL1 Animals & Pet Supplies node. */ var petSuppliesSubdivision = helper.AddSubdivision( animalsSubdivision, new ProductCondition { Operand = "CategoryL2", Attribute = "Pet Supplies" }, "petSuppliesSubdivision" ); var brandA = helper.AddUnit( petSuppliesSubdivision, new ProductCondition { Operand = "Brand", Attribute = "Brand A" }, 0.35, false, "brandA" ); /* * If you won't bid on Brand B, set the helper method's bidAmount to '0' and isNegative to true. * The helper method will create a NegativeAdGroupCriterion and apply the condition. */ var brandB = helper.AddUnit( petSuppliesSubdivision, new ProductCondition { Operand = "Brand", Attribute = "Brand B" }, 0, true, "brandB" ); var otherBrands = helper.AddUnit( petSuppliesSubdivision, new ProductCondition { Operand = "Brand", Attribute = null }, 0.35, false, "otherBrands" ); var otherPetSupplies = helper.AddUnit( animalsSubdivision, new ProductCondition { Operand = "CategoryL2", Attribute = null }, 0.35, false, "otherPetSupplies" ); var electronics = helper.AddUnit( root, new ProductCondition { Operand = "CategoryL1", Attribute = "Electronics" }, 0.35, false, "electronics" ); var otherCategoryL1 = helper.AddUnit( root, new ProductCondition { Operand = "CategoryL1", Attribute = null }, 0.35, false, "otherCategoryL1" ); OutputStatusMessage("Applying product partitions to the ad group . . . \n"); applyBulkProductPartitionActionsResults = await ApplyBulkProductPartitionActions(helper.PartitionActions); productPartitions = await GetBulkAdGroupProductPartitionTree(adGroupId); /* * The product partition group tree now has 9 nodes. * * All other (Root Node) | +-- Animals & Pet Supplies (CategoryL1) | | | +-- Pet Supplies (CategoryL2) | | | | | +-- Brand A | | | | | +-- Brand B | | | | | +-- All other (Brand) | | | +-- All other (CategoryL2) | +-- Electronics (CategoryL1) | +-- All other (CategoryL1) | */ OutputStatusMessage("The product partition group tree now has 9 nodes: \n"); OutputProductPartitions(productPartitions); #endregion InitializeTree #region UpdateTree /* * Let's replace the Electronics (CategoryL1) node created above with an Electronics (CategoryL1) node that * has children i.e. Brand C (Brand), Brand D (Brand), and All other (Brand) as follows: * * Electronics (CategoryL1) | +-- Brand C (Brand) | +-- Brand D (Brand) | +-- All other (Brand) | */ helper = new ProductPartitionHelper(adGroupId); /* * To replace a node we must know its Id and its ParentCriterionId. In this case the parent of the node * we are replacing is All other (Root Node). The node that we are replacing is Electronics (CategoryL1). */ var rootId = GetNodeByClientId(applyBulkProductPartitionActionsResults, "root").AdGroupCriterion.Id; electronics.AdGroupCriterion.Id = GetNodeByClientId(applyBulkProductPartitionActionsResults, "electronics").AdGroupCriterion.Id; helper.DeletePartition(electronics); var parent = new BulkAdGroupProductPartition { AdGroupCriterion = new BiddableAdGroupCriterion() { Id = rootId } }; var electronicsSubdivision = helper.AddSubdivision( parent, new ProductCondition { Operand = "CategoryL1", Attribute = "Electronics" }, "electronicsSubdivision" ); var brandC = helper.AddUnit( electronicsSubdivision, new ProductCondition { Operand = "Brand", Attribute = "Brand C" }, 0.35, false, "brandC" ); var brandD = helper.AddUnit( electronicsSubdivision, new ProductCondition { Operand = "Brand", Attribute = "Brand D" }, 0.35, false, "brandD" ); var otherElectronicsBrands = helper.AddUnit( electronicsSubdivision, new ProductCondition { Operand = "Brand", Attribute = null }, 0.35, false, "otherElectronicsBrands" ); OutputStatusMessage( "Updating the product partition group to refine Electronics (CategoryL1) with 3 child nodes . . . \n" ); applyBulkProductPartitionActionsResults = await ApplyBulkProductPartitionActions(helper.PartitionActions); productPartitions = await GetBulkAdGroupProductPartitionTree(adGroupId); /* * The product partition group tree now has 12 nodes, including the children of Electronics (CategoryL1): * * All other (Root Node) | +-- Animals & Pet Supplies (CategoryL1) | | | +-- Pet Supplies (CategoryL2) | | | | | +-- Brand A | | | | | +-- Brand B | | | | | +-- All other (Brand) | | | +-- All other (CategoryL2) | +-- Electronics (CategoryL1) | | | +-- Brand C (Brand) | | | +-- Brand D (Brand) | | | +-- All other (Brand) | +-- All other (CategoryL1) | */ OutputStatusMessage( "The product partition group tree now has 12 nodes, including the children of Electronics (CategoryL1): \n" ); OutputProductPartitions(productPartitions); #endregion UpdateTree #region CleanUp //Delete the campaign, ad group, criterion, and ad that were previously added. //You should remove this region if you want to view the added entities in the //Bing Ads web application or another tool. //You must set the Id field to the corresponding entity identifier, and the Status field to Deleted. //When you delete a BulkCampaign, the dependent entities such as BulkAdGroup and BulkAdGroupProductPartition //are deleted without being specified explicitly. uploadEntities = new List <BulkEntity>(); foreach (var campaignResult in campaignResults) { campaignResult.Campaign.Status = CampaignStatus.Deleted; uploadEntities.Add(campaignResult); } // Upload and write the output OutputStatusMessage( "Deleting the campaign, product conditions, ad group, product partitions, and product ad... \n" ); Reader = await WriteEntitiesAndUploadFileAsync(uploadEntities); downloadEntities = Reader.ReadEntities().ToList(); OutputBulkCampaigns(downloadEntities.OfType <BulkCampaign>().ToList()); Reader.Dispose(); #endregion Cleanup } // Catch Microsoft Account authorization exceptions. catch (OAuthTokenRequestException ex) { OutputStatusMessage(string.Format("Couldn't get OAuth tokens. Error: {0}. Description: {1}", ex.Details.Error, ex.Details.Description)); } // Catch Bulk service exceptions catch (FaultException <Microsoft.BingAds.V12.Bulk.AdApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException <Microsoft.BingAds.V12.Bulk.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 Campaign Management service exceptions catch (FaultException <Microsoft.BingAds.V12.CampaignManagement.AdApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException <Microsoft.BingAds.V12.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 (BulkOperationInProgressException ex) { OutputStatusMessage("The result file for the bulk operation is not yet available for download."); OutputStatusMessage(ex.Message); } catch (BulkOperationCouldNotBeCompletedException <DownloadStatus> ex) { OutputStatusMessage(string.Join("; ", ex.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (BulkOperationCouldNotBeCompletedException <UploadStatus> ex) { OutputStatusMessage(string.Join("; ", ex.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (Exception ex) { OutputStatusMessage(ex.Message); } finally { if (Reader != null) { Reader.Dispose(); } if (Writer != null) { Writer.Dispose(); } } }
public async override Task RunAsync(AuthorizationData authorizationData) { try { #region CampaignThroughAdGroupSetup // You will need to use the Campaign Management service to get the Bing Merchant Center Store Id. This will be used // when creating a new Bing Shopping Campaign. // For other operations such as adding product conditions, you can manage Bing Shopping Campaigns solely with the Bulk Service. CampaignService = new ServiceClient<ICampaignManagementService>(authorizationData); // Get a list of all Bing Merchant Center stores associated with your CustomerId IList<BMCStore> stores = await GetBMCStoresByCustomerIdAsync(); if (stores == null) { OutputStatusMessage( String.Format("You do not have any BMC stores registered for CustomerId {0}.\n", authorizationData.CustomerId) ); return; } BulkService = new BulkServiceManager(authorizationData); var progress = new Progress<BulkOperationProgressInfo>(x => OutputStatusMessage(String.Format("{0} % Complete", x.PercentComplete.ToString(CultureInfo.InvariantCulture)))); const int campaignIdKey = -123; const int adGroupIdKey = -1234; var uploadEntities = new List<BulkEntity>(); /* Add a new Bing Shopping campaign that will be associated with a ProductScope criterion. * - Set the CampaignType element of the Campaign to Shopping. * - Create a ShoppingSetting instance and set its Priority (0, 1, or 2), SalesCountryCode, and StoreId elements. * Add this shopping setting to the Settings list of the Campaign. */ var bulkCampaign = new BulkCampaign { // ClientId may be used to associate records in the bulk upload file with records in the results file. The value of this field // is not used or stored by the server; it is simply copied from the uploaded record to the corresponding result record. // Note: This bulk file Client Id is not related to an application Client Id for OAuth. ClientId = "YourClientIdGoesHere", Campaign = new Campaign { // When using the Campaign Management service, the Id cannot be set. In the context of a BulkCampaign, the Id is optional // and may be used as a negative reference key during bulk upload. For example the same negative value set for the campaign Id // will be used when associating this new campaign with a new campaign product scope in the BulkCampaignProductScope object below. Id = campaignIdKey, CampaignType = CampaignType.Shopping, Settings = new[] { new ShoppingSetting() { Priority = 0, SalesCountryCode = "US", StoreId = (int)stores[0].Id } }, Name = "Bing Shopping Campaign " + DateTime.UtcNow, Description = "Bing Shopping Campaign Example.", BudgetType = BudgetLimitType.MonthlyBudgetSpendUntilDepleted, MonthlyBudget = 1000.00, TimeZone = "PacificTimeUSCanadaTijuana", // DaylightSaving is not supported in the Bulk file schema. Whether or not you specify it in a BulkCampaign, // the value is not written to the Bulk file, and by default DaylightSaving is set to true. DaylightSaving = true, } }; /* Optionally, you can create a ProductScope criterion that will be associated with your Bing Shopping campaign. * Use the product scope criterion to include a subset of your product catalog, for example a specific brand, * category, or product type. A campaign can only be associated with one ProductScope, which contains a list * of up to 7 ProductCondition. You'll also be able to specify more specific product conditions for each ad group. */ var bulkCampaignProductScope = new BulkCampaignProductScope { CampaignCriterion = new CampaignCriterion() { CampaignId = campaignIdKey, BidAdjustment = null, // Reserved for future use Criterion = new ProductScope() { Conditions = new ProductCondition[] { new ProductCondition { Operand = "Condition", Attribute = "New" }, new ProductCondition { Operand = "CustomLabel0", Attribute = "MerchantDefinedCustomLabel" }, } }, }, Status = Status.Active, }; // Specify one or more ad groups. var bulkAdGroup = new BulkAdGroup { CampaignId = campaignIdKey, AdGroup = new AdGroup { Id = adGroupIdKey, Name = "Product Categories", AdDistribution = AdDistribution.Search, BiddingModel = BiddingModel.Keyword, PricingModel = PricingModel.Cpc, StartDate = null, EndDate = new Microsoft.BingAds.V10.CampaignManagement.Date { Month = 12, Day = 31, Year = 2016 }, Language = "English", Status = AdGroupStatus.Active }, }; /* * Create a product ad. You must add at least one ProductAd to the corresponding ad group. * A ProductAd is not used directly for delivered ad copy. Instead, the delivery engine generates * product ads from the product details that it finds in your Bing Merchant Center store's product catalog. * The primary purpose of the ProductAd object is to provide promotional text that the delivery engine * adds to the product ads that it generates. For example, if the promotional text is set to * “Free shipping on $99 purchases”, the delivery engine will set the product ad’s description to * “Free shipping on $99 purchases.” */ var bulkProductAd = new BulkProductAd { AdGroupId = adGroupIdKey, ProductAd = new ProductAd { PromotionalText = "Free shipping on $99 purchases." } }; uploadEntities.Add(bulkCampaign); uploadEntities.Add(bulkAdGroup); uploadEntities.Add(bulkCampaignProductScope); uploadEntities.Add(bulkProductAd); // Write the upload output var Reader = await UploadEntities(uploadEntities); var bulkEntities = Reader.ReadEntities().ToList(); var campaignResults = bulkEntities.OfType<BulkCampaign>().ToList(); OutputBulkCampaigns(campaignResults); var adGroupResults = bulkEntities.OfType<BulkAdGroup>().ToList(); OutputBulkAdGroups(adGroupResults); var productAdResults = bulkEntities.OfType<BulkProductAd>().ToList(); OutputBulkProductAds(productAdResults); var campaignProductScopeResults = bulkEntities.OfType<BulkCampaignProductScope>().ToList(); OutputBulkCampaignProductScopes(campaignProductScopeResults); Reader.Dispose(); #endregion CampaignThroughAdGroupSetup #region BidAllProducts var adGroupId = (long)adGroupResults[0].AdGroup.Id; var helper = new ProductPartitionHelper(adGroupId); var root = helper.AddUnit( null, new ProductCondition { Operand = "All", Attribute = null }, 0.35, false, "root" ); OutputStatusMessage("Applying only the root as a Unit with a bid . . . \n"); var applyBulkProductPartitionActionsResults = await ApplyBulkProductPartitionActions(helper.PartitionActions); var productPartitions = await GetBulkAdGroupProductPartitionTree(adGroupId); OutputStatusMessage("The ad group's product partition only has a tree root node: \n"); OutputProductPartitions(productPartitions); /* * Let's update the bid of the root Unit we just added. */ var updatedRoot = GetNodeByClientId(applyBulkProductPartitionActionsResults, "root"); var bid = new FixedBid { Bid = new Bid { Amount = 0.45 } }; ((BiddableAdGroupCriterion)(updatedRoot.AdGroupCriterion)).CriterionBid = bid; helper = new ProductPartitionHelper(adGroupId); helper.UpdatePartition(updatedRoot); OutputStatusMessage("Updating the bid for the tree root node . . . \n"); await ApplyBulkProductPartitionActions(helper.PartitionActions); productPartitions = await GetBulkAdGroupProductPartitionTree(adGroupId); OutputStatusMessage("Updated the bid for the tree root node: \n"); OutputProductPartitions(productPartitions); #endregion BidAllProducts #region InitializeTree /* * Now we will overwrite any existing tree root, and build a product partition group tree structure in multiple steps. * You could build the entire tree in a single call since there are less than 20,000 nodes; however, * we will build it in steps to demonstrate how to use the results from bulk upload to update the tree. * * For a list of validation rules, see the Bing Shopping Campaigns technical guide: * https://msdn.microsoft.com/en-US/library/bing-ads-campaign-management-bing-shopping-campaigns.aspx */ helper = new ProductPartitionHelper(adGroupId); /* * Check whether a root node exists already. */ var existingRoot = GetNodeByClientId(applyBulkProductPartitionActionsResults, "root"); if (existingRoot != null) { existingRoot.ClientId = "deletedroot"; helper.DeletePartition(existingRoot); } root = helper.AddSubdivision( null, new ProductCondition { Operand = "All", Attribute = null }, "root" ); /* * The direct children of any node must have the same Operand. * For this example we will use CategoryL1 nodes as children of the root. * For a list of valid CategoryL1 through CategoryL5 values, see the Bing Category Taxonomy: * http://advertise.bingads.microsoft.com/en-us/WWDocs/user/search/en-us/Bing_Category_Taxonomy.txt */ var animalsSubdivision = helper.AddSubdivision( root, new ProductCondition { Operand = "CategoryL1", Attribute = "Animals & Pet Supplies" }, "animalsSubdivision" ); /* * If you use a CategoryL2 node, it must be a descendant (child or later) of a CategoryL1 node. * In other words you cannot have a CategoryL2 node as parent of a CategoryL1 node. * For this example we will a CategoryL2 node as child of the CategoryL1 Animals & Pet Supplies node. */ var petSuppliesSubdivision = helper.AddSubdivision( animalsSubdivision, new ProductCondition { Operand = "CategoryL2", Attribute = "Pet Supplies" }, "petSuppliesSubdivision" ); var brandA = helper.AddUnit( petSuppliesSubdivision, new ProductCondition { Operand = "Brand", Attribute = "Brand A" }, 0.35, false, "brandA" ); /* * If you won't bid on Brand B, set the helper method's bidAmount to '0' and isNegative to true. * The helper method will create a NegativeAdGroupCriterion and apply the condition. */ var brandB = helper.AddUnit( petSuppliesSubdivision, new ProductCondition { Operand = "Brand", Attribute = "Brand B" }, 0, true, "brandB" ); var otherBrands = helper.AddUnit( petSuppliesSubdivision, new ProductCondition { Operand = "Brand", Attribute = null }, 0.35, false, "otherBrands" ); var otherPetSupplies = helper.AddUnit( animalsSubdivision, new ProductCondition { Operand = "CategoryL2", Attribute = null }, 0.35, false, "otherPetSupplies" ); var electronics = helper.AddUnit( root, new ProductCondition { Operand = "CategoryL1", Attribute = "Electronics" }, 0.35, false, "electronics" ); var otherCategoryL1 = helper.AddUnit( root, new ProductCondition { Operand = "CategoryL1", Attribute = null }, 0.35, false, "otherCategoryL1" ); OutputStatusMessage("Applying product partitions to the ad group . . . \n"); applyBulkProductPartitionActionsResults = await ApplyBulkProductPartitionActions(helper.PartitionActions); productPartitions = await GetBulkAdGroupProductPartitionTree(adGroupId); /* * The product partition group tree now has 9 nodes. All other (Root Node) | +-- Animals & Pet Supplies (CategoryL1) | | | +-- Pet Supplies (CategoryL2) | | | | | +-- Brand A | | | | | +-- Brand B | | | | | +-- All other (Brand) | | | +-- All other (CategoryL2) | +-- Electronics (CategoryL1) | +-- All other (CategoryL1) */ OutputStatusMessage("The product partition group tree now has 9 nodes: \n"); OutputProductPartitions(productPartitions); #endregion InitializeTree #region UpdateTree /* * Let's replace the Electronics (CategoryL1) node created above with an Electronics (CategoryL1) node that * has children i.e. Brand C (Brand), Brand D (Brand), and All other (Brand) as follows: Electronics (CategoryL1) | +-- Brand C (Brand) | +-- Brand D (Brand) | +-- All other (Brand) */ helper = new ProductPartitionHelper(adGroupId); /* * To replace a node we must know its Id and its ParentCriterionId. In this case the parent of the node * we are replacing is All other (Root Node). The node that we are replacing is Electronics (CategoryL1). */ var rootId = GetNodeByClientId(applyBulkProductPartitionActionsResults, "root").AdGroupCriterion.Id; electronics.AdGroupCriterion.Id = GetNodeByClientId(applyBulkProductPartitionActionsResults, "electronics").AdGroupCriterion.Id; helper.DeletePartition(electronics); var parent = new BulkAdGroupProductPartition { AdGroupCriterion = new BiddableAdGroupCriterion() { Id = rootId } }; var electronicsSubdivision = helper.AddSubdivision( parent, new ProductCondition { Operand = "CategoryL1", Attribute = "Electronics" }, "electronicsSubdivision" ); var brandC = helper.AddUnit( electronicsSubdivision, new ProductCondition { Operand = "Brand", Attribute = "Brand C" }, 0.35, false, "brandC" ); var brandD = helper.AddUnit( electronicsSubdivision, new ProductCondition { Operand = "Brand", Attribute = "Brand D" }, 0.35, false, "brandD" ); var otherElectronicsBrands = helper.AddUnit( electronicsSubdivision, new ProductCondition { Operand = "Brand", Attribute = null }, 0.35, false, "otherElectronicsBrands" ); OutputStatusMessage( "Updating the product partition group to refine Electronics (CategoryL1) with 3 child nodes . . . \n" ); applyBulkProductPartitionActionsResults = await ApplyBulkProductPartitionActions(helper.PartitionActions); productPartitions = await GetBulkAdGroupProductPartitionTree(adGroupId); /* * The product partition group tree now has 12 nodes, including the children of Electronics (CategoryL1): All other (Root Node) | +-- Animals & Pet Supplies (CategoryL1) | | | +-- Pet Supplies (CategoryL2) | | | | | +-- Brand A | | | | | +-- Brand B | | | | | +-- All other (Brand) | | | +-- All other (CategoryL2) | +-- Electronics (CategoryL1) | | | +-- Brand C (Brand) | | | +-- Brand D (Brand) | | | +-- All other (Brand) | +-- All other (CategoryL1) */ OutputStatusMessage( "The product partition group tree now has 12 nodes, including the children of Electronics (CategoryL1): \n" ); OutputProductPartitions(productPartitions); #endregion UpdateTree var Service = new ServiceClient<ICampaignManagementService>(authorizationData); var getCampaignIds = new List<long>(); getCampaignIds.Add((long)campaignResults[0].Campaign.Id); var request = new GetCampaignsByIdsRequest { AccountId = authorizationData.AccountId, CampaignIds = getCampaignIds, CampaignType = CampaignType.Shopping }; await Service.CallAsync((s, r) => s.GetCampaignsByIdsAsync(r), request); #region CleanUp /* Delete the campaign, ad group, criterion, and ad that were previously added. * You should remove this region if you want to view the added entities in the * Bing Ads web application or another tool. */ var campaignId = campaignResults[0].Campaign.Id; bulkCampaign = new BulkCampaign { Campaign = new Campaign { Id = campaignId, Status = CampaignStatus.Deleted } }; uploadEntities = new List<BulkEntity>(); uploadEntities.Add(bulkCampaign); // Write the upload output Reader = await UploadEntities(uploadEntities); bulkEntities = Reader.ReadEntities().ToList(); campaignResults = bulkEntities.OfType<BulkCampaign>().ToList(); OutputBulkCampaigns(campaignResults); Reader.Dispose(); OutputStatusMessage(String.Format("Deleted CampaignId {0}\n", campaignResults[0].Campaign.Id)); #endregion Cleanup } // Catch Microsoft Account authorization exceptions. catch (OAuthTokenRequestException ex) { OutputStatusMessage(string.Format("Couldn't get OAuth tokens. Error: {0}. Description: {1}", ex.Details.Error, ex.Details.Description)); } // Catch Bulk service exceptions catch (FaultException<Microsoft.BingAds.V10.Bulk.AdApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException<Microsoft.BingAds.V10.Bulk.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 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 (BulkOperationInProgressException ex) { OutputStatusMessage("The result file for the bulk operation is not yet available for download."); OutputStatusMessage(ex.Message); } catch (BulkOperationCouldNotBeCompletedException<DownloadStatus> ex) { OutputStatusMessage(string.Join("; ", ex.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (BulkOperationCouldNotBeCompletedException<UploadStatus> ex) { OutputStatusMessage(string.Join("; ", ex.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (Exception ex) { OutputStatusMessage(ex.Message); } finally { if (Reader != null) { Reader.Dispose(); } if (Writer != null) { Writer.Dispose(); } } }
public async override Task RunAsync(AuthorizationData authorizationData) { try { AdInsightService = new ServiceClient <IAdInsightService>(authorizationData); CampaignService = new ServiceClient <ICampaignManagementService>(authorizationData); // To get started with dynamic search ads, first you'll need to add a new Campaign // with its type set to DynamicSearchAds. When you create the campaign, you'll need to // include a DynamicSearchAdsSetting that specifies the target website domain and language. var campaigns = new[] { new Campaign { CampaignType = CampaignType.DynamicSearchAds, Settings = new [] { new DynamicSearchAdsSetting { DomainName = "contoso.com", Language = "English" } }, 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 = Microsoft.BingAds.V10.CampaignManagement.BudgetLimitType.DailyBudgetStandard, // You can set your campaign bid strategy to Enhanced CPC (EnhancedCpcBiddingScheme) // and then, at any time, set an individual ad group bid strategy to // Manual CPC (ManualCpcBiddingScheme). BiddingScheme = new EnhancedCpcBiddingScheme { }, TimeZone = "PacificTimeUSCanadaTijuana", DaylightSaving = true, // Used with CustomParameters defined in lower level entities such as ads. TrackingUrlTemplate = "http://tracker.example.com/?season={_season}&promocode={_promocode}&u={lpurl}" }, }; AddCampaignsResponse addCampaignsResponse = await AddCampaignsAsync(authorizationData.AccountId, campaigns); long?[] campaignIds = addCampaignsResponse.CampaignIds.ToArray(); Microsoft.BingAds.V10.CampaignManagement.BatchError[] campaignErrors = addCampaignsResponse.PartialErrors.ToArray(); OutputCampaignsWithPartialErrors(campaigns, campaignIds, campaignErrors); // Next, create a new AdGroup within the dynamic search ads campaign. var adGroups = new[] { new AdGroup { Name = "Women's Red Shoe Sale", AdDistribution = AdDistribution.Search, BiddingModel = BiddingModel.Keyword, PricingModel = PricingModel.Cpc, StartDate = null, EndDate = new Date { Month = 12, Day = 31, Year = DateTime.UtcNow.Year + 1 }, SearchBid = new Bid { Amount = 0.09 }, Language = "English", // For ad groups you can use either of the InheritFromParentBiddingScheme or ManualCpcBiddingScheme objects. // If you do not set this element, then InheritFromParentBiddingScheme is used by default. BiddingScheme = new ManualCpcBiddingScheme { }, } }; AddAdGroupsResponse addAdGroupsResponse = await AddAdGroupsAsync((long)campaignIds[0], adGroups); long?[] adGroupIds = addAdGroupsResponse.AdGroupIds.ToArray(); Microsoft.BingAds.V10.CampaignManagement.BatchError[] adGroupErrors = addAdGroupsResponse.PartialErrors.ToArray(); OutputAdGroupsWithPartialErrors(adGroups, adGroupIds, adGroupErrors); // You can add one or more Webpage criterion to each ad group that helps determine // whether or not to serve dynamic search ads. var adGroupCriterions = new List <AdGroupCriterion>(); var adGroupWebpagePositivePageContent = new BiddableAdGroupCriterion { AdGroupId = (long)adGroupIds[0], CriterionBid = new FixedBid { Bid = new Bid { Amount = 0.50 } }, Criterion = new Webpage { Parameter = new WebpageParameter { Conditions = new[] { new WebpageCondition { Argument = "flowers", Operand = WebpageConditionOperand.PageContent, } }, CriterionName = "Ad Group Webpage Positive Page Content Criterion" }, }, // DestinationUrl and FinalUrls are not supported with Webpage criterion. // The Final URL is dynamically created at the ad level. DestinationUrl = null, FinalUrls = null, // 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 webpage criterion, // and can be used by the criterion, 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" }, } } }; adGroupCriterions.Add(adGroupWebpagePositivePageContent); // To discover the categories that you can use for Webpage criterion (positive or negative), // use the GetDomainCategories operation with the Ad Insight service. var getDomainCategoriesResponse = await GetDomainCategoriesAsync(DOMAIN_NAME, LANGUAGE); var categories = getDomainCategoriesResponse.Categories; // If any categories are available let's use one as a condition. if (categories.Count > 0) { var adGroupWebpagePositiveCategory = new BiddableAdGroupCriterion { AdGroupId = (long)adGroupIds[0], CriterionBid = new FixedBid { Bid = new Bid { Amount = 0.50 } }, Criterion = new Webpage { Parameter = new WebpageParameter { Conditions = new[] { new WebpageCondition { Argument = categories[0].CategoryName, Operand = WebpageConditionOperand.Category, } }, CriterionName = "Ad Group Webpage Positive Category Criterion" }, } }; adGroupCriterions.Add(adGroupWebpagePositiveCategory); } // If you want to exclude certain portions of your website, you can add negative Webpage // criterion at the campaign and ad group level. var adGroupWebpageNegativeUrl = new NegativeAdGroupCriterion { AdGroupId = (long)adGroupIds[0], Criterion = new Webpage { Parameter = new WebpageParameter { // You can choose whether you want the criterion argument to match partial URLs, // page content, page title, or categories that Bing thinks applies to your website. Conditions = new[] { new WebpageCondition { Argument = DOMAIN_NAME, Operand = WebpageConditionOperand.Url, } }, // If you do not specify any name, then it will be set to a concatenated list of conditions. CriterionName = null } }, }; adGroupCriterions.Add(adGroupWebpageNegativeUrl); OutputStatusMessage("Adding Ad Group Webpage Criterion . . . \n"); OutputAdGroupCriterions(adGroupCriterions); AddAdGroupCriterionsResponse addAdGroupCriterionsResponse = await AddAdGroupCriterionsAsync(adGroupCriterions, CriterionType.Webpage); long?[] adGroupCriterionIds = addAdGroupCriterionsResponse.AdGroupCriterionIds.ToArray(); OutputStatusMessage("New Ad Group Criterion Ids:\n"); OutputIds(adGroupCriterionIds); BatchErrorCollection[] adGroupCriterionErrors = addAdGroupCriterionsResponse.NestedPartialErrors.ToArray(); OutputStatusMessage("\nAddAdGroupCriterions Errors:\n"); OutputBatchErrorCollections(adGroupCriterionErrors); // The negative Webpage criterion at the campaign level applies to all ad groups // within the campaign; however, if you define ad group level negative Webpage criterion, // the campaign criterion is ignored for that ad group. var campaignCriterions = new List <CampaignCriterion>(); var campaignWebpageNegative = new NegativeCampaignCriterion { CampaignId = (long)campaignIds[0], Criterion = new Webpage { Parameter = new WebpageParameter { Conditions = new[] { new WebpageCondition { Argument = DOMAIN_NAME + "\\seattle", Operand = WebpageConditionOperand.Url, } }, CriterionName = "Campaign Negative Webpage Url Criterion" } } }; campaignCriterions.Add(campaignWebpageNegative); OutputStatusMessage("Adding Campaign Webpage Criterion . . . \n"); OutputCampaignCriterions(campaignCriterions); AddCampaignCriterionsResponse addCampaignCriterionsResponse = await AddCampaignCriterionsAsync(campaignCriterions, CampaignCriterionType.Webpage); long?[] campaignCriterionIds = addCampaignCriterionsResponse.CampaignCriterionIds.ToArray(); OutputStatusMessage("\nNew Campaign Criterion Ids:\n"); OutputIds(campaignCriterionIds); BatchErrorCollection[] campaignCriterionErrors = addCampaignCriterionsResponse.NestedPartialErrors.ToArray(); OutputStatusMessage("\nAddCampaignCriterions Errors:\n"); OutputBatchErrorCollections(campaignCriterionErrors); // Finally you can add a DynamicSearchAd into the ad group. The ad title and display URL // are generated automatically based on the website domain and language that you want to target. var ads = new Ad[] { new DynamicSearchAd { Text = "Find New Customers & Increase Sales! Start Advertising on Contoso Today.", Path1 = "seattle", Path2 = "shoe sale", // You cannot set FinalUrls. The Final URL will be a dynamically selected landing page. // The final URL is distinct from the path that customers will see and click on in your ad. FinalUrls = null, // 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 ad, // and can be used by the ad, webpage, 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" }, } } }, }; AddAdsResponse addAdsResponse = await AddAdsAsync((long)adGroupIds[0], ads); long?[] adIds = addAdsResponse.AdIds.ToArray(); Microsoft.BingAds.V10.CampaignManagement.BatchError[] adErrors = addAdsResponse.PartialErrors.ToArray(); OutputAdsWithPartialErrors(ads, adIds, adErrors); // Retrieve the Webpage criterion for the campaign. var getCampaignCriterionsByIdsResponse = await GetCampaignCriterionsByIdsAsync( (long)campaignIds[0], null, CampaignCriterionType.Webpage ); OutputStatusMessage("Retrieving the Campaign Webpage Criterions that we added . . . \n"); campaignCriterions = getCampaignCriterionsByIdsResponse.CampaignCriterions.ToList(); OutputCampaignCriterions(campaignCriterions); // Retrieve the Webpage criterion for the ad group and then test some update scenarios. var getAdGroupCriterionsByIdsResponse = await GetAdGroupCriterionsByIdsAsync( (long)adGroupIds[0], null, CriterionType.Webpage ); OutputStatusMessage("Retrieving the Ad Group Webpage Criterions that we added . . . \n"); adGroupCriterions = getAdGroupCriterionsByIdsResponse.AdGroupCriterions.ToList(); OutputAdGroupCriterions(adGroupCriterions); // You can update the bid for BiddableAdGroupCriterion var updateBid = new FixedBid { Bid = new Bid { Amount = 0.75 } }; // You can update the Webpage criterion name but cannot update the conditions. // To update the conditions you must delete the criterion and add a new criterion. // This update attempt will return an error. var updateCriterionAttemptFailure = new Webpage { Parameter = new WebpageParameter { Conditions = new[] { new WebpageCondition { Argument = "Books", Operand = WebpageConditionOperand.PageContent, } }, CriterionName = "Update Attempt Failure" }, }; var updateCriterionAttemptSuccess = new Webpage { Parameter = new WebpageParameter { CriterionName = "Update Attempt Success" }, }; foreach (var adGroupCriterion in adGroupCriterions) { var biddableAdGroupCriterion = adGroupCriterion as BiddableAdGroupCriterion; if (biddableAdGroupCriterion != null) { ((BiddableAdGroupCriterion)(adGroupCriterion)).CriterionBid = updateBid; ((BiddableAdGroupCriterion)(adGroupCriterion)).Criterion = updateCriterionAttemptSuccess; } else { var negativeAdGroupCriterion = adGroupCriterion as NegativeAdGroupCriterion; if (negativeAdGroupCriterion != null) { ((NegativeAdGroupCriterion)(adGroupCriterion)).Criterion = updateCriterionAttemptFailure; } } } OutputStatusMessage("Updating Ad Group Webpage Criterion . . . \n"); OutputAdGroupCriterions(adGroupCriterions); UpdateAdGroupCriterionsResponse updateAdGroupCriterionsResponse = await UpdateAdGroupCriterionsAsync(adGroupCriterions, CriterionType.Webpage); adGroupCriterionErrors = updateAdGroupCriterionsResponse.NestedPartialErrors.ToArray(); OutputStatusMessage("UpdateAdGroupCriterions Errors:\n"); OutputBatchErrorCollections(adGroupCriterionErrors); // Delete the campaign, ad group, criterion, and ad that were previously added. // You should remove this operation if you want to view the added entities in the // Bing Ads web application or another tool. await DeleteCampaignsAsync(authorizationData.AccountId, new[] { (long)campaignIds[0] }); OutputStatusMessage(string.Format("\nDeleted Campaign Id {0}\n", 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 (Exception ex) { OutputStatusMessage(ex.Message); } }
public async override Task RunAsync(AuthorizationData authorizationData) { try { ApiEnvironment environment = ((OAuthDesktopMobileAuthCodeGrant)authorizationData.Authentication).Environment; // The Bing Merchant Center Store Id cannot be retrieved via the Bulk service, // so we'll use the Campaign Management service i.e., the GetBMCStoresByCustomerId service operation below. CampaignManagementExampleHelper = new CampaignManagementExampleHelper( OutputStatusMessageDefault: this.OutputStatusMessage); CampaignManagementExampleHelper.CampaignManagementService = new ServiceClient <ICampaignManagementService>( authorizationData: authorizationData, environment: environment); BulkServiceManager = new BulkServiceManager( authorizationData: authorizationData, apiEnvironment: environment); var progress = new Progress <BulkOperationProgressInfo>(x => OutputStatusMessage(string.Format("{0} % Complete", x.PercentComplete.ToString(CultureInfo.InvariantCulture)))); // Get a list of all Bing Merchant Center stores associated with your CustomerId. OutputStatusMessage("-----\nGetBMCStoresByCustomerId:"); IList <BMCStore> stores = (await CampaignManagementExampleHelper.GetBMCStoresByCustomerIdAsync())?.BMCStores; if (stores == null) { OutputStatusMessage( string.Format("You do not have any BMC stores registered for CustomerId {0}.", authorizationData.CustomerId) ); return; } var uploadEntities = new List <BulkEntity>(); // Create a Shopping campaign with product conditions. var bulkCampaign = new BulkCampaign { Campaign = new Campaign { Id = campaignIdKey, CampaignType = CampaignType.Shopping, Languages = new string[] { "All" }, Name = "Women's Shoes " + DateTime.UtcNow, DailyBudget = 50, BudgetType = BudgetLimitType.DailyBudgetStandard, Settings = new[] { new ShoppingSetting() { Priority = 0, SalesCountryCode = "US", StoreId = (int)stores[0].Id } }, TimeZone = "PacificTimeUSCanadaTijuana", } }; uploadEntities.Add(bulkCampaign); // Optionally, you can create a ProductScope criterion that will be associated with your Bing Shopping campaign. // You'll also be able to add more specific product conditions for each ad group. var bulkCampaignProductScope = new BulkCampaignProductScope { BiddableCampaignCriterion = new BiddableCampaignCriterion() { CampaignId = campaignIdKey, CriterionBid = null, // Not applicable for product scope Criterion = new ProductScope() { Conditions = new ProductCondition[] { new ProductCondition { Operand = "Condition", Attribute = "New" }, new ProductCondition { Operand = "CustomLabel0", Attribute = "MerchantDefinedCustomLabel" }, } }, Status = CampaignCriterionStatus.Active }, }; uploadEntities.Add(bulkCampaignProductScope); // Create the ad group that will have the product partitions. var bulkAdGroup = new BulkAdGroup { CampaignId = campaignIdKey, AdGroup = new AdGroup { Id = adGroupIdKey, Name = "Women's Red Shoe Sale", StartDate = null, EndDate = new Microsoft.BingAds.V13.CampaignManagement.Date { Month = 12, Day = 31, Year = DateTime.UtcNow.Year + 1 }, CpcBid = new Bid { Amount = 0.09 }, }, }; uploadEntities.Add(bulkAdGroup); // Create a product ad. You must add at least one product ad to the ad group. // The product ad identifier can be used for reporting analytics. // Use Merchant Promotions if you want tags to appear at the bottom of your product ad // as "special offer" links, helping to increase customer engagement. For details // on Merchant Promotions see https://help.bingads.microsoft.com/#apex/3/en/56805/0. var bulkProductAd = new BulkProductAd { AdGroupId = adGroupIdKey, ProductAd = new ProductAd { } }; uploadEntities.Add(bulkProductAd); // Upload and write the output OutputStatusMessage("-----\nAdding the campaign, product scope, ad group, and ad..."); var Reader = await WriteEntitiesAndUploadFileAsync(uploadEntities); var downloadEntities = Reader.ReadEntities().ToList(); OutputStatusMessage("Upload results:"); var campaignResults = downloadEntities.OfType <BulkCampaign>().ToList(); OutputBulkCampaigns(campaignResults); var adGroupResults = downloadEntities.OfType <BulkAdGroup>().ToList(); OutputBulkAdGroups(adGroupResults); var campaignProductScopeResults = downloadEntities.OfType <BulkCampaignProductScope>().ToList(); OutputBulkCampaignProductScopes(campaignProductScopeResults); var productAdResults = downloadEntities.OfType <BulkProductAd>().ToList(); OutputBulkProductAds(productAdResults); Reader.Dispose(); // Bid all products var adGroupId = (long)adGroupResults[0].AdGroup.Id; var helper = new ProductPartitionHelper(adGroupId); var root = helper.AddUnit( null, new ProductCondition { Operand = "All", Attribute = null }, 0.35, false, "root" ); OutputStatusMessage("-----\nApplying only the root as a Unit with a bid..."); var applyBulkProductPartitionActionsResults = await ApplyBulkProductPartitionActions(helper.PartitionActions); var productPartitions = await GetBulkAdGroupProductPartitionTree(adGroupId); OutputStatusMessage("The ad group's product partition only has a tree root node:"); OutputProductPartitions(productPartitions); // Let's update the bid of the root Unit we just added. var updatedRoot = GetNodeByClientId(applyBulkProductPartitionActionsResults, "root"); var bid = new FixedBid { Amount = 0.45 }; ((BiddableAdGroupCriterion)(updatedRoot.AdGroupCriterion)).CriterionBid = bid; helper = new ProductPartitionHelper(adGroupId); helper.UpdatePartition(updatedRoot); OutputStatusMessage("Updating the bid for the tree root node..."); await ApplyBulkProductPartitionActions(helper.PartitionActions); productPartitions = await GetBulkAdGroupProductPartitionTree(adGroupId); OutputStatusMessage("Updated the bid for the tree root node:"); OutputProductPartitions(productPartitions); // Initialize and overwrite any existing tree root, and build a product partition group tree structure in multiple steps. // You could build the entire tree in a single call since there are less than 20,000 nodes; however, // we will build it in steps to demonstrate how to use the results from bulk upload to update the tree. helper = new ProductPartitionHelper(adGroupId); // Check whether a root node exists already. var existingRoot = GetNodeByClientId(applyBulkProductPartitionActionsResults, "root"); if (existingRoot != null) { existingRoot.ClientId = "deletedroot"; helper.DeletePartition(existingRoot); } root = helper.AddSubdivision( null, new ProductCondition { Operand = "All", Attribute = null }, "root" ); // The direct children of any node must have the same Operand. // For this example we will use CategoryL1 nodes as children of the root. // For a list of valid CategoryL1 through CategoryL5 values, see the Bing Category Taxonomy: // http://go.microsoft.com/fwlink?LinkId=507666 var animalsSubdivision = helper.AddSubdivision( root, new ProductCondition { Operand = "CategoryL1", Attribute = "Animals & Pet Supplies" }, "animalsSubdivision" ); // If you use a CategoryL2 node, it must be a descendant (child or later) of a CategoryL1 node. // In other words you cannot have a CategoryL2 node as parent of a CategoryL1 node. // For this example we will a CategoryL2 node as child of the CategoryL1 Animals & Pet Supplies node. var petSuppliesSubdivision = helper.AddSubdivision( animalsSubdivision, new ProductCondition { Operand = "CategoryL2", Attribute = "Pet Supplies" }, "petSuppliesSubdivision" ); var brandA = helper.AddUnit( petSuppliesSubdivision, new ProductCondition { Operand = "Brand", Attribute = "Brand A" }, 0.35, false, "brandA" ); // If you won't bid on Brand B, set the helper method's bidAmount to '0' and isNegative to true. // The helper method will create a NegativeAdGroupCriterion and apply the condition. var brandB = helper.AddUnit( petSuppliesSubdivision, new ProductCondition { Operand = "Brand", Attribute = "Brand B" }, 0, true, "brandB" ); var otherBrands = helper.AddUnit( petSuppliesSubdivision, new ProductCondition { Operand = "Brand", Attribute = null }, 0.35, false, "otherBrands" ); var otherPetSupplies = helper.AddUnit( animalsSubdivision, new ProductCondition { Operand = "CategoryL2", Attribute = null }, 0.35, false, "otherPetSupplies" ); var electronics = helper.AddUnit( root, new ProductCondition { Operand = "CategoryL1", Attribute = "Electronics" }, 0.35, false, "electronics" ); var otherCategoryL1 = helper.AddUnit( root, new ProductCondition { Operand = "CategoryL1", Attribute = null }, 0.35, false, "otherCategoryL1" ); OutputStatusMessage("-----\nApplying product partitions to the ad group..."); applyBulkProductPartitionActionsResults = await ApplyBulkProductPartitionActions(helper.PartitionActions); productPartitions = await GetBulkAdGroupProductPartitionTree(adGroupId); // The product partition group tree now has 9 nodes. //All other (Root Node) // | // +-- Animals & Pet Supplies (CategoryL1) // | | // | +-- Pet Supplies (CategoryL2) // | | | // | | +-- Brand A // | | | // | | +-- Brand B // | | | // | | +-- All other (Brand) // | | // | +-- All other (CategoryL2) // | // +-- Electronics (CategoryL1) // | // +-- All other (CategoryL1) OutputStatusMessage("The product partition group tree now has 9 nodes:"); OutputProductPartitions(productPartitions); // Let's replace the Electronics (CategoryL1) node created above with an Electronics (CategoryL1) node that // has children i.e. Brand C (Brand), Brand D (Brand), and All other (Brand) as follows: //Electronics (CategoryL1) //| //+-- Brand C (Brand) //| //+-- Brand D (Brand) //| //+-- All other (Brand) helper = new ProductPartitionHelper(adGroupId); // To replace a node we must know its Id and its ParentCriterionId. In this case the parent of the node // we are replacing is All other (Root Node). The node that we are replacing is Electronics (CategoryL1). var rootId = GetNodeByClientId(applyBulkProductPartitionActionsResults, "root").AdGroupCriterion.Id; electronics.AdGroupCriterion.Id = GetNodeByClientId(applyBulkProductPartitionActionsResults, "electronics").AdGroupCriterion.Id; helper.DeletePartition(electronics); var parent = new BulkAdGroupProductPartition { AdGroupCriterion = new BiddableAdGroupCriterion() { Id = rootId } }; var electronicsSubdivision = helper.AddSubdivision( parent, new ProductCondition { Operand = "CategoryL1", Attribute = "Electronics" }, "electronicsSubdivision" ); var brandC = helper.AddUnit( electronicsSubdivision, new ProductCondition { Operand = "Brand", Attribute = "Brand C" }, 0.35, false, "brandC" ); var brandD = helper.AddUnit( electronicsSubdivision, new ProductCondition { Operand = "Brand", Attribute = "Brand D" }, 0.35, false, "brandD" ); var otherElectronicsBrands = helper.AddUnit( electronicsSubdivision, new ProductCondition { Operand = "Brand", Attribute = null }, 0.35, false, "otherElectronicsBrands" ); OutputStatusMessage( "Updating the product partition group to refine Electronics (CategoryL1) with 3 child nodes . . . \n" ); applyBulkProductPartitionActionsResults = await ApplyBulkProductPartitionActions(helper.PartitionActions); productPartitions = await GetBulkAdGroupProductPartitionTree(adGroupId); // The product partition group tree now has 12 nodes, including the children of Electronics (CategoryL1): //All other (Root Node) // | // +-- Animals & Pet Supplies (CategoryL1) // | | // | +-- Pet Supplies (CategoryL2) // | | | // | | +-- Brand A // | | | // | | +-- Brand B // | | | // | | +-- All other (Brand) // | | // | +-- All other (CategoryL2) // | // +-- Electronics (CategoryL1) // | | // | +-- Brand C (Brand) // | | // | +-- Brand D (Brand) // | | // | +-- All other (Brand) // | // +-- All other (CategoryL1) OutputStatusMessage( "The product partition group tree now has 12 nodes, including the children of Electronics (CategoryL1): \n" ); OutputProductPartitions(productPartitions); // Delete the campaign and everything it contains e.g., ad groups and ads. uploadEntities = new List <BulkEntity>(); foreach (var campaignResult in campaignResults) { campaignResult.Campaign.Status = CampaignStatus.Deleted; uploadEntities.Add(campaignResult); } // Upload and write the output OutputStatusMessage("-----\nDeleting the campaign and everything it contains e.g., ad groups and ads..."); Reader = await WriteEntitiesAndUploadFileAsync(uploadEntities); downloadEntities = Reader.ReadEntities().ToList(); OutputStatusMessage("Upload results:"); campaignResults = downloadEntities.OfType <BulkCampaign>().ToList(); OutputBulkCampaigns(campaignResults); Reader.Dispose(); } // Catch Microsoft Account authorization exceptions. catch (OAuthTokenRequestException ex) { OutputStatusMessage(string.Format("Couldn't get OAuth tokens. Error: {0}. Description: {1}", ex.Details.Error, ex.Details.Description)); } // Catch Bulk service exceptions catch (FaultException <Microsoft.BingAds.V13.Bulk.AdApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException <Microsoft.BingAds.V13.Bulk.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 Campaign Management service exceptions catch (FaultException <Microsoft.BingAds.V13.CampaignManagement.AdApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException <Microsoft.BingAds.V13.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 (BulkOperationInProgressException ex) { OutputStatusMessage("The result file for the bulk operation is not yet available for download."); OutputStatusMessage(ex.Message); } catch (BulkOperationCouldNotBeCompletedException <DownloadStatus> ex) { OutputStatusMessage(string.Join("; ", ex.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (BulkOperationCouldNotBeCompletedException <UploadStatus> ex) { OutputStatusMessage(string.Join("; ", ex.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (Exception ex) { OutputStatusMessage(ex.Message); } finally { if (Reader != null) { Reader.Dispose(); } if (Writer != null) { Writer.Dispose(); } } }
/// <summary> /// Outputs the FixedBid. /// </summary> protected void OutputFixedBid(FixedBid fixedBid) { if (fixedBid != null && fixedBid.Bid != null) { OutputStatusMessage(string.Format("Bid Amount: {0}", fixedBid.Bid.Amount)); } }
public async override Task RunAsync(AuthorizationData authorizationData) { try { ApiEnvironment environment = ((OAuthDesktopMobileAuthCodeGrant)authorizationData.Authentication).Environment; AdInsightExampleHelper AdInsightExampleHelper = new AdInsightExampleHelper(this.OutputStatusMessage); AdInsightExampleHelper.AdInsightService = new ServiceClient <IAdInsightService>(authorizationData, environment); CampaignManagementExampleHelper CampaignManagementExampleHelper = new CampaignManagementExampleHelper(this.OutputStatusMessage); BulkServiceManager = new BulkServiceManager(authorizationData, environment); var progress = new Progress <BulkOperationProgressInfo>(x => OutputStatusMessage(String.Format("{0} % Complete", x.PercentComplete.ToString(CultureInfo.InvariantCulture)))); var uploadEntities = new List <BulkEntity>(); #region Add // To get started with dynamic search ads, first you'll need to add a new Campaign // with its type set to DynamicSearchAds. When you create the campaign, you'll need to // include a DynamicSearchAdsSetting that specifies the target website domain and language. var bulkCampaign = new BulkCampaign { ClientId = "YourClientIdGoesHere", Campaign = new Campaign { Id = campaignIdKey, CampaignType = CampaignType.DynamicSearchAds, Settings = new[] { new DynamicSearchAdsSetting { DomainName = "contoso.com", Language = "English" } }, 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 = Microsoft.BingAds.V12.CampaignManagement.BudgetLimitType.DailyBudgetStandard, // You can set your campaign bid strategy to Enhanced CPC (EnhancedCpcBiddingScheme) // and then, at any time, set an individual ad group bid strategy to // Manual CPC (ManualCpcBiddingScheme). BiddingScheme = new EnhancedCpcBiddingScheme { }, TimeZone = "PacificTimeUSCanadaTijuana", // Used with CustomParameters defined in lower level entities such as ads. TrackingUrlTemplate = "http://tracker.example.com/?season={_season}&promocode={_promocode}&u={lpurl}" }, }; uploadEntities.Add(bulkCampaign); // Next, create a new AdGroup within the dynamic search ads campaign. var bulkAdGroup = new BulkAdGroup { CampaignId = campaignIdKey, AdGroup = new AdGroup { Id = adGroupIdKey, Name = "Women's Red Shoe Sale", StartDate = null, EndDate = new Microsoft.BingAds.V12.CampaignManagement.Date { Month = 12, Day = 31, Year = DateTime.UtcNow.Year + 1 }, Language = "English", Status = AdGroupStatus.Active, // For ad groups you can use either of the InheritFromParentBiddingScheme or ManualCpcBiddingScheme objects. // If you do not set this element, then InheritFromParentBiddingScheme is used by default. BiddingScheme = new ManualCpcBiddingScheme { }, }, }; uploadEntities.Add(bulkAdGroup); // You can add one or more Webpage criterion to each ad group that helps determine // whether or not to serve dynamic search ads. var adGroupWebpagePositivePageContent = new BulkAdGroupDynamicSearchAdTarget { BiddableAdGroupCriterion = new BiddableAdGroupCriterion { AdGroupId = adGroupIdKey, CriterionBid = new FixedBid { Amount = 0.50 }, Criterion = new Webpage { Parameter = new WebpageParameter { Conditions = new[] { new WebpageCondition { Argument = "flowers", Operand = WebpageConditionOperand.PageContent, } }, CriterionName = "Ad Group Webpage Positive Page Content Criterion" }, }, // DestinationUrl and FinalUrls are not supported with Webpage criterion. // The Final URL is dynamically created at the ad level. DestinationUrl = null, FinalUrls = null, // In this example we are deferring to the campaign level tracking template. TrackingUrlTemplate = null, // Set custom parameters that are specific to this webpage criterion. UrlCustomParameters = new CustomParameters { Parameters = new[] { new CustomParameter() { Key = "promoCode", Value = "PROMO1" }, new CustomParameter() { Key = "season", Value = "summer" }, } } } }; uploadEntities.Add(adGroupWebpagePositivePageContent); // To discover the categories that you can use for Webpage criterion (positive or negative), // use the GetDomainCategories operation with the Ad Insight service. var getDomainCategoriesResponse = await AdInsightExampleHelper.GetDomainCategoriesAsync( categoryName : null, domainName : DOMAIN_NAME, language : LANGUAGE); var categories = getDomainCategoriesResponse.Categories; // If any categories are available let's use one as a condition. if (categories.Count > 0) { var adGroupWebpagePositiveCategory = new BulkAdGroupDynamicSearchAdTarget { BiddableAdGroupCriterion = new BiddableAdGroupCriterion { AdGroupId = adGroupIdKey, CriterionBid = new FixedBid { Amount = 0.50 }, Criterion = new Webpage { Parameter = new WebpageParameter { Conditions = new[] { new WebpageCondition { Argument = categories[0].CategoryName, Operand = WebpageConditionOperand.Category, } }, CriterionName = "Ad Group Webpage Positive Category Criterion" }, } } }; uploadEntities.Add(adGroupWebpagePositiveCategory); } // If you want to exclude certain portions of your website, you can add negative Webpage // criterion at the campaign and ad group level. var adGroupWebpageNegativeUrl = new BulkAdGroupNegativeDynamicSearchAdTarget { NegativeAdGroupCriterion = new NegativeAdGroupCriterion { AdGroupId = adGroupIdKey, Criterion = new Webpage { Parameter = new WebpageParameter { // You can choose whether you want the criterion argument to match partial URLs, // page content, page title, or categories that Bing thinks applies to your website. Conditions = new[] { new WebpageCondition { Argument = DOMAIN_NAME, Operand = WebpageConditionOperand.Url, } }, // If you do not specify any name, then it will be set to a concatenated list of conditions. CriterionName = null } } } }; uploadEntities.Add(adGroupWebpageNegativeUrl); // The negative Webpage criterion at the campaign level applies to all ad groups // within the campaign; however, if you define ad group level negative Webpage criterion, // the campaign criterion is ignored for that ad group. var campaignWebpageNegative = new BulkCampaignNegativeDynamicSearchAdTarget { NegativeCampaignCriterion = new NegativeCampaignCriterion { CampaignId = campaignIdKey, Criterion = new Webpage { Parameter = new WebpageParameter { Conditions = new[] { new WebpageCondition { Argument = DOMAIN_NAME + "\\seattle", Operand = WebpageConditionOperand.Url, } }, CriterionName = "Campaign Negative Webpage Url Criterion" } } } }; uploadEntities.Add(campaignWebpageNegative); // Finally you can add a DynamicSearchAd into the ad group. The ad title and display URL // are generated automatically based on the website domain and language that you want to target. var bulkDynamicSearchAd = new BulkDynamicSearchAd { ClientId = "here", AdGroupId = adGroupIdKey, DynamicSearchAd = new DynamicSearchAd { Text = "Find New Customers & Increase Sales! Start Advertising on Contoso Today.", Path1 = "seattle", Path2 = "shoe sale", // You cannot set FinalUrls. The Final URL will be a dynamically selected landing page. // The final URL is distinct from the path that customers will see and click on in your ad. FinalUrls = null, // In this example we are deferring to the campaign level tracking template. TrackingUrlTemplate = null, // Set custom parameters that are specific to this ad. UrlCustomParameters = new CustomParameters { Parameters = new[] { new CustomParameter() { Key = "promoCode", Value = "PROMO1" }, new CustomParameter() { Key = "season", Value = "summer" }, } } }, }; uploadEntities.Add(bulkDynamicSearchAd); // Upload and write the output OutputStatusMessage("Adding campaign, ad group, criterions, and ads . . .\n"); var Reader = await WriteEntitiesAndUploadFileAsync(uploadEntities); var downloadEntities = Reader.ReadEntities().ToList(); var campaignResults = downloadEntities.OfType <BulkCampaign>().ToList(); OutputBulkCampaigns(campaignResults); var adGroupResults = downloadEntities.OfType <BulkAdGroup>().ToList(); OutputBulkAdGroups(adGroupResults); var campaignNegativeDynamicSearchAdTargetResults = downloadEntities.OfType <BulkCampaignNegativeDynamicSearchAdTarget>().ToList(); OutputBulkCampaignNegativeDynamicSearchAdTargets(campaignNegativeDynamicSearchAdTargetResults); var adGroupDynamicSearchAdTargetResults = downloadEntities.OfType <BulkAdGroupDynamicSearchAdTarget>().ToList(); OutputBulkAdGroupDynamicSearchAdTargets(adGroupDynamicSearchAdTargetResults); var adGroupNegativeDynamicSearchAdTargetResults = downloadEntities.OfType <BulkAdGroupNegativeDynamicSearchAdTarget>().ToList(); OutputBulkAdGroupNegativeDynamicSearchAdTargets(adGroupNegativeDynamicSearchAdTargetResults); var dynamicSearchAdResults = downloadEntities.OfType <BulkDynamicSearchAd>().ToList(); OutputBulkDynamicSearchAds(dynamicSearchAdResults); Reader.Dispose(); #endregion Add #region Update uploadEntities = new List <BulkEntity>(); // You can update the bid for BiddableAdGroupCriterion var updateBid = new FixedBid { Amount = 0.75 }; // You can update the Webpage criterion name but cannot update the conditions. // To update the conditions you must delete the criterion and add a new criterion. // This update attempt will return an error. var updateCriterionAttemptFailure = new Webpage { Parameter = new WebpageParameter { Conditions = new[] { new WebpageCondition { Argument = "Books", Operand = WebpageConditionOperand.PageContent, } }, CriterionName = "Update Attempt Failure" }, }; var updateCriterionAttemptSuccess = new Webpage { Parameter = new WebpageParameter { CriterionName = "Update Attempt Success" }, }; foreach (var adGroupDynamicSearchAdTargetResult in adGroupDynamicSearchAdTargetResults) { var biddableAdGroupCriterion = adGroupDynamicSearchAdTargetResult.BiddableAdGroupCriterion as BiddableAdGroupCriterion; if (biddableAdGroupCriterion != null) { ((BiddableAdGroupCriterion)adGroupDynamicSearchAdTargetResult.BiddableAdGroupCriterion).CriterionBid = updateBid; adGroupDynamicSearchAdTargetResult.BiddableAdGroupCriterion.Criterion = updateCriterionAttemptSuccess; uploadEntities.Add(adGroupDynamicSearchAdTargetResult); } } foreach (var adGroupNegativeDynamicSearchAdTargetResult in adGroupNegativeDynamicSearchAdTargetResults) { var negativeAdGroupCriterion = adGroupNegativeDynamicSearchAdTargetResult.NegativeAdGroupCriterion as NegativeAdGroupCriterion; if (negativeAdGroupCriterion != null) { adGroupNegativeDynamicSearchAdTargetResult.NegativeAdGroupCriterion.Criterion = updateCriterionAttemptFailure; uploadEntities.Add(adGroupNegativeDynamicSearchAdTargetResult); } } OutputStatusMessage("Updating Ad Group Webpage Criterion . . . \n"); Reader = await WriteEntitiesAndUploadFileAsync(uploadEntities); downloadEntities = Reader.ReadEntities().ToList(); adGroupDynamicSearchAdTargetResults = downloadEntities.OfType <BulkAdGroupDynamicSearchAdTarget>().ToList(); OutputBulkAdGroupDynamicSearchAdTargets(adGroupDynamicSearchAdTargetResults); adGroupNegativeDynamicSearchAdTargetResults = downloadEntities.OfType <BulkAdGroupNegativeDynamicSearchAdTarget>().ToList(); OutputBulkAdGroupNegativeDynamicSearchAdTargets(adGroupNegativeDynamicSearchAdTargetResults); Reader.Dispose(); #endregion Update #region Get var entities = new[] { DownloadEntity.AdGroupDynamicSearchAdTargets, DownloadEntity.AdGroupNegativeDynamicSearchAdTargets }; var downloadParameters = new DownloadParameters { CampaignIds = null, DataScope = DataScope.EntityData | DataScope.EntityPerformanceData, PerformanceStatsDateRange = new PerformanceStatsDateRange { PredefinedTime = ReportTimePeriod.LastFourWeeks }, DownloadEntities = entities, FileType = FileType, LastSyncTimeInUTC = null, ResultFileDirectory = FileDirectory, ResultFileName = DownloadFileName, OverwriteResultFile = true }; // You may optionally cancel the DownloadFileAsync operation after a specified time interval. var tokenSource = new CancellationTokenSource(); tokenSource.CancelAfter(TimeoutInMilliseconds); var resultFilePath = await BulkServiceManager.DownloadFileAsync(downloadParameters, progress, tokenSource.Token); OutputStatusMessage(String.Format("Download result file: {0}\n", resultFilePath)); #endregion Get #region CleanUp // Delete the campaign, ad group, criterion, and ad that were previously added. // You should remove this operation if you want to view the added entities in the // Bing Ads web application or another tool. // You must set the Id field to the corresponding entity identifier, and the Status field to Deleted. // When you delete a BulkCampaign, the dependent entities such as BulkAdGroup // are deleted without being specified explicitly. uploadEntities = new List <BulkEntity>(); foreach (var campaignResult in campaignResults) { campaignResult.Campaign.Status = CampaignStatus.Deleted; uploadEntities.Add(campaignResult); } OutputStatusMessage("\nDeleting DSA campaign, criterions, and ad . . .\n"); Reader = await WriteEntitiesAndUploadFileAsync(uploadEntities); downloadEntities = Reader.ReadEntities().ToList(); campaignResults = downloadEntities.OfType <BulkCampaign>().ToList(); OutputBulkCampaigns(campaignResults); Reader.Dispose(); #endregion Cleanup } // Catch Microsoft Account authorization exceptions. catch (OAuthTokenRequestException ex) { OutputStatusMessage(string.Format("Couldn't get OAuth tokens. Error: {0}. Description: {1}", ex.Details.Error, ex.Details.Description)); } // Catch Bulk service exceptions catch (FaultException <Microsoft.BingAds.V12.Bulk.AdApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException <Microsoft.BingAds.V12.Bulk.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 (BulkOperationInProgressException ex) { OutputStatusMessage("The result file for the bulk operation is not yet available for download."); OutputStatusMessage(ex.Message); } catch (BulkOperationCouldNotBeCompletedException <DownloadStatus> ex) { OutputStatusMessage(string.Join("; ", ex.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (BulkOperationCouldNotBeCompletedException <UploadStatus> ex) { OutputStatusMessage(string.Join("; ", ex.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (Exception ex) { OutputStatusMessage(ex.Message); } finally { if (Reader != null) { Reader.Dispose(); } if (Writer != null) { Writer.Dispose(); } } }
public async override Task RunAsync(AuthorizationData authorizationData) { try { #region CampaignThroughAdGroupSetup // You will need to use the Campaign Management service to get the Bing Merchant Center Store Id. This will be used // when creating a new Bing Shopping Campaign. // For other operations such as adding product conditions, you can manage Bing Shopping Campaigns soley with the Bulk Service. CampaignService = new ServiceClient <ICampaignManagementService>(authorizationData); // Get a list of all Bing Merchant Center stores associated with your CustomerId IList <BMCStore> stores = await GetBMCStoresByCustomerIdAsync(); if (stores == null) { OutputStatusMessage( String.Format("You do not have any BMC stores registered for CustomerId {0}.\n", authorizationData.CustomerId) ); return; } BulkService = new BulkServiceManager(authorizationData); var progress = new Progress <BulkOperationProgressInfo>(x => OutputStatusMessage(String.Format("{0} % Complete", x.PercentComplete.ToString(CultureInfo.InvariantCulture)))); const int campaignIdKey = -123; const int adGroupIdKey = -1234; var uploadEntities = new List <BulkEntity>(); /* Add a new Bing Shopping campaign that will be associated with a ProductScope criterion. * - Set the CampaignType element of the Campaign to Shopping. * - Create a ShoppingSetting instance and set its Priority (0, 1, or 2), SalesCountryCode, and StoreId elements. * Add this shopping setting to the Settings list of the Campaign. */ var bulkCampaign = new BulkCampaign { // ClientId may be used to associate records in the bulk upload file with records in the results file. The value of this field // is not used or stored by the server; it is simply copied from the uploaded record to the corresponding result record. // Note: This bulk file Client Id is not related to an application Client Id for OAuth. ClientId = "YourClientIdGoesHere", Campaign = new Campaign { // When using the Campaign Management service, the Id cannot be set. In the context of a BulkCampaign, the Id is optional // and may be used as a negative reference key during bulk upload. For example the same negative value set for the campaign Id // will be used when associating this new campaign with a new campaign product scope in the BulkCampaignProductScope object below. Id = campaignIdKey, CampaignType = CampaignType.Shopping, Settings = new[] { new ShoppingSetting() { Priority = 0, SalesCountryCode = "US", StoreId = (int)stores[0].Id } }, Name = "Bing Shopping Campaign " + DateTime.UtcNow, Description = "Bing Shopping Campaign Example.", BudgetType = BudgetLimitType.MonthlyBudgetSpendUntilDepleted, MonthlyBudget = 1000.00, TimeZone = "PacificTimeUSCanadaTijuana", } }; /* Optionally, you can create a ProductScope criterion that will be associated with your Bing Shopping campaign. * Use the product scope criterion to include a subset of your product catalog, for example a specific brand, * category, or product type. A campaign can only be associated with one ProductScope, which contains a list * of up to 7 ProductCondition. You'll also be able to specify more specific product conditions for each ad group. */ var bulkCampaignProductScope = new BulkCampaignProductScope { CampaignCriterion = new CampaignCriterion() { CampaignId = campaignIdKey, BidAdjustment = null, // Reserved for future use Criterion = new ProductScope() { Conditions = new ProductCondition[] { new ProductCondition { Operand = "Condition", Attribute = "New" }, new ProductCondition { Operand = "CustomLabel0", Attribute = "MerchantDefinedCustomLabel" }, } }, }, Status = Status.Active, }; // Specify one or more ad groups. var bulkAdGroup = new BulkAdGroup { CampaignId = campaignIdKey, AdGroup = new AdGroup { Id = adGroupIdKey, Name = "Product Categories", AdDistribution = AdDistribution.Search, BiddingModel = BiddingModel.Keyword, PricingModel = PricingModel.Cpc, StartDate = null, EndDate = new Microsoft.BingAds.CampaignManagement.Date { Month = 12, Day = 31, Year = 2016 }, Language = "English" }, }; /* * Create a product ad. You must add at least one ProductAd to the corresponding ad group. * A ProductAd is not used directly for delivered ad copy. Instead, the delivery engine generates * product ads from the product details that it finds in your Bing Merchant Center store's product catalog. * The primary purpose of the ProductAd object is to provide promotional text that the delivery engine * adds to the product ads that it generates. For example, if the promotional text is set to * “Free shipping on $99 purchases”, the delivery engine will set the product ad’s description to * “Free shipping on $99 purchases.” */ var bulkProductAd = new BulkProductAd { AdGroupId = adGroupIdKey, ProductAd = new ProductAd { PromotionalText = "Free shipping on $99 purchases." } }; uploadEntities.Add(bulkCampaign); uploadEntities.Add(bulkAdGroup); uploadEntities.Add(bulkCampaignProductScope); uploadEntities.Add(bulkProductAd); // Write the upload output var Reader = await UploadEntities(uploadEntities); var bulkEntities = Reader.ReadEntities().ToList(); var campaignResults = bulkEntities.OfType <BulkCampaign>().ToList(); OutputBulkCampaigns(campaignResults); var adGroupResults = bulkEntities.OfType <BulkAdGroup>().ToList(); OutputBulkAdGroups(adGroupResults); var productAdResults = bulkEntities.OfType <BulkProductAd>().ToList(); OutputBulkProductAds(productAdResults); var campaignProductScopeResults = bulkEntities.OfType <BulkCampaignProductScope>().ToList(); OutputBulkCampaignProductScopes(campaignProductScopeResults); Reader.Dispose(); #endregion CampaignThroughAdGroupSetup #region BidAllProducts var adGroupId = (long)adGroupResults[0].AdGroup.Id; var helper = new ProductPartitionHelper(adGroupId); var root = helper.AddUnit( null, new ProductCondition { Operand = "All", Attribute = null }, 0.35, false, "root" ); OutputStatusMessage("Applying only the root as a Unit with a bid . . . \n"); var bulkApplyProductPartitionActionsResults = await BulkApplyProductPartitionActions(helper.PartitionActions); var productPartitions = await GetBulkAdGroupProductPartitionTree(adGroupId); OutputStatusMessage("The ad group's product partition only has a tree root node: \n"); OutputProductPartitions(productPartitions); /* * Let's update the bid of the root Unit we just added. */ var updatedRoot = GetNodeByClientId(bulkApplyProductPartitionActionsResults, "root"); var bid = new FixedBid { Bid = new Bid { Amount = 0.45 } }; ((BiddableAdGroupCriterion)(updatedRoot.AdGroupCriterion)).CriterionBid = bid; helper = new ProductPartitionHelper(adGroupId); helper.UpdatePartition(updatedRoot); OutputStatusMessage("Updating the bid for the tree root node . . . \n"); await BulkApplyProductPartitionActions(helper.PartitionActions); productPartitions = await GetBulkAdGroupProductPartitionTree(adGroupId); OutputStatusMessage("Updated the bid for the tree root node: \n"); OutputProductPartitions(productPartitions); #endregion BidAllProducts #region InitializeTree /* * Now we will overwrite any existing tree root, and build a product partition group tree structure in multiple steps. * You could build the entire tree in a single call since there are less than 5,000 nodes; however, * we will build it in steps to demonstrate how to use the results from ApplyProductPartitionActions to update the tree. * * For a list of validation rules, see the Bing Shopping Campaigns technical guide: * https://msdn.microsoft.com/en-US/library/bing-ads-campaign-management-bing-shopping-campaigns.aspx */ helper = new ProductPartitionHelper(adGroupId); /* * Check whether a root node exists already. */ productPartitions = await GetBulkAdGroupProductPartitionTree(adGroupId); var existingRoot = GetNodeByClientId(bulkApplyProductPartitionActionsResults, "root"); if (existingRoot != null) { helper.DeletePartition(existingRoot); } root = helper.AddSubdivision( null, new ProductCondition { Operand = "All", Attribute = null }, "root" ); /* * The direct children of any node must have the same Operand. * For this example we will use CategoryL1 nodes as children of the root. * For a list of valid CategoryL1 through CategoryL5 values, see the Bing Category Taxonomy: * http://advertise.bingads.microsoft.com/en-us/WWDocs/user/search/en-us/Bing_Category_Taxonomy.txt */ var animalsSubdivision = helper.AddSubdivision( root, new ProductCondition { Operand = "CategoryL1", Attribute = "Animals & Pet Supplies" }, "animalsSubdivision" ); /* * If you use a CategoryL2 node, it must be a descendant (child or later) of a CategoryL1 node. * In other words you cannot have a CategoryL2 node as parent of a CategoryL1 node. * For this example we will a CategoryL2 node as child of the CategoryL1 Animals & Pet Supplies node. */ var petSuppliesSubdivision = helper.AddSubdivision( animalsSubdivision, new ProductCondition { Operand = "CategoryL2", Attribute = "Pet Supplies" }, "petSuppliesSubdivision" ); var brandA = helper.AddUnit( petSuppliesSubdivision, new ProductCondition { Operand = "Brand", Attribute = "Brand A" }, 0.35, false, "brandA" ); /* * If you won't bid on Brand B, set the helper method's bidAmount to '0' and isNegative to true. * The helper method will create a NegativeAdGroupCriterion and apply the condition. */ var brandB = helper.AddUnit( petSuppliesSubdivision, new ProductCondition { Operand = "Brand", Attribute = "Brand B" }, 0, true, "brandB" ); var otherBrands = helper.AddUnit( petSuppliesSubdivision, new ProductCondition { Operand = "Brand", Attribute = null }, 0.35, false, "otherBrands" ); var otherPetSupplies = helper.AddUnit( animalsSubdivision, new ProductCondition { Operand = "CategoryL2", Attribute = null }, 0.35, false, "otherPetSupplies" ); var electronics = helper.AddUnit( root, new ProductCondition { Operand = "CategoryL1", Attribute = "Electronics" }, 0.35, false, "electronics" ); var otherCategoryL1 = helper.AddUnit( root, new ProductCondition { Operand = "CategoryL1", Attribute = null }, 0.35, false, "otherCategoryL1" ); OutputStatusMessage("Applying product partitions to the ad group . . . \n"); bulkApplyProductPartitionActionsResults = await BulkApplyProductPartitionActions(helper.PartitionActions); productPartitions = await GetBulkAdGroupProductPartitionTree(adGroupId); /* * The product partition group tree now has 9 nodes. * * All other (Root Node) | +-- Animals & Pet Supplies (CategoryL1) | | | +-- Pet Supplies (CategoryL2) | | | | | +-- Brand A | | | | | +-- Brand B | | | | | +-- All other (Brand) | | | +-- All other (CategoryL2) | +-- Electronics (CategoryL1) | +-- All other (CategoryL1) | */ OutputStatusMessage("The product partition group tree now has 9 nodes: \n"); OutputProductPartitions(productPartitions); #endregion InitializeTree #region UpdateTree /* * Let's replace the Electronics (CategoryL1) node created above with an Electronics (CategoryL1) node that * has children i.e. Brand C (Brand), Brand D (Brand), and All other (Brand) as follows: * * Electronics (CategoryL1) | +-- Brand C (Brand) | +-- Brand D (Brand) | +-- All other (Brand) | */ helper = new ProductPartitionHelper(adGroupId); /* * To replace a node we must know its Id and its ParentCriterionId. In this case the parent of the node * we are replacing is All other (Root Node). The node that we are replacing is Electronics (CategoryL1). */ var rootId = GetNodeByClientId(bulkApplyProductPartitionActionsResults, "root").AdGroupCriterion.Id; electronics.AdGroupCriterion.Id = GetNodeByClientId(bulkApplyProductPartitionActionsResults, "electronics").AdGroupCriterion.Id; helper.DeletePartition(electronics); var parent = new BulkAdGroupProductPartition { AdGroupCriterion = new BiddableAdGroupCriterion() { Id = rootId } }; var electronicsSubdivision = helper.AddSubdivision( parent, new ProductCondition { Operand = "CategoryL1", Attribute = "Electronics" }, "electronicsSubdivision" ); var brandC = helper.AddUnit( electronicsSubdivision, new ProductCondition { Operand = "Brand", Attribute = "Brand C" }, 0.35, false, "brandC" ); var brandD = helper.AddUnit( electronicsSubdivision, new ProductCondition { Operand = "Brand", Attribute = "Brand D" }, 0.35, false, "brandD" ); var otherElectronicsBrands = helper.AddUnit( electronicsSubdivision, new ProductCondition { Operand = "Brand", Attribute = null }, 0.35, false, "otherElectronicsBrands" ); OutputStatusMessage( "Updating the product partition group to refine Electronics (CategoryL1) with 3 child nodes . . . \n" ); bulkApplyProductPartitionActionsResults = await BulkApplyProductPartitionActions(helper.PartitionActions); productPartitions = await GetBulkAdGroupProductPartitionTree(adGroupId); /* * The product partition group tree now has 12 nodes, including the children of Electronics (CategoryL1): * * All other (Root Node) | +-- Animals & Pet Supplies (CategoryL1) | | | +-- Pet Supplies (CategoryL2) | | | | | +-- Brand A | | | | | +-- Brand B | | | | | +-- All other (Brand) | | | +-- All other (CategoryL2) | +-- Electronics (CategoryL1) | | | +-- Brand C (Brand) | | | +-- Brand D (Brand) | | | +-- All other (Brand) | +-- All other (CategoryL1) | */ OutputStatusMessage( "The product partition group tree now has 12 nodes, including the children of Electronics (CategoryL1): \n" ); OutputProductPartitions(productPartitions); #endregion UpdateTree #region CleanUp /* Delete the campaign, ad group, criterion, and ad that were previously added. * You should remove this region if you want to view the added entities in the * Bing Ads web application or another tool. */ var campaignId = campaignResults[0].Campaign.Id; bulkCampaign = new BulkCampaign { Campaign = new Campaign { Id = campaignId, Status = CampaignStatus.Deleted } }; uploadEntities = new List <BulkEntity>(); uploadEntities.Add(bulkCampaign); // Write the upload output Reader = await UploadEntities(uploadEntities); bulkEntities = Reader.ReadEntities().ToList(); campaignResults = bulkEntities.OfType <BulkCampaign>().ToList(); OutputBulkCampaigns(campaignResults); Reader.Dispose(); OutputStatusMessage(String.Format("Deleted CampaignId {0}\n", campaignResults[0].Campaign.Id)); #endregion Cleanup } // Catch Microsoft Account authorization exceptions. catch (OAuthTokenRequestException ex) { OutputStatusMessage(string.Format("Couldn't get OAuth tokens. Error: {0}. Description: {1}", ex.Details.Error, ex.Details.Description)); } // Catch Bulk service exceptions catch (FaultException <Microsoft.BingAds.Bulk.AdApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException <Microsoft.BingAds.Bulk.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 (BulkOperationInProgressException ex) { OutputStatusMessage("The result file for the bulk operation is not yet available for download."); OutputStatusMessage(ex.Message); } catch (BulkOperationCouldNotBeCompletedException <DownloadStatus> ex) { OutputStatusMessage(string.Join("; ", ex.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (BulkOperationCouldNotBeCompletedException <UploadStatus> ex) { OutputStatusMessage(string.Join("; ", ex.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (Exception ex) { OutputStatusMessage(ex.Message); } finally { if (Reader != null) { Reader.Dispose(); } if (Writer != null) { Writer.Dispose(); } } }