Skip to content

Commit 8fbe67e

Browse files
committed
Fix null annotations on collection binding
1 parent ad3164f commit 8fbe67e

2 files changed

Lines changed: 48 additions & 3 deletions

File tree

src/generator/Vertical/Cli/SourceGenerator/Utilities/BindingExpressionHelper.cs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,10 +119,22 @@ private string GetCollectionValueExpression(
119119
{
120120
var elementConversionExpression = GetConversionExpression(valueType);
121121
var formattedExpression = string.Format(collectionConversionTemplate, valueType);
122+
var isNullableCollection = collectionType is
123+
{
124+
IsReferenceType: true,
125+
NullableAnnotation: NullableAnnotation.Annotated
126+
};
127+
var contextMethod = isNullableCollection
128+
? "GetCollectionValueOrDefault"
129+
: "GetCollectionValue";
130+
var resolvedCollectionType = isNullableCollection
131+
? collectionType.WithNullableAnnotation(NullableAnnotation.NotAnnotated)
132+
: collectionType;
133+
var bindingNameAnnotation = isNullableCollection ? "!" : null;
122134

123135
return elementConversionExpression != "null"
124-
? $"{contextParameter}.GetCollectionValue(x => x.{bindingName}, {elementConversionExpression}, {formattedExpression})"
125-
: $"{contextParameter}.GetCollectionValue<{valueType}, {collectionType}>(x => x.{bindingName}, null, {formattedExpression})";
136+
? $"{contextParameter}.{contextMethod}<{valueType}, {resolvedCollectionType}>(x => x.{bindingName}{bindingNameAnnotation}, {elementConversionExpression}, {formattedExpression})"
137+
: $"{contextParameter}.{contextMethod}<{valueType}, {resolvedCollectionType}>(x => x.{bindingName}{bindingNameAnnotation}, null, {formattedExpression})";
126138
}
127139

128140
private string GetConversionExpression(ITypeSymbol valueType)

src/lib/Vertical/Cli/Binding/BindingContext.cs

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,40 @@ public TCollection GetCollectionValue<TElement, TCollection>(
106106
if (TryGetCollectionParseResultValue(binding, elementConverter, createCollection, out var collection))
107107
return collection;
108108

109-
return bindingArgs.TryGetDefaultValue(out collection) ? collection : default!;
109+
return bindingArgs.TryGetDefaultValue(out collection)
110+
? collection
111+
: createCollection([]);
112+
}
113+
114+
/// <summary>
115+
/// Gets a collection property value.
116+
/// </summary>
117+
/// <param name="propertyExpression">The expression that identifies the model property being bound.</param>
118+
/// <param name="elementConverter">A function that converts string arguments to the target element type.</param>
119+
/// <param name="createCollection">A function that converts enumerable values to the strong collection type.</param>
120+
/// <typeparam name="TElement">The collection or array's element type.</typeparam>
121+
/// <typeparam name="TCollection">The collection type.</typeparam>
122+
/// <returns>The collection value</returns>
123+
public TCollection? GetCollectionValueOrDefault<TElement, TCollection>(
124+
Expression<Func<TModel, TCollection>> propertyExpression,
125+
ValueConverter<TElement>? elementConverter,
126+
Func<IEnumerable<TElement>, TCollection> createCollection)
127+
where TCollection : IEnumerable<TElement>
128+
{
129+
var binding = GetPropertyBinding(propertyExpression);
130+
var bindingArgs = new PropertyBinder<TCollection>(binding, this, null);
131+
132+
binding.TryBindValue(bindingArgs);
133+
134+
if (bindingArgs.TryGetExplicitValue(out var value))
135+
return value;
136+
137+
if (TryGetCollectionParseResultValue(binding, elementConverter, createCollection, out var collection))
138+
return collection;
139+
140+
return bindingArgs.TryGetDefaultValue(out collection)
141+
? collection
142+
: default;
110143
}
111144

112145
/// <summary>

0 commit comments

Comments
 (0)