Dot.Blog

Consulting DotNet C#, XAML, WinUI, WPF, MAUI, IA

Construire une app MAUI avec une API ASP.NET auto-hébergée (Kestrel) pour la recherche sémantique

Créer une application .NET locale combinant interface utilisateur (UI) et moteur IA vectoriel devient possible grâce à .NET 9, MAUI et Kestrel. Ce scénario vous permet de distribuer une app autonome, sans serveur externe, qui :

  • héberge en local un moteur ASP.NET via Kestrel,
  • utilise QDrant (en local ou Docker),
  • interroge OpenAI pour calculer des embeddings,
  • fournit une interface graphique multiplateforme.

🎯 Objectif de cet exemple

L'application doit permettre à l’utilisateur de :

  1. Entrer une requête textuelle ;
  2. Envoyer cette requête à une API locale qui interroge QDrant ;
  3. Obtenir une réponse affichée dans l’interface MAUI.

🧱 Étapes du projet

🔹 1. Créer un projet MAUI

dotnet new maui -n MauiSearchApp

🔹 2. Ajouter le projet ASP.NET Core

Ajoutez un projet classlib ou un projet aspnet (sans Program.cs) dans la solution :

dotnet new classlib -n LocalSearchApi

Dans LocalSearchApi, ajoutez :

  • les contrôleurs,
  • les dépendances à Qdrant.Client,
  • la logique d’embedding.

Important : veillez à ne pas exécuter WebApplication.Run() directement ici.

🔹 3. Lancer Kestrel depuis MAUI

Dans MauiProgram.cs, modifiez comme suit :

using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Hosting;
using Microsoft.AspNetCore.Hosting;

public static class MauiProgram
{
    public static MauiApp CreateMauiApp()
    {
        var builder = MauiApp.CreateBuilder();
        builder.UseMauiApp<App>();

        // Démarrer l’API dans un thread séparé

        Task.Run(() => StartApi());
        return builder.Build();
    }

    private static void StartApi()
    {
        var builder = WebApplication.CreateBuilder();
        builder.WebHost.UseUrls("http://localhost:5001");

        builder.Services.AddControllers();
        builder.Services.AddEndpointsApiExplorer();

        var app = builder.Build();
        app.MapControllers();
        app.Run(); // blocant, donc lancé dans un thread dédié
    }
}

🧠 Exemple d'API SemanticSearchController

Dans le projet LocalSearchApi (ou dans le même projet MAUI si besoin), ajoutez :

 
[ApiController]
[Route("search")]

public class SemanticSearchController : ControllerBase
{
    [HttpPost]
    public IActionResult Post([FromBody] SearchQuery query)
    {
        var fakeResults = new[]
        {
            new SearchResult { Text = "Résultat 1", Score = 0.91 },
            new SearchResult { Text = "Résultat 2", Score = 0.84 }
        };

        return Ok(fakeResults); // à remplacer par appel QDrant
    }
}

public class SearchQuery
{
    public string Query { get; set; }
}

public class SearchResult
{
    public string Text { get; set; }
    public double Score { get; set; }
}

🖼️ Interface MAUI : MainPage.xaml

<VerticalStackLayout Padding="30" Spacing="20">
    <Entry x:Name="QueryEntry" Placeholder="Entrez votre question"/>
    <Button Text="Rechercher" Clicked="OnSearchClicked"/>
    <CollectionView x:Name="ResultsView">
        <CollectionView.ItemTemplate>
            <DataTemplate>
                <Label Text="{Binding Text}" />
            </DataTemplate>
        </CollectionView.ItemTemplate>
    </CollectionView>
</VerticalStackLayout>

Et le code-behind :

private async void OnSearchClicked(object sender, EventArgs e)
{
    var query = QueryEntry.Text;
    var http = new HttpClient();
    var payload = JsonSerializer.Serialize(new { query });
    var content = new StringContent(payload, Encoding.UTF8, "application/json");
    var response = await http.PostAsync("http://localhost:5001/search", content);
    var json = await response.Content.ReadAsStringAsync();

    var results = JsonSerializer.Deserialize<List<SearchResult>>(json);
    ResultsView.ItemsSource = results;
}

public class SearchResult
{
    public string Text { get; set; }
    public double Score { get; set; }
}

🧪 Lancer et tester

  1. Lancer QDrant (via Docker ou installation locale)
  2. Démarrer votre application MAUI
  3. Interroger une collection locale avec un moteur sémantique
  4. Visualiser les résultats instantanément dans l’UI

✅ Avantages de cette approche

  • Zéro déploiement serveur
  • Application locale et distribuable
  • Idéale pour les outils internes, assistants documentaires, explorateurs IA
  • Fonctionne sans Azure ni service cloud obligatoire

🧩 Pour aller plus loin

  • Ajouter le support de Qdrant.Client
  • Intégrer OpenAI pour le calcul des embeddings (cf. article précédent)
  • Support multi-plateforme (Android : prévoir un port ou tunnel)
  • Persistance des résultats
  • Pagination / tri / feedback utilisateur

Dans quelques jours (1/5/26) je vous montrerai comment publier la même App MAUI hébergeant Kestrel sous la forme d'un exécutable autonome !

Stay Tuned !

Faites des heureux, PARTAGEZ l'article !