Skip to content

Commit 5bc9f35

Browse files
committed
Added/updates samples, Added TTL checks, Changed Native Mode AI Header
1 parent 6cfb99a commit 5bc9f35

12 files changed

Lines changed: 157 additions & 30 deletions

AIContext/AIContextVersion.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@ namespace Aerospike.Database.LINQPadDriver.Extensions
22
{
33
public static class AIContextVersion
44
{
5-
public const string Current = "2026.07.17.01";
5+
public const string Current = "2026.07.21.03";
66
}
77
}

Aerospike.Database.LINQPadDriver.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@
2020
<Deterministic>true</Deterministic>
2121

2222
<!-- Current validated package version. Increment for the next release. -->
23-
<Version>7.0.53.18</Version>
24-
<FileVersion>7.0.53.18</FileVersion>
23+
<Version>7.0.53.22</Version>
24+
<FileVersion>7.0.53.22</FileVersion>
2525
<VersionSuffix Condition="'$(Configuration)' == 'Debug'">debug</VersionSuffix>
2626
<InformationalVersion Condition="'$(Configuration)' == 'Debug'">$(Version)-debug</InformationalVersion>
2727
<InformationalVersion Condition="'$(Configuration)' != 'Debug'">$(Version)</InformationalVersion>
7.46 MB
Binary file not shown.
-1.2 KB
Binary file not shown.

LINQPadAIGeneratedQuery.cs

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -476,16 +476,23 @@ private static AIGeneratedQueryMode DetermineGeneratedQueryMode(
476476
var hasMediumNativeCue = HasMediumNativeModeCue(requestText);
477477
var hasDriverCue = HasDriverModeCue(requestText);
478478

479-
if((hasStrongNativeCue || hasMediumNativeCue) && hasDriverCue)
479+
if(hasStrongNativeCue)
480+
{
481+
if(hasDriverCue)
482+
{
483+
modeWarning = "Mode conflict detected (native and driver cues). Honoring explicit native intent from the request.";
484+
}
485+
486+
return AIGeneratedQueryMode.NativeAerospikeClient;
487+
}
488+
489+
if(hasMediumNativeCue && hasDriverCue)
480490
{
481491
modeWarning = "Mode conflict detected (native and driver cues). Defaulting to LINQPad Driver mode. Add mode:native or mode:driver to force mode selection.";
482492
return AIGeneratedQueryMode.Driver;
483493
}
484494

485-
if(hasStrongNativeCue)
486-
return AIGeneratedQueryMode.NativeAerospikeClient;
487-
488-
if(hasMediumNativeCue && !hasDriverCue)
495+
if(hasMediumNativeCue)
489496
return AIGeneratedQueryMode.NativeAerospikeClient;
490497

491498
return AIGeneratedQueryMode.Driver;
@@ -523,6 +530,7 @@ private static bool HasStrongNativeModeCue(string text)
523530
|| text.Contains("native Aerospike API", StringComparison.OrdinalIgnoreCase)
524531
|| text.Contains("native Aerospike C# client", StringComparison.OrdinalIgnoreCase)
525532
|| text.Contains("native C# client", StringComparison.OrdinalIgnoreCase)
533+
|| text.Contains("native API code", StringComparison.OrdinalIgnoreCase)
526534
|| text.Contains("native API driver", StringComparison.OrdinalIgnoreCase)
527535
|| text.Contains("native driver", StringComparison.OrdinalIgnoreCase)
528536
|| text.Contains("no LINQPad driver", StringComparison.OrdinalIgnoreCase)
@@ -598,11 +606,6 @@ private static string CreateGeneratedQuery(
598606
{
599607
generatedHeader = EnsureNativeAerospikeClientHeader("<Query Kind=\"Statements\" />");
600608
normalizedCode = NormalizeNativeAerospikeClientCode(normalizedCode);
601-
warningComment =
602-
"// NOTE: Native Aerospike client mode. This query intentionally does not copy LINQPad driver connection metadata." + Environment.NewLine +
603-
"// Confirm host, port, TLS, authentication, and policy settings before running." + Environment.NewLine +
604-
"// The Aerospike.Client NuGet package is referenced in the LINQPad query header." + Environment.NewLine +
605-
Environment.NewLine;
606609
}
607610
else if(TryGetCurrentQueryHeader(out var currentHeader))
608611
{
@@ -1019,17 +1022,10 @@ private static string EnsureNativeAerospikeClientHeader(string header)
10191022
{
10201023
// Native Aerospike client generated queries are standalone LINQPad Statements queries.
10211024
// Do not depend on a copied LINQPad-driver connection header.
1022-
// Build the header explicitly so the Aerospike.Client NuGet reference cannot be lost
1023-
// when the source header is the self-closing form: <Query Kind="Statements" />.
10241025
var nativeHeader =
10251026
"<Query Kind=\"Statements\">" + Environment.NewLine +
10261027
" <NuGetReference>Aerospike.Client</NuGetReference>" + Environment.NewLine +
1027-
" <Namespace>System</Namespace>" + Environment.NewLine +
1028-
" <Namespace>System.Collections</Namespace>" + Environment.NewLine +
1029-
" <Namespace>System.Collections.Generic</Namespace>" + Environment.NewLine +
1030-
" <Namespace>System.Linq</Namespace>" + Environment.NewLine +
1031-
" <Namespace>System.Security.Authentication</Namespace>" + Environment.NewLine +
1032-
" <Namespace>System.Security.Cryptography.X509Certificates</Namespace>" + Environment.NewLine +
1028+
" <Namespace>Aerospike</Namespace>" + Environment.NewLine +
10331029
" <Namespace>Aerospike.Client</Namespace>" + Environment.NewLine +
10341030
"</Query>";
10351031

3.18 MB
Binary file not shown.
7.46 MB
Binary file not shown.

TestProjects/Aerospike.Database.LINQPadDriverTests/AIHelpersTests.cs

Lines changed: 101 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,31 @@ public class AIHelpersTests
1616
/// Helper method to invoke private StripAIRequestCommentLines via reflection.
1717
/// </summary>
1818
private static string StripAIRequestCommentLines(string request)
19+
{
20+
return InvokePrivateStringMethod("StripAIRequestCommentLines", new object[] { request }, new[] { typeof(string) });
21+
}
22+
23+
private static string EnsureNativeAerospikeClientHeader(string header)
24+
{
25+
return InvokePrivateStringMethod("EnsureNativeAerospikeClientHeader", new object[] { header }, new[] { typeof(string) });
26+
}
27+
28+
private static object InvokePrivateMethod(string methodName, object[] args, Type[] parameterTypes)
1929
{
2030
var method = typeof(LINQPadAIGeneratedQuery).GetMethod(
21-
"StripAIRequestCommentLines",
31+
methodName,
2232
BindingFlags.NonPublic | BindingFlags.Static,
2333
null,
24-
new[] { typeof(string) },
34+
parameterTypes,
2535
null);
2636

27-
Assert.IsNotNull(method, "StripAIRequestCommentLines method should exist");
28-
return (string)method!.Invoke(null, new object[] { request })!;
37+
Assert.IsNotNull(method, methodName + " method should exist");
38+
return method!.Invoke(null, args);
39+
}
40+
41+
private static string InvokePrivateStringMethod(string methodName, object[] args, Type[] parameterTypes)
42+
{
43+
return (string) InvokePrivateMethod(methodName, args, parameterTypes)!;
2944
}
3045

3146
#region StripAIRequestCommentLines Tests
@@ -357,6 +372,88 @@ public void StripAIRequestCommentLines_ResultIsTrimmed()
357372

358373
#endregion
359374

375+
#region Native Header Generation
376+
377+
[TestMethod]
378+
public void EnsureNativeAerospikeClientHeader_UsesMinimalStandaloneHeader()
379+
{
380+
// Arrange
381+
const string inputHeader = "<Query Kind=\"Statements\" />";
382+
383+
// Act
384+
var result = EnsureNativeAerospikeClientHeader(inputHeader);
385+
386+
// Assert
387+
Assert.AreEqual(
388+
"<Query Kind=\"Statements\">" + Environment.NewLine +
389+
" <NuGetReference>Aerospike.Client</NuGetReference>" + Environment.NewLine +
390+
" <Namespace>Aerospike</Namespace>" + Environment.NewLine +
391+
" <Namespace>Aerospike.Client</Namespace>" + Environment.NewLine +
392+
"</Query>",
393+
result);
394+
}
395+
396+
[TestMethod]
397+
public void EnsureNativeAerospikeClientHeader_DoesNotIncludeConnectionMetadata()
398+
{
399+
// Arrange
400+
const string inputHeader = "<Query Kind=\"Statements\"><Connection><ID>abc</ID></Connection></Query>";
401+
402+
// Act
403+
var result = EnsureNativeAerospikeClientHeader(inputHeader);
404+
405+
// Assert
406+
Assert.IsFalse(result.Contains("<Connection>", StringComparison.Ordinal));
407+
Assert.IsFalse(result.Contains("<ID>", StringComparison.Ordinal));
408+
Assert.IsFalse(result.Contains("<CustomCxString>", StringComparison.Ordinal));
409+
StringAssert.Contains(result, "<NuGetReference>Aerospike.Client</NuGetReference>");
410+
StringAssert.Contains(result, "<Namespace>Aerospike</Namespace>");
411+
StringAssert.Contains(result, "<Namespace>Aerospike.Client</Namespace>");
412+
}
413+
414+
#endregion
415+
416+
#region Mode Detection
417+
418+
[TestMethod]
419+
public void DetermineGeneratedQueryMode_StrongNativeAndDriverCue_PrefersNative()
420+
{
421+
// Arrange
422+
const string request = "Can you translate this LINQPad driver query to native Aerospike C# client code using AsEnumerable()";
423+
var args = new object[] { request, string.Empty, string.Empty, null };
424+
425+
// Act
426+
var mode = InvokePrivateMethod(
427+
"DetermineGeneratedQueryMode",
428+
args,
429+
new[] { typeof(string), typeof(string), typeof(string), typeof(string).MakeByRefType() });
430+
var warning = (string) args[3];
431+
432+
// Assert
433+
Assert.AreEqual("NativeAerospikeClient", mode!.ToString());
434+
Assert.IsFalse(string.IsNullOrWhiteSpace(warning));
435+
StringAssert.Contains(warning, "Honoring explicit native intent");
436+
}
437+
438+
[TestMethod]
439+
public void DetermineGeneratedQueryMode_NativeApiCodePhrase_PrefersNative()
440+
{
441+
// Arrange
442+
const string request = "Can you translate this linq query into native API code, use filter expressions on invoice set: test.Customer.AsEnumerable().GroupJoin(...)";
443+
var args = new object[] { request, string.Empty, string.Empty, null };
444+
445+
// Act
446+
var mode = InvokePrivateMethod(
447+
"DetermineGeneratedQueryMode",
448+
args,
449+
new[] { typeof(string), typeof(string), typeof(string), typeof(string).MakeByRefType() });
450+
451+
// Assert
452+
Assert.AreEqual("NativeAerospikeClient", mode!.ToString());
453+
}
454+
455+
#endregion
456+
360457
#region Edge Cases and Robustness
361458

362459
[TestMethod]

linqpad-samples/AI/00-ReadMe.linq

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,13 +77,16 @@ Renders the current 'AIContext' as Markdown in LINQPad.
7777
9. '09-CDT-Nested-Query.linq'
7878
Demonstrates how to generate a query where the data is nested within multiple maps (documents).
7979
80+
10. '10-Translate-LINQ-to-Native.linq'
81+
Demonstrates how to translate a LINQ query into Aerospike native API calls using server expressions.
82+
8083
Recommended Additional Samples
8184
------------------------------
8285
8386
The following sample scripts are recommended additions based on the LINQPad AI
8487
overview deck and demo workflow.
8588
86-
10. '10-Generate-Customer-Invoice-Artist-Purchase-Query.linq'
89+
11. '11-Generate-Customer-Invoice-Artist-Purchase-Query.linq'
8790
8891
Purpose:
8992
Generate a LINQ query-syntax script that returns customers, their invoices, and
@@ -109,7 +112,7 @@ when those sets are available in the current connection metadata.
109112
- Generate a bounded result set with 'Take(...)'.
110113
- Use 'Dump()' for output.
111114
112-
11. '11-Explain-AValue-TryApply-Customer-Query.linq'
115+
12. '12-Explain-AValue-TryApply-Customer-Query.linq'
113116
114117
Purpose:
115118
Ask AI to explain an existing LINQPad Aerospike query and identify whether the
@@ -151,7 +154,7 @@ Expected AI behavior:
151154
string-indexer access when available.
152155
- Call out that server-side filtering may be more efficient for large sets.
153156
154-
12. '12-Translate-TryApply-Query-To-Native-Server-Expression.linq'
157+
13. '13-Translate-TryApply-Query-To-Native-Server-Expression.linq'
155158
156159
Purpose:
157160
Ask AI to translate a LINQPad-driver client-side AValue/TryApply query into

linqpad-samples/AI/04-AValue-TryApply-Examples.linq

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ Generate LINQPad C# Statements examples for AValue-backed properties.
2424
Use query syntax where practical.
2525
Use test.Customer.AsEnumerable().
2626
Show:
27-
1. A filter using FirstName.TryApply<string,bool>(name => name.StartsWith("a")).
27+
1. A filter using FirstName.TryApply<string,bool>(name => name.StartsWith("M")).
2828
2. A projection using FirstName.Apply<string,int>(name => name.Length).
2929
3. A numeric conversion example using CanConvert<decimal>() and Convert<decimal>().
3030
4. Use generated properties, not string-indexer access.

0 commit comments

Comments
 (0)