1
1
// Licensed to the .NET Foundation under one or more agreements.
2
2
// The .NET Foundation licenses this file to you under the MIT license.
3
3
4
+ using System . ComponentModel ;
5
+ using System . Diagnostics . CodeAnalysis ;
6
+ using System . Globalization ;
4
7
using System . Text ;
5
8
using Microsoft . AspNetCore . Http ;
6
9
using Microsoft . AspNetCore . Mvc . Abstractions ;
@@ -699,6 +702,94 @@ public async Task BindParameter_FromFormData_BindsCorrectly(Dictionary<string, S
699
702
Assert . Equal ( new [ ] { "line 1" , "line 2" } , entry . RawValue ) ;
700
703
}
701
704
705
+ [ Fact ]
706
+ public async Task BindParameter_PrefersTypeConverter_OverTryParse ( )
707
+ {
708
+ // Arrange
709
+ var parameterBinder = ModelBindingTestHelper . GetParameterBinder ( ) ;
710
+ var parameter = new ParameterDescriptor ( )
711
+ {
712
+ Name = "Parameter1" ,
713
+ BindingInfo = new BindingInfo ( ) ,
714
+ ParameterType = typeof ( SampleModel )
715
+ } ;
716
+
717
+ var testContext = ModelBindingTestHelper . GetTestContext ( request =>
718
+ {
719
+ request . QueryString = QueryString . Create ( "Parameter1" , "someValue" ) ;
720
+ } ) ;
721
+
722
+ var modelState = testContext . ModelState ;
723
+
724
+ // Act
725
+ var modelBindingResult = await parameterBinder . BindModelAsync ( parameter , testContext ) ;
726
+
727
+ // Assert
728
+
729
+ // ModelBindingResult
730
+ Assert . True ( modelBindingResult . IsModelSet ) ;
731
+
732
+ // Model
733
+ var model = Assert . IsType < SampleModel > ( modelBindingResult . Model ) ;
734
+ Assert . Equal ( "someValue" , model . Value ) ;
735
+ Assert . Equal ( "Converter" , model . Source ) ;
736
+
737
+ // ModelState
738
+ Assert . True ( modelState . IsValid ) ;
739
+
740
+ Assert . Single ( modelState . Keys ) ;
741
+ var key = Assert . Single ( modelState . Keys ) ;
742
+ Assert . Equal ( "Parameter1" , key ) ;
743
+ Assert . Equal ( "someValue" , modelState [ key ] . AttemptedValue ) ;
744
+ Assert . Equal ( "someValue" , modelState [ key ] . RawValue ) ;
745
+ Assert . Empty ( modelState [ key ] . Errors ) ;
746
+ Assert . Equal ( ModelValidationState . Valid , modelState [ key ] . ValidationState ) ;
747
+ }
748
+
749
+ [ Fact ]
750
+ public async Task BindParameter_BindsUsingTryParse ( )
751
+ {
752
+ // Arrange
753
+ var parameterBinder = ModelBindingTestHelper . GetParameterBinder ( ) ;
754
+ var parameter = new ParameterDescriptor ( )
755
+ {
756
+ Name = "Parameter1" ,
757
+ BindingInfo = new BindingInfo ( ) ,
758
+ ParameterType = typeof ( SampleTryParsableModel )
759
+ } ;
760
+
761
+ var testContext = ModelBindingTestHelper . GetTestContext ( request =>
762
+ {
763
+ request . QueryString = QueryString . Create ( "Parameter1" , "someValue" ) ;
764
+ } ) ;
765
+
766
+ var modelState = testContext . ModelState ;
767
+
768
+ // Act
769
+ var modelBindingResult = await parameterBinder . BindModelAsync ( parameter , testContext ) ;
770
+
771
+ // Assert
772
+
773
+ // ModelBindingResult
774
+ Assert . True ( modelBindingResult . IsModelSet ) ;
775
+
776
+ // Model
777
+ var model = Assert . IsType < SampleTryParsableModel > ( modelBindingResult . Model ) ;
778
+ Assert . Equal ( "someValue" , model . Value ) ;
779
+ Assert . Equal ( "TryParse" , model . Source ) ;
780
+
781
+ // ModelState
782
+ Assert . True ( modelState . IsValid ) ;
783
+
784
+ Assert . Single ( modelState . Keys ) ;
785
+ var key = Assert . Single ( modelState . Keys ) ;
786
+ Assert . Equal ( "Parameter1" , key ) ;
787
+ Assert . Equal ( "someValue" , modelState [ key ] . AttemptedValue ) ;
788
+ Assert . Equal ( "someValue" , modelState [ key ] . RawValue ) ;
789
+ Assert . Empty ( modelState [ key ] . Errors ) ;
790
+ Assert . Equal ( ModelValidationState . Valid , modelState [ key ] . ValidationState ) ;
791
+ }
792
+
702
793
private class Person
703
794
{
704
795
public Address Address { get ; set ; }
@@ -712,4 +803,47 @@ private class Address
712
803
713
804
public int Zip { get ; set ; }
714
805
}
806
+
807
+ [ TypeConverter ( typeof ( SampleModelTypeConverter ) ) ]
808
+ private class SampleModel
809
+ {
810
+ public string Value { get ; set ; }
811
+ public string Source { get ; set ; }
812
+
813
+ public static bool TryParse ( [ NotNullWhen ( true ) ] string s , [ MaybeNullWhen ( false ) ] out SampleModel result )
814
+ {
815
+ result = new SampleModel ( ) { Value = s , Source = "TryParse" } ;
816
+ return true ;
817
+ }
818
+ }
819
+
820
+ private class SampleTryParsableModel
821
+ {
822
+ public string Value { get ; set ; }
823
+ public string Source { get ; set ; }
824
+
825
+ public static bool TryParse ( [ NotNullWhen ( true ) ] string s , [ MaybeNullWhen ( false ) ] out SampleTryParsableModel result )
826
+ {
827
+ result = new SampleTryParsableModel ( ) { Value = s , Source = "TryParse" } ;
828
+ return true ;
829
+ }
830
+ }
831
+
832
+ private class SampleModelTypeConverter : TypeConverter
833
+ {
834
+ public override bool CanConvertFrom ( ITypeDescriptorContext context , Type sourceType )
835
+ {
836
+ return sourceType == typeof ( string ) || base . CanConvertFrom ( context , sourceType ) ;
837
+ }
838
+
839
+ public override object ConvertFrom ( ITypeDescriptorContext context , CultureInfo culture , object value )
840
+ {
841
+ if ( value is string s )
842
+ {
843
+ return new SampleModel ( ) { Value = s , Source = "Converter" } ;
844
+ }
845
+
846
+ return base . ConvertFrom ( context , culture , value ) ;
847
+ }
848
+ }
715
849
}
0 commit comments