Esercizio - Importare contenuto esterno
In questo esercizio si estende il connettore Microsoft Graph personalizzato con il codice per importare i file markdown locali in Microsoft 365.
Prima di iniziare
Prima di eseguire questo esercizio, assicurarsi di completare l'esercizio precedente in questo modulo.
Per seguire questo esercizio, copiare i file di contenuto di esempio usati in questo esercizio da GitHub e archiviarli nel progetto, in una cartella denominata content.
Per il corretto funzionamento del codice, la cartella del contenuto e il relativo contenuto devono essere copiati nella cartella di output di compilazione.
Nell'editor di codice:
Aprire il file con estensione csproj e prima del
</Project>
tag aggiungere il codice seguente:<ItemGroup> <ContentFiles Include="content\**" CopyToOutputDirectory="PreserveNewest" /> </ItemGroup> <Target Name="CopyContentFolder" AfterTargets="Build"> <Copy SourceFiles="@(ContentFiles)" DestinationFiles="@(ContentFiles->'$(OutputPath)\content\%(RecursiveDir)%(Filename)%(Extension)')" /> </Target>
Salvare le modifiche.
Aggiungere librerie per analizzare Markdown e YAML
Il connettore Microsoft Graph che si sta creando importa i file Markdown locali in Microsoft 365. Ognuno di questi file contiene un'intestazione con metadati nel formato YAML, noto anche come frontmatter. Inoltre, il contenuto di ogni file viene scritto in Markdown. Per estrarre i metadati e convertire il corpo in HTML, usare librerie personalizzate:
- Aprire un terminale e impostare la directory di lavoro sul progetto.
- Per aggiungere la libreria di elaborazione Markdown, eseguire il comando seguente:
dotnet add package Markdig
. - Per aggiungere la libreria di elaborazione YAML, eseguire il comando seguente:
dotnet add package YamlDotNet
.
Definire la classe per rappresentare il file importato
Per semplificare l'uso dei file markdown importati e del relativo contenuto, definiremo una classe con le proprietà necessarie.
Nell'editor di codice:
Aprire il file ContentService.cs .
Aggiungere l'istruzione
using
e aggiornare laDocument
classe come indicato di seguito:using YamlDotNet.Serialization; public interface IMarkdown { string? Markdown { get; set; } } class Document : IMarkdown { [YamlMember(Alias = "title")] public string? Title { get; set; } [YamlMember(Alias = "description")] public string? Description { get; set; } public string? Markdown { get; set; } public string? Content { get; set; } public string? RelativePath { get; set; } public string? IconUrl { get; set; } public string? Url { get; set; } }
L'interfaccia
IMarkdown
rappresenta il contenuto del file markdown locale. Deve essere definito separatamente per supportare la deserializzazione del contenuto del file. LaDocument
classe rappresenta il documento finale con proprietà YAML analizzate e contenuto HTML.YamlMember
gli attributi eseguono il mapping delle proprietà ai metadati nell'intestazione di ogni documento.Salvare le modifiche.
Implementare il caricamento di file Markdown
Il passaggio successivo consiste nell'implementare la logica che carica i file Markdown locali, estrae i metadati e converte il contenuto in HTML.
Per prima cosa, aggiungere metodi helper per usare facilmente le Markdig
librerie e YamlDotNet
.
Nell'editor di codice:
Creare un nuovo file denominato MarkdownExtensions.cs.
Nel file aggiungere il codice seguente:
// from: https://khalidabuhakmeh.com/parse-markdown-front-matter-with-csharp using Markdig; using Markdig.Extensions.Yaml; using Markdig.Syntax; using YamlDotNet.Serialization; public static class MarkdownExtensions { private static readonly IDeserializer YamlDeserializer = new DeserializerBuilder() .IgnoreUnmatchedProperties() .Build(); private static readonly MarkdownPipeline Pipeline = new MarkdownPipelineBuilder() .UseYamlFrontMatter() .Build(); }
La
YamlDeserializer
proprietà definisce un nuovo deserializzatore per il blocco YAML in ognuno dei file markdown che si sta estraendo. Il deserializzatore viene configurato in modo da ignorare tutte le proprietà che non fanno parte della classe in cui viene deserializzato il file.La
Pipeline
proprietà definisce una pipeline di elaborazione per il parser markdown. È possibile configurarla per analizzare l'intestazione YAML. Senza questa configurazione, le informazioni dell'intestazione verranno eliminate.Successivamente, estendere la
MarkdownExtensions
classe con il codice seguente:public static T GetContents<T>(this string markdown) where T : IMarkdown, new() { var document = Markdown.Parse(markdown, Pipeline); var block = document .Descendants<YamlFrontMatterBlock>() .FirstOrDefault(); if (block == null) return new T { Markdown = markdown }; var yaml = block // this is not a mistake // we have to call .Lines 2x .Lines // StringLineGroup[] .Lines // StringLine[] .OrderByDescending(x => x.Line) .Select(x => $"{x}\n") .ToList() .Select(x => x.Replace("---", string.Empty)) .Where(x => !string.IsNullOrWhiteSpace(x)) .Aggregate((s, agg) => agg + s); var t = YamlDeserializer.Deserialize<T>(yaml); t.Markdown = markdown.Substring(block.Span.End + 1); return t; }
Il
GetContents
metodo converte una stringa markdown con metadati YAML nell'intestazione nel tipo specificato, che implementa l'interfacciaIMarkdown
. Dalla stringa markdown estrae l'intestazione YAML e la deserializza nel tipo specificato. Quindi, estrae il corpo dell'articolo e lo imposta sullaMarkdown
proprietà per un'ulteriore elaborazione.Salvare le modifiche.
Estrarre il contenuto markdown e YAML
Con i metodi helper implementati, implementare il Extract
metodo per caricare i file markdown locali ed estrarre informazioni da essi.
Nell'editor di codice:
Aprire il file ContentService.cs .
Nella parte superiore del file aggiungere l'istruzione using seguente:
using Markdig;
ContentService
Nella classe implementare quindi ilExtract
metodo usando il codice seguente:static IEnumerable<Document> Extract() { var docs = new List<Document>(); var contentFolder = "content"; var baseUrl = new Uri("https://learn.microsoft.com/graph/"); var contentFolderPath = Path.Combine(Directory.GetCurrentDirectory(), contentFolder); var files = Directory.GetFiles(contentFolder, "*.md", SearchOption.AllDirectories); foreach (var file in files) { try { var contents = File.ReadAllText(file); var doc = contents.GetContents<Document>(); doc.Content = Markdown.ToHtml(doc.Markdown ?? ""); doc.RelativePath = Path.GetRelativePath(contentFolderPath, file); doc.Url = new Uri(baseUrl, doc.RelativePath!.Replace(".md", "")).ToString(); doc.IconUrl = "https://raw.githubusercontent.com/waldekmastykarz/img/main/microsoft-graph.png"; docs.Add(doc); } catch (Exception ex) { Console.WriteLine(ex.Message); } } return docs; }
Il metodo inizia con il caricamento dei file markdown dalla cartella del contenuto . Per ogni file, carica il contenuto come stringa. Converte la stringa in un oggetto con i metadati e il contenuto archiviati in proprietà separate usando il metodo di
GetContents
estensione definito in precedenza nellaMarkdownExtensions
classe . Successivamente, converte la stringa markdown in HTML. Compila il relativo URL su Internet usando il percorso relativo del file. Infine, archivia il percorso relativo del file e aggiunge l'oggetto a una raccolta per un'ulteriore elaborazione.Salvare le modifiche.
Trasformare il contenuto in elementi esterni
Dopo aver letto il contenuto esterno, il passaggio successivo consiste nel trasformarlo in elementi esterni, che verranno caricati in Microsoft 365.
Iniziare con l'implementazione del GetDocId
metodo che genera un ID univoco per ogni elemento esterno in base al relativo percorso del file.
Nell'editor di codice:
Verificare che si stia modificando il file ContentService.cs .
ContentService
Nella classe aggiungere il metodo seguente:static string GetDocId(Document doc) { var id = doc.RelativePath! .Replace(Path.DirectorySeparatorChar.ToString(), "__") .Replace(".md", ""); return id; }
Il
GetDocId
metodo usa il percorso del file relativo del documento e sostituisce tutti i separatori di directory con un doppio carattere di sottolineatura. Ciò è necessario perché i caratteri separatori di percorso non possono essere usati in un ID elemento esterno.Salvare le modifiche.
Implementare ora il Transform
metodo , che converte gli oggetti che rappresentano i file markdown locali in elementi esterni di Microsoft Graph.
Nell'editor di codice:
Verificare di essere nel file ContentService.cs .
Implementare il
Transform
metodo usando il codice seguente:static IEnumerable<ExternalItem> Transform(IEnumerable<Document> documents) { return documents.Select(doc => { var docId = GetDocId(doc); return new ExternalItem { Id = docId, Properties = new() { AdditionalData = new Dictionary<string, object> { { "title", a.Title ?? "" }, { "description", a.Description ?? "" }, { "url", new Uri(baseUrl, a.RelativePath!.Replace(".md", "")).ToString() } } }, Content = new() { Value = a.Content ?? "", Type = ExternalItemContentType.Html }, Acl = new() { new() { Type = AclType.Everyone, Value = "everyone", AccessType = AccessType.Grant } } }; }); }
Prima di tutto, si definisce un URL di base. Questo URL viene usato per compilare un URL completo per ogni elemento, in modo che, quando l'elemento viene visualizzato agli utenti, possa passare all'elemento originale. Successivamente, si trasforma ogni elemento da un
DocsArticle
oggetto in unExternalItem
oggetto . Per iniziare, ottenere un ID univoco per ogni elemento in base al relativo percorso del file. Creare quindi una nuova istanza diExternalItem
e riempirne le proprietà con le informazioni dell'oggettoDocsArticle
. Impostare quindi il contenuto dell'elemento sul contenuto HTML estratto dal file locale e impostare il tipo di contenuto dell'elemento su HTML. Infine, si configura l'autorizzazione dell'elemento in modo che sia visibile a tutti gli utenti dell'organizzazione.Salvare le modifiche.
Caricare elementi esterni in Microsoft 365
L'ultimo passaggio dell'elaborazione del contenuto consiste nel caricare gli elementi esterni trasformati in Microsoft 365.
Nell'editor di codice:
Verificare che si stia modificando il file ContentService.cs .
ContentService
Nella classe implementare ilLoad
metodo usando il codice seguente:static async Task Load(IEnumerable<ExternalItem> items) { foreach (var item in items) { Console.Write(string.Format("Loading item {0}...", item.Id)); try { await GraphService.Client.External .Connections[Uri.EscapeDataString(ConnectionConfiguration. ExternalConnection.Id!)] .Items[item.Id] .PutAsync(item); Console.WriteLine("DONE"); } catch (Exception ex) { Console.WriteLine("ERROR"); Console.WriteLine(ex.Message); } } }
Per ogni elemento esterno, si usa Microsoft Graph .NET SDK per chiamare l'API Microsoft Graph e caricare l'elemento. Nella richiesta specificare l'ID della connessione esterna creata in precedenza, l'ID dell'elemento da caricare e il contenuto completo dell'elemento.
Salvare le modifiche.
Aggiungere il comando di caricamento del contenuto
Prima di poter testare il codice, è necessario estendere l'applicazione console con un comando che richiama la logica di caricamento del contenuto.
Nell'editor di codice:
Aprire il file Program.cs .
Aggiungere un nuovo comando per caricare il contenuto usando il codice seguente:
var loadContentCommand = new Command("load-content", "Loads content into the external connection"); loadContentCommand.SetHandler(ContentService.LoadContent);
Registrare il comando appena definito con il comando radice in modo che possa essere richiamato usando il codice seguente:
rootCommand.AddCommand(loadContentCommand);
Salvare le modifiche.
Eseguire il test del codice
L'ultima cosa da fare è verificare che il connettore Microsoft Graph importi correttamente il contenuto esterno.
- Aprire una finestra terminale.
- Modificare la directory di lavoro nel progetto.
- Compilare il progetto eseguendo il
dotnet build
comando . - Avviare il caricamento del contenuto eseguendo il
dotnet run -- load-content
comando . - Attendere il completamento del comando e caricare il contenuto.