混合バッチの処理
このページの翻訳はAIによって自動的に行われました。可能な限り正確な翻訳を心掛けていますが、原文と異なる表現や解釈が含まれる場合があります。正確で公式な情報については、必ず英語の原文をご参照ください。
操作の種類 (連絡先の追加や更新など) が混在するバッチは、例外処理が複雑になる可能性があります。次のシナリオについて考えてみます。
-
2000件の連絡先のバッチがCSVからインポートされます
-
CSVには、名前、Twitterユーザー名、電子メール アドレスなど、10個のファセット情報が含まれています
-
連絡先は新規または既存のものでもかまいません
-
既存のコンタクトは、既存のファセットデータを持っている場合と持っていない場合があります
-
インポート中に競合が発生する可能性があります
すべての連絡先がTwitterのユーザー名またはメールアドレスで識別されると仮定します。次の例では、スローされる可能性のある例外の数を最小限に抑えるための決定が行われています。
-
既存の連絡先は、最初に .GetAsync<Contact> に電話することで識別されます。これはxConnectへの別の呼び出しですが、すでに存在するレコードに対して新しい連絡先を作成しようとはしないことを意味します。既に存在する識別子を使用して新しい連絡先を作成すると、例外が発生するため、これらの操作を再試行する必要があります。
-
コンタクトが存在するが、そのファセットデータが変更されていない場合、更新は送信されません。必須ではありません。
-
すべての「追加」および「更新操作」は引き続き1つのバッチとして送信されますが、例外が発生するリスクは最小限に抑えられています。
サンプル・コンソール・アプリケーション
次のサンプル・コンソール・アプリケーションを使用すると、次のことができます。
-
xDBに既知のコンタクトを事前入力します。
-
新規および既存の連絡先のバッチの追加をシミュレートします。
サンプル・コンソール・アプリケーションを使用するには、次のようにします。
-
Visual Studioで新しいコンソール アプリケーションを作成します。
-
Sitecoreフィードから次のNuGetパッケージを追加します。
-
Sitecore.XConnect.Client
-
Sitecore.XConnect.Collection.Model
-
Sitecore.XConnect
-
-
次のコードをProgram.csに貼り付けます。
using Sitecore.XConnect; using Sitecore.XConnect.Client; using Sitecore.XConnect.Client.WebApi; using Sitecore.XConnect.Collection.Model; using Sitecore.XConnect.Schema; using Sitecore.Xdb.Common.Web; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Sitecore.XConnect.Operations; using Sitecore.Xdb.Common.Web; using Console = System.Console; using PersonalInformation = Sitecore.XConnect.Collection.Model.PersonalInformation; namespace Sitecore.Documentation { public class Program { private static int charcount = 20; private static void Main(string[] args) { MainAsync(args).ConfigureAwait(false).GetAwaiter().GetResult(); Console.ForegroundColor = ConsoleColor.DarkGreen; Console.WriteLine(""); Console.WriteLine("END OF PROGRAM."); Console.ReadKey(); } private static async Task MainAsync(string[] args) { CertificateHttpClientHandlerModifierOptions options = CertificateHttpClientHandlerModifierOptions.Parse( "StoreName=My;StoreLocation=LocalMachine;FindType=FindByThumbprint;FindValue=5496B9D84850AFE5A3554B7B2D23F3B5C79CA53A"); var certificateModifier = new CertificateHttpClientHandlerModifier(options); List<IHttpClientModifier> clientModifiers = new List<IHttpClientModifier>(); var timeoutClientModifier = new TimeoutHttpClientModifier(new TimeSpan(0, 0, 20)); clientModifiers.Add(timeoutClientModifier); var collectionClient = new CollectionWebApiClient(new Uri("https://tut4xconnect.dev.local/odata"), clientModifiers, new[] {certificateModifier}); var searchClient = new SearchWebApiClient(new Uri("https://tut4xconnect.dev.local/odata"), clientModifiers, new[] {certificateModifier}); var configurationClient = new ConfigurationWebApiClient( new Uri("https://tut4xconnect.dev.local/configuration"), clientModifiers, new[] {certificateModifier}); var cfg = new XConnectClientConfiguration( new XdbRuntimeModel(CollectionModel.Model), collectionClient, searchClient, configurationClient); try { await cfg.InitializeAsync(); // Print xConnect if configuration is valid var arr = new[] { @" ______ __ ", @" / \ | \ ", @" __ __ | $$$$$$\ ______ _______ _______ ______ _______ _| $$_ ", @"| \ / \| $$ \$$ / \ | \ | \ / \ / \| $$ \ ", @"\$$\/ $$| $$ | $$$$$$\| $$$$$$$\| $$$$$$$\| $$$$$$\| $$$$$$$ \$$$$$$ ", @" >$$ $$ | $$ __ | $$ | $$| $$ | $$| $$ | $$| $$ $$| $$ | $$ __ ", @" / $$$$\ | $$__/ \| $$__/ $$| $$ | $$| $$ | $$| $$$$$$$$| $$_____ | $$| \", @"| $$ \$$\ \$$ $$ \$$ $$| $$ | $$| $$ | $$ \$$ \ \$$ \ \$$ $$", @" \$$ \$$ \$$$$$$ \$$$$$$ \$$ \$$ \$$ \$$ \$$$$$$$ \$$$$$$$ \$$$$ " }; Console.WindowWidth = 160; foreach (string line in arr) Console.WriteLine(line); } catch (XdbModelConflictException ce) { Console.WriteLine("ERROR:" + ce.Message); return; } // Initialize a client using the validated configuration using (var client = new XConnectClient(cfg)) { try { Console.WriteLine(); Console.ForegroundColor = ConsoleColor.Yellow; Console.WriteLine("#############################################################################"); Console.WriteLine("Prefill the xDB with some test data:"); Console.WriteLine("#############################################################################"); Console.WriteLine(); Console.ForegroundColor = ConsoleColor.White; var newContacts = GenerateListOfPeople(); Console.ForegroundColor = ConsoleColor.Green; Console.WriteLine("#############################################################################"); Console.WriteLine("Contacts created in xDB:"); Console.WriteLine("#############################################################################"); Console.WriteLine(); Console.ForegroundColor = ConsoleColor.White; Console.WriteLine(new string('-', charcount * 4)); foreach (var p in newContacts) { var newContact = new Contact(); client.AddContactIdentifier(newContact, new ContactIdentifier(p.Source, p.Identifier, ContactIdentifierType.Known)); client.SetFacet(newContact, new PersonalInformation() {FirstName = p.Firstname, LastName = p.LastName}); client.AddContact(newContact); var row = p.Firstname.PadRight(charcount) + "| " + p.LastName.PadRight(charcount) + "| " + p.Identifier.PadRight(charcount) + "| " + p.Source.PadRight(charcount); Console.WriteLine(row); Console.WriteLine(new string('-', charcount * 4)); } Console.WriteLine(); try { client.Submit(); } catch (XdbExecutionException ex) { Console.WriteLine(ex.Message); } Console.ForegroundColor = ConsoleColor.Green; Console.WriteLine("#############################################################################"); Console.WriteLine("Create contact data that you want to import - mix new and existing contacts!:"); Console.WriteLine("#############################################################################"); Console.ForegroundColor = ConsoleColor.White; Console.WriteLine(); var newAndUpdatedContacts = GenerateListOfPeople(); Console.ForegroundColor = ConsoleColor.Green; Console.WriteLine("#############################################################################"); Console.WriteLine("Contact data to import:"); Console.WriteLine("#############################################################################"); Console.WriteLine(); Console.ForegroundColor = ConsoleColor.White; Console.WriteLine(new string('-', charcount * 4)); foreach (var p in newAndUpdatedContacts) { var row = p.Firstname.PadRight(charcount) + "| " + p.LastName.PadRight(charcount) + "| " + p.Identifier.PadRight(charcount) + "| " + p.Source.PadRight(charcount); Console.WriteLine(row); Console.WriteLine(new string('-', charcount * 4)); } // Create identifiers from Identifier/Source properties so that we can retrieve contacts var identifiers = newAndUpdatedContacts.Select(x => new IdentifiedContactReference(x.Source, x.Identifier)).ToArray(); // Retrieve all contacts using list of identifiers - make sure you also retrieve the PersonalInfo facet in the expand options, // as we want to write to these. var contactsTask = client.GetAsync<Contact>(identifiers, new ContactExecutionOptions( new ContactExpandOptions(PersonalInformation.DefaultFacetKey))); var contactResults = await contactsTask; if (contactResults != null && contactResults.Any()) { foreach (var result in contactResults) { if (result.Exists) { // Get existing contact's identifiers var contactIdentifiers = result.Entity.Identifiers.Select(i => i.Identifier); var csvEntry = newAndUpdatedContacts.FirstOrDefault(p => result.Entity.Identifiers.Select(c => p.Identifier == c.Identifier).FirstOrDefault()); Console.WriteLine(); Console.ForegroundColor = ConsoleColor.Cyan; Console.WriteLine("Updating existing contact: " + csvEntry.Identifier + "/" + csvEntry.Source); Console.ForegroundColor = ConsoleColor.White; if (csvEntry != null) { csvEntry.ContactExists = true; var personalInfoFacet = result.Entity.GetFacet<PersonalInformation>(PersonalInformation .DefaultFacetKey); if (personalInfoFacet != null) { Console.WriteLine("Contact has PersonalInformation facet."); // Check if facet is identical to what's in the CSV - if so, ignore. No need to send unnecessarny facet updates. bool FacetHasChanged = false; if (!Helpers.IsIdenticalOrEmptyOrNull(personalInfoFacet.FirstName, csvEntry.Firstname)) { Console.WriteLine("First name was: " + personalInfoFacet.FirstName); personalInfoFacet.FirstName = csvEntry.Firstname; FacetHasChanged = true; Console.WriteLine("First name changed to: " + csvEntry.Firstname); } if (!Helpers.IsIdenticalOrEmptyOrNull(personalInfoFacet.LastName, csvEntry.LastName)) { Console.WriteLine("Last name was: " + personalInfoFacet.LastName); personalInfoFacet.LastName = csvEntry.LastName; FacetHasChanged = true; Console.WriteLine("Last name changed to: " + csvEntry.LastName); } if (FacetHasChanged) { client.SetFacet<PersonalInformation>(result.Entity, PersonalInformation.DefaultFacetKey, personalInfoFacet); } else { Console.WriteLine("No facet data has changed."); } } else { Console.WriteLine("Contact does not have PersonalInfo facet - creating new facet."); // Set PersonalInfo facet data - note that we are not performing any validation; if first name and surname were not provided, those values will be null or empty. // This method adds a SetFacetOperation for each contact and adds it to the batch. client.SetFacet<PersonalInformation>(result.Entity, PersonalInformation.DefaultFacetKey, new PersonalInformation() { FirstName = csvEntry.Firstname, LastName = csvEntry.LastName }); } } } } } // Now deal with new contacts foreach (var csvEntry in newAndUpdatedContacts.Where(x => !x.ContactExists)) { Console.ForegroundColor = ConsoleColor.Cyan; ; Console.WriteLine("Creating new contact: " + csvEntry.Identifier + "/" + csvEntry.Source); Console.ForegroundColor = ConsoleColor.White; Console.WriteLine(); // Create a new contact object for each row var contact = new Contact(); // In this example, we have elected not to add contacts that do not have a Identifier naem if (!string.IsNullOrEmpty(csvEntry.Identifier)) { // In this instance, Identifier names are the unique identifier. client.AddContactIdentifier(contact, new ContactIdentifier(csvEntry.Source, csvEntry.Identifier, ContactIdentifierType.Known)); client.SetFacet<PersonalInformation>(contact, PersonalInformation.DefaultFacetKey, new PersonalInformation() { FirstName = csvEntry.Firstname, LastName = csvEntry.LastName }); // Add the contact - this method creates an AddContactOperation for each contact and adds it to the batch. client.AddContact(contact); } } await client.SubmitAsync(); } catch (XdbExecutionException ex) { // Handle conflicts that may have happened when udpating existing contacts var setPersonalOperations = ex.GetOperations(client) .OfType<SetFacetOperation<PersonalInformation>>() .Where(x => x.Result.Status == SaveResultStatus.Conflict); // Handle contacts that already exist var contactExists = ex.GetOperations(client).OfType<AddContactOperation>() .Where(x => x.Result.Status == SaveResultStatus.AlreadyExists); } } } private static List<Person> GenerateListOfPeople() { var contacts = new List<Person>(); var createMoreContacts = true; string source = string.Empty; string identifier = string.Empty; string firstname = string.Empty; string lastname = string.Empty; int count = 1; while (createMoreContacts == true) { Console.ForegroundColor = ConsoleColor.Yellow; Console.WriteLine("Contact #" + count); Console.ForegroundColor = ConsoleColor.White; count++; Console.Write("Contact identifier source: "); source = Console.ReadLine(); Console.Write("Contact identifier: "); identifier = Console.ReadLine(); Console.Write("First name: "); firstname = Console.ReadLine(); Console.Write("Last name: "); lastname = Console.ReadLine(); var person = new Person() { Firstname = firstname, LastName = lastname, Identifier = identifier, Source = source }; contacts.Add(person); Console.WriteLine(); Console.Write("Create more contacts (Y/N)?: "); var createMore = Console.ReadLine(); Console.WriteLine(); if (createMore.ToLowerInvariant() == "n") { createMoreContacts = false; } } return contacts; } } public class Person { public string Firstname { get; set; } public string LastName { get; set; } public string Identifier { get; set; } public string Source { get; set; } public bool ContactExists { get; set; } } public static class Helpers { public static bool IsIdenticalOrEmptyOrNull(string current, string imported) { if (current.Equals(imported) || string.IsNullOrEmpty(imported)) { return true; } return false; } } } -
サンプルのxConnectサービスURL (https://xconnect.dev.local/) を置き換えます。
-
サンプルの証明書の拇印を置き換えます。証明書の拇印は、<xconnect-root>\App_Config\AppSettings.configファイルにあります。
-
F5キーを押して、アプリケーションを実行します。次の例は、コンソール・アプリケーションの出力を示しています。
______ __ / \ | \ __ __ | $$$$$$\ ______ _______ _______ ______ _______ _| $$_ | \ / \| $$ \$$ / \ | \ | \ / \ / \| $$ \ \$$\/ $$| $$ | $$$$$$\| $$$$$$$\| $$$$$$$\| $$$$$$\| $$$$$$$ \$$$$$$ >$$ $$ | $$ __ | $$ | $$| $$ | $$| $$ | $$| $$ $$| $$ | $$ __ / $$$$\ | $$__/ \| $$__/ $$| $$ | $$| $$ | $$| $$$$$$$$| $$_____ | $$| \ | $$ \$$\ \$$ $$ \$$ $$| $$ | $$| $$ | $$ \$$ \ \$$ \ \$$ $$ \$$ \$$ \$$$$$$ \$$$$$$ \$$ \$$ \$$ \$$ \$$$$$$$ \$$$$$$$ \$$$$ ############################################################################# Prefill the xDB with some test data: ############################################################################# Contact #1 Contact identifier source: source45 Contact identifier: mhwelander First name: Martina Last name: Welander Create more contacts (Y/N)?: Y Contact #2 Contact identifier source: source45 Contact identifier: myrtlesitecore First name: Myrtle Last name: Sitecore Create more contacts (Y/N)?: N ############################################################################# Contacts created in xDB: ############################################################################# -------------------------------------------------------------------------------- Martina | Welander | mhwelander | source45 -------------------------------------------------------------------------------- Myrtle | Sitecore | myrtlesitecore | source45 -------------------------------------------------------------------------------- ############################################################################# Create contact data that you want to import - mix new and existing contacts!: ############################################################################# Contact #1 Contact identifier source: source45 Contact identifier: mhwelander First name: Martina Updated Last name: Welander Updated Create more contacts (Y/N)?: Y Contact #2 Contact identifier source: source45 Contact identifier: bobmcbob First name: Bob Last name: McBob Create more contacts (Y/N)?: N ############################################################################# Contact data to import: ############################################################################# -------------------------------------------------------------------------------- Martina Updated | Welander Updated | mhwelander | source45 -------------------------------------------------------------------------------- Bob | McBob | bobmcbob | source45 -------------------------------------------------------------------------------- Updating existing contact: mhwelander/source45 Contact has PersonalInformation facet. First name was: Martina First name changed to: Martina Updated Last name was: Welander Last name changed to: Welander Updated Creating new contact: bobmcbob/source45 END OF PROGRAM.