From 31bb8591f30d4ec96ce8e438765f7c120b5052aa Mon Sep 17 00:00:00 2001 From: Shay Rojansky Date: Sat, 18 Apr 2026 10:02:04 +0200 Subject: [PATCH] Minor fixes to MEVD.Abstractions * Correct StorageName behavior when external serialization is enabled * Disable warning for net462 --- ....Extensions.VectorData.Abstractions.csproj | 3 ++ .../CollectionModelBuilder.cs | 10 ++-- .../CollectionModelBuilderTests.cs | 50 +++++++++++++++++++ 3 files changed, 58 insertions(+), 5 deletions(-) diff --git a/src/Libraries/Microsoft.Extensions.VectorData.Abstractions/Microsoft.Extensions.VectorData.Abstractions.csproj b/src/Libraries/Microsoft.Extensions.VectorData.Abstractions/Microsoft.Extensions.VectorData.Abstractions.csproj index a75cb820c8c..b8edb533255 100644 --- a/src/Libraries/Microsoft.Extensions.VectorData.Abstractions/Microsoft.Extensions.VectorData.Abstractions.csproj +++ b/src/Libraries/Microsoft.Extensions.VectorData.Abstractions/Microsoft.Extensions.VectorData.Abstractions.csproj @@ -10,6 +10,9 @@ 75 AI $(NoWarn);S125;S1135;S2302 + + true https://learn.microsoft.com/dotnet/ai/vector-stores/overview vector, database, similarity, ai, query diff --git a/src/Libraries/Microsoft.Extensions.VectorData.Abstractions/ProviderServices/CollectionModelBuilder.cs b/src/Libraries/Microsoft.Extensions.VectorData.Abstractions/ProviderServices/CollectionModelBuilder.cs index 39cb5720d25..e4013407193 100644 --- a/src/Libraries/Microsoft.Extensions.VectorData.Abstractions/ProviderServices/CollectionModelBuilder.cs +++ b/src/Libraries/Microsoft.Extensions.VectorData.Abstractions/ProviderServices/CollectionModelBuilder.cs @@ -318,6 +318,11 @@ protected virtual void ProcessProperty(PropertyInfo? clrProperty, VectorStorePro throw new UnreachableException(); } + if (clrProperty is not null) + { + property.PropertyInfo = clrProperty; + } + // Apply storage name: attribute first, then definition (which takes precedence). SetPropertyStorageName(property, attributeStorageName); if (definitionProperty is not null) @@ -330,11 +335,6 @@ protected virtual void ProcessProperty(PropertyInfo? clrProperty, VectorStorePro property.ProviderAnnotations = new Dictionary(definitionProperty.ProviderAnnotations); } - if (clrProperty is not null) - { - property.PropertyInfo = clrProperty; - } - PropertyMap.Add(propertyName, property); } diff --git a/test/Libraries/Microsoft.Extensions.VectorData.Abstractions.Tests/CollectionModelBuilderTests.cs b/test/Libraries/Microsoft.Extensions.VectorData.Abstractions.Tests/CollectionModelBuilderTests.cs index f1d9ab2568a..48f9eeea1ab 100644 --- a/test/Libraries/Microsoft.Extensions.VectorData.Abstractions.Tests/CollectionModelBuilderTests.cs +++ b/test/Libraries/Microsoft.Extensions.VectorData.Abstractions.Tests/CollectionModelBuilderTests.cs @@ -469,6 +469,56 @@ public void IsAutoGenerated_omitted_falls_back_to_SupportsKeyAutoGeneration_fals Assert.False(model.KeyProperty.IsAutoGenerated); } + [Fact] + public void StorageName_from_attribute_is_ignored_when_UsesExternalSerializer() + { + // When UsesExternalSerializer is true, the provider uses an external serializer (e.g. System.Text.Json, BSON) + // to determine storage names. Attribute-specified StorageName should be ignored to stay in sync with the serializer. + var builder = new CustomModelBuilder(new CollectionModelBuildingOptions + { + SupportsMultipleVectors = true, + RequiresAtLeastOneVector = false, + UsesExternalSerializer = true + }); + + var model = builder.Build(typeof(RecordWithStorageNames), typeof(int), definition: null, defaultEmbeddingGenerator: null); + + // StorageName should be the CLR property name, NOT the attribute-specified name + Assert.Equal("Id", model.KeyProperty.StorageName); + Assert.Equal("Name", model.DataProperties[0].StorageName); + Assert.Equal("Embedding", model.VectorProperty.StorageName); + } + + [Fact] + public void StorageName_from_attribute_is_applied_when_not_UsesExternalSerializer() + { + var builder = new CustomModelBuilder(new CollectionModelBuildingOptions + { + SupportsMultipleVectors = true, + RequiresAtLeastOneVector = false, + UsesExternalSerializer = false + }); + + var model = builder.Build(typeof(RecordWithStorageNames), typeof(int), definition: null, defaultEmbeddingGenerator: null); + + // StorageName should be the attribute-specified name + Assert.Equal("id", model.KeyProperty.StorageName); + Assert.Equal("name", model.DataProperties[0].StorageName); + Assert.Equal("embedding", model.VectorProperty.StorageName); + } + + public class RecordWithStorageNames + { + [VectorStoreKey(StorageName = "id")] + public int Id { get; set; } + + [VectorStoreData(StorageName = "name")] + public required string Name { get; set; } + + [VectorStoreVector(3, StorageName = "embedding")] + public ReadOnlyMemory Embedding { get; set; } + } + public class RecordWithGuidKeyAutoGeneratedTrue { [VectorStoreKey(IsAutoGenerated = true)]