Azure Language Services with C#

Posted on Tue 16 September 2025 in ai,

In another article I wrote about

  • deploying Azure resources through bicep
  • creating your project in Azure Language Services via the Text Authoring API
  • Uploading your training data
  • Using the Text Authoring API to train your model
  • Then finally deploy it, ready to be consumed by your applications!

So we're ready to integrate with the language service, it has some quirks though, the only method available to get label classification result takes a batch of messages and returns a ClassifyDocumentOperation, which contains a ClassifyDocumentResultCollection.

        var batchInput = new List<string> { message };

        ClassifyDocumentOperation operation = await _textAnalyticsClient.SingleLabelClassifyAsync(
            WaitUntil.Completed,
            batchInput,
            _languageSettings.projectName,
            _languageSettings.deploymentName);

This is fine but for a lot of use cases you may have an api calling this and only really want a single message classified at a time, and you may not want your larger function to deal with extracting the only result from the collection result. So we'll move it into a simple Classify function which calls the deployed classification model and deals with extracting the single result.

Call it through code

    private async Task<ClassifyDocumentResult> ClassifyAsync(string message)
    {
        var batchInput = new List<string> { message };

        ClassifyDocumentOperation operation = await _textAnalyticsClient.SingleLabelClassifyAsync(
            WaitUntil.Completed,
            batchInput,
            _languageSettings.projectName,
            _languageSettings.deploymentName);

        await foreach (var documentsInPage in operation.Value)
        {
            var result = documentsInPage.FirstOrDefault();
            if (result != null && !result.HasError)
            {
                return result;
            }
            else if (result?.HasError == true)
            {
                _logger.LogError("Error back from Azure Language Service enquiry");
                _logger.LogError(result.Error.Message);
            }
        }

        return null;
    }

You may notice in here, I have

_textAnalyticsClient 
& 
_languageSettings

LanguageSettings is a small settings class which allows us to get our config at startup and add it to the DI and TextAnalyticsClient is an Azure package which allows us to make calls to the Azure Language Service.

    public class LanguageSettings
    {
        public string url { get; set; }

        public string projectName { get; set; }

        public string deploymentName { get; set; }
    }

Then in our program.cs (or startup.cs if you're still using that.)

var languageSettings = services.AddSettings<LanguageSettings>(builder.Configuration);

var client = new TextAnalyticsClient(new Uri(languageSettings.url), new DefaultAzureCredential());
services.AddSingleton(client);
services.AddSingleton(languageSettings);

In our consuming class, we can pass them through into the constructor like so below.

using Azure.AI.TextAnalytics;


public class MyClassificationService : IMyClassificationService
{

    private readonly TextAnalyticsClient _textAnalyticsClient;
    private readonly LanguageSettings _languageSettings;

    public MyClassificationService(TextAnalyticsClient textAnalyticsClient, LanguageSettings langSettings)
    {
        _textAnalyticsClient = textAnalyticsClient;
        _languageSettings = langSettings;
    }

}

The next step will be to have a method within MyClassificationService which is public can be used by other classes.

    var result = await ClassifyAsync(dto.Message);
    if (result != null)
    {
        var classification = result.ClassificationCategories.FirstOrDefault();
        if (!string.IsNullOrEmpty(classification.Category))
        {
            myReturnDto.ClassificationConfidenceScore = classification.ConfidenceScore;
            myReturnDto.Classification = classification.Category;
        }
    }

We can also do a language detection on our mesage using the same textAnalyticsClient, this can be very useful if you have users with multiple languages accessing your services, however it should be noted you need to have training data on multiple languages if you expect your classifier above to be accurate.

    try
    {
        Response<DetectedLanguage> response = _textAnalyticsClient.DetectLanguage(dto.Message);

        DetectedLanguage language = response.Value;
        Console.WriteLine($"Detected language {language.Name} with confidence score {language.ConfidenceScore}.");

        myReturnDto.ClassifiedLanguage = language.Name;
        myReturnDto.LanguageClassificationConfidenceScore = language.ConfidenceScore;
    }
    catch (RequestFailedException exception)
    {
        Console.WriteLine($"Error Code: {exception.ErrorCode}");
        Console.WriteLine($"Message: {exception.Message}");
    }

And at that point, thats us able to call Azure's Language Service against the model we've trained and get a classification back for our message request!