混合バッチの取り扱い
このページの翻訳はAIによって自動的に行われました。可能な限り正確な翻訳を心掛けていますが、原文と異なる表現や解釈が含まれる場合があります。正確で公式な情報については、必ず英語の原文をご参照ください。
接続の追加や更新など、操作タイプの混合バッチは例外処理をより複雑にすることがあります。以下のシナリオを考えてみましょう。
- CSVから2000件の連絡先がインポートされます
- CSVには名前、Twitterユーザー名、メールアドレスなどの10のファセット情報が含まれています
- 連絡先は新しくなる場合もあれば既存の場合もあります
- 既存の連絡先がすでにファセットデータを持っている場合もあれば、そうでない場合もあります
- 輸入中に利益相反が生じることがあります
すべての連絡先がTwitterのユーザー名またはメールアドレスで識別されていると仮定します。以下の例では、除外される可能性を最小限に抑えるための決定がなされています:
- 既存の連絡先は、最初に .GetAsync
を呼び出して識別します。これはxConnectへの別の呼び出しですが、既存のレコードに対して新しい連絡先を作成しようとはしません。既存の識別子で新しい連絡先を作成すると例外が発生し、その場合は操作をやり直す必要があります。 - 連絡先が存在してもファセットデータが変わっていなければ、更新は送信しません。必須ではありません。
- すべての「追加」および「更新」操作は依然として一括で提出されますが、例外が発生するリスクは最小限に抑えています。
コンソールの例
以下のコンソールアプリケーション例を用いて以下が可能になります:
- 既知の連絡先で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
clientModifiers = new List (); 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
(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 .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
(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 (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
(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
>() .Where(x => x.Result.Status == SaveResultStatus.Conflict); // Handle contacts that already exist var contactExists = ex.GetOperations(client).OfType
() .Where(x => x.Result.Status == SaveResultStatus.AlreadyExists); } } } private static List
GenerateListOfPeople() { var contacts = new List (); 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/)を置き換えてください。
-
証明書のサンプルを置き換えてください。証明書の指紋は
\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 | source45Myrtle | 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 | source45Bob | McBob | bobmcbob | source45Updating 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.