/// <summary> /// Get a function to access the key field of a search document. /// </summary> /// <param name="async">Whether to run sync or async.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>A Task representing the operation.</returns> private async Task GetKeyFieldAccessorAsync(bool async, CancellationToken cancellationToken) { // Case 1: The user provided an explicit accessor and we're done if (KeyFieldAccessor != null) { return; } // Case 2: Infer the accessor from FieldBuilder try { FieldBuilder builder = new FieldBuilder { Serializer = SearchClient.Serializer }; IDictionary <string, SearchField> fields = builder.BuildMapping(typeof(T)); KeyValuePair <string, SearchField> keyField = fields.FirstOrDefault(pair => pair.Value.IsKey == true); if (!keyField.Equals(default(KeyValuePair <string, SearchField>))) { KeyFieldAccessor = CompileAccessor(keyField.Key); return; } } catch { // Ignore any errors because this type might not have been // designed with FieldBuilder in mind } // Case 3: Fetch the index to find the key Exception failure = null; try { // Call the service to find the name of the key SearchIndexClient indexClient = SearchClient.GetSearchIndexClient(); SearchIndex index = async ? await indexClient.GetIndexAsync(IndexName, cancellationToken).ConfigureAwait(false) : indexClient.GetIndex(IndexName, cancellationToken); SearchField keyField = index.Fields.Single(f => f.IsKey == true); string key = keyField.Name; if (typeof(T).IsAssignableFrom(typeof(SearchDocument))) { // Case 3a: If it's a dynamic SearchDocument, lookup // the name of the key in the dictionary KeyFieldAccessor = (T doc) => (doc as SearchDocument)?.GetString(key); return; } else { // Case 3b: We'll see if there's a property with the // same name and use that as the accessor if (typeof(T).GetProperty(key) != null || typeof(T).GetField(key) != null) { KeyFieldAccessor = CompileAccessor(key); return; } } } catch (Exception ex) { // We'll provide any exceptions as a hint because it could // be something like using the wrong API Key type when // moving from SearchClient up to SearchIndexClient that // potentially could be addressed if the user really wanted failure = ex; } // Case 4: Throw and tell the user to provide an explicit accessor. throw new InvalidOperationException( $"Failed to discover the Key field of document type {typeof(T).Name} for Azure Cognitive Search index {IndexName}. " + $"Please set {typeof(SearchIndexingBufferedSenderOptions<T>).Name}.{nameof(SearchIndexingBufferedSenderOptions<T>.KeyFieldAccessor)} explicitly.", failure);