diff --git a/src/EventSourcingTests/Bugs/Bug_3643_using_struct_in_map_flattableprojection.cs b/src/EventSourcingTests/Bugs/Bug_3643_using_struct_in_map_flattableprojection.cs new file mode 100644 index 0000000000..991d4ee3d7 --- /dev/null +++ b/src/EventSourcingTests/Bugs/Bug_3643_using_struct_in_map_flattableprojection.cs @@ -0,0 +1,55 @@ +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Marten.Events.Projections; +using Marten.Events.Projections.Flattened; +using Marten.Testing.Harness; +using Shouldly; +using Xunit; +using Xunit.Abstractions; + +namespace EventSourcingTests.Bugs; + +public class Bug_3643_using_struct_in_map_flattableprojection : BugIntegrationContext{ + private readonly ITestOutputHelper _output; + public Bug_3643_using_struct_in_map_flattableprojection(ITestOutputHelper output) + { + _output = output; + } + + public record MyEvent(Guid Id, MyStruct MyStruct, MyClass MyClass); + public record struct MyStruct(string PrimitiveValue); + public record MyClass(string PrimitiveValue); + + public class MyTableProjection : FlatTableProjection{ + public MyTableProjection() : base("my_table_projection", SchemaNameSource.EventSchema){ + Table.AddColumn("id").AsPrimaryKey(); + Options.TeardownDataOnRebuild = true; + + Project(map => { + map.Map(x => x.MyStruct.PrimitiveValue); + map.Map(x => x.MyClass.PrimitiveValue); + }); + } + } + + + [Fact] + public async Task add_event_that_requires_mapping_a_struct_and_a_class(){ + StoreOptions(x => { + x.Projections.Add(ProjectionLifecycle.Inline); + x.Logger(new TestOutputMartenLogger(_output)); + }); + + await theStore.Storage.ApplyAllConfiguredChangesToDatabaseAsync(); + using(var session = theStore.LightweightSession()){ + var e = new MyEvent(Guid.NewGuid(), new MyStruct("StructValue"), null); + session.Events.StartStream(Guid.NewGuid(), e); + await session.SaveChangesAsync(); + (string structvalue, string classvalue) result = (await session.AdvancedSql.QueryAsync($"SELECT ROW(my_struct_primitive_value), ROW(my_class_primitive_value) FROM bugs.my_table_projection", CancellationToken.None)).First(); + result.structvalue.ShouldBe("StructValue"); + result.classvalue.ShouldBeNull(); + } + } +} \ No newline at end of file diff --git a/src/Marten/Events/Projections/Flattened/MemberMap.cs b/src/Marten/Events/Projections/Flattened/MemberMap.cs index 577cec87b1..511788cc7a 100644 --- a/src/Marten/Events/Projections/Flattened/MemberMap.cs +++ b/src/Marten/Events/Projections/Flattened/MemberMap.cs @@ -2,6 +2,7 @@ using System.Linq; using System.Linq.Expressions; using System.Reflection; +using System.Text; using JasperFx.CodeGeneration.Model; using JasperFx.Core; using Marten.Linq.Parsing; @@ -76,6 +77,17 @@ public string ToInsertExpression(Table table) public string ToValueAccessorCode(Variable eventVariable) { - return $"{eventVariable.Usage}.Data.{_members.Select(x => x.Name).Join("?.")}"; + var accessor = new StringBuilder(); + for(var i = 0; i < _members.Length; i++){ + accessor.Append(_members[i].Name); + if(i < _members.Length - 1){ + if(_members[i] is PropertyInfo propertyinfo && propertyinfo.PropertyType.IsValueType && !propertyinfo.PropertyType.IsPrimitive){ + accessor.Append('.'); + }else{ + accessor.Append("?."); + } + } + } + return $"{eventVariable.Usage}.Data.{accessor}"; } }