Skip to content

Commit 640003e

Browse files
authored
Merge pull request #28 from tgharold/24-add-index-number-to-member-name-for-enumerables-1
v2: Report the index position in IEnumerable validations
2 parents d4b01ae + 219fc02 commit 640003e

File tree

3 files changed

+72
-23
lines changed

3 files changed

+72
-23
lines changed

BREAKING-CHANGES.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Breaking Changes
2+
3+
Any breaking changes will require either a change in the code that uses the library or describes a change in behavior.
4+
5+
## v2.0.0: November 2022
6+
7+
The member names reported back for IEnumerable collection properties will now return the index inside of square brackets as suggested by issue #24.
8+
9+
- OLD: `Items.SimpleA.BoolC`
10+
- NEW: `Items[1].SimpleA.BoolC`
11+
12+
This will impact any code that was parsing the member name string if IEnumerable properties were validated.

src/RecursiveDataAnnotationsValidation/RecursiveDataAnnotationValidator.cs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,10 +98,12 @@ private bool TryValidateObjectRecursive(
9898
continue;
9999

100100
case IEnumerable asEnumerable:
101+
var arrayIndex = -1;
101102
foreach (var item in asEnumerable)
102103
{
103-
//NOTE: This does not tell you which item in the IEnumerable<T> failed
104-
//Possibly, should have a separate case for Array/Dictionary
104+
arrayIndex++;
105+
106+
//NOTE: Possibly should have a separate case for Dictionary which reports on the key
105107

106108
if (item == null) continue;
107109
nestedResults = new List<ValidationResult>();
@@ -119,7 +121,9 @@ private bool TryValidateObjectRecursive(
119121
validationResults.Add(
120122
new ValidationResult(
121123
validationResult.ErrorMessage,
122-
validationResult.MemberNames.Select(x => property1.Name + '.' + x)
124+
validationResult.MemberNames
125+
.Select(x => $"{property1.Name}[{arrayIndex}].{x}")
126+
.ToList()
123127
));
124128
}
125129
}

test/RecursiveDataAnnotationsValidation.Tests/EnumerableExampleTests.cs

Lines changed: 53 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public void Passes_all_validation_with_children()
3434
{
3535
var model = new EnumerableExample
3636
{
37-
Name = "Passes all",
37+
Name = "Passes all with children",
3838
Age = 75,
3939
Items = new List<ItemExample>
4040
{
@@ -125,7 +125,7 @@ public void Fails_on_Items_Child2_SimpleA_BoolC()
125125
{
126126
var model = new EnumerableExample
127127
{
128-
Name = "Passes all",
128+
Name = "Fails_on_Items_Child2_SimpleA_BoolC",
129129
Age = 75,
130130
Items = new List<ItemExample>
131131
{
@@ -159,23 +159,22 @@ public void Fails_on_Items_Child2_SimpleA_BoolC()
159159

160160
Assert.False(result);
161161
Assert.NotEmpty(validationResults);
162-
Assert.NotNull(validationResults
163-
.FirstOrDefault(x => x.MemberNames.Contains("Items.SimpleA.BoolC")));
162+
AssertThereIsAnErrorForMemberName("Items[1].SimpleA.BoolC", validationResults);
164163
}
165164

166165
private readonly EnumerableExample _multipleFailureModel = new EnumerableExample
167166
{
168-
Name = "Passes all",
167+
Name = "Multiple failures",
169168
Age = 75,
170169
Items = new List<ItemExample>
171170
{
172171
new ItemExample
173172
{
174-
Name = "Child 3E",
173+
Name = "Child 0E",
175174
SimpleA = new SimpleExample
176175
{
177176
IntegerA = 65,
178-
StringB = "child-3E-stringB",
177+
StringB = "child-0E-stringB",
179178
BoolC = true,
180179
ExampleEnumD = ExampleEnum.ValueB
181180
}
@@ -207,22 +206,22 @@ public void Fails_on_Items_Child2_SimpleA_BoolC()
207206
{
208207
new ItemExample
209208
{
210-
Name = "Child 1L",
209+
Name = "Child 0L",
211210
SimpleA = new SimpleExample
212211
{
213212
IntegerA = 123,
214-
StringB = "child-1L-stringB",
213+
StringB = "child-0L-stringB",
215214
BoolC = true,
216215
ExampleEnumD = ExampleEnum.ValueC
217216
}
218217
},
219218
new ItemExample
220219
{
221-
Name = "Child 3L",
220+
Name = "Child 1L",
222221
SimpleA = new SimpleExample
223222
{
224223
IntegerA = 13,
225-
StringB = "child-3L-stringB",
224+
StringB = "child-1L-stringB",
226225
BoolC = true,
227226
ExampleEnumD = null
228227
}
@@ -243,11 +242,11 @@ public void Fails_on_Items_Child2_SimpleA_BoolC()
243242
{
244243
new ItemExample
245244
{
246-
Name = "Child 1C",
245+
Name = "Child 0C",
247246
SimpleA = new SimpleExample
248247
{
249248
IntegerA = null,
250-
StringB = "child-1C-stringB",
249+
StringB = "child-0C-stringB",
251250
BoolC = true,
252251
ExampleEnumD = null
253252
}
@@ -258,27 +257,61 @@ public void Fails_on_Items_Child2_SimpleA_BoolC()
258257
SimpleA = new SimpleExample
259258
{
260259
IntegerA = null,
261-
StringB = "child-2C-string-abc",
260+
StringB = "child-1C-string-abc",
262261
BoolC = false,
263262
ExampleEnumD = ExampleEnum.ValueA
264263
}
265264
},
265+
new ItemExample
266+
{
267+
Name = "Child 2C",
268+
SimpleA = new SimpleExample
269+
{
270+
IntegerA = 250,
271+
StringB = "child-2C-stringB",
272+
BoolC = null,
273+
ExampleEnumD = null
274+
}
275+
},
276+
new ItemExample
277+
{
278+
Name = "Child 3C",
279+
SimpleA = new SimpleExample
280+
{
281+
IntegerA = null,
282+
StringB = null,
283+
BoolC = true,
284+
ExampleEnumD = ExampleEnum.ValueA
285+
}
286+
},
266287
}
267288
};
268289

269290
[Theory]
270-
[InlineData("Items.SimpleA.BoolC")]
271-
[InlineData("ItemsCollection.Name")]
272-
[InlineData("ItemsCollection.SimpleA.ExampleEnumD")]
273-
[InlineData("ItemsCollection.SimpleA.IntegerA")]
274-
[InlineData("ItemsList.SimpleA.ExampleEnumD")]
275-
public void Multiple_failures(string expectedMemberName)
291+
[InlineData("Items[1].SimpleA.BoolC")]
292+
[InlineData("ItemsCollection[1].Name")]
293+
[InlineData("ItemsCollection[0].SimpleA.ExampleEnumD")]
294+
[InlineData("ItemsCollection[1].SimpleA.IntegerA")]
295+
[InlineData("ItemsCollection[2].SimpleA.BoolC")]
296+
[InlineData("ItemsCollection[2].SimpleA.ExampleEnumD")]
297+
[InlineData("ItemsCollection[3].SimpleA.IntegerA")]
298+
[InlineData("ItemsCollection[3].SimpleA.StringB")]
299+
[InlineData("ItemsList[1].SimpleA.ExampleEnumD")]
300+
public void Multiple_failures_contains_expected_memberName(string expectedMemberName)
276301
{
277302
var validationResults = new List<ValidationResult>();
278303
var result = _sut.TryValidateObjectRecursive(_multipleFailureModel, validationResults);
279304

280305
Assert.False(result);
281306
Assert.NotEmpty(validationResults);
307+
AssertThereIsAnErrorForMemberName(expectedMemberName, validationResults);
308+
}
309+
310+
private void AssertThereIsAnErrorForMemberName(
311+
string expectedMemberName,
312+
IEnumerable<ValidationResult> validationResults
313+
)
314+
{
282315
var memberNames = validationResults
283316
.SelectMany(x => x.MemberNames)
284317
.OrderBy(x => x)

0 commit comments

Comments
 (0)