PageRenderTime 39ms CodeModel.GetById 1ms RepoModel.GetById 0ms app.codeStats 0ms

/test/System.Web.Mvc.Test/Test/DefaultModelBinderTest.cs

https://bitbucket.org/mdavid/aspnetwebstack
C# | 1202 lines | 914 code | 176 blank | 112 comment | 0 complexity | 126d762d8d0bfe7c00726505144abf06 MD5 | raw file
  1. using System.Collections.Generic;
  2. using System.ComponentModel;
  3. using System.ComponentModel.DataAnnotations;
  4. using System.Globalization;
  5. using System.IO;
  6. using System.Linq;
  7. using Microsoft.Web.UnitTestUtil;
  8. using Moq;
  9. using Moq.Protected;
  10. using Xunit;
  11. using Assert = Microsoft.TestCommon.AssertEx;
  12. namespace System.Web.Mvc.Test
  13. {
  14. [CLSCompliant(false)]
  15. public class DefaultModelBinderTest
  16. {
  17. [Fact]
  18. public void BindComplexElementalModelReturnsIfOnModelUpdatingReturnsFalse()
  19. {
  20. // Arrange
  21. ControllerContext controllerContext = new Mock<ControllerContext>().Object;
  22. MyModel model = new MyModel() { ReadWriteProperty = 3 };
  23. ModelBindingContext bindingContext = new ModelBindingContext()
  24. {
  25. ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, model.GetType()),
  26. };
  27. Mock<DefaultModelBinderHelper> mockHelper = new Mock<DefaultModelBinderHelper>() { CallBase = true };
  28. mockHelper.Setup(b => b.PublicOnModelUpdating(controllerContext, It.IsAny<ModelBindingContext>())).Returns(false);
  29. DefaultModelBinderHelper helper = mockHelper.Object;
  30. // Act
  31. helper.BindComplexElementalModel(controllerContext, bindingContext, model);
  32. // Assert
  33. Assert.Equal(3, model.ReadWriteProperty);
  34. mockHelper.Verify();
  35. mockHelper.Verify(b => b.PublicGetModelProperties(controllerContext, It.IsAny<ModelBindingContext>()), Times.Never());
  36. mockHelper.Verify(b => b.PublicBindProperty(controllerContext, It.IsAny<ModelBindingContext>(), It.IsAny<PropertyDescriptor>()), Times.Never());
  37. }
  38. [Fact]
  39. public void BindComplexModelCanBindArrays()
  40. {
  41. // Arrange
  42. ControllerContext controllerContext = new Mock<ControllerContext>().Object;
  43. ModelBindingContext bindingContext = new ModelBindingContext()
  44. {
  45. ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, typeof(int[])),
  46. ModelName = "foo",
  47. PropertyFilter = _ => false,
  48. ValueProvider = new SimpleValueProvider()
  49. {
  50. { "foo[0]", null },
  51. { "foo[1]", null },
  52. { "foo[2]", null }
  53. }
  54. };
  55. Mock<IModelBinder> mockInnerBinder = new Mock<IModelBinder>();
  56. mockInnerBinder
  57. .Setup(b => b.BindModel(It.IsAny<ControllerContext>(), It.IsAny<ModelBindingContext>()))
  58. .Returns(
  59. delegate(ControllerContext cc, ModelBindingContext bc)
  60. {
  61. Assert.Equal(controllerContext, cc);
  62. Assert.Equal(typeof(int), bc.ModelType);
  63. Assert.Equal(bindingContext.ModelState, bc.ModelState);
  64. Assert.Equal(bindingContext.PropertyFilter, bc.PropertyFilter);
  65. Assert.Equal(bindingContext.ValueProvider, bc.ValueProvider);
  66. return Int32.Parse(bc.ModelName.Substring(4, 1), CultureInfo.InvariantCulture);
  67. });
  68. DefaultModelBinder binder = new DefaultModelBinder()
  69. {
  70. Binders = new ModelBinderDictionary()
  71. {
  72. { typeof(int), mockInnerBinder.Object }
  73. }
  74. };
  75. // Act
  76. object newModel = binder.BindComplexModel(controllerContext, bindingContext);
  77. // Assert
  78. var newIntArray = Assert.IsType<int[]>(newModel);
  79. Assert.Equal(new[] { 0, 1, 2 }, newIntArray);
  80. }
  81. [Fact]
  82. public void BindComplexModelCanBindCollections()
  83. {
  84. // Arrange
  85. ControllerContext controllerContext = new Mock<ControllerContext>().Object;
  86. ModelBindingContext bindingContext = new ModelBindingContext()
  87. {
  88. ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, typeof(IList<int>)),
  89. ModelName = "foo",
  90. PropertyFilter = _ => false,
  91. ValueProvider = new SimpleValueProvider()
  92. {
  93. { "foo[0]", null },
  94. { "foo[1]", null },
  95. { "foo[2]", null }
  96. }
  97. };
  98. Mock<IModelBinder> mockInnerBinder = new Mock<IModelBinder>();
  99. mockInnerBinder
  100. .Setup(b => b.BindModel(It.IsAny<ControllerContext>(), It.IsAny<ModelBindingContext>()))
  101. .Returns(
  102. delegate(ControllerContext cc, ModelBindingContext bc)
  103. {
  104. Assert.Equal(controllerContext, cc);
  105. Assert.Equal(typeof(int), bc.ModelType);
  106. Assert.Equal(bindingContext.ModelState, bc.ModelState);
  107. Assert.Equal(bindingContext.PropertyFilter, bc.PropertyFilter);
  108. Assert.Equal(bindingContext.ValueProvider, bc.ValueProvider);
  109. return Int32.Parse(bc.ModelName.Substring(4, 1), CultureInfo.InvariantCulture);
  110. });
  111. DefaultModelBinder binder = new DefaultModelBinder()
  112. {
  113. Binders = new ModelBinderDictionary()
  114. {
  115. { typeof(int), mockInnerBinder.Object }
  116. }
  117. };
  118. // Act
  119. object newModel = binder.BindComplexModel(controllerContext, bindingContext);
  120. // Assert
  121. var modelAsList = Assert.IsAssignableFrom<IList<int>>(newModel);
  122. Assert.Equal(new[] { 0, 1, 2 }, modelAsList.ToArray());
  123. }
  124. [Fact]
  125. public void BindComplexModelCanBindDictionariesWithDotsNotation()
  126. {
  127. // Arrange
  128. ControllerContext controllerContext = new Mock<ControllerContext>().Object;
  129. ModelBindingContext bindingContext = new ModelBindingContext()
  130. {
  131. ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, typeof(IDictionary<string, CountryState>)),
  132. ModelName = "countries",
  133. PropertyFilter = _ => true,
  134. ValueProvider = new DictionaryValueProvider<object>(new Dictionary<string, object>()
  135. {
  136. { "countries.CA.Name", "Canada" },
  137. { "countries.CA.States[0]", "Québec" },
  138. { "countries.CA.States[1]", "British Columbia" },
  139. { "countries.US.Name", "United States" },
  140. { "countries.US.States[0]", "Washington" },
  141. { "countries.US.States[1]", "Oregon" }
  142. }, CultureInfo.CurrentCulture)
  143. };
  144. DefaultModelBinder binder = new DefaultModelBinder();
  145. // Act
  146. object newModel = binder.BindComplexModel(controllerContext, bindingContext);
  147. // Assert
  148. var modelAsDictionary = Assert.IsAssignableFrom<IDictionary<string, CountryState>>(newModel);
  149. Assert.Equal(2, modelAsDictionary.Count);
  150. Assert.Equal("Canada", modelAsDictionary["CA"].Name);
  151. Assert.Equal("United States", modelAsDictionary["US"].Name);
  152. Assert.Equal(2, modelAsDictionary["CA"].States.Count());
  153. Assert.True(modelAsDictionary["CA"].States.Contains("Québec"));
  154. Assert.True(modelAsDictionary["CA"].States.Contains("British Columbia"));
  155. Assert.Equal(2, modelAsDictionary["US"].States.Count());
  156. Assert.True(modelAsDictionary["US"].States.Contains("Washington"));
  157. Assert.True(modelAsDictionary["US"].States.Contains("Oregon"));
  158. }
  159. [Fact]
  160. public void BindComplexModelCanBindDictionariesWithBracketsNotation()
  161. {
  162. // Arrange
  163. ControllerContext controllerContext = new Mock<ControllerContext>().Object;
  164. ModelBindingContext bindingContext = new ModelBindingContext()
  165. {
  166. ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, typeof(IDictionary<string, CountryState>)),
  167. ModelName = "countries",
  168. PropertyFilter = _ => true,
  169. ValueProvider = new DictionaryValueProvider<object>(new Dictionary<string, object>()
  170. {
  171. { "countries[CA].Name", "Canada" },
  172. { "countries[CA].States[0]", "Québec" },
  173. { "countries[CA].States[1]", "British Columbia" },
  174. { "countries[US].Name", "United States" },
  175. { "countries[US].States[0]", "Washington" },
  176. { "countries[US].States[1]", "Oregon" }
  177. }, CultureInfo.CurrentCulture)
  178. };
  179. DefaultModelBinder binder = new DefaultModelBinder();
  180. // Act
  181. object newModel = binder.BindComplexModel(controllerContext, bindingContext);
  182. // Assert
  183. var modelAsDictionary = Assert.IsAssignableFrom<IDictionary<string, CountryState>>(newModel);
  184. Assert.Equal(2, modelAsDictionary.Count);
  185. Assert.Equal("Canada", modelAsDictionary["CA"].Name);
  186. Assert.Equal("United States", modelAsDictionary["US"].Name);
  187. Assert.Equal(2, modelAsDictionary["CA"].States.Count());
  188. Assert.True(modelAsDictionary["CA"].States.Contains("Québec"));
  189. Assert.True(modelAsDictionary["CA"].States.Contains("British Columbia"));
  190. Assert.Equal(2, modelAsDictionary["US"].States.Count());
  191. Assert.True(modelAsDictionary["US"].States.Contains("Washington"));
  192. Assert.True(modelAsDictionary["US"].States.Contains("Oregon"));
  193. }
  194. [Fact]
  195. public void BindComplexModelCanBindDictionariesWithBracketsAndDotsNotation()
  196. {
  197. // Arrange
  198. ControllerContext controllerContext = new Mock<ControllerContext>().Object;
  199. ModelBindingContext bindingContext = new ModelBindingContext()
  200. {
  201. ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, typeof(IDictionary<string, CountryState>)),
  202. ModelName = "countries",
  203. PropertyFilter = _ => true,
  204. ValueProvider = new DictionaryValueProvider<object>(new Dictionary<string, object>()
  205. {
  206. { "countries[CA].Name", "Canada" },
  207. { "countries[CA].States[0]", "Québec" },
  208. { "countries.CA.States[1]", "British Columbia" },
  209. { "countries.US.Name", "United States" },
  210. { "countries.US.States[0]", "Washington" },
  211. { "countries.US.States[1]", "Oregon" }
  212. }, CultureInfo.CurrentCulture)
  213. };
  214. DefaultModelBinder binder = new DefaultModelBinder();
  215. // Act
  216. object newModel = binder.BindComplexModel(controllerContext, bindingContext);
  217. // Assert
  218. var modelAsDictionary = Assert.IsAssignableFrom<IDictionary<string, CountryState>>(newModel);
  219. Assert.Equal(2, modelAsDictionary.Count);
  220. Assert.Equal("Canada", modelAsDictionary["CA"].Name);
  221. Assert.Equal("United States", modelAsDictionary["US"].Name);
  222. Assert.Equal(1, modelAsDictionary["CA"].States.Count());
  223. Assert.True(modelAsDictionary["CA"].States.Contains("Québec"));
  224. // We do not accept double notation for a same entry, so we can't find that state.
  225. Assert.False(modelAsDictionary["CA"].States.Contains("British Columbia"));
  226. Assert.Equal(2, modelAsDictionary["US"].States.Count());
  227. Assert.True(modelAsDictionary["US"].States.Contains("Washington"));
  228. Assert.True(modelAsDictionary["US"].States.Contains("Oregon"));
  229. }
  230. [Fact]
  231. public void BindComplexModelCanBindDictionaries()
  232. {
  233. // Arrange
  234. ControllerContext controllerContext = new Mock<ControllerContext>().Object;
  235. ModelBindingContext bindingContext = new ModelBindingContext()
  236. {
  237. ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, typeof(IDictionary<int, string>)),
  238. ModelName = "foo",
  239. PropertyFilter = _ => false,
  240. ValueProvider = new SimpleValueProvider()
  241. {
  242. { "foo[0].key", null }, { "foo[0].value", null },
  243. { "foo[1].key", null }, { "foo[1].value", null },
  244. { "foo[2].key", null }, { "foo[2].value", null }
  245. }
  246. };
  247. Mock<IModelBinder> mockIntBinder = new Mock<IModelBinder>();
  248. mockIntBinder
  249. .Setup(b => b.BindModel(It.IsAny<ControllerContext>(), It.IsAny<ModelBindingContext>()))
  250. .Returns(
  251. delegate(ControllerContext cc, ModelBindingContext bc)
  252. {
  253. Assert.Equal(controllerContext, cc);
  254. Assert.Equal(typeof(int), bc.ModelType);
  255. Assert.Equal(bindingContext.ModelState, bc.ModelState);
  256. Assert.Equal(new ModelBindingContext().PropertyFilter, bc.PropertyFilter);
  257. Assert.Equal(bindingContext.ValueProvider, bc.ValueProvider);
  258. return Int32.Parse(bc.ModelName.Substring(4, 1), CultureInfo.InvariantCulture) + 10;
  259. });
  260. Mock<IModelBinder> mockStringBinder = new Mock<IModelBinder>();
  261. mockStringBinder
  262. .Setup(b => b.BindModel(It.IsAny<ControllerContext>(), It.IsAny<ModelBindingContext>()))
  263. .Returns(
  264. delegate(ControllerContext cc, ModelBindingContext bc)
  265. {
  266. Assert.Equal(controllerContext, cc);
  267. Assert.Equal(typeof(string), bc.ModelType);
  268. Assert.Equal(bindingContext.ModelState, bc.ModelState);
  269. Assert.Equal(bindingContext.PropertyFilter, bc.PropertyFilter);
  270. Assert.Equal(bindingContext.ValueProvider, bc.ValueProvider);
  271. return (Int32.Parse(bc.ModelName.Substring(4, 1), CultureInfo.InvariantCulture) + 10) + "Value";
  272. });
  273. DefaultModelBinder binder = new DefaultModelBinder()
  274. {
  275. Binders = new ModelBinderDictionary()
  276. {
  277. { typeof(int), mockIntBinder.Object },
  278. { typeof(string), mockStringBinder.Object }
  279. }
  280. };
  281. // Act
  282. object newModel = binder.BindComplexModel(controllerContext, bindingContext);
  283. // Assert
  284. var modelAsDictionary = Assert.IsAssignableFrom<IDictionary<int, string>>(newModel);
  285. Assert.Equal(3, modelAsDictionary.Count);
  286. Assert.Equal("10Value", modelAsDictionary[10]);
  287. Assert.Equal("11Value", modelAsDictionary[11]);
  288. Assert.Equal("12Value", modelAsDictionary[12]);
  289. }
  290. [Fact]
  291. public void BindComplexModelCanBindObjects()
  292. {
  293. // Arrange
  294. ControllerContext controllerContext = new Mock<ControllerContext>().Object;
  295. ModelWithoutBindAttribute model = new ModelWithoutBindAttribute()
  296. {
  297. Foo = "FooPreValue",
  298. Bar = "BarPreValue",
  299. Baz = "BazPreValue",
  300. };
  301. ModelBindingContext bindingContext = new ModelBindingContext()
  302. {
  303. ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, model.GetType()),
  304. ValueProvider = new SimpleValueProvider() { { "Foo", null }, { "Bar", null } }
  305. };
  306. Mock<IModelBinder> mockInnerBinder = new Mock<IModelBinder>();
  307. mockInnerBinder
  308. .Setup(b => b.BindModel(It.IsAny<ControllerContext>(), It.IsAny<ModelBindingContext>()))
  309. .Returns(
  310. delegate(ControllerContext cc, ModelBindingContext bc)
  311. {
  312. Assert.Equal(controllerContext, cc);
  313. Assert.Equal(bindingContext.ValueProvider, bc.ValueProvider);
  314. return bc.ModelName + "PostValue";
  315. });
  316. DefaultModelBinder binder = new DefaultModelBinder()
  317. {
  318. Binders = new ModelBinderDictionary()
  319. {
  320. { typeof(string), mockInnerBinder.Object }
  321. }
  322. };
  323. // Act
  324. object updatedModel = binder.BindComplexModel(controllerContext, bindingContext);
  325. // Assert
  326. Assert.Same(model, updatedModel);
  327. Assert.Equal("FooPostValue", model.Foo);
  328. Assert.Equal("BarPostValue", model.Bar);
  329. Assert.Equal("BazPreValue", model.Baz);
  330. }
  331. [Fact]
  332. public void BindComplexModelReturnsNullArrayIfNoValuesProvided()
  333. {
  334. // Arrange
  335. ModelBindingContext bindingContext = new ModelBindingContext()
  336. {
  337. ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, typeof(int[])),
  338. ModelName = "foo",
  339. ValueProvider = new SimpleValueProvider() { { "foo", null } }
  340. };
  341. Mock<IModelBinder> mockInnerBinder = new Mock<IModelBinder>();
  342. mockInnerBinder
  343. .Setup(b => b.BindModel(It.IsAny<ControllerContext>(), It.IsAny<ModelBindingContext>()))
  344. .Returns(
  345. delegate(ControllerContext cc, ModelBindingContext bc) { return Int32.Parse(bc.ModelName.Substring(4, 1), CultureInfo.InvariantCulture); });
  346. DefaultModelBinder binder = new DefaultModelBinder()
  347. {
  348. Binders = new ModelBinderDictionary()
  349. {
  350. { typeof(int), mockInnerBinder.Object }
  351. }
  352. };
  353. // Act
  354. object newModel = binder.BindComplexModel(null, bindingContext);
  355. // Assert
  356. Assert.Null(newModel);
  357. }
  358. [Fact]
  359. public void BindComplexModelWhereModelTypeContainsBindAttribute()
  360. {
  361. // Arrange
  362. ControllerContext controllerContext = new Mock<ControllerContext>().Object;
  363. ModelWithBindAttribute model = new ModelWithBindAttribute()
  364. {
  365. Foo = "FooPreValue",
  366. Bar = "BarPreValue",
  367. Baz = "BazPreValue",
  368. };
  369. ModelBindingContext bindingContext = new ModelBindingContext()
  370. {
  371. ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, model.GetType()),
  372. ValueProvider = new SimpleValueProvider() { { "Foo", null }, { "Bar", null } }
  373. };
  374. Mock<IModelBinder> mockInnerBinder = new Mock<IModelBinder>();
  375. mockInnerBinder
  376. .Setup(b => b.BindModel(It.IsAny<ControllerContext>(), It.IsAny<ModelBindingContext>()))
  377. .Returns(
  378. delegate(ControllerContext cc, ModelBindingContext bc)
  379. {
  380. Assert.Equal(controllerContext, cc);
  381. Assert.Equal(bindingContext.ValueProvider, bc.ValueProvider);
  382. return bc.ModelName + "PostValue";
  383. });
  384. DefaultModelBinder binder = new DefaultModelBinder()
  385. {
  386. Binders = new ModelBinderDictionary()
  387. {
  388. { typeof(string), mockInnerBinder.Object }
  389. }
  390. };
  391. // Act
  392. binder.BindComplexModel(controllerContext, bindingContext);
  393. // Assert
  394. Assert.Equal("FooPreValue", model.Foo);
  395. Assert.Equal("BarPostValue", model.Bar);
  396. Assert.Equal("BazPreValue", model.Baz);
  397. }
  398. [Fact]
  399. public void BindComplexModelWhereModelTypeDoesNotContainBindAttribute()
  400. {
  401. // Arrange
  402. ControllerContext controllerContext = new Mock<ControllerContext>().Object;
  403. ModelWithoutBindAttribute model = new ModelWithoutBindAttribute()
  404. {
  405. Foo = "FooPreValue",
  406. Bar = "BarPreValue",
  407. Baz = "BazPreValue",
  408. };
  409. ModelBindingContext bindingContext = new ModelBindingContext()
  410. {
  411. ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, model.GetType()),
  412. ValueProvider = new SimpleValueProvider() { { "Foo", null }, { "Bar", null } }
  413. };
  414. Mock<IModelBinder> mockInnerBinder = new Mock<IModelBinder>();
  415. mockInnerBinder
  416. .Setup(b => b.BindModel(It.IsAny<ControllerContext>(), It.IsAny<ModelBindingContext>()))
  417. .Returns(
  418. delegate(ControllerContext cc, ModelBindingContext bc)
  419. {
  420. Assert.Equal(controllerContext, cc);
  421. Assert.Equal(bindingContext.ValueProvider, bc.ValueProvider);
  422. return bc.ModelName + "PostValue";
  423. });
  424. DefaultModelBinder binder = new DefaultModelBinder()
  425. {
  426. Binders = new ModelBinderDictionary()
  427. {
  428. { typeof(string), mockInnerBinder.Object }
  429. }
  430. };
  431. // Act
  432. binder.BindComplexModel(controllerContext, bindingContext);
  433. // Assert
  434. Assert.Equal("FooPostValue", model.Foo);
  435. Assert.Equal("BarPostValue", model.Bar);
  436. Assert.Equal("BazPreValue", model.Baz);
  437. }
  438. // BindModel tests
  439. [Fact]
  440. public void BindModelCanBindObjects()
  441. {
  442. // Arrange
  443. ControllerContext controllerContext = new Mock<ControllerContext>().Object;
  444. ModelWithoutBindAttribute model = new ModelWithoutBindAttribute()
  445. {
  446. Foo = "FooPreValue",
  447. Bar = "BarPreValue",
  448. Baz = "BazPreValue",
  449. };
  450. ModelBindingContext bindingContext = new ModelBindingContext()
  451. {
  452. ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, model.GetType()),
  453. ValueProvider = new SimpleValueProvider() { { "Foo", null }, { "Bar", null } }
  454. };
  455. Mock<IModelBinder> mockInnerBinder = new Mock<IModelBinder>();
  456. mockInnerBinder
  457. .Setup(b => b.BindModel(It.IsAny<ControllerContext>(), It.IsAny<ModelBindingContext>()))
  458. .Returns(
  459. delegate(ControllerContext cc, ModelBindingContext bc)
  460. {
  461. Assert.Equal(controllerContext, cc);
  462. Assert.Equal(bindingContext.ValueProvider, bc.ValueProvider);
  463. return bc.ModelName + "PostValue";
  464. });
  465. DefaultModelBinder binder = new DefaultModelBinder()
  466. {
  467. Binders = new ModelBinderDictionary()
  468. {
  469. { typeof(string), mockInnerBinder.Object }
  470. }
  471. };
  472. // Act
  473. object updatedModel = binder.BindModel(controllerContext, bindingContext);
  474. // Assert
  475. Assert.Same(model, updatedModel);
  476. Assert.Equal("FooPostValue", model.Foo);
  477. Assert.Equal("BarPostValue", model.Bar);
  478. Assert.Equal("BazPreValue", model.Baz);
  479. }
  480. [Fact]
  481. public void BindModelCanBindSimpleTypes()
  482. {
  483. // Arrange
  484. ModelBindingContext bindingContext = new ModelBindingContext()
  485. {
  486. ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, typeof(int)),
  487. ModelName = "foo",
  488. ValueProvider = new SimpleValueProvider()
  489. {
  490. { "foo", "42" }
  491. }
  492. };
  493. DefaultModelBinder binder = new DefaultModelBinder();
  494. // Act
  495. object updatedModel = binder.BindModel(new ControllerContext(), bindingContext);
  496. // Assert
  497. Assert.Equal(42, updatedModel);
  498. }
  499. [Fact]
  500. public void BindModel_PerformsValidationByDefault()
  501. {
  502. // Arrange
  503. ModelMetadata metadata = new DataAnnotationsModelMetadataProvider().GetMetadataForType(null, typeof(string));
  504. ControllerContext controllerContext = new ControllerContext();
  505. controllerContext.Controller = new SimpleController();
  506. ModelBindingContext bindingContext = new ModelBindingContext()
  507. {
  508. ModelMetadata = metadata,
  509. ModelName = "foo",
  510. ValueProvider = new CustomUnvalidatedValueProvider()
  511. };
  512. DefaultModelBinder binder = new DefaultModelBinder();
  513. // Act
  514. object updatedModel = binder.BindModel(controllerContext, bindingContext);
  515. // Assert
  516. Assert.Equal("fooValidated", updatedModel);
  517. }
  518. [Fact]
  519. public void BindModel_SkipsValidationIfControllerOptsOut()
  520. {
  521. // Arrange
  522. ModelMetadata metadata = new DataAnnotationsModelMetadataProvider().GetMetadataForType(null, typeof(string));
  523. ControllerContext controllerContext = new ControllerContext();
  524. controllerContext.Controller = new SimpleController();
  525. controllerContext.Controller.ValidateRequest = false;
  526. ModelBindingContext bindingContext = new ModelBindingContext()
  527. {
  528. ModelMetadata = metadata,
  529. ModelName = "foo",
  530. ValueProvider = new CustomUnvalidatedValueProvider()
  531. };
  532. DefaultModelBinder binder = new DefaultModelBinder();
  533. // Act
  534. object updatedModel = binder.BindModel(controllerContext, bindingContext);
  535. // Assert
  536. Assert.Equal("fooUnvalidated", updatedModel);
  537. }
  538. [Fact]
  539. public void BindModel_SkipsValidationIfModelOptsOut()
  540. {
  541. // Arrange
  542. ModelMetadata metadata = new DataAnnotationsModelMetadataProvider().GetMetadataForType(null, typeof(string));
  543. metadata.RequestValidationEnabled = false;
  544. ControllerContext controllerContext = new ControllerContext();
  545. controllerContext.Controller = new SimpleController();
  546. ModelBindingContext bindingContext = new ModelBindingContext()
  547. {
  548. ModelMetadata = metadata,
  549. ModelName = "foo",
  550. ValueProvider = new CustomUnvalidatedValueProvider()
  551. };
  552. DefaultModelBinder binder = new DefaultModelBinder();
  553. // Act
  554. object updatedModel = binder.BindModel(controllerContext, bindingContext);
  555. // Assert
  556. Assert.Equal("fooUnvalidated", updatedModel);
  557. }
  558. [Fact]
  559. public void BindModelReturnsNullIfKeyNotFound()
  560. {
  561. // Arrange
  562. ModelBindingContext bindingContext = new ModelBindingContext()
  563. {
  564. ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, typeof(int)),
  565. ModelName = "foo",
  566. ValueProvider = new SimpleValueProvider()
  567. };
  568. DefaultModelBinder binder = new DefaultModelBinder();
  569. // Act
  570. object returnedModel = binder.BindModel(new ControllerContext(), bindingContext);
  571. // Assert
  572. Assert.Null(returnedModel);
  573. }
  574. [Fact]
  575. public void BindModelThrowsIfBindingContextIsNull()
  576. {
  577. // Arrange
  578. DefaultModelBinder binder = new DefaultModelBinder();
  579. // Act & assert
  580. Assert.ThrowsArgumentNull(
  581. delegate { binder.BindModel(new ControllerContext(), null); }, "bindingContext");
  582. }
  583. [Fact]
  584. public void BindModelValuesCanBeOverridden()
  585. {
  586. // Arrange
  587. ModelBindingContext bindingContext = new ModelBindingContext()
  588. {
  589. ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => new ModelWithoutBindAttribute(), typeof(ModelWithoutBindAttribute)),
  590. ModelName = "",
  591. ValueProvider = new SimpleValueProvider()
  592. {
  593. { "foo", "FooPostValue" },
  594. { "bar", "BarPostValue" },
  595. { "baz", "BazPostValue" }
  596. }
  597. };
  598. Mock<DefaultModelBinder> binder = new Mock<DefaultModelBinder> { CallBase = true };
  599. binder.Protected().Setup<object>("GetPropertyValue",
  600. ItExpr.IsAny<ControllerContext>(), ItExpr.IsAny<ModelBindingContext>(),
  601. ItExpr.IsAny<PropertyDescriptor>(), ItExpr.IsAny<IModelBinder>())
  602. .Returns("Hello, world!");
  603. // Act
  604. ModelWithoutBindAttribute model = (ModelWithoutBindAttribute)binder.Object.BindModel(new ControllerContext(), bindingContext);
  605. // Assert
  606. Assert.Equal("Hello, world!", model.Bar);
  607. Assert.Equal("Hello, world!", model.Baz);
  608. Assert.Equal("Hello, world!", model.Foo);
  609. }
  610. [Fact]
  611. public void BindModelWithTypeConversionErrorUpdatesModelStateMessage()
  612. {
  613. // Arrange
  614. ModelBindingContext bindingContext = new ModelBindingContext
  615. {
  616. ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => new PropertyTestingModel(), typeof(PropertyTestingModel)),
  617. ModelName = "",
  618. ValueProvider = new SimpleValueProvider()
  619. {
  620. { "IntReadWrite", "foo" }
  621. },
  622. };
  623. DefaultModelBinder binder = new DefaultModelBinder();
  624. // Act
  625. PropertyTestingModel model = (PropertyTestingModel)binder.BindModel(new ControllerContext(), bindingContext);
  626. // Assert
  627. ModelState modelState = bindingContext.ModelState["IntReadWrite"];
  628. Assert.NotNull(modelState);
  629. Assert.Single(modelState.Errors);
  630. Assert.Equal("The value 'foo' is not valid for IntReadWrite.", modelState.Errors[0].ErrorMessage);
  631. }
  632. [Fact]
  633. public void BindModelWithPrefix()
  634. {
  635. // Arrange
  636. ModelWithoutBindAttribute model = new ModelWithoutBindAttribute()
  637. {
  638. Foo = "FooPreValue",
  639. Bar = "BarPreValue",
  640. Baz = "BazPreValue",
  641. };
  642. ModelBindingContext bindingContext = new ModelBindingContext()
  643. {
  644. ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, model.GetType()),
  645. ModelName = "prefix",
  646. ValueProvider = new SimpleValueProvider()
  647. {
  648. { "prefix.foo", "FooPostValue" },
  649. { "prefix.bar", "BarPostValue" }
  650. }
  651. };
  652. DefaultModelBinder binder = new DefaultModelBinder();
  653. // Act
  654. object updatedModel = binder.BindModel(new ControllerContext(), bindingContext);
  655. // Assert
  656. Assert.Same(model, updatedModel);
  657. Assert.Equal("FooPostValue", model.Foo);
  658. Assert.Equal("BarPostValue", model.Bar);
  659. Assert.Equal("BazPreValue", model.Baz);
  660. }
  661. [Fact]
  662. public void BindModelWithPrefixAndFallback()
  663. {
  664. // Arrange
  665. ModelWithoutBindAttribute model = new ModelWithoutBindAttribute()
  666. {
  667. Foo = "FooPreValue",
  668. Bar = "BarPreValue",
  669. Baz = "BazPreValue",
  670. };
  671. ModelBindingContext bindingContext = new ModelBindingContext()
  672. {
  673. FallbackToEmptyPrefix = true,
  674. ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, model.GetType()),
  675. ModelName = "prefix",
  676. ValueProvider = new SimpleValueProvider()
  677. {
  678. { "foo", "FooPostValue" },
  679. { "bar", "BarPostValue" }
  680. }
  681. };
  682. DefaultModelBinder binder = new DefaultModelBinder();
  683. // Act
  684. object updatedModel = binder.BindModel(new ControllerContext(), bindingContext);
  685. // Assert
  686. Assert.Same(model, updatedModel);
  687. Assert.Equal("FooPostValue", model.Foo);
  688. Assert.Equal("BarPostValue", model.Bar);
  689. Assert.Equal("BazPreValue", model.Baz);
  690. }
  691. [Fact]
  692. public void BindModelWithPrefixReturnsNullIfFallbackNotSpecifiedAndValueProviderContainsNoEntries()
  693. {
  694. // Arrange
  695. ModelWithoutBindAttribute model = new ModelWithoutBindAttribute()
  696. {
  697. Foo = "FooPreValue",
  698. Bar = "BarPreValue",
  699. Baz = "BazPreValue",
  700. };
  701. ModelBindingContext bindingContext = new ModelBindingContext()
  702. {
  703. ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, model.GetType()),
  704. ModelName = "prefix",
  705. ValueProvider = new SimpleValueProvider()
  706. {
  707. { "foo", "FooPostValue" },
  708. { "bar", "BarPostValue" }
  709. }
  710. };
  711. DefaultModelBinder binder = new DefaultModelBinder();
  712. // Act
  713. object updatedModel = binder.BindModel(new ControllerContext(), bindingContext);
  714. // Assert
  715. Assert.Null(updatedModel);
  716. }
  717. [Fact]
  718. public void BindModelReturnsNullIfSimpleTypeNotFound()
  719. {
  720. // DevDiv 216165: ModelBinders should not try and instantiate simple types
  721. // Arrange
  722. ModelBindingContext bindingContext = new ModelBindingContext()
  723. {
  724. ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, typeof(string)),
  725. ModelName = "prefix",
  726. ValueProvider = new SimpleValueProvider()
  727. {
  728. { "prefix.foo", "foo" },
  729. { "prefix.bar", "bar" }
  730. }
  731. };
  732. DefaultModelBinder binder = new DefaultModelBinder();
  733. // Act
  734. object updatedModel = binder.BindModel(new ControllerContext(), bindingContext);
  735. // Assert
  736. Assert.Null(updatedModel);
  737. }
  738. // BindProperty tests
  739. [Fact]
  740. public void BindPropertyCanUpdateComplexReadOnlyProperties()
  741. {
  742. // Arrange
  743. // the Customer type contains a single read-only Address property
  744. Customer model = new Customer();
  745. ModelBindingContext bindingContext = new ModelBindingContext()
  746. {
  747. ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, model.GetType()),
  748. ValueProvider = new SimpleValueProvider() { { "Address", null } }
  749. };
  750. Mock<IModelBinder> mockInnerBinder = new Mock<IModelBinder>();
  751. mockInnerBinder
  752. .Setup(b => b.BindModel(It.IsAny<ControllerContext>(), It.IsAny<ModelBindingContext>()))
  753. .Returns(
  754. delegate(ControllerContext cc, ModelBindingContext bc)
  755. {
  756. Address address = (Address)bc.Model;
  757. address.Street = "1 Microsoft Way";
  758. address.Zip = "98052";
  759. return address;
  760. });
  761. PropertyDescriptor pd = TypeDescriptor.GetProperties(model)["Address"];
  762. DefaultModelBinderHelper helper = new DefaultModelBinderHelper()
  763. {
  764. Binders = new ModelBinderDictionary()
  765. {
  766. { typeof(Address), mockInnerBinder.Object }
  767. }
  768. };
  769. // Act
  770. helper.PublicBindProperty(new ControllerContext(), bindingContext, pd);
  771. // Assert
  772. Assert.Equal("1 Microsoft Way", model.Address.Street);
  773. Assert.Equal("98052", model.Address.Zip);
  774. }
  775. [Fact]
  776. public void BindPropertyDoesNothingIfValueProviderContainsNoEntryForProperty()
  777. {
  778. // Arrange
  779. MyModel2 model = new MyModel2() { IntReadWrite = 3 };
  780. ModelBindingContext bindingContext = new ModelBindingContext()
  781. {
  782. ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, model.GetType()),
  783. ValueProvider = new SimpleValueProvider()
  784. };
  785. PropertyDescriptor pd = TypeDescriptor.GetProperties(model)["IntReadWrite"];
  786. DefaultModelBinderHelper helper = new DefaultModelBinderHelper();
  787. // Act
  788. helper.PublicBindProperty(new ControllerContext(), bindingContext, pd);
  789. // Assert
  790. Assert.Equal(3, model.IntReadWrite);
  791. }
  792. [Fact]
  793. public void BindProperty()
  794. {
  795. // Arrange
  796. ControllerContext controllerContext = new Mock<ControllerContext>().Object;
  797. MyModel2 model = new MyModel2() { IntReadWrite = 3 };
  798. ModelBindingContext bindingContext = new ModelBindingContext()
  799. {
  800. ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, model.GetType()),
  801. ValueProvider = new SimpleValueProvider()
  802. {
  803. { "IntReadWrite", "42" }
  804. }
  805. };
  806. PropertyDescriptor pd = TypeDescriptor.GetProperties(model)["IntReadWrite"];
  807. Mock<DefaultModelBinderHelper> mockHelper = new Mock<DefaultModelBinderHelper>() { CallBase = true };
  808. mockHelper.Setup(b => b.PublicOnPropertyValidating(controllerContext, bindingContext, pd, 42)).Returns(true).Verifiable();
  809. mockHelper.Setup(b => b.PublicSetProperty(controllerContext, bindingContext, pd, 42)).Verifiable();
  810. mockHelper.Setup(b => b.PublicOnPropertyValidated(controllerContext, bindingContext, pd, 42)).Verifiable();
  811. DefaultModelBinderHelper helper = mockHelper.Object;
  812. // Act
  813. helper.PublicBindProperty(controllerContext, bindingContext, pd);
  814. // Assert
  815. mockHelper.Verify();
  816. }
  817. [Fact]
  818. public void BindPropertyReturnsIfOnPropertyValidatingReturnsFalse()
  819. {
  820. // Arrange
  821. ControllerContext controllerContext = new Mock<ControllerContext>().Object;
  822. MyModel2 model = new MyModel2() { IntReadWrite = 3 };
  823. ModelBindingContext bindingContext = new ModelBindingContext()
  824. {
  825. ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, model.GetType()),
  826. ValueProvider = new SimpleValueProvider()
  827. {
  828. { "IntReadWrite", "42" }
  829. }
  830. };
  831. PropertyDescriptor pd = TypeDescriptor.GetProperties(model)["IntReadWrite"];
  832. Mock<DefaultModelBinderHelper> mockHelper = new Mock<DefaultModelBinderHelper>() { CallBase = true };
  833. mockHelper.Setup(b => b.PublicOnPropertyValidating(controllerContext, bindingContext, pd, 42)).Returns(false);
  834. DefaultModelBinderHelper helper = mockHelper.Object;
  835. // Act
  836. helper.PublicBindProperty(controllerContext, bindingContext, pd);
  837. // Assert
  838. Assert.Equal(3, model.IntReadWrite);
  839. mockHelper.Verify();
  840. mockHelper.Verify(b => b.PublicSetProperty(controllerContext, bindingContext, pd, 42), Times.Never());
  841. mockHelper.Verify(b => b.PublicOnPropertyValidated(controllerContext, bindingContext, pd, 42), Times.Never());
  842. }
  843. [Fact]
  844. public void BindPropertySetsPropertyToNullIfUserLeftTextEntryFieldBlankForOptionalValue()
  845. {
  846. // Arrange
  847. MyModel2 model = new MyModel2() { NullableIntReadWrite = 8 };
  848. ModelBindingContext bindingContext = new ModelBindingContext()
  849. {
  850. ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, model.GetType()),
  851. ValueProvider = new SimpleValueProvider() { { "NullableIntReadWrite", null } }
  852. };
  853. Mock<IModelBinder> mockInnerBinder = new Mock<IModelBinder>();
  854. mockInnerBinder.Setup(b => b.BindModel(new ControllerContext(), It.IsAny<ModelBindingContext>())).Returns((object)null);
  855. PropertyDescriptor pd = TypeDescriptor.GetProperties(model)["NullableIntReadWrite"];
  856. DefaultModelBinderHelper helper = new DefaultModelBinderHelper()
  857. {
  858. Binders = new ModelBinderDictionary()
  859. {
  860. { typeof(int?), mockInnerBinder.Object }
  861. }
  862. };
  863. // Act
  864. helper.PublicBindProperty(new ControllerContext(), bindingContext, pd);
  865. // Assert
  866. Assert.Empty(bindingContext.ModelState);
  867. Assert.Null(model.NullableIntReadWrite);
  868. }
  869. [Fact]
  870. public void BindPropertyUpdatesPropertyOnFailureIfInnerBinderReturnsNonNullObject()
  871. {
  872. // Arrange
  873. MyModel2 model = new MyModel2() { IntReadWriteNonNegative = 8 };
  874. ModelBindingContext bindingContext = new ModelBindingContext()
  875. {
  876. ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, model.GetType()),
  877. ValueProvider = new SimpleValueProvider() { { "IntReadWriteNonNegative", null } }
  878. };
  879. Mock<IModelBinder> mockInnerBinder = new Mock<IModelBinder>();
  880. mockInnerBinder
  881. .Setup(b => b.BindModel(It.IsAny<ControllerContext>(), It.IsAny<ModelBindingContext>()))
  882. .Returns(
  883. delegate(ControllerContext cc, ModelBindingContext bc)
  884. {
  885. bc.ModelState.AddModelError("IntReadWriteNonNegative", "Some error text.");
  886. return 4;
  887. });
  888. PropertyDescriptor pd = TypeDescriptor.GetProperties(model)["IntReadWriteNonNegative"];
  889. DefaultModelBinderHelper helper = new DefaultModelBinderHelper()
  890. {
  891. Binders = new ModelBinderDictionary()
  892. {
  893. { typeof(int), mockInnerBinder.Object }
  894. }
  895. };
  896. // Act
  897. helper.PublicBindProperty(new ControllerContext(), bindingContext, pd);
  898. // Assert
  899. Assert.Equal(false, bindingContext.ModelState.IsValidField("IntReadWriteNonNegative"));
  900. var error = Assert.Single(bindingContext.ModelState["IntReadWriteNonNegative"].Errors);
  901. Assert.Equal("Some error text.", error.ErrorMessage);
  902. Assert.Equal(4, model.IntReadWriteNonNegative);
  903. }
  904. [Fact]
  905. public void BindPropertyUpdatesPropertyOnSuccess()
  906. {
  907. // Arrange
  908. // Effectively, this is just testing updating a single property named "IntReadWrite"
  909. ControllerContext controllerContext = new Mock<ControllerContext>().Object;
  910. MyModel2 model = new MyModel2() { IntReadWrite = 3 };
  911. ModelBindingContext bindingContext = new ModelBindingContext()
  912. {
  913. ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, model.GetType()),
  914. ModelName = "foo",
  915. ModelState = new ModelStateDictionary() { { "blah", new ModelState() } },
  916. ValueProvider = new SimpleValueProvider() { { "foo.IntReadWrite", null } }
  917. };
  918. Mock<IModelBinder> mockInnerBinder = new Mock<IModelBinder>();
  919. mockInnerBinder
  920. .Setup(b => b.BindModel(It.IsAny<ControllerContext>(), It.IsAny<ModelBindingContext>()))
  921. .Returns(
  922. delegate(ControllerContext cc, ModelBindingContext bc)
  923. {
  924. Assert.Equal(controllerContext, cc);
  925. Assert.Equal(3, bc.Model);
  926. Assert.Equal(typeof(int), bc.ModelType);
  927. Assert.Equal("foo.IntReadWrite", bc.ModelName);
  928. Assert.Equal(new ModelBindingContext().PropertyFilter, bc.PropertyFilter);
  929. Assert.Equal(bindingContext.ModelState, bc.ModelState);
  930. Assert.Equal(bindingContext.ValueProvider, bc.ValueProvider);
  931. return 4;
  932. });
  933. PropertyDescriptor pd = TypeDescriptor.GetProperties(model)["IntReadWrite"];
  934. DefaultModelBinderHelper helper = new DefaultModelBinderHelper()
  935. {
  936. Binders = new ModelBinderDictionary()
  937. {
  938. { typeof(int), mockInnerBinder.Object }
  939. }
  940. };
  941. // Act
  942. helper.PublicBindProperty(controllerContext, bindingContext, pd);
  943. // Assert
  944. Assert.Equal(4, model.IntReadWrite);
  945. }
  946. // BindSimpleModel tests
  947. [Fact]
  948. public void BindSimpleModelCanReturnArrayTypes()
  949. {
  950. // Arrange
  951. ValueProviderResult result = new ValueProviderResult(42, null, null);
  952. ModelBindingContext bindingContext = new ModelBindingContext()
  953. {
  954. ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, typeof(int[])),
  955. ModelName = "foo",
  956. };
  957. DefaultModelBinder binder = new DefaultModelBinder();
  958. // Act
  959. object returnedValue = binder.BindSimpleModel(null, bindingContext, result);
  960. // Assert
  961. var returnedValueAsIntArray = Assert.IsType<int[]>(returnedValue);
  962. Assert.Single(returnedValueAsIntArray);
  963. Assert.Equal(42, returnedValueAsIntArray[0]);
  964. }
  965. [Fact]
  966. public void BindSimpleModelCanReturnCollectionTypes()
  967. {
  968. // Arrange
  969. ValueProviderResult result = new ValueProviderResult(new string[] { "42", "82" }, null, null);
  970. ModelBindingContext bindingContext = new ModelBindingContext()
  971. {
  972. ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, typeof(IEnumerable<int>)),
  973. ModelName = "foo",
  974. };
  975. DefaultModelBinder binder = new DefaultModelBinder();
  976. // Act
  977. object returnedValue = binder.BindSimpleModel(null, bindingContext, result);
  978. // Assert
  979. var returnedValueAsList = Assert.IsAssignableFrom<IEnumerable<int>>(returnedValue).ToList();
  980. Assert.Equal(2, returnedValueAsList.Count);
  981. Assert.Equal(42, returnedValueAsList[0]);
  982. Assert.Equal(82, returnedValueAsList[1]);
  983. }
  984. [Fact]
  985. public void BindSimpleModelCanReturnElementalTypes()
  986. {
  987. // Arrange
  988. ValueProviderResult result = new ValueProviderResult("42", null, null);
  989. ModelBindingContext bindingContext = new ModelBindingContext()
  990. {
  991. ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, typeof(int)),
  992. ModelName = "foo",
  993. };
  994. DefaultModelBinder binder = new DefaultModelBinder();
  995. // Act
  996. object returnedValue = binder.BindSimpleModel(null, bindingContext, result);
  997. // Assert
  998. Assert.Equal(42, returnedValue);
  999. }
  1000. [Fact]
  1001. public void BindSimpleModelCanReturnStrings()
  1002. {
  1003. // Arrange
  1004. ValueProviderResult result = new ValueProviderResult(new object[] { "42" }, null, null);
  1005. ModelBindingContext bindingContext = new ModelBindingContext()
  1006. {
  1007. ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, typeof(string)),
  1008. ModelName = "foo",
  1009. };
  1010. DefaultModelBinder binder = new DefaultModelBinder();
  1011. // Act
  1012. object returnedValue = binder.BindSimpleModel(null, bindingContext, result);
  1013. // Assert
  1014. Assert.Equal("42", returnedValue);
  1015. }
  1016. [Fact]
  1017. public void BindSimpleModelChecksValueProviderResultRawValueType()
  1018. {
  1019. // Arrange
  1020. ValueProviderResult result = new ValueProviderResult(new MemoryStream(), null, null);
  1021. ModelBindingContext bindingContext = new ModelBindingContext()
  1022. {
  1023. ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, typeof(Stream)),
  1024. ModelName = "foo",
  1025. };
  1026. DefaultModelBinder