最初のカスタム Microsoft Graph コネクタを構築する
Microsoft Graph connectors let you add your own data into Microsoft Graph and have it power various Microsoft 365 experiences.
This .NET Core application shows you how to use the Microsoft Graph connectors API to create a custom connector and use it to power Microsoft Search. This tutorial uses a sample data appliance parts inventory for the Contoso Appliance Repair organization.
How does the sample work?
The sample creates a Windows desktop app that acquires a token from the Microsoft identity platform, and uses it to send requests to the Microsoft Graph connectors API. The connectors API sends its response after the access is validated.

Prerequisites
Install Visual Studio 2019 with .NET Core 3.1 SDK on your development computer.
Make sure that you have a personal Microsoft account, or a work or school account.
Install the Entity Framework Core Tools as a global tool using the following command:
dotnet tool install --global dotnet-efInstall a tool to update a SQLite database. For example, the DB Browser for SQLite.
Download the ApplianceParts.csv file from the Search connector sample repo.
ヒント
The best way to download files from GitHub is to go to the top level of the project. From the green Code download button on the right, choose Download ZIP. The ZIP file contains the contents of the repository.
ポータルにアプリケーションを登録する
After all the prerequisites are in place, you will be able to register an application in the Azure AD admin center. The registration is necessary in order to authenticate the application and use it to make calls to the Microsoft Graph connectors API.
Go to the Azure Active Directory admin center and sign in with an administrator account.
In the left pane, select Azure Active Directory, and under Manage, select App registrations.
Select New registration.

Complete the Register an application form with the following values, and then select Register.
Name: Parts Inventory Connector
Supported account types: Accounts in this organizational directory only (Microsoft only - Single tenant)
Redirect URI: Leave blank

On the Parts Inventory Connector overview page, copy the values of Application (client) ID and Directory (tenant) ID. You will need both in the following section.

Select API Permissions under Manage.
Select Add a permission, and then select Microsoft Graph.
Select Application permissions, and then select the ExternalItem.ReadWrite.All permission. Select Add permissions.

Select Grant admin consent for {TENANT}, and then select Yes when prompted.

Select Certificates & secrets under Manage, and then select New client secret.
Enter a description and choose an expiration time for the secret, and then select Add.

Copy and save the new secret; you will need it in the following section.
アプリケーションのビルド
In this step, you'll create a .NET Core console app. After that, you will create a new connection, register the schema, and sync the items.
Create a .NET Core console app
Launch Visual Studio 2019 and go to File > New > Project.
Select the Console App (.NET Core) template, and then select Next.
Enter the Project name: "PartsInventoryConnector".
Select the checkbox for Place solution and project in the same directory, and then select Create.

重要
Before moving to the next step, copy the ApplianceParts.csv file to the root folder of the project.
Add NuGet packages
To add NuGet packages, first right-click Project Solution, and then select Open in Terminal.

Next, run the following command line interface (CLI) commands in the developer command prompt.
dotnet add package CsvHelper --version 12.1.2
dotnet add package Microsoft.EntityFrameworkCore.Design --version 3.1.3
dotnet add package Microsoft.EntityFrameworkCore.Sqlite --version 3.1.3
dotnet add package Microsoft.Extensions.Configuration.UserSecrets --version 3.1.3
dotnet add package Microsoft.Graph.Beta --version 0.17.0-preview
dotnet add package Microsoft.Identity.Client --version 4.13.0
ヒント
If the add package command fails, check the Package Source of your project:
- Select the project in the Solution Explorer.
- Go to Tools > Nuget Package Manager > Package Manager Settings.
- Check the Package Sources, and make sure that nuget.org is installed as the package source.
- Name: nuget.org
- Source: https://api.nuget.org/v3/index.json
Add Azure AD authentication
This authentication is required to get the necessary OAuth access token to call the connectors API.
Create a new directory named Authentication in the PartsInventoryConnector directory.
Create a new file in the Authentication directory named ClientCredentialAuthProvider.cs, and then place the following code in that file:
// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. using Microsoft.Graph; using Microsoft.Identity.Client; using System; using System.Net.Http; using System.Net.Http.Headers; using System.Threading.Tasks; namespace PartsInventoryConnector.Authentication { public class ClientCredentialAuthProvider : IAuthenticationProvider { private IConfidentialClientApplication _msalClient; private int _maxRetries = 3; public ClientCredentialAuthProvider(string appId, string tenantId, string secret) { _msalClient = ConfidentialClientApplicationBuilder .Create(appId) .WithTenantId(tenantId) .WithClientSecret(secret) .Build(); } public async Task AuthenticateRequestAsync(HttpRequestMessage request) { int retryCount = 0; do { try { var result = await _msalClient .AcquireTokenForClient(new[] { "https://graph.microsoft.com/.default" }) .ExecuteAsync(); if (!string.IsNullOrEmpty(result.AccessToken)) { request.Headers.Authorization = new AuthenticationHeaderValue("bearer", result.AccessToken); break; } } catch (MsalServiceException serviceException) { if (serviceException.ErrorCode == "temporarily_unavailable") { await Task.Delay(10000); } else { throw serviceException; } } catch (Exception exception) { throw exception; } retryCount++; } while (retryCount < _maxRetries); } } }
Add user experience
Create a new directory in the PartsInventoryConnector directory named Console.
Create a new file in the Console directory named MenuChoice.cs, and then place the following code in that file:
// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. namespace PartsInventoryConnector.Console { public enum MenuChoice { Invalid = 0, CreateConnection, RegisterSchema, PushAllItems, Exit } }
Set up data model
Create a new directory in the PartsInventoryConnector directory named Models.
Create a new file in the Models directory named AppliancePart.cs, and then place the following code in that file:
// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. using Microsoft.Graph; using Newtonsoft.Json; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; namespace PartsInventoryConnector.Models { public class AppliancePart { [Key] public int PartNumber { get; set; } public string Name { get; set; } public string Description { get; set; } public double Price { get; set; } public int Inventory { get; set; } [JsonProperty("appliances@odata.type")] private const string AppliancesODataType = "Collection(String)"; public List<string> Appliances { get; set; } public Properties AsExternalItemProperties() { var properties = new Properties { AdditionalData = new Dictionary<string, object> { { "partNumber", PartNumber }, { "name", Name }, { "description", Description }, { "price", Price }, { "inventory", Inventory }, { "appliances@odata.type", "Collection(String)" }, { "appliances", Appliances } } }; return properties; } } }Create a new file in the Models directory named ApplianceDbContext.cs, and then place the following code in that file:
using Microsoft.Data.Sqlite; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.ChangeTracking; using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Linq; namespace PartsInventoryConnector.Models { public class ApplianceDbContext : DbContext { public DbSet<AppliancePart> Parts { get; set; } protected override void OnConfiguring(DbContextOptionsBuilder options) { options.UseSqlite("Data Source=parts.db"); } protected override void OnModelCreating(ModelBuilder modelBuilder) { // EF Core can't store lists, so add a converter for the Appliances // property to serialize as a JSON string on save to DB modelBuilder.Entity<AppliancePart>() .Property(ap => ap.Appliances) .HasConversion( v => JsonConvert.SerializeObject(v), v => JsonConvert.DeserializeObject<List<string>>(v) ); // Add LastUpdated and IsDeleted shadow properties modelBuilder.Entity<AppliancePart>() .Property<DateTime>("LastUpdated") .HasDefaultValueSql("datetime()") .ValueGeneratedOnAddOrUpdate(); modelBuilder.Entity<AppliancePart>() .Property<bool>("IsDeleted") .IsRequired() .HasDefaultValue(false); // Exclude any soft-deleted items (IsDeleted = 1) from // the default query sets modelBuilder.Entity<AppliancePart>() .HasQueryFilter(a => !EF.Property<bool>(a, "IsDeleted")); } public override int SaveChanges() { // Prevent deletes of data, instead mark the item as deleted // by setting IsDeleted = true. foreach(var entry in ChangeTracker.Entries() .Where(e => e.State == EntityState.Deleted)) { if (entry.Entity.GetType() == typeof(AppliancePart)) { SoftDelete(entry); } } return base.SaveChanges(); } private void SoftDelete(EntityEntry entry) { var partNumber = new SqliteParameter("@partNumber", entry.OriginalValues["PartNumber"]); Database.ExecuteSqlRaw( "UPDATE Parts SET IsDeleted = 1 WHERE PartNumber = @partNumber", partNumber); entry.State = EntityState.Detached; } } }Create a new directory named Data in the PartsInventoryConnector directory.
Create a new file in the Data directory named CsvDataLoader.cs, and then place the following code in that file:
using CsvHelper; using CsvHelper.Configuration; using CsvHelper.TypeConversion; using PartsInventoryConnector.Models; using System.Collections.Generic; using System.IO; using System.Linq; namespace PartsInventoryConnector.Data { public static class CsvDataLoader { public static List<AppliancePart> LoadPartsFromCsv(string filePath) { using (var reader = new StreamReader(filePath)) using (var csv = new CsvReader(reader)) { csv.Configuration.RegisterClassMap<AppliancePartMap>(); return new List<AppliancePart>(csv.GetRecords<AppliancePart>()); } } } public class ApplianceListConverter : DefaultTypeConverter { public override object ConvertFromString(string text, IReaderRow row, MemberMapData memberMapData) { var appliances = text.Split(';'); return new List<string>(appliances); } } public class AppliancePartMap : ClassMap<AppliancePart> { public AppliancePartMap() { Map(m => m.PartNumber); Map(m => m.Name); Map(m => m.Description); Map(m => m.Price); Map(m => m.Inventory); Map(m => m.Appliances).TypeConverter<ApplianceListConverter>(); } } }
Write the Microsoft Graph helper service
Create a new directory named MicrosoftGraph in the PartsInventoryConnector directory.
Create a new file in the MicrosoftGraph directory named CustomSerializer.cs, and then place the following code in that file:
using Microsoft.Graph; using Newtonsoft.Json; using Newtonsoft.Json.Converters; using Newtonsoft.Json.Serialization; using System; using System.IO; namespace PartsInventoryConnector.MicrosoftGraph { // The Microsoft Graph SDK serializes enumerations in camelCase. // The Microsoft Graph service currently requires the PropertyType enum // to be PascalCase. This will override the Microsoft Graph serialization // If the Microsoft Graph service changes to accept camelCase this will no // longer be necessary. class CustomContractResolver : DefaultContractResolver { protected override JsonConverter ResolveContractConverter(Type objectType) { if (typeof(PropertyType).IsAssignableFrom(objectType)) { // This default converter uses PascalCase return new StringEnumConverter(); } return base.ResolveContractConverter(objectType); } } // In order to hook up the custom contract resolver for // PropertyType, we need to implement a custom serializer to // pass to the MicrosoftGraphServiceClient. public class CustomSerializer : ISerializer { private Serializer _microsoftGraphSerializer; private JsonSerializerSettings _jsonSerializerSettings; public CustomSerializer() { _microsoftGraphSerializer = new Serializer(); _jsonSerializerSettings = new JsonSerializerSettings { ContractResolver = new CustomContractResolver() }; } // For deserialize, just pass through to the default // Microsoft Graph SDK serializer public T DeserializeObject<T>(Stream stream) { return _microsoftGraphSerializer.DeserializeObject<T>(stream); } // For deserialize, just pass through to the default // Microsoft Graph SDK serializer public T DeserializeObject<T>(string inputString) { return _microsoftGraphSerializer.DeserializeObject<T>(inputString); } public string SerializeObject(object serializeableObject) { // If a Schema object is being serialized, do the conversion // ourselves if (serializeableObject is Schema) { var foo = JsonConvert.SerializeObject(serializeableObject, _jsonSerializerSettings); return foo; } // Otherwise, just pass through to the default Microsoft Graph SDK serializer return _microsoftGraphSerializer.SerializeObject(serializeableObject); } } }Create a new file in the Microsoft Graph directory named MicrosoftGraphHelper.cs, and then place the following code in that file. This code contains methods that use the MicrosoftGraphServiceClient to build and send calls to the Microsoft Graph service and process the response.
// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. using Microsoft.Graph; using Newtonsoft.Json; using System.Net.Http; using System.Threading.Tasks; namespace PartsInventoryConnector.MicrosoftGraph { public class MicrosoftGraphHelper { private GraphServiceClient _microsoftGraphClient; public MicrosoftGraphHelper(IAuthenticationProvider authProvider) { // Configure a default HttpProvider with our // custom serializer to handle the PropertyType serialization var serializer = new CustomSerializer(); var httpProvider = new HttpProvider(serializer); // Initialize the Microsoft Graph client _microsoftGraphClient = new GraphServiceClient(authProvider, httpProvider); } } }
Initialize the Microsoft Graph helper service
Open Program.cs and replace the entire contents with the following code:
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Graph;
using PartsInventoryConnector.Authentication;
using PartsInventoryConnector.Console;
using PartsInventoryConnector.Data;
using PartsInventoryConnector.MicrosoftGraph;
using PartsInventoryConnector.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace PartsInventoryConnector
{
class Program
{
private static MicrosoftGraphHelper _microsoftGraphHelper;
private static ExternalConnection _currentConnection;
private static string _tenantId;
static async Task Main(string[] args)
{
try
{
// Load configuration from appsettings.json
var appConfig = LoadAppSettings();
if (appConfig == null)
{
return;
}
// Save tenant ID for setting ACL on items
_tenantId = appConfig["tenantId"];
// Initialize the auth provider
var authProvider = new ClientCredentialAuthProvider(
appConfig["appId"],
appConfig["tenantId"],
appConfig["appSecret"]
);
// Check if the database is empty
using (var db = new ApplianceDbContext())
{
if (db.Parts.IgnoreQueryFilters().Count() <= 0)
{
ImportCsvToDatabase(db, "ApplianceParts.csv");
}
}
_microsoftGraphHelper = new MicrosoftGraphHelper(authProvider);
do
{
var userChoice = DoMenuPrompt();
switch (userChoice)
{
case MenuChoice.CreateConnection:
await CreateConnectionAsync();
break;
case MenuChoice.RegisterSchema:
await RegisterSchemaAsync();
break;
case MenuChoice.PushAllItems:
await UpdateItemsFromDatabase();
break;
case MenuChoice.Exit:
// Exit the program
return;
case MenuChoice.Invalid:
default:
break;
}
} while (true);
}
catch (Exception ex)
{
System.Console.WriteLine(ex.Message);
}
}
private static void ImportCsvToDatabase(ApplianceDbContext db, string partsFilePath)
{
var parts = CsvDataLoader.LoadPartsFromCsv(partsFilePath);
db.AddRange(parts);
db.SaveChanges();
}
private static MenuChoice DoMenuPrompt()
{
System.Console.WriteLine($"Current connection: {(_currentConnection == null ? "NONE" : _currentConnection.Name)}");
System.Console.WriteLine("Please choose one of the following options:");
System.Console.WriteLine($"{Convert.ToInt32(MenuChoice.CreateConnection)}. Create a connection");
System.Console.WriteLine($"{Convert.ToInt32(MenuChoice.RegisterSchema)}. Register schema for current connection");
System.Console.WriteLine($"{Convert.ToInt32(MenuChoice.PushAllItems)}. Push ALL items to current connection");
System.Console.WriteLine($"{Convert.ToInt32(MenuChoice.Exit)}. Exit");
try
{
var choice = int.Parse(System.Console.ReadLine());
return (MenuChoice)choice;
}
catch (FormatException)
{
return MenuChoice.Invalid;
}
}
private static string PromptForInput(string prompt, bool valueRequired)
{
string response = null;
do
{
System.Console.WriteLine($"{prompt}:");
response = System.Console.ReadLine();
if (valueRequired && string.IsNullOrEmpty(response))
{
System.Console.WriteLine("You must provide a value");
}
} while (valueRequired && string.IsNullOrEmpty(response));
return response;
}
private static IConfigurationRoot LoadAppSettings()
{
var appConfig = new ConfigurationBuilder()
.AddUserSecrets<Program>()
.Build();
// Check for required settings
if (string.IsNullOrEmpty(appConfig["appId"]) ||
string.IsNullOrEmpty(appConfig["appSecret"]) ||
string.IsNullOrEmpty(appConfig["tenantId"]))
{
return null;
}
return appConfig;
}
}
}
Create the connection
Under MicrosoftGraph, open the MicrosoftGraphHelper.cs file and add the following code after the constructor method:
#region Connections public async Task<ExternalConnection> CreateConnectionAsync(string id, string name, string description) { var newConnection = new ExternalConnection { // Need to set to null, service returns 400 // if @odata.type property is sent ODataType = null, Id = id, Name = name, Description = description }; return await _microsoftGraphClient.External.Connections.Request().AddAsync(newConnection); } #endregionOpen the Program.cs file and add the following code after the Main method:
private static async Task CreateConnectionAsync() { var connectionId = PromptForInput("Enter a unique ID for the new connection", true); var connectionName = PromptForInput("Enter a name for the new connection", true); var connectionDescription = PromptForInput("Enter a description for the new connection", false); try { // Create the connection _currentConnection = await _microsoftGraphHelper.CreateConnectionAsync(connectionId, connectionName, connectionDescription); System.Console.WriteLine("New connection created"); System.Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(_currentConnection, Newtonsoft.Json.Formatting.Indented)); } catch (ServiceException serviceException) { System.Console.WriteLine(serviceException.Message); return; } }
Register schema
Under MicrosoftGraph, open the MicrosoftGraphHelper.cs file and add the following code after the constructor method:
#region Schema public async Task RegisterSchemaAsync(string connectionId, Schema schema) { // Need access to the HTTP response here since we are doing an // async request. The new schema object isn't returned, we need // the Location header from the response var asyncNewSchemaRequest = _microsoftGraphClient.External.Connections[connectionId].Schema .Request() .Header("Prefer", "respond-async") .GetHttpRequestMessage(); asyncNewSchemaRequest.Method = HttpMethod.Post; asyncNewSchemaRequest.Content = _microsoftGraphClient.HttpProvider.Serializer.SerializeAsJsonContent(schema); var response = await _microsoftGraphClient.HttpProvider.SendAsync(asyncNewSchemaRequest); if (response.IsSuccessStatusCode) { // Get the operation ID from the Location header var operationId = ExtractOperationId(response.Headers.Location); await CheckSchemaStatusAsync(connectionId, operationId); } else { throw new ServiceException( new Error { Code = response.StatusCode.ToString(), Message = "Registering schema failed" } ); } } private string ExtractOperationId(System.Uri uri) { int numSegments = uri.Segments.Length; return uri.Segments[numSegments - 1]; } public async Task CheckSchemaStatusAsync(string connectionId, string operationId) { do { var operation = await _microsoftGraphClient.External.Connections[connectionId] .Operations[operationId] .Request() .GetAsync(); if (operation.Status == ConnectionOperationStatus.Completed) { return; } else if (operation.Status == ConnectionOperationStatus.Failed) { throw new ServiceException( new Error { Code = operation.Error.ErrorCode, Message = operation.Error.Message } ); } await Task.Delay(3000); } while (true); } #endregionOpen the Program.cs file and add the following code after the Main method:
private static async Task RegisterSchemaAsync() { if (_currentConnection == null) { System.Console.WriteLine("No connection selected. Please create a new connection or select an existing connection."); return; } System.Console.WriteLine("Registering schema, this may take a moment..."); try { // Register the schema var schema = new Schema { // Need to set to null, service returns 400 // if @odata.type property is sent ODataType = null, BaseType = "microsoft.graph.externalItem", Properties = new List<Property> { new Property { Name = "partNumber", Type = PropertyType.Int64, IsQueryable = true, IsSearchable = false, IsRetrievable = true }, new Property { Name = "name", Type = PropertyType.String, IsQueryable = true, IsSearchable = true, IsRetrievable = true }, new Property { Name = "description", Type = PropertyType.String, IsQueryable = false, IsSearchable = true, IsRetrievable = true }, new Property { Name = "price", Type = PropertyType.Double, IsQueryable = true, IsSearchable = false, IsRetrievable = true }, new Property { Name = "inventory", Type = PropertyType.Int64, IsQueryable = true, IsSearchable = false, IsRetrievable = true }, new Property { Name = "appliances", Type = PropertyType.StringCollection, IsQueryable = true, IsSearchable = true, IsRetrievable = true } } }; await _microsoftGraphHelper.RegisterSchemaAsync(_currentConnection.Id, schema); System.Console.WriteLine("Schema registered"); } catch (ServiceException serviceException) { System.Console.WriteLine($"{serviceException.StatusCode} error registering schema:"); System.Console.WriteLine(serviceException.Message); return; } }
Sync items
Under Microsoft Graph, open the MicrosoftGraphHelper.cs file and add the following code after the Constructor method:
#region PushData public async Task AddOrUpdateItem(string connectionId, ExternalItem item) { // The SDK's auto-generated request builder uses POST here, // which isn't correct. For now, get the HTTP request and change it // to PUT manually. var putItemRequest = _microsoftGraphClient.External.Connections[connectionId] .Items[item.Id].Request().GetHttpRequestMessage(); putItemRequest.Method = HttpMethod.Put; putItemRequest.Content = _microsoftGraphClient.HttpProvider.Serializer.SerializeAsJsonContent(item); var response = await _microsoftGraphClient.HttpProvider.SendAsync(putItemRequest); if (!response.IsSuccessStatusCode) { throw new ServiceException( new Error { Code = response.StatusCode.ToString(), Message = "Error indexing item." } ); } } #endregionOpen the Program.cs file and add the following code after the Main method:
private static async Task UpdateItemsFromDatabase() { if (_currentConnection == null) { System.Console.WriteLine("No connection selected. Please create a new connection or select an existing connection."); return; } List<AppliancePart> partsToUpload = null; using (var db = new ApplianceDbContext()) { partsToUpload = db.Parts .ToList(); } System.Console.WriteLine($"Processing {partsToUpload.Count()} add/updates"); foreach (var part in partsToUpload) { var newItem = new ExternalItem { Id = part.PartNumber.ToString(), Content = new ExternalItemContent { // Need to set to null, service returns 400 // if @odata.type property is sent ODataType = null, Type = ExternalItemContentType.Text, Value = part.Description }, Acl = new List<Acl> { new Acl { AccessType = AccessType.Grant, Type = AclType.Everyone, Value = _tenantId, IdentitySource = "Azure Active Directory" } }, Properties = part.AsExternalItemProperties() }; try { System.Console.Write($"Uploading part number {part.PartNumber}..."); await _microsoftGraphHelper.AddOrUpdateItem(_currentConnection.Id, newItem); System.Console.WriteLine("DONE"); } catch (ServiceException serviceException) { System.Console.WriteLine("FAILED"); System.Console.WriteLine($"{serviceException.StatusCode} error adding or updating part {part.PartNumber}"); System.Console.WriteLine(serviceException.Message); } } }
アプリケーションの構成
Open your command line interface (CLI) in the directory where PartsInventoryConnector.csproj is located.
Run the following command to initialize the user secrets for the project.
dotnet user-secrets initRun the following commands to store your app ID, app secret, and tenant ID in the user secret store.
dotnet user-secrets set appId "YOUR_APP_ID_HERE" dotnet user-secrets set appSecret "YOUR_APP_SECRET_HERE" dotnet user-secrets set tenantId "YOUR_TENANT_ID_HERE"
CSV からデータベースを作成する
Open your command line interface (CLI) in the directory where PartsInventoryConnector.csproj is located.
Run the following commands:
dotnet ef migrations add InitialCreate dotnet ef database update
注意
Run the following commands if a schema changes in the CSV file, and reflect those changes into the SQLite database.
dotnet ef database drop dotnet ef database update
アプリケーションを実行する
In this step, you will build and run the sample. This code sample will create a new connection, register the schema, and then push items from the ApplianceParts.csv file into that connection.
- Open your command-line interface (CLI) in the PartsInventoryConnector directory.
- Use the command
dotnet buildto build the sample. - Use the command
dotnet runto run the sample. - Select 1. Create a connection. Enter a unique identifier, name, and description for that connection.
- Select 2. Register schema for the current connection option, and then wait for the operation to complete.
- Select 3. Push all items to current connection.
注意
If step 5 results in an error, wait a few minutes, and then select 3. Push all items to current connection.
検索でデータを表示する
To make it easier for users to find the information that they have permission to see, create search verticals and result types to customize the search results in Microsoft SharePoint, Microsoft Office, and Microsoft Search in Bing.
Create a vertical
To create and enable a search vertical at the organization level, sign in to the Microsoft 365 admin center by using the global administrator role, and do the following:
- Go to Settings > Search & intelligence > Customizations.
- Go to Vertical, and then select Add.
- Provide the following details:
Name the vertical: Appliance Parts.

Content source: The connector created with the app (Parts Inventory).

Add a query: Leave blank.

Filters: Leave blank.

Create a result type
To create a result type:
- Go to Settings > Search & intelligence > Customizations.
- Go to the Result type tab, and then select Add.
- Provide the following details:
Name: Appliance Part

Content source: The connector created in the app.

Rules: None

Paste contents of result-type.json into the layout designer textbox.

検索結果の検索
In this step, you will search for parts in SharePoint.
Go to the root SharePoint site for your tenant.
Using the search box at the top of the page, search for hinge.

When the search completes with 0 results, select the Appliance Parts tab. Results from the connector are displayed.

概要
You have successfully completed the .NET Core Microsoft Graph connectors tutorial: you created a custom connector and used it to power Microsoft Search.
Next steps
To find out more about all the data that you can access with your custom connector, see Microsoft Graph connectors overview.
このセクションに問題がある場合 このセクションを改善できるよう、フィードバックをお送りください。
フィードバック
フィードバックの送信と表示