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
10 changes: 8 additions & 2 deletions src/Marten/StoreOptions.Identity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -147,8 +147,14 @@ public ValueTypeInfo RegisterValueType(Type type)
return valueType;
}

var builder = type.GetMethods(BindingFlags.Static | BindingFlags.Public).FirstOrDefault(x =>
x.GetParameters().Length == 1 && x.GetParameters()[0].ParameterType == valueProperty.PropertyType);
var candidateBuilders = type.GetMethods(BindingFlags.Static | BindingFlags.Public).Where(x =>
{
var parameters = x.GetParameters();
return parameters.Length == 1 && parameters[0].ParameterType == valueProperty.PropertyType;
}).ToArray();

var builder = candidateBuilders.FirstOrDefault(x => x.ReturnType == type)
?? candidateBuilders.FirstOrDefault();

if (builder != null)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Marten;
using Marten.Testing.Harness;
using Shouldly;
using Vogen;

namespace ValueTypeTests.Bugs;

public class Bug_4288_value_type_with_nullable_factory : BugIntegrationContext
{
[Fact]
public void can_generate_ddl_for_index_over_value_type_with_nullable_sibling_factory()
{
// Adding a `static FromNullable(string?)` sibling to a Vogen value object used to make
// Marten select that method as the value type's builder, which then crashed in
// ValueTypeInfo.CreateWrapper because the return type is Nullable<Bug4288Value>, not
// Bug4288Value. The crash surfaced when generating DDL for any computed index over
// the value type.
StoreOptions(opts =>
{
opts.RegisterValueType<Bug4288Value>();
opts.Schema.For<Bug4288Doc>().Index(x => x.Value);
});

Should.NotThrow(() => theStore.Storage.ToDatabaseScript());
}

[Fact]
public async Task can_round_trip_and_query_value_type_with_nullable_sibling_factory()
{
StoreOptions(opts =>
{
opts.RegisterValueType<Bug4288Value>();
});

var doc = new Bug4288Doc { Value = Bug4288Value.From("abc") };
theSession.Store(doc);
await theSession.SaveChangesAsync();

var key = Bug4288Value.From("abc");
var found = await theSession.Query<Bug4288Doc>()
.Where(x => x.Value == key)
.ToListAsync();

found.Count.ShouldBe(1);
found.Single().Id.ShouldBe(doc.Id);
}
}

[ValueObject<string>]
public readonly partial struct Bug4288Value
{
private static Validation Validate(string value)
=> string.IsNullOrWhiteSpace(value) ? Validation.Invalid("Cannot be empty.") : Validation.Ok;

public static Bug4288Value? FromNullable(string? value)
=> value is null ? null : From(value);
}

public class Bug4288Doc
{
public Guid Id { get; set; }
public Bug4288Value? Value { get; set; }
}
24 changes: 24 additions & 0 deletions src/ValueTypeTests/registration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,15 @@ public void register_happy_path_with_static_method()
value.ValueProperty.Name.ShouldBe("Value");
}

[Fact]
public void picks_builder_whose_return_type_matches_value_type()
{
var options = new StoreOptions();
var value = options.RegisterValueType(typeof(SpecialValueWithNullableSibling));
value.Builder.Name.ShouldBe("From");
value.Builder.ReturnType.ShouldBe(typeof(SpecialValueWithNullableSibling));
}

[Theory]
[InlineData(typeof(NotValidId))]
[InlineData(typeof(DefinitelyNotValid))]
Expand Down Expand Up @@ -53,6 +62,21 @@ private SpecialValue(string value)
public static SpecialValue From(string value) => new SpecialValue(value);
}

public readonly struct SpecialValueWithNullableSibling
{
private SpecialValueWithNullableSibling(string value)
{
Value = value;
}

public string Value { get; }

public static SpecialValueWithNullableSibling? FromNullable(string? value)
=> value is null ? null : From(value);

public static SpecialValueWithNullableSibling From(string value) => new(value);
}

public class NotValidId(string Value);

public class DefinitelyNotValid;
Loading