Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions AOT-Sample/AOT-Sample.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>

<!-- Enable AOT compilation -->
<PublishAot>true</PublishAot>

<!-- Enable trimming -->
<PublishTrimmed>true</PublishTrimmed>
<TrimMode>full</TrimMode>

<!-- Enable all AOT and trim warnings -->
<EnableTrimAnalyzer>true</EnableTrimAnalyzer>
<EnableAotAnalyzer>true</EnableAotAnalyzer>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>

<!-- Additional AOT and trim settings -->
<SuppressTrimAnalysisWarnings>false</SuppressTrimAnalysisWarnings>
<EnableSingleFileAnalyzer>true</EnableSingleFileAnalyzer>
<IsAotCompatible>true</IsAotCompatible>

<!-- More detailed trim warnings -->
<TrimmerSingleWarn>false</TrimmerSingleWarn>
<EnableTrimAnalyzer>true</EnableTrimAnalyzer>

<!-- ILC flags for more detailed output -->
<IlcInstructionSet></IlcInstructionSet>
<IlcFoldIdenticalMethodBodies>false</IlcFoldIdenticalMethodBodies>

<!-- Enable detailed ILC diagnostic output -->
<IlcDiagnosticFile>$(OutputPath)IlcDiagnostics.txt</IlcDiagnosticFile>
<IlcDisableReflection>false</IlcDisableReflection>

<!-- Detailed AOT warnings -->
<_SuppressIlcGeneratedFileMessage>false</_SuppressIlcGeneratedFileMessage>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Azure.Cosmos" Version="3.44.0" />
</ItemGroup>

<!-- Enable this to see the entire AOT warns from Microsoft.Azure.Cosmos.Client, not just the apis used by the sample -->
<!--
<ItemGroup>
<TrimmerRootAssembly Include="Microsoft.Azure.Cosmos.Client" />
</ItemGroup>
-->

</Project>
174 changes: 174 additions & 0 deletions AOT-Sample/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
namespace AOTSample
{
using System;
using System.Text.Json;
using Microsoft.Azure.Cosmos;

public class Program
{
public static async Task Main(string[] args)
{
const string CosmosBaseUri = "https://{0}.documents.azure.com:443/";
string accountName = "cosmosaot2";
string? primaryKey = Environment.GetEnvironmentVariable("KEY");
Console.WriteLine($"COSMOS_PRIMARY_KEY: {primaryKey}");

if (string.IsNullOrEmpty(primaryKey))
{
Console.WriteLine("ERROR: KEY environment variable is not set");
return;
}

CosmosClientOptions clientOptions = new CosmosClientOptions { AllowBulkExecution = true };
clientOptions.CosmosClientTelemetryOptions.DisableDistributedTracing = false;

CosmosClient client = new CosmosClient(
string.Format(CosmosBaseUri, accountName),
primaryKey,
clientOptions);

FeedIterator<DatabaseProperties> db_feed_itr = client.GetDatabaseQueryIterator<DatabaseProperties>();
while (db_feed_itr.HasMoreResults)
{
FeedResponse<DatabaseProperties> db_response = await db_feed_itr.ReadNextAsync();
foreach (DatabaseProperties db_properties in db_response)
{
Console.WriteLine($"Database: {db_properties.Id}");

Database database = client.GetDatabase(db_properties.Id);
FeedIterator<ContainerProperties> container_feed_itr = database.GetContainerQueryIterator<ContainerProperties>();

while (container_feed_itr.HasMoreResults)
{
FeedResponse<ContainerProperties> container_response = await container_feed_itr.ReadNextAsync();
foreach (ContainerProperties container_properties in container_response)
{
Console.WriteLine($"Container: {container_properties.Id}");

Console.WriteLine($"Container PartitionKeyPath: {container_properties.PartitionKeyPath}");
if (container_properties.PartitionKeyPaths != null && container_properties.PartitionKeyPaths.Count > 0)
{
Console.WriteLine($"Container PartitionKeyPaths: [{string.Join(", ", container_properties.PartitionKeyPaths)}]");
}

Container container = client.GetContainer(db_properties.Id, container_properties.Id);

String itemsQuery = "SELECT * FROM c";
QueryDefinition itemsQueryDef = new QueryDefinition(itemsQuery);

FeedIterator queryIterator = container.GetItemQueryStreamIterator(
itemsQueryDef,
requestOptions: new QueryRequestOptions { MaxItemCount = -1 }
);

while (queryIterator.HasMoreResults)
{
ResponseMessage response = await queryIterator.ReadNextAsync();

if (response.Content == null)
{
Console.WriteLine("QueryResponse.Content is null");
continue;
}
if (response.Content.CanSeek && response.Content.Length == 0)
{
Console.WriteLine("QueryResponse.Content stream is empty");
continue;
}

try
{
using JsonDocument itemsQueryResultDoc = JsonDocument.Parse(response.Content);
Console.WriteLine($"Raw JSON (Query result): {itemsQueryResultDoc.RootElement.GetRawText()}");

if (itemsQueryResultDoc.RootElement.TryGetProperty("Documents", out JsonElement documentsElement))
{
foreach (JsonElement item in documentsElement.EnumerateArray())
{
string itemId = ExtractItemId(item);
PartitionKey itemPartitionKey = CreatePartitionKey(item, container_properties.PartitionKeyPath);
Console.WriteLine($"Item PartitionKey: {itemPartitionKey}");

try
{
ItemResponse<JsonElement> itemResponse = await container.ReadItemAsync<JsonElement>(
id: itemId,
partitionKey: itemPartitionKey,
requestOptions: new ItemRequestOptions
{
// EnableContentResponseOnWrite = false,
}
);

Console.WriteLine($"Raw JSON (Read item result): {itemResponse.Resource.GetRawText()}");
}
catch (CosmosException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound)
{
Console.WriteLine("ReadItemAsync: Item not found");
}
catch (CosmosException ex)
{
Console.WriteLine($"ReadItemAsync: Error reading. {ex.Message}");
}
}
}
else
{
Console.WriteLine($"Raw JSON: {itemsQueryResultDoc.RootElement.GetRawText()}");
}
}
catch (JsonException ex)
{
Console.WriteLine($"JsonException: {ex.Message}");
}
}
}
}
}
}
}

private static string ExtractItemId(JsonElement item)
{
return item.TryGetProperty("id", out JsonElement idElement)
? idElement.GetString() ?? "unknown"
: "unknown";
}

private static PartitionKey CreatePartitionKey(JsonElement jsonElement, string? partitionKeyPath)
{
if (string.IsNullOrEmpty(partitionKeyPath))
{
return PartitionKey.Null;
}

string[] pathParts = partitionKeyPath.TrimStart('/').Split('/');

JsonElement currentElement = jsonElement;
foreach (string part in pathParts)
{
if (currentElement.TryGetProperty(part, out JsonElement nextElement))
{
currentElement = nextElement;
}
else
{
// Property not found, return null partition key
return PartitionKey.Null;
}
}

return currentElement.ValueKind switch
{
JsonValueKind.String => new PartitionKey(currentElement.GetString() ?? string.Empty),
JsonValueKind.Number => currentElement.TryGetInt32(out int intValue)
? new PartitionKey((double)intValue)
: new PartitionKey(currentElement.GetDouble()),
JsonValueKind.True => new PartitionKey(true),
JsonValueKind.False => new PartitionKey(false),
JsonValueKind.Null => PartitionKey.Null,
_ => new PartitionKey(currentElement.GetRawText())
};
}
}
}
63 changes: 63 additions & 0 deletions AOT-Sample/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Azure Cosmos DB .NET AOT Sample

This is a sample console application demonstrating how to use the Microsoft.Azure.Cosmos SDK with Native AOT compilation in .NET 9.

## Features

- ? **Native AOT Compilation** - Compiles to a self-contained native executable
- ? **Full Trimming** - Uses aggressive IL trimming for smaller size
- ? **AOT & Trim Analysis** - All AOT and trim analyzers enabled
- ? **Azure Cosmos DB Integration** - Uses Microsoft.Azure.Cosmos NuGet package
- ?? **Dependency Warnings Suppressed** - Known AOT warnings from dependencies are suppressed

## Project Configuration

Key settings for AOT compatibility:

```xml
<PublishAot>true</PublishAot>
<PublishTrimmed>true</PublishTrimmed>
<TrimMode>full</TrimMode>
<EnableTrimAnalyzer>true</EnableTrimAnalyzer>
<EnableAotAnalyzer>true</EnableAotAnalyzer>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<IsAotCompatible>true</IsAotCompatible>
```

## Build & Run

### Debug Build
```bash
dotnet build
dotnet run
```

### AOT Release Build
```bash
dotnet publish -c Release
# Run the native executable
./bin/Release/net9.0/win-arm64/publish/AOT-Sample.exe
```

## Known Limitations

The current Microsoft.Azure.Cosmos SDK (3.44.0) has some AOT/trim warnings that are suppressed:

- **IL2104**: Trim warnings from dependencies
- **IL3000**: Single-file app compatibility issues
- **IL3053**: AOT analysis warnings

These warnings are from dependencies (Newtonsoft.Json, System.Configuration.ConfigurationManager, etc.) and don't affect the basic functionality.

## File Size

The compiled native executable is approximately **26.76 MB** and includes the entire .NET runtime and Cosmos SDK.

## Next Steps

To build a production-ready AOT application with Cosmos DB:

1. Use real connection strings and endpoints
2. Implement proper error handling
3. Consider using System.Text.Json instead of Newtonsoft.Json for better AOT compatibility
4. Test thoroughly with your specific Cosmos DB operations
Loading