diff --git a/source/Sylvan.Data/CompiledDataBinder.cs b/source/Sylvan.Data/CompiledDataBinder.cs index db4a697..d70cf40 100644 --- a/source/Sylvan.Data/CompiledDataBinder.cs +++ b/source/Sylvan.Data/CompiledDataBinder.cs @@ -78,7 +78,7 @@ ReadOnlyCollection logicalSchema // stores contextual state used by the binder. var state = new List(); - + var physicalColumns = new HashSet(); foreach (var col in physicalSchema) { @@ -188,7 +188,9 @@ ReadOnlyCollection logicalSchema .GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) .Where(p => p.SetMethod != null && p.GetCustomAttribute() == null) .ToDictionary(p => p.Name, p => p, StringComparer.OrdinalIgnoreCase); - + HashSet requiredProperties = new(); + // always require at least 1 property to be bound. + int boundPropertyCount = 0; foreach (var kvp in properties.ToArray()) { var key = kvp.Key; @@ -198,6 +200,11 @@ ReadOnlyCollection logicalSchema var columnOrdinal = memberAttribute?.Order; var columnName = memberAttribute?.Name ?? property.Name; + if (memberAttribute?.IsRequired == true) + { + requiredProperties.Add(property.Name); + } + var col = GetCol(columnOrdinal, columnName); Type colType = col?.DataType ?? typeof(object); if (col == null) @@ -413,12 +420,13 @@ ReadOnlyCollection logicalSchema expr ); } - + boundPropertyCount++; var setExpr = Expression.Call(itemParam, setter, expr); bodyExpressions.Add(Expression.Assign(idxVar, ordinalExpr)); bodyExpressions.Add(setExpr); physicalColumns.Remove(col); properties.Remove(key); + requiredProperties.Remove(key); } var props = properties.ToArray(); foreach (var kvp in props) @@ -448,19 +456,26 @@ ReadOnlyCollection logicalSchema string[]? unboundProperties = null; string[]? unboundColumns = null; - if (opts.BindingMode.HasFlag(DataBindingMode.AllProperties) && properties.Any()) + if (boundPropertyCount == 0 || opts.BindingMode.HasFlag(DataBindingMode.AllProperties) && properties.Any()) { unboundProperties = properties.Select(p => p.Value.Name).ToArray(); } - - if (opts.BindingMode.HasFlag(DataBindingMode.AllColumns) && physicalColumns.Any()) + else + { + if (requiredProperties.Any()) + { + unboundProperties = requiredProperties.ToArray(); + } + } + + if (boundPropertyCount == 0 || opts.BindingMode.HasFlag(DataBindingMode.AllColumns) && physicalColumns.Any()) { unboundColumns = physicalColumns.Select(p => p.ColumnName).ToArray(); } if (unboundColumns != null || unboundProperties != null) { - throw new UnboundMemberException(unboundProperties, unboundColumns); + throw new UnboundMemberException(unboundProperties ?? Array.Empty(), unboundColumns ?? Array.Empty()); } this.state = state.ToArray();