Last part of #3676
Uses the correct IEnumerable<> in validation strategies
This commit is contained in:
parent
8a310b35a4
commit
ee6ef3f25f
|
|
@ -4,6 +4,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
{
|
{
|
||||||
|
|
@ -36,6 +37,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public class DefaultCollectionValidationStrategy : IValidationStrategy
|
public class DefaultCollectionValidationStrategy : IValidationStrategy
|
||||||
{
|
{
|
||||||
|
private static readonly MethodInfo _getEnumerator = typeof(DefaultCollectionValidationStrategy)
|
||||||
|
.GetMethod(nameof(GetEnumerator), BindingFlags.Static | BindingFlags.NonPublic);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets an instance of <see cref="DefaultCollectionValidationStrategy"/>.
|
/// Gets an instance of <see cref="DefaultCollectionValidationStrategy"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -51,14 +55,26 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
string key,
|
string key,
|
||||||
object model)
|
object model)
|
||||||
{
|
{
|
||||||
return new Enumerator(metadata.ElementMetadata, key, (IEnumerable)model);
|
var enumerator = GetEnumeratorForElementType(metadata, model);
|
||||||
|
return new Enumerator(metadata.ElementMetadata, key, enumerator);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerator GetEnumeratorForElementType(ModelMetadata metadata, object model)
|
||||||
|
{
|
||||||
|
var getEnumeratorMethod = _getEnumerator.MakeGenericMethod(metadata.ElementType);
|
||||||
|
return (IEnumerator)getEnumeratorMethod.Invoke(null, new object[] { model });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called via reflection.
|
||||||
|
private static IEnumerator GetEnumerator<T>(object model)
|
||||||
|
{
|
||||||
|
return (model as IEnumerable<T>)?.GetEnumerator() ?? ((IEnumerable)model).GetEnumerator();
|
||||||
}
|
}
|
||||||
|
|
||||||
private class Enumerator : IEnumerator<ValidationEntry>
|
private class Enumerator : IEnumerator<ValidationEntry>
|
||||||
{
|
{
|
||||||
private readonly string _key;
|
private readonly string _key;
|
||||||
private readonly ModelMetadata _metadata;
|
private readonly ModelMetadata _metadata;
|
||||||
private readonly IEnumerable _model;
|
|
||||||
private readonly IEnumerator _enumerator;
|
private readonly IEnumerator _enumerator;
|
||||||
|
|
||||||
private ValidationEntry _entry;
|
private ValidationEntry _entry;
|
||||||
|
|
@ -67,13 +83,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
public Enumerator(
|
public Enumerator(
|
||||||
ModelMetadata metadata,
|
ModelMetadata metadata,
|
||||||
string key,
|
string key,
|
||||||
IEnumerable model)
|
IEnumerator enumerator)
|
||||||
{
|
{
|
||||||
_metadata = metadata;
|
_metadata = metadata;
|
||||||
_key = key;
|
_key = key;
|
||||||
_model = model;
|
_enumerator = enumerator;
|
||||||
|
|
||||||
_enumerator = _model.GetEnumerator();
|
|
||||||
|
|
||||||
_index = -1;
|
_index = -1;
|
||||||
}
|
}
|
||||||
|
|
@ -116,7 +130,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
|
|
||||||
public void Reset()
|
public void Reset()
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
_enumerator.Reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
string key,
|
string key,
|
||||||
object model)
|
object model)
|
||||||
{
|
{
|
||||||
return new Enumerator(metadata.ElementMetadata, key, ElementKeys, (IEnumerable)model);
|
var enumerator = DefaultCollectionValidationStrategy.GetEnumeratorForElementType(metadata, model);
|
||||||
|
return new Enumerator(metadata.ElementMetadata, key, ElementKeys, enumerator);
|
||||||
}
|
}
|
||||||
|
|
||||||
private class Enumerator : IEnumerator<ValidationEntry>
|
private class Enumerator : IEnumerator<ValidationEntry>
|
||||||
|
|
@ -70,13 +71,13 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
ModelMetadata metadata,
|
ModelMetadata metadata,
|
||||||
string key,
|
string key,
|
||||||
IEnumerable<string> elementKeys,
|
IEnumerable<string> elementKeys,
|
||||||
IEnumerable model)
|
IEnumerator enumerator)
|
||||||
{
|
{
|
||||||
_metadata = metadata;
|
_metadata = metadata;
|
||||||
_key = key;
|
_key = key;
|
||||||
|
|
||||||
_keyEnumerator = elementKeys.GetEnumerator();
|
_keyEnumerator = elementKeys.GetEnumerator();
|
||||||
_enumerator = model.GetEnumerator();
|
_enumerator = enumerator;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ValidationEntry Current
|
public ValidationEntry Current
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
@ -84,6 +86,62 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void EnumerateElements_TwoEnumerableImplemenations()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var model = new TwiceEnumerable(new int[] { 2, 3, 5 });
|
||||||
|
|
||||||
|
var metadata = TestModelMetadataProvider.CreateDefaultProvider().GetMetadataForType(typeof(TwiceEnumerable));
|
||||||
|
var strategy = DefaultCollectionValidationStrategy.Instance;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var enumerator = strategy.GetChildren(metadata, "prefix", model);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Collection(
|
||||||
|
BufferEntries(enumerator).OrderBy(e => e.Key),
|
||||||
|
e =>
|
||||||
|
{
|
||||||
|
Assert.Equal("prefix[0]", e.Key);
|
||||||
|
Assert.Equal(2, e.Model);
|
||||||
|
Assert.Same(metadata.ElementMetadata, e.Metadata);
|
||||||
|
},
|
||||||
|
e =>
|
||||||
|
{
|
||||||
|
Assert.Equal("prefix[1]", e.Key);
|
||||||
|
Assert.Equal(3, e.Model);
|
||||||
|
Assert.Same(metadata.ElementMetadata, e.Metadata);
|
||||||
|
},
|
||||||
|
e =>
|
||||||
|
{
|
||||||
|
Assert.Equal("prefix[2]", e.Key);
|
||||||
|
Assert.Equal(5, e.Model);
|
||||||
|
Assert.Same(metadata.ElementMetadata, e.Metadata);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 'int' is chosen by validation because it's declared on the more derived type.
|
||||||
|
private class TwiceEnumerable : List<string>, IEnumerable<int>
|
||||||
|
{
|
||||||
|
private readonly IEnumerable<int> _enumerable;
|
||||||
|
|
||||||
|
public TwiceEnumerable(IEnumerable<int> enumerable)
|
||||||
|
{
|
||||||
|
_enumerable = enumerable;
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator<int> IEnumerable<int>.GetEnumerator()
|
||||||
|
{
|
||||||
|
return _enumerable.GetEnumerator();
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator IEnumerable.GetEnumerator()
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private List<ValidationEntry> BufferEntries(IEnumerator<ValidationEntry> enumerator)
|
private List<ValidationEntry> BufferEntries(IEnumerator<ValidationEntry> enumerator)
|
||||||
{
|
{
|
||||||
var entries = new List<ValidationEntry>();
|
var entries = new List<ValidationEntry>();
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
@ -85,6 +87,41 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void EnumerateElements_TwoEnumerableImplemenations()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var model = new TwiceEnumerable(new int[] { 2, 3, 5 });
|
||||||
|
|
||||||
|
var metadata = TestModelMetadataProvider.CreateDefaultProvider().GetMetadataForType(typeof(TwiceEnumerable));
|
||||||
|
var strategy = new ExplicitIndexCollectionValidationStrategy(new string[] { "zero", "one", "two" });
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var enumerator = strategy.GetChildren(metadata, "prefix", model);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Collection(
|
||||||
|
BufferEntries(enumerator).OrderBy(e => e.Key),
|
||||||
|
e =>
|
||||||
|
{
|
||||||
|
Assert.Equal("prefix[one]", e.Key);
|
||||||
|
Assert.Equal(3, e.Model);
|
||||||
|
Assert.Same(metadata.ElementMetadata, e.Metadata);
|
||||||
|
},
|
||||||
|
e =>
|
||||||
|
{
|
||||||
|
Assert.Equal("prefix[two]", e.Key);
|
||||||
|
Assert.Equal(5, e.Model);
|
||||||
|
Assert.Same(metadata.ElementMetadata, e.Metadata);
|
||||||
|
},
|
||||||
|
e =>
|
||||||
|
{
|
||||||
|
Assert.Equal("prefix[zero]", e.Key);
|
||||||
|
Assert.Equal(2, e.Model);
|
||||||
|
Assert.Same(metadata.ElementMetadata, e.Metadata);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void EnumerateElements_RunOutOfIndices()
|
public void EnumerateElements_RunOutOfIndices()
|
||||||
{
|
{
|
||||||
|
|
@ -145,6 +182,27 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 'int' is chosen by validation because it's declared on the more derived type.
|
||||||
|
private class TwiceEnumerable : List<string>, IEnumerable<int>
|
||||||
|
{
|
||||||
|
private readonly IEnumerable<int> _enumerable;
|
||||||
|
|
||||||
|
public TwiceEnumerable(IEnumerable<int> enumerable)
|
||||||
|
{
|
||||||
|
_enumerable = enumerable;
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator<int> IEnumerable<int>.GetEnumerator()
|
||||||
|
{
|
||||||
|
return _enumerable.GetEnumerator();
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator IEnumerable.GetEnumerator()
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private List<ValidationEntry> BufferEntries(IEnumerator<ValidationEntry> enumerator)
|
private List<ValidationEntry> BufferEntries(IEnumerator<ValidationEntry> enumerator)
|
||||||
{
|
{
|
||||||
var entries = new List<ValidationEntry>();
|
var entries = new List<ValidationEntry>();
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue