Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/mono.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/mcs
diff options
context:
space:
mode:
Diffstat (limited to 'mcs')
-rw-r--r--mcs/ChangeLog4
-rw-r--r--mcs/Makefile12
-rw-r--r--mcs/build/ChangeLog5
-rw-r--r--mcs/build/Makefile6
-rw-r--r--mcs/build/profiles/moonlight.make (renamed from mcs/build/profiles/net_2_1.make)4
-rw-r--r--mcs/build/profiles/moonlight_bootstrap.make (renamed from mcs/build/profiles/net_2_1_bootstrap.make)2
-rw-r--r--mcs/build/profiles/moonlight_raw.make (renamed from mcs/build/profiles/net_2_1_raw.make)4
-rw-r--r--mcs/class/ChangeLog8
-rw-r--r--mcs/class/Makefile9
-rw-r--r--mcs/class/System.ComponentModel.Composition/src/ComponentModel/System/ComponentModel/Composition/Hosting/DirectoryCatalog.cs4
-rw-r--r--mcs/class/System.Core/ChangeLog4
-rw-r--r--mcs/class/System.Core/Makefile2
-rw-r--r--mcs/class/System.Core/moonlight_bootstrap_System.Core.dll.sources1
-rw-r--r--mcs/class/System.Core/moonlight_raw_System.Core.dll.sources (renamed from mcs/class/System.Core/net_2_1_raw_System.Core.dll.sources)0
-rw-r--r--mcs/class/System.Core/net_2_1_bootstrap_System.Core.dll.sources1
-rw-r--r--mcs/class/System.Net/ChangeLog4
-rw-r--r--mcs/class/System.Net/System.Net/ChangeLog6
-rw-r--r--mcs/class/System.Net/System.Net/WebClient_2_1.cs4
-rw-r--r--mcs/class/System.Net/System.Net/WebRequest_2_1.cs9
-rw-r--r--mcs/class/System.Net/moonlight_raw_System.Net.dll.sources (renamed from mcs/class/System.Net/net_2_1_raw_System.Net.dll.sources)0
-rw-r--r--mcs/class/System.Runtime.Serialization/ChangeLog4
-rw-r--r--mcs/class/System.Runtime.Serialization/monotouch_System.Runtime.Serialization.dll.sources2
-rw-r--r--mcs/class/System.Runtime.Serialization/moonlight_raw_System.Runtime.Serialization.dll.sources (renamed from mcs/class/System.Runtime.Serialization/net_2_1_raw_System.Runtime.Serialization.dll.sources)0
-rw-r--r--mcs/class/System.Security/ChangeLog4
-rw-r--r--mcs/class/System.Security/Makefile2
-rw-r--r--mcs/class/System.ServiceModel.Web/ChangeLog6
-rw-r--r--mcs/class/System.ServiceModel.Web/System.Runtime.Serialization.Json/ChangeLog5
-rw-r--r--mcs/class/System.ServiceModel.Web/System.Runtime.Serialization.Json/JavaScriptReader.cs2
-rw-r--r--mcs/class/System.ServiceModel.Web/common_System.ServiceModel.Web.dll.sources5
-rw-r--r--mcs/class/System.ServiceModel.Web/moonlight_raw_System.ServiceModel.Web.dll.sources (renamed from mcs/class/System.ServiceModel.Web/net_2_1_raw_System.ServiceModel.Web.dll.sources)0
-rw-r--r--mcs/class/System.ServiceModel.Web/net_2_0_System.ServiceModel.Web.dll.sources5
-rwxr-xr-xmcs/class/System.ServiceModel/ChangeLog13
-rw-r--r--mcs/class/System.ServiceModel/Dummy_2_1.cs49
-rwxr-xr-xmcs/class/System.ServiceModel/Makefile2
-rwxr-xr-xmcs/class/System.ServiceModel/System.ServiceModel.Configuration/ChangeLog23
-rw-r--r--mcs/class/System.ServiceModel/System.ServiceModel.Configuration/HttpTransportElement.cs64
-rw-r--r--mcs/class/System.ServiceModel/System.ServiceModel.Configuration/HttpsTransportElement.cs26
-rw-r--r--mcs/class/System.ServiceModel/System.ServiceModel.Configuration/IssuedTokenClientElement.cs3
-rw-r--r--mcs/class/System.ServiceModel/System.ServiceModel.Configuration/LocalServiceSecuritySettingsElement.cs24
-rw-r--r--mcs/class/System.ServiceModel/System.ServiceModel.Configuration/MsmqBindingElementBase.cs6
-rw-r--r--mcs/class/System.ServiceModel/System.ServiceModel.Configuration/MsmqElementBase.cs72
-rw-r--r--mcs/class/System.ServiceModel/System.ServiceModel.Configuration/MsmqIntegrationElement.cs27
-rw-r--r--mcs/class/System.ServiceModel/System.ServiceModel.Configuration/MsmqTransportElement.cs34
-rw-r--r--mcs/class/System.ServiceModel/System.ServiceModel.Configuration/NamedPipeConnectionPoolSettingsElement.cs3
-rw-r--r--mcs/class/System.ServiceModel/System.ServiceModel.Configuration/NamedPipeTransportElement.cs44
-rw-r--r--mcs/class/System.ServiceModel/System.ServiceModel.Configuration/ServiceDebugElement.cs4
-rw-r--r--mcs/class/System.ServiceModel/System.ServiceModel.Configuration/StandardBindingReliableSessionElement.cs3
-rw-r--r--mcs/class/System.ServiceModel/System.ServiceModel.Configuration/TcpConnectionPoolSettingsElement.cs6
-rw-r--r--mcs/class/System.ServiceModel/System.ServiceModel.Configuration/TcpTransportElement.cs33
-rw-r--r--mcs/class/System.ServiceModel/System.ServiceModel.Configuration/TransportElement.cs36
-rw-r--r--mcs/class/System.ServiceModel/System.ServiceModel.Description/ChangeLog5
-rw-r--r--mcs/class/System.ServiceModel/System.ServiceModel.Description/ServiceEndpointCollection.cs14
-rwxr-xr-xmcs/class/System.ServiceModel/System.ServiceModel/ChangeLog6
-rw-r--r--mcs/class/System.ServiceModel/System.ServiceModel/ServiceHostBase.cs4
-rw-r--r--mcs/class/System.ServiceModel/monotouch_System.ServiceModel.dll.sources2
-rwxr-xr-xmcs/class/System.ServiceModel/moonlight_raw_System.ServiceModel.dll.sources (renamed from mcs/class/System.ServiceModel/net_2_1_raw_System.ServiceModel.dll.sources)0
-rw-r--r--mcs/class/System.ServiceModel/net_4_0_System.ServiceModel.dll.sources5
-rw-r--r--mcs/class/System.Web.Mvc/ChangeLog5
-rw-r--r--mcs/class/System.Web.Mvc/Makefile1
-rw-r--r--mcs/class/System.Web.Mvc2/ChangeLog5
-rw-r--r--mcs/class/System.Web.Mvc2/GlobalSuppressions.cs26
-rw-r--r--mcs/class/System.Web.Mvc2/Makefile50
-rw-r--r--mcs/class/System.Web.Mvc2/Properties/AssemblyInfo.cs63
-rw-r--r--mcs/class/System.Web.Mvc2/Properties/ChangeLog5
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc.csproj381
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc.dll.sources279
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/AcceptVerbsAttribute.cs71
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ActionDescriptor.cs230
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ActionExecutedContext.cs68
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ActionExecutingContext.cs57
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ActionFilterAttribute.cs34
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ActionMethodDispatcher.cs86
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ActionMethodDispatcherCache.cs27
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ActionMethodSelector.cs125
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ActionMethodSelectorAttribute.cs21
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ActionNameAttribute.cs39
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ActionNameSelectorAttribute.cs21
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ActionResult.cs21
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ActionSelector.cs17
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/Ajax/AjaxExtensions.cs322
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/Ajax/AjaxOptions.cs166
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/Ajax/InsertionMode.cs19
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/AjaxHelper.cs89
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/AjaxHelper`1.cs35
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/AjaxRequestExtensions.cs26
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/AntiForgeryData.cs129
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/AntiForgeryDataSerializer.cs128
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/AreaHelpers.cs43
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/AreaRegistration.cs62
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/AreaRegistrationContext.cs111
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/AssociatedMetadataProvider.cs95
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/AssociatedValidatorProvider.cs63
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/ActionDescriptorCreator.cs18
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/AsyncActionDescriptor.cs40
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/AsyncActionMethodSelector.cs206
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/AsyncControllerActionInvoker.cs282
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/AsyncManager.cs79
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/AsyncResultWrapper.cs265
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/AsyncUtil.cs75
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/AsyncVoid.cs19
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/BeginInvokeDelegate.cs17
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/EndInvokeDelegate.cs17
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/EndInvokeDelegate`1.cs17
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/IAsyncActionInvoker.cs20
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/IAsyncController.cs20
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/IAsyncManagerContainer.cs22
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/OperationCounter.cs62
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/ReflectedAsyncActionDescriptor.cs183
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/ReflectedAsyncControllerDescriptor.cs72
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/SimpleAsyncResult.cs67
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/SingleEntryGate.cs32
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/SynchronizationContextUtil.cs55
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/SynchronousOperationException.cs39
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/Trigger.cs33
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/TriggerListener.cs68
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/AsyncController.cs111
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/AsyncTimeoutAttribute.cs53
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/AuthorizationContext.cs50
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/AuthorizeAttribute.cs135
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/BindAttribute.cs63
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/BuildManagerWrapper.cs43
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ByteArrayModelBinder.cs43
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ChildActionOnlyAttribute.cs30
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ClientDataTypeModelValidatorProvider.cs79
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ContentResult.cs53
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/Controller.cs599
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ControllerActionInvoker.cs333
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ControllerBase.cs116
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ControllerBuilder.cs87
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ControllerContext.cs134
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ControllerDescriptor.cs59
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ControllerDescriptorCache.cs26
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ControllerTypeCache.cs127
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/CustomModelBinderAttribute.cs27
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/DataAnnotationsModelMetadata.cs63
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/DataAnnotationsModelMetadataProvider.cs83
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/DataAnnotationsModelValidator.cs55
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/DataAnnotationsModelValidatorProvider.cs187
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/DataAnnotationsModelValidator`1.cs26
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/DataErrorInfoModelValidatorProvider.cs87
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/DefaultControllerFactory.cs187
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/DefaultModelBinder.cs709
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/DefaultViewLocationCache.cs60
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/DescriptorUtil.cs34
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/DictionaryHelpers.cs52
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/DictionaryValueProvider`1.cs64
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/DynamicTypeGenerator.cs125
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/EmptyModelMetadataProvider.cs23
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/EmptyModelValidatorProvider.cs22
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/EmptyResult.cs29
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/Error.cs84
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ExceptionContext.cs56
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ExpressionHelper.cs123
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ExpressionUtil/BinaryExpressionFingerprint.cs111
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ExpressionUtil/CachedExpressionCompiler.cs86
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ExpressionUtil/CompiledExpressionDelegate`2.cs18
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ExpressionUtil/ConditionalExpressionFingerprint.cs97
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ExpressionUtil/ConstantExpressionFingerprint.cs72
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ExpressionUtil/ExpressionFingerprint.cs174
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ExpressionUtil/ExpressionParser.cs30
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ExpressionUtil/FastTrack`2.cs112
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ExpressionUtil/HashCodeCombiner.cs61
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ExpressionUtil/MemberExpressionFingerprint.cs78
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ExpressionUtil/MethodCallExpressionFingerprint.cs95
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ExpressionUtil/ParameterExpressionFingerprint.cs42
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ExpressionUtil/ParserContext.cs27
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ExpressionUtil/UnaryExpressionFingerprint.cs87
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/FieldValidationMetadata.cs49
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/FileContentResult.cs41
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/FilePathResult.cs39
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/FileResult.cs143
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/FileStreamResult.cs56
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/FilterAttribute.cs35
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/FilterInfo.cs48
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/FormCollection.cs89
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/FormContext.cs84
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/FormMethod.cs18
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/FormValueProvider.cs25
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/FormValueProviderFactory.cs27
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/HandleErrorAttribute.cs119
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/HandleErrorInfo.cs51
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/HiddenInputAttribute.cs24
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/Html/ChildActionExtensions.cs150
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/Html/DefaultDisplayTemplates.cs206
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/Html/DefaultEditorTemplates.cs215
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/Html/DisplayExtensions.cs102
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/Html/DisplayTextExtensions.cs32
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/Html/EditorExtensions.cs103
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/Html/FormExtensions.cs152
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/Html/InputExtensions.cs398
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/Html/LabelExtensions.cs49
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/Html/LinkExtensions.cs116
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/Html/MvcForm.cs69
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/Html/PartialExtensions.cs36
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/Html/RenderPartialExtensions.cs35
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/Html/SelectExtensions.cs256
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/Html/TemplateHelpers.cs277
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/Html/TextAreaExtensions.cs172
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/Html/ValidationExtensions.cs304
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/HtmlHelper.cs361
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/HtmlHelper`1.cs35
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/HttpAntiForgeryException.cs37
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/HttpDeleteAttribute.cs27
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/HttpFileCollectionValueProvider.cs52
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/HttpFileCollectionValueProviderFactory.cs27
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/HttpGetAttribute.cs27
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/HttpHandlerUtil.cs87
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/HttpPostAttribute.cs27
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/HttpPostedFileBaseModelBinder.cs48
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/HttpPutAttribute.cs27
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/HttpRequestExtensions.cs56
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/HttpUnauthorizedResult.cs30
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/HttpVerbs.cs24
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/IActionFilter.cs19
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/IActionInvoker.cs18
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/IAuthorizationFilter.cs18
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/IBuildManager.cs26
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/IController.cs19
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/IControllerFactory.cs20
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/IExceptionFilter.cs18
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/IModelBinder.cs18
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/IResultFilter.cs19
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/IRouteWithArea.cs21
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ITempDataProvider.cs20
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/IValueProvider.cs20
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/IView.cs19
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/IViewDataContainer.cs21
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/IViewEngine.cs20
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/IViewLocationCache.cs20
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/InputType.cs21
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/JavaScriptResult.cs36
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/JsonRequestBehavior.cs18
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/JsonResult.cs72
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/LinqBinaryModelBinder.cs26
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelBinderAttribute.cs54
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelBinderDictionary.cs148
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelBinders.cs77
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelBindingContext.cs125
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelClientValidationRangeRule.cs22
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelClientValidationRegexRule.cs21
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelClientValidationRequiredRule.cs20
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelClientValidationRule.cs43
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelClientValidationStringLengthRule.cs22
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelError.cs46
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelErrorCollection.cs28
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelMetadata.cs388
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelMetadataProvider.cs23
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelMetadataProviders.cs26
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelState.cs31
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelStateDictionary.cs170
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelValidationResult.cs40
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelValidator.cs83
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelValidatorProvider.cs19
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelValidatorProviderCollection.cs47
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelValidatorProviders.cs29
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/MultiSelectList.cs126
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/MvcHandler.cs212
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/MvcHtmlString.cs101
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/MvcHttpHandler.cs101
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/MvcRouteHandler.cs27
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/NameValueCollectionExtensions.cs39
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/NameValueCollectionValueProvider.cs68
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/NoAsyncTimeoutAttribute.cs24
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/NonActionAttribute.cs22
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/NullViewLocationCache.cs30
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/OutputCacheAttribute.cs146
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ParameterBindingInfo.cs43
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ParameterDescriptor.cs69
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ParameterInfoUtil.cs42
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/PartialViewResult.cs37
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/PathHelpers.cs97
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/QueryStringValueProvider.cs27
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/QueryStringValueProviderFactory.cs27
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/RangeAttributeAdapter.cs26
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ReaderWriterCache`2.cs72
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/RedirectResult.cs52
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/RedirectToRouteResult.cs71
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ReflectedActionDescriptor.cs131
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ReflectedControllerDescriptor.cs91
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ReflectedParameterBindingInfo.cs73
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ReflectedParameterDescriptor.cs90
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/RegularExpressionAttributeAdapter.cs26
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/RequireHttpsAttribute.cs47
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/RequiredAttributeAdapter.cs26
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/Resources/MvcResources.Designer.cs797
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/Resources/MvcResources.resx367
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ResultExecutedContext.cs57
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ResultExecutingContext.cs45
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/RouteCollectionExtensions.cs181
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/RouteDataValueProvider.cs26
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/RouteDataValueProviderFactory.cs27
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/RouteValuesHelpers.cs61
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/SelectList.cs46
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/SelectListItem.cs32
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/SessionStateTempDataProvider.cs65
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/StringLengthAttributeAdapter.cs26
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/TagBuilder.cs214
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/TagRenderMode.cs20
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/TempDataDictionary.cs219
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/TemplateInfo.cs71
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/TryGetValueDelegate.cs17
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/TypeCacheSerializer.cs128
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/TypeCacheUtil.cs103
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/TypeDescriptorHelper.cs282
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/TypeHelpers.cs138
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/UrlHelper.cs209
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/UrlParameter.cs27
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ValidateAntiForgeryTokenAttribute.cs94
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ValidateInputAttribute.cs40
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ValueProviderCollection.cs54
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ValueProviderDictionary.cs208
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ValueProviderFactories.cs32
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ValueProviderFactory.cs19
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ValueProviderFactoryCollection.cs53
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ValueProviderResult.cs147
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ValueProviderUtil.cs63
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewContext.cs153
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewDataDictionary.cs356
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewDataDictionary`1.cs66
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewDataInfo.cs55
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewEngineCollection.cs95
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewEngineResult.cs54
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewEngines.cs27
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewMasterPage.cs77
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewMasterPage`1.cs53
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewPage.cs378
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewPageControlBuilder.cs36
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewPage`1.cs62
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewResult.cs47
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewResultBase.cs103
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewTemplateUserControl.cs15
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewTemplateUserControl`1.cs19
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewType.cs32
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewTypeControlBuilder.cs39
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewTypeParserFilter.cs156
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewUserControl.cs200
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewUserControlControlBuilder.cs36
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewUserControl`1.cs62
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/VirtualPathProviderViewEngine.cs266
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/WebFormView.cs109
-rw-r--r--mcs/class/System.Web.Mvc2/System.Web.Mvc/WebFormViewEngine.cs98
-rw-r--r--mcs/class/System.XML/ChangeLog5
-rw-r--r--mcs/class/System.XML/Makefile2
-rw-r--r--mcs/class/System.XML/System.Xml.Schema/ChangeLog5
-rw-r--r--mcs/class/System.XML/System.Xml.Schema/XmlSchemaComplexType.cs13
-rw-r--r--mcs/class/System.XML/Test/System.Xml.Schema/ChangeLog4
-rw-r--r--mcs/class/System.XML/Test/System.Xml.Schema/XmlSchemaValidatorTests.cs7
-rw-r--r--mcs/class/System.XML/Test/XmlFiles/xsd/584664a.xml4
-rw-r--r--mcs/class/System.XML/Test/XmlFiles/xsd/584664a.xsd19
-rw-r--r--mcs/class/System.XML/Test/XmlFiles/xsd/584664b.xml4
-rw-r--r--mcs/class/System.XML/Test/XmlFiles/xsd/584664b.xsd18
-rw-r--r--mcs/class/System.XML/Test/XmlFiles/xsd/ChangeLog5
-rw-r--r--mcs/class/System.XML/moonlight_raw_System.Xml.dll.sources (renamed from mcs/class/System.XML/net_2_1_raw_System.Xml.dll.sources)0
-rw-r--r--mcs/class/System.Xml.Linq/System.Xml.Linq/ChangeLog4
-rw-r--r--mcs/class/System.Xml.Linq/System.Xml.Linq/XElement.cs17
-rw-r--r--mcs/class/System/ChangeLog4
-rw-r--r--mcs/class/System/monotouch_System.dll.sources2
-rw-r--r--mcs/class/System/moonlight_bootstrap_System.dll.sources (renamed from mcs/class/System/net_2_1_bootstrap_System.dll.sources)2
-rw-r--r--mcs/class/System/moonlight_raw_System.dll.sources (renamed from mcs/class/System/net_2_1_raw_System.dll.sources)0
-rw-r--r--mcs/class/corlib/ChangeLog5
-rw-r--r--mcs/class/corlib/Makefile8
-rw-r--r--mcs/class/corlib/Mono/Runtime.cs2
-rw-r--r--mcs/class/corlib/System.IO/ChangeLog4
-rw-r--r--mcs/class/corlib/System.IO/SearchOption.cs2
-rw-r--r--mcs/class/corlib/System/ChangeLog14
-rw-r--r--mcs/class/corlib/System/TimeSpan.cs17
-rw-r--r--mcs/class/corlib/moonlight_bootstrap_corlib.dll.sources2
-rw-r--r--mcs/class/corlib/moonlight_raw_corlib.dll.sources (renamed from mcs/class/corlib/net_2_1_raw_corlib.dll.sources)0
-rw-r--r--mcs/class/corlib/net_2_1_bootstrap_corlib.dll.sources2
-rw-r--r--mcs/errors/ChangeLog4
-rw-r--r--mcs/errors/Makefile4
-rw-r--r--mcs/ilasm/parser/ChangeLog8
-rw-r--r--mcs/ilasm/parser/ILParser.jay6
-rw-r--r--mcs/mcs/ChangeLog4
-rw-r--r--mcs/mcs/Makefile2
-rw-r--r--mcs/tests/Makefile4
-rw-r--r--mcs/tools/ChangeLog9
-rw-r--r--mcs/tools/Makefile9
-rw-r--r--mcs/tools/compiler-tester/Makefile4
379 files changed, 25420 insertions, 133 deletions
diff --git a/mcs/ChangeLog b/mcs/ChangeLog
index e4e31b255d4..401517b94e5 100644
--- a/mcs/ChangeLog
+++ b/mcs/ChangeLog
@@ -1,3 +1,7 @@
+2010-03-16 Jb Evain <jbevain@novell.com>
+
+ * Makefile: rename the net_2_1 profile to moonlight.
+
2010-03-03 Rolf Bjarne Kvinge <RKvinge@novell.com>
* Makefile: Make basic the bootstrapping profile for net_2_1_bootstrap.
diff --git a/mcs/Makefile b/mcs/Makefile
index b30f44ec5df..311d477cedf 100644
--- a/mcs/Makefile
+++ b/mcs/Makefile
@@ -5,9 +5,9 @@ SUBDIRS := build jay mcs class nunit24 ilasm tools tests errors docs
basic_SUBDIRS := build jay mcs class tools
net_2_0_bootstrap_SUBDIRS := build tools
net_2_0_SUBDIRS := build mcs class nunit24 ilasm tools tests errors
-net_2_1_bootstrap_SUBDIRS := build mcs class
-net_2_1_raw_SUBDIRS := build mcs class tools
-net_2_1_SUBDIRS := tools tests errors
+moonlight_bootstrap_SUBDIRS := build mcs class
+moonlight_raw_SUBDIRS := build mcs class tools
+moonlight_SUBDIRS := tools tests errors
monotouch_SUBDIRS := build mcs class
monotouch_bootstrap_SUBDIRS := build mcs class
net_3_5_SUBDIRS := build class
@@ -106,11 +106,11 @@ _boot_ = all clean install
$(_boot_:%=profile-do--net_4_0--%): profile-do--net_4_0--%: profile-do--net_4_0_bootstrap--%
$(_boot_:%=profile-do--net_4_0_bootstrap--%): profile-do--net_4_0_bootstrap--%: profile-do--net_2_0--%
$(_boot_:%=profile-do--net_3_5--%): profile-do--net_3_5--%: profile-do--net_2_0--%
-$(_boot_:%=profile-do--net_2_1--%): profile-do--net_2_1--%: profile-do--net_2_1_raw--%
+$(_boot_:%=profile-do--moonlight--%): profile-do--moonlight--%: profile-do--moonlight_raw--%
$(_boot_:%=profile-do--monotouch--%): profile-do--monotouch--%: profile-do--monotouch_bootstrap--%
$(_boot_:%=profile-do--monotouch_bootstrap--%): profile-do--monotouch_bootstrap--%: profile-do--net_2_0--%
-$(_boot_:%=profile-do--net_2_1_raw--%): profile-do--net_2_1_raw--%: profile-do--net_2_1_bootstrap--%
-$(_boot_:%=profile-do--net_2_1_bootstrap--%): profile-do--net_2_1_bootstrap--%: profile-do--basic--%
+$(_boot_:%=profile-do--moonlight_raw--%): profile-do--moonlight_raw--%: profile-do--moonlight_bootstrap--%
+$(_boot_:%=profile-do--moonlight_bootstrap--%): profile-do--moonlight_bootstrap--%: profile-do--basic--%
$(_boot_:%=profile-do--net_2_0--%): profile-do--net_2_0--%: profile-do--net_2_0_bootstrap--%
$(_boot_:%=profile-do--net_2_0_bootstrap--%): profile-do--net_2_0_bootstrap--%: profile-do--basic--%
diff --git a/mcs/build/ChangeLog b/mcs/build/ChangeLog
index 2b6610dd975..89fa33c42e5 100644
--- a/mcs/build/ChangeLog
+++ b/mcs/build/ChangeLog
@@ -1,3 +1,8 @@
+2010-03-16 Jb Evain <jbevain@novell.com>
+
+ * profiles/net_2_1_*.make: rename to moonlight_*.make and
+ define the MOONLIGHT symbol.
+
2010-03-03 Jonathan Pryor <jpryor@novell.com>
* library.make: Add the generated per-profile .source file to
diff --git a/mcs/build/Makefile b/mcs/build/Makefile
index 08640a9f4ac..af6ce7356b4 100644
--- a/mcs/build/Makefile
+++ b/mcs/build/Makefile
@@ -19,9 +19,9 @@ PROFILES = \
basic \
net_2_0_bootstrap \
net_2_0 \
- net_2_1_bootstrap \
- net_2_1_raw \
- net_2_1 \
+ moonlight_bootstrap \
+ moonlight_raw \
+ moonlight \
net_3_5 \
net_4_0_bootstrap \
net_4_0
diff --git a/mcs/build/profiles/net_2_1.make b/mcs/build/profiles/moonlight.make
index f6a58a5c348..f3aba438b8f 100644
--- a/mcs/build/profiles/net_2_1.make
+++ b/mcs/build/profiles/moonlight.make
@@ -3,7 +3,7 @@
my_runtime = $(RUNTIME) $(RUNTIME_FLAGS) --security=temporary-smcs-hack
INTERNAL_SMCS = $(my_runtime) $(topdir)/class/lib/$(PROFILE)/smcs.exe
-BOOTSTRAP_PROFILE = net_2_1_bootstrap
+BOOTSTRAP_PROFILE = moonlight_bootstrap
BOOTSTRAP_MCS = MONO_PATH="$(topdir)/class/lib/$(BOOTSTRAP_PROFILE)$(PLATFORM_PATH_SEPARATOR)$$MONO_PATH" $(my_runtime) $(topdir)/class/lib/$(BOOTSTRAP_PROFILE)/smcs.exe
MCS = MONO_PATH="$(topdir)/class/lib/$(PROFILE)$(PLATFORM_PATH_SEPARATOR)$$MONO_PATH" $(INTERNAL_SMCS)
@@ -11,7 +11,7 @@ MCS = MONO_PATH="$(topdir)/class/lib/$(PROFILE)$(PLATFORM_PATH_SEPARATOR)$$MONO_
profile-check:
@:
-PROFILE_MCS_FLAGS = -d:NET_1_1 -d:NET_2_0 -d:NET_2_1
+PROFILE_MCS_FLAGS = -d:NET_1_1 -d:NET_2_0 -d:NET_2_1 -d:MOONLIGHT
FRAMEWORK_VERSION = 2.1
NO_TEST = yes
diff --git a/mcs/build/profiles/net_2_1_bootstrap.make b/mcs/build/profiles/moonlight_bootstrap.make
index 230c3918983..b6bb5556819 100644
--- a/mcs/build/profiles/net_2_1_bootstrap.make
+++ b/mcs/build/profiles/moonlight_bootstrap.make
@@ -9,7 +9,7 @@ MCS = MONO_PATH="$(topdir)/class/lib/$(PROFILE)$(PLATFORM_PATH_SEPARATOR)$(topdi
profile-check:
@:
-PROFILE_MCS_FLAGS = -d:NET_1_1 -d:NET_2_0 -d:NET_2_1
+PROFILE_MCS_FLAGS = -d:NET_1_1 -d:NET_2_0 -d:NET_2_1 -d:MOONLIGHT
FRAMEWORK_VERSION = 2.1
NO_TEST = yes
diff --git a/mcs/build/profiles/net_2_1_raw.make b/mcs/build/profiles/moonlight_raw.make
index f6a58a5c348..f3aba438b8f 100644
--- a/mcs/build/profiles/net_2_1_raw.make
+++ b/mcs/build/profiles/moonlight_raw.make
@@ -3,7 +3,7 @@
my_runtime = $(RUNTIME) $(RUNTIME_FLAGS) --security=temporary-smcs-hack
INTERNAL_SMCS = $(my_runtime) $(topdir)/class/lib/$(PROFILE)/smcs.exe
-BOOTSTRAP_PROFILE = net_2_1_bootstrap
+BOOTSTRAP_PROFILE = moonlight_bootstrap
BOOTSTRAP_MCS = MONO_PATH="$(topdir)/class/lib/$(BOOTSTRAP_PROFILE)$(PLATFORM_PATH_SEPARATOR)$$MONO_PATH" $(my_runtime) $(topdir)/class/lib/$(BOOTSTRAP_PROFILE)/smcs.exe
MCS = MONO_PATH="$(topdir)/class/lib/$(PROFILE)$(PLATFORM_PATH_SEPARATOR)$$MONO_PATH" $(INTERNAL_SMCS)
@@ -11,7 +11,7 @@ MCS = MONO_PATH="$(topdir)/class/lib/$(PROFILE)$(PLATFORM_PATH_SEPARATOR)$$MONO_
profile-check:
@:
-PROFILE_MCS_FLAGS = -d:NET_1_1 -d:NET_2_0 -d:NET_2_1
+PROFILE_MCS_FLAGS = -d:NET_1_1 -d:NET_2_0 -d:NET_2_1 -d:MOONLIGHT
FRAMEWORK_VERSION = 2.1
NO_TEST = yes
diff --git a/mcs/class/ChangeLog b/mcs/class/ChangeLog
index 44fb1d7a4a4..bceb2ec4601 100644
--- a/mcs/class/ChangeLog
+++ b/mcs/class/ChangeLog
@@ -1,3 +1,11 @@
+2010-03-16 Jb Evain <jbevain@novell.com>
+
+ * Makefile: rename the net_2_1 profile to moonlight.
+
+2010-03-15 Marek Habersack <mhabersack@novell.com>
+
+ * Makefile (net_2_0_dirs): added System.Web.Mvc2
+
2010-03-11 Atsushi Enomoto <atsushi@ximian.com>
* Makefile : build Sys.Json after Sys.SM.Web.
diff --git a/mcs/class/Makefile b/mcs/class/Makefile
index c61e7fee132..6684a19ee35 100644
--- a/mcs/class/Makefile
+++ b/mcs/class/Makefile
@@ -106,6 +106,7 @@ net_2_0_dirs := \
System.Web.DynamicData \
System.ServiceModel.Web \
System.Web.Mvc \
+ System.Web.Mvc2 \
Mono.C5 \
Mono.Management \
Mono.Options \
@@ -119,7 +120,7 @@ net_2_0_only_dirs := \
System.Web.Extensions_1.0 \
System.Web.Extensions.Design_1.0
-net_2_1_dirs := \
+moonlight_dirs := \
corlib \
Mono.CompilerServices.SymbolWriter \
System.Core \
@@ -169,8 +170,8 @@ net_4_0_dirs := \
net_2_0_bootstrap_SUBDIRS := $(bootstrap_dirs)
net_2_0_SUBDIRS := $(common_dirs) $(net_2_0_dirs) $(net_2_0_only_dirs)
-net_2_1_bootstrap_SUBDIRS := corlib System Mono.CompilerServices.SymbolWriter System.Core
-net_2_1_raw_SUBDIRS := $(net_2_1_dirs)
+moonlight_bootstrap_SUBDIRS := corlib System Mono.CompilerServices.SymbolWriter System.Core
+moonlight_raw_SUBDIRS := $(moonlight_dirs)
monotouch_bootstrap_SUBDIRS := corlib System Mono.CompilerServices.SymbolWriter System.Core
monotouch_SUBDIRS := $(monotouch_dirs)
net_3_5_SUBDIRS := $(net_3_5_dirs)
@@ -179,7 +180,7 @@ net_4_0_SUBDIRS := $(common_dirs) $(net_2_0_dirs) $(net_4_0_dirs)
include ../build/rules.make
-SUBDIRS = $(common_dirs) $(net_2_0_dirs) $(net_2_0_only_dirs) $(net_2_1_dirs) $(net_3_5_dirs) $(net_4_0_dirs)
+SUBDIRS = $(common_dirs) $(net_2_0_dirs) $(net_2_0_only_dirs) $(moonlight_dirs) $(monotouch_dirs) $(net_3_5_dirs) $(net_4_0_dirs)
DIST_ONLY_SUBDIRS = dlr
diff --git a/mcs/class/System.ComponentModel.Composition/src/ComponentModel/System/ComponentModel/Composition/Hosting/DirectoryCatalog.cs b/mcs/class/System.ComponentModel.Composition/src/ComponentModel/System/ComponentModel/Composition/Hosting/DirectoryCatalog.cs
index ae15ef4b290..616ef4577a4 100644
--- a/mcs/class/System.ComponentModel.Composition/src/ComponentModel/System/ComponentModel/Composition/Hosting/DirectoryCatalog.cs
+++ b/mcs/class/System.ComponentModel.Composition/src/ComponentModel/System/ComponentModel/Composition/Hosting/DirectoryCatalog.cs
@@ -468,7 +468,7 @@ namespace System.ComponentModel.Composition.Hosting
private string[] GetFiles()
{
string[] files = Directory.GetFiles(this._fullPath, this._searchPattern);
- return Array.ConvertAll<string, string>(files, (file) => file.ToUpperInvariant());
+ return Array.ConvertAll<string, string>(files, (file) => file);
}
private static string GetFullPath(string path)
@@ -478,7 +478,7 @@ namespace System.ComponentModel.Composition.Hosting
path = IOPath.Combine(AppDomain.CurrentDomain.BaseDirectory, path);
}
- return IOPath.GetFullPath(path).ToUpperInvariant();
+ return IOPath.GetFullPath(path);
}
private void Initialize(string path, string searchPattern)
diff --git a/mcs/class/System.Core/ChangeLog b/mcs/class/System.Core/ChangeLog
index 5bddef0064e..d093c0ce646 100644
--- a/mcs/class/System.Core/ChangeLog
+++ b/mcs/class/System.Core/ChangeLog
@@ -1,3 +1,7 @@
+2010-03-16 Jb Evain <jbevain@novell.com>
+
+ * net_2_1_*.dll.sources: rename to moonlight_*.dll.sources.
+
2009-12-17 Marek Safar <marek.safar@gmail.com>
* Makefile: Compile itself using bootstrap System.Core.
diff --git a/mcs/class/System.Core/Makefile b/mcs/class/System.Core/Makefile
index dde17448c70..893de76b60b 100644
--- a/mcs/class/System.Core/Makefile
+++ b/mcs/class/System.Core/Makefile
@@ -14,7 +14,7 @@ ifeq (4.0, $(FRAMEWORK_VERSION))
LIB_MCS_FLAGS += -d:CODEPLEX_40
endif
-FULL_PROFILE := $(filter net_2_0 net_4_0 net_2_1_raw monotouch, $(PROFILE))
+FULL_PROFILE := $(filter net_2_0 net_4_0 moonlight_raw monotouch, $(PROFILE))
ifdef FULL_PROFILE
LIBRARY_COMPILE = $(BOOT_COMPILE)
endif
diff --git a/mcs/class/System.Core/moonlight_bootstrap_System.Core.dll.sources b/mcs/class/System.Core/moonlight_bootstrap_System.Core.dll.sources
new file mode 100644
index 00000000000..82f86b00544
--- /dev/null
+++ b/mcs/class/System.Core/moonlight_bootstrap_System.Core.dll.sources
@@ -0,0 +1 @@
+#include moonlight_raw_System.Core.dll.sources
diff --git a/mcs/class/System.Core/net_2_1_raw_System.Core.dll.sources b/mcs/class/System.Core/moonlight_raw_System.Core.dll.sources
index 844e40b1a78..844e40b1a78 100644
--- a/mcs/class/System.Core/net_2_1_raw_System.Core.dll.sources
+++ b/mcs/class/System.Core/moonlight_raw_System.Core.dll.sources
diff --git a/mcs/class/System.Core/net_2_1_bootstrap_System.Core.dll.sources b/mcs/class/System.Core/net_2_1_bootstrap_System.Core.dll.sources
deleted file mode 100644
index 5e32150b429..00000000000
--- a/mcs/class/System.Core/net_2_1_bootstrap_System.Core.dll.sources
+++ /dev/null
@@ -1 +0,0 @@
-#include net_2_1_raw_System.Core.dll.sources
diff --git a/mcs/class/System.Net/ChangeLog b/mcs/class/System.Net/ChangeLog
index db2a5875fd9..7da7dd94bd6 100644
--- a/mcs/class/System.Net/ChangeLog
+++ b/mcs/class/System.Net/ChangeLog
@@ -1,3 +1,7 @@
+2010-03-16 Jb Evain <jbevain@novell.com>
+
+ * net_2_1_*.dll.sources: rename to moonlight_*.dll.sources.
+
2010-03-10 Sebastien Pouliot <sebastien@ximian.com>
* net_2_1_raw_System.Net.dll.sources: Add types from (new internal)
diff --git a/mcs/class/System.Net/System.Net/ChangeLog b/mcs/class/System.Net/System.Net/ChangeLog
index 8dab13acca7..1e6edcb2027 100644
--- a/mcs/class/System.Net/System.Net/ChangeLog
+++ b/mcs/class/System.Net/System.Net/ChangeLog
@@ -1,3 +1,9 @@
+2010-03-12 Sebastien Pouliot <sebastien@ximian.com>
+
+ * WebClient_2_1.cs: Directly set 'progress' delegate field
+ * WebRequest_2_1.cs: Avoid using reflection (since it was not
+ really needed anyway)
+
2010-03-03 Andreia Gaita <avidigal@novell.com>
* InternalWebRequestStreamWrapper.cs: don't add a newline to the
diff --git a/mcs/class/System.Net/System.Net/WebClient_2_1.cs b/mcs/class/System.Net/System.Net/WebClient_2_1.cs
index 0fc7bf404e6..a78e29396b8 100644
--- a/mcs/class/System.Net/System.Net/WebClient_2_1.cs
+++ b/mcs/class/System.Net/System.Net/WebClient_2_1.cs
@@ -538,12 +538,12 @@ namespace System.Net {
WebRequest request = WebRequest.Create (uri);
- request.SetupProgressDelegate (delegate (long read, long length) {
+ request.progress = delegate (long read, long length) {
callback_data.sync_context.Post (delegate (object sender) {
OnDownloadProgressChanged (new DownloadProgressChangedEventArgs (read, length, callback_data.user_token));
}, null);
- });
+ };
return request;
}
diff --git a/mcs/class/System.Net/System.Net/WebRequest_2_1.cs b/mcs/class/System.Net/System.Net/WebRequest_2_1.cs
index 68b18aa63b2..6307146ac46 100644
--- a/mcs/class/System.Net/System.Net/WebRequest_2_1.cs
+++ b/mcs/class/System.Net/System.Net/WebRequest_2_1.cs
@@ -43,6 +43,8 @@ namespace System.Net {
static IWebRequestCreate default_creator;
static Dictionary<string, IWebRequestCreate> registred_prefixes;
+ internal Action<long,long> progress;
+
public abstract string ContentType { get; set; }
public abstract WebHeaderCollection Headers { get; set; }
public abstract string Method { get; set; }
@@ -150,13 +152,6 @@ namespace System.Net {
return true;
}
- internal void SetupProgressDelegate (Action<long,long> progress)
- {
- FieldInfo fi = GetType ().GetField ("progress", BindingFlags.Instance | BindingFlags.NonPublic);
- if (fi != null)
- fi.SetValue (this, progress);
- }
-
static Exception NotImplemented ()
{
// hide the "normal" NotImplementedException from corcompare-like tools
diff --git a/mcs/class/System.Net/net_2_1_raw_System.Net.dll.sources b/mcs/class/System.Net/moonlight_raw_System.Net.dll.sources
index 85566a02931..85566a02931 100644
--- a/mcs/class/System.Net/net_2_1_raw_System.Net.dll.sources
+++ b/mcs/class/System.Net/moonlight_raw_System.Net.dll.sources
diff --git a/mcs/class/System.Runtime.Serialization/ChangeLog b/mcs/class/System.Runtime.Serialization/ChangeLog
index 701f3bbfe19..06bfd0b1de3 100644
--- a/mcs/class/System.Runtime.Serialization/ChangeLog
+++ b/mcs/class/System.Runtime.Serialization/ChangeLog
@@ -1,3 +1,7 @@
+2010-03-16 Jb Evain <jbevain@novell.com>
+
+ * net_2_1_*.dll.sources: rename to moonlight_*.dll.sources.
+
2010-03-03 Atsushi Enomoto <atsushi@ximian.com>
* System.Runtime.Serialization.dll.sources:
diff --git a/mcs/class/System.Runtime.Serialization/monotouch_System.Runtime.Serialization.dll.sources b/mcs/class/System.Runtime.Serialization/monotouch_System.Runtime.Serialization.dll.sources
index 602d0d07a69..7885b804a7c 100644
--- a/mcs/class/System.Runtime.Serialization/monotouch_System.Runtime.Serialization.dll.sources
+++ b/mcs/class/System.Runtime.Serialization/monotouch_System.Runtime.Serialization.dll.sources
@@ -1,2 +1,2 @@
-#include net_2_1_raw_System.Runtime.Serialization.dll.sources
+#include moonlight_raw_System.Runtime.Serialization.dll.sources
System.Xml/OnXmlDictionaryReaderClose.cs
diff --git a/mcs/class/System.Runtime.Serialization/net_2_1_raw_System.Runtime.Serialization.dll.sources b/mcs/class/System.Runtime.Serialization/moonlight_raw_System.Runtime.Serialization.dll.sources
index 59cd659ac4d..59cd659ac4d 100644
--- a/mcs/class/System.Runtime.Serialization/net_2_1_raw_System.Runtime.Serialization.dll.sources
+++ b/mcs/class/System.Runtime.Serialization/moonlight_raw_System.Runtime.Serialization.dll.sources
diff --git a/mcs/class/System.Security/ChangeLog b/mcs/class/System.Security/ChangeLog
index 770ffc3a8a1..82455b4257c 100644
--- a/mcs/class/System.Security/ChangeLog
+++ b/mcs/class/System.Security/ChangeLog
@@ -1,3 +1,7 @@
+2010-03-16 Jb Evain <jbevain@novell.com>
+
+ * Makefile: rename the net_2_1 profile to moonlight.
+
2009-06-05 Marek Safar <marek.safar@gmail.com>
* Makefile: Fixed NET_2_0 conditional to actually handle Mono.Security
diff --git a/mcs/class/System.Security/Makefile b/mcs/class/System.Security/Makefile
index 3d55d17cf2b..bf261067d24 100644
--- a/mcs/class/System.Security/Makefile
+++ b/mcs/class/System.Security/Makefile
@@ -13,7 +13,7 @@ LIB_MCS_FLAGS += -r:Mono.Security.dll -nowarn:414
TEST_MCS_FLAGS += -nowarn:168,183,414
endif
-VALID_PROFILE := $(filter net_1_1 net_2_0 net_2_1_raw net_4_0, $(PROFILE))
+VALID_PROFILE := $(filter net_1_1 net_2_0 moonlight_raw net_4_0, $(PROFILE))
ifndef VALID_PROFILE
# @echo "** Warning: System.Security.dll built without parts that depend on: Mono.Security.dll "
else
diff --git a/mcs/class/System.ServiceModel.Web/ChangeLog b/mcs/class/System.ServiceModel.Web/ChangeLog
index bfa93fd472e..d0c60882654 100644
--- a/mcs/class/System.ServiceModel.Web/ChangeLog
+++ b/mcs/class/System.ServiceModel.Web/ChangeLog
@@ -1,5 +1,11 @@
2010-03-12 Atsushi Enomoto <atsushi@ximian.com>
+ * net_2_0_System.ServiceModel.Web.dll.sources,
+ common_System.ServiceModel.Web.dll.sources : UriTemplates are
+ moved to Sys.SM.dll in 4.0 profile.
+
+2010-03-12 Atsushi Enomoto <atsushi@ximian.com>
+
* monotouch_System.ServiceModel.Web.dll.sources :
This also needs new files.
diff --git a/mcs/class/System.ServiceModel.Web/System.Runtime.Serialization.Json/ChangeLog b/mcs/class/System.ServiceModel.Web/System.Runtime.Serialization.Json/ChangeLog
index ef28ea94c9f..1c166454aad 100644
--- a/mcs/class/System.ServiceModel.Web/System.Runtime.Serialization.Json/ChangeLog
+++ b/mcs/class/System.ServiceModel.Web/System.Runtime.Serialization.Json/ChangeLog
@@ -1,3 +1,8 @@
+2010-03-13 Kornél Pál <kornelpal@gmail.com>
+
+ * JavaScriptReader.cs: Deserialize "false" correctly.
+ Fixed bug #586712.
+
2010-03-10 Atsushi Enomoto <atsushi@ximian.com>
* JavaScriptReader.cs : moved from Sys.Json/JsonReader.cs.
diff --git a/mcs/class/System.ServiceModel.Web/System.Runtime.Serialization.Json/JavaScriptReader.cs b/mcs/class/System.ServiceModel.Web/System.Runtime.Serialization.Json/JavaScriptReader.cs
index b3be29ae8d9..746030825a7 100644
--- a/mcs/class/System.ServiceModel.Web/System.Runtime.Serialization.Json/JavaScriptReader.cs
+++ b/mcs/class/System.ServiceModel.Web/System.Runtime.Serialization.Json/JavaScriptReader.cs
@@ -86,7 +86,7 @@ namespace System.Runtime.Serialization.Json
return true;
case 'f':
Expect ("false");
- return true;
+ return false;
case 'n':
Expect ("null");
// FIXME: what should we return?
diff --git a/mcs/class/System.ServiceModel.Web/common_System.ServiceModel.Web.dll.sources b/mcs/class/System.ServiceModel.Web/common_System.ServiceModel.Web.dll.sources
index ae726bc6b95..04dbe4f9bd7 100644
--- a/mcs/class/System.ServiceModel.Web/common_System.ServiceModel.Web.dll.sources
+++ b/mcs/class/System.ServiceModel.Web/common_System.ServiceModel.Web.dll.sources
@@ -37,8 +37,3 @@ System.ServiceModel.Web/WebServiceHost.cs
System.ServiceModel/WebHttpBinding.cs
System.ServiceModel/WebHttpSecurity.cs
System.ServiceModel/WebHttpSecurityMode.cs
-System/UriTemplate.cs
-System/UriTemplateEquivalenceComparer.cs
-System/UriTemplateMatch.cs
-System/UriTemplateMatchException.cs
-System/UriTemplateTable.cs
diff --git a/mcs/class/System.ServiceModel.Web/net_2_1_raw_System.ServiceModel.Web.dll.sources b/mcs/class/System.ServiceModel.Web/moonlight_raw_System.ServiceModel.Web.dll.sources
index 07c6038f68e..07c6038f68e 100644
--- a/mcs/class/System.ServiceModel.Web/net_2_1_raw_System.ServiceModel.Web.dll.sources
+++ b/mcs/class/System.ServiceModel.Web/moonlight_raw_System.ServiceModel.Web.dll.sources
diff --git a/mcs/class/System.ServiceModel.Web/net_2_0_System.ServiceModel.Web.dll.sources b/mcs/class/System.ServiceModel.Web/net_2_0_System.ServiceModel.Web.dll.sources
index 27493ec8201..effcff77237 100644
--- a/mcs/class/System.ServiceModel.Web/net_2_0_System.ServiceModel.Web.dll.sources
+++ b/mcs/class/System.ServiceModel.Web/net_2_0_System.ServiceModel.Web.dll.sources
@@ -46,3 +46,8 @@ System.ServiceModel.Syndication/TextSyndicationContentKind.cs
System.ServiceModel.Syndication/UrlSyndicationContent.cs
System.ServiceModel.Syndication/Workspace.cs
System.ServiceModel.Syndication/XmlSyndicationContent.cs
+System/UriTemplate.cs
+System/UriTemplateEquivalenceComparer.cs
+System/UriTemplateMatch.cs
+System/UriTemplateMatchException.cs
+System/UriTemplateTable.cs
diff --git a/mcs/class/System.ServiceModel/ChangeLog b/mcs/class/System.ServiceModel/ChangeLog
index d2293ebdb7c..b974f89c548 100755
--- a/mcs/class/System.ServiceModel/ChangeLog
+++ b/mcs/class/System.ServiceModel/ChangeLog
@@ -1,3 +1,16 @@
+2010-03-16 Jb Evain <jbevain@novell.com>
+
+ * net_2_1_*.dll.sources: rename to moonlight_*.dll.sources.
+
+2010-03-15 Astushi Enomoto <atsushi@ximian.com>
+
+ * net_4_0_System.ServiceModel.dll.sources:
+ more types from Sys.SM.Web.dll in 4.0 profile here.
+
+2010-03-15 Astushi Enomoto <atsushi@ximian.com>
+
+ * Dummy_2_1.cs : added cosmetic silverlight sdk compatibility stuff.
+
2010-03-11 Astushi Enomoto <atsushi@ximian.com>
* System.ServiceModel.dll.sources,
diff --git a/mcs/class/System.ServiceModel/Dummy_2_1.cs b/mcs/class/System.ServiceModel/Dummy_2_1.cs
index a3aa98d78e4..4c90120ed87 100644
--- a/mcs/class/System.ServiceModel/Dummy_2_1.cs
+++ b/mcs/class/System.ServiceModel/Dummy_2_1.cs
@@ -1,3 +1,4 @@
+using System.Reflection;
using System.Runtime.Serialization;
namespace System.ServiceModel
@@ -7,6 +8,19 @@ namespace System.ServiceModel
{
public InstanceContext (object dummy) {}
}
+ // introduced for silverlight sdk compatibility
+ internal class OperationFormatStyleHelper
+ {
+ public static bool IsDefined (OperationFormatStyle style)
+ {
+ switch (style) {
+ case OperationFormatStyle.Document:
+ case OperationFormatStyle.Rpc:
+ return true;
+ }
+ return false;
+ }
+ }
}
namespace System.ServiceModel.Channels
{
@@ -23,6 +37,41 @@ namespace System.ServiceModel.Description
public interface IWsdlExportExtension {}
public interface IWsdlImportExtension {}
public interface IContractBehavior {}
+
+ // introduced for silverlight sdk compatibility
+ internal class ServiceReflector
+ {
+ public static T GetSingleAttribute<T> (ICustomAttributeProvider p, Type [] types)
+ {
+ T ret = default (T);
+ foreach (Type t in types) {
+ foreach (object att in p.GetCustomAttributes (t, false)) {
+ if (att is T) {
+ if (ret != null)
+ throw new InvalidOperationException (String.Format ("More than one {0} attributes are found in the argument types", typeof (T)));
+ ret = (T) att;
+ }
+ }
+ }
+ return ret;
+ }
+ }
+}
+namespace System.ServiceModel.DiagnosticUtility
+{
+ // introduced for silverlight sdk compatibility
+ internal class ExceptionUtility
+ {
+ public static Exception ThrowHelperError (Exception error)
+ {
+ return error;
+ }
+
+ public static Exception ThrowHelperArgumentNull (string arg)
+ {
+ return new ArgumentNullException (arg);
+ }
+ }
}
namespace System.ServiceModel.Dispatcher
{
diff --git a/mcs/class/System.ServiceModel/Makefile b/mcs/class/System.ServiceModel/Makefile
index a090d8cc0da..6bb0e3c915b 100755
--- a/mcs/class/System.ServiceModel/Makefile
+++ b/mcs/class/System.ServiceModel/Makefile
@@ -31,7 +31,7 @@ LIB_MCS_FLAGS += /d:NET_3_0 \
/r:Mono.Security.dll
endif
-ifeq (net_2_1_raw, $(PROFILE))
+ifeq (moonlight_raw, $(PROFILE))
LIB_MCS_FLAGS += /r:System.Net
endif
diff --git a/mcs/class/System.ServiceModel/System.ServiceModel.Configuration/ChangeLog b/mcs/class/System.ServiceModel/System.ServiceModel.Configuration/ChangeLog
index 756dae16d18..3822f5351fa 100755
--- a/mcs/class/System.ServiceModel/System.ServiceModel.Configuration/ChangeLog
+++ b/mcs/class/System.ServiceModel/System.ServiceModel.Configuration/ChangeLog
@@ -1,3 +1,26 @@
+2010-03-16 Atsushi Enomoto <atsushi@ximian.com>
+
+ * TcpTransportElement.cs
+ MsmqIntegrationElement.cs
+ MsmqElementBase.cs
+ NamedPipeTransportElement.cs
+ HttpsTransportElement.cs
+ TransportElement.cs
+ MsmqTransportElement.cs
+ HttpTransportElement.cs : implement missing methods.
+
+2010-03-16 Atsushi Enomoto <atsushi@ximian.com>
+
+ * IssuedTokenClientElement.cs, MsmqBindingElementBase.cs,
+ NamedPipeConnectionPoolSettingsElement.cs,
+ TcpConnectionPoolSettingsElement.cs,
+ StandardBindingReliableSessionElement.cs,
+ LocalServiceSecuritySettingsElement.cs : use TimeSpanConverter.
+
+2010-03-15 Atsushi Enomoto <atsushi@ximian.com>
+
+ * ServiceDebugElement.cs : binding names could rather be empty.
+
2010-03-09 Atsushi Enomoto <atsushi@ximian.com>
* XmlDictionaryReaderQuotasElement.cs : add ApplyConfiguration().
diff --git a/mcs/class/System.ServiceModel/System.ServiceModel.Configuration/HttpTransportElement.cs b/mcs/class/System.ServiceModel/System.ServiceModel.Configuration/HttpTransportElement.cs
index 2412b52d9bc..1933d25a0e8 100644
--- a/mcs/class/System.ServiceModel/System.ServiceModel.Configuration/HttpTransportElement.cs
+++ b/mcs/class/System.ServiceModel/System.ServiceModel.Configuration/HttpTransportElement.cs
@@ -4,7 +4,7 @@
// Author:
// Atsushi Enomoto <atsushi@ximian.com>
//
-// Copyright (C) 2006 Novell, Inc. http://www.novell.com
+// Copyright (C) 2006,2010 Novell, Inc. http://www.novell.com
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
@@ -192,12 +192,64 @@ namespace System.ServiceModel.Configuration
set { base ["useDefaultWebProxy"] = value; }
}
-
- [MonoTODO]
- protected internal override BindingElement CreateBindingElement () {
- throw new NotImplementedException ();
+ public override void ApplyConfiguration (BindingElement bindingElement)
+ {
+ var b = (HttpTransportBindingElement) bindingElement;
+ base.ApplyConfiguration (b);
+ b.AllowCookies = AllowCookies;
+ b.AuthenticationScheme = AuthenticationScheme;
+ b.BypassProxyOnLocal = BypassProxyOnLocal;
+ b.HostNameComparisonMode = HostNameComparisonMode;
+ b.KeepAliveEnabled = KeepAliveEnabled;
+ b.MaxBufferSize = MaxBufferSize;
+ b.ProxyAddress = ProxyAddress;
+ b.ProxyAuthenticationScheme = ProxyAuthenticationScheme;
+ b.Realm = Realm;
+ b.TransferMode = TransferMode;
+ b.UnsafeConnectionNtlmAuthentication = UnsafeConnectionNtlmAuthentication;
+ b.UseDefaultWebProxy = UseDefaultWebProxy;
+ }
+
+ public override void CopyFrom (ServiceModelExtensionElement from)
+ {
+ var e = (HttpTransportElement) from;
+ base.CopyFrom (from);
+ AllowCookies = e.AllowCookies;
+ AuthenticationScheme = e.AuthenticationScheme;
+ BypassProxyOnLocal = e.BypassProxyOnLocal;
+ HostNameComparisonMode = e.HostNameComparisonMode;
+ KeepAliveEnabled = e.KeepAliveEnabled;
+ MaxBufferSize = e.MaxBufferSize;
+ ProxyAddress = e.ProxyAddress;
+ ProxyAuthenticationScheme = e.ProxyAuthenticationScheme;
+ Realm = e.Realm;
+ TransferMode = e.TransferMode;
+ UnsafeConnectionNtlmAuthentication = e.UnsafeConnectionNtlmAuthentication;
+ UseDefaultWebProxy = e.UseDefaultWebProxy;
+ }
+
+ protected override TransportBindingElement CreateDefaultBindingElement ()
+ {
+ return new HttpTransportBindingElement ();
+ }
+
+ protected internal override void InitializeFrom (BindingElement bindingElement)
+ {
+ var b = (HttpTransportBindingElement) bindingElement;
+ base.InitializeFrom (b);
+ AllowCookies = b.AllowCookies;
+ AuthenticationScheme = b.AuthenticationScheme;
+ BypassProxyOnLocal = b.BypassProxyOnLocal;
+ HostNameComparisonMode = b.HostNameComparisonMode;
+ KeepAliveEnabled = b.KeepAliveEnabled;
+ MaxBufferSize = b.MaxBufferSize;
+ ProxyAddress = b.ProxyAddress;
+ ProxyAuthenticationScheme = b.ProxyAuthenticationScheme;
+ Realm = b.Realm;
+ TransferMode = b.TransferMode;
+ UnsafeConnectionNtlmAuthentication = b.UnsafeConnectionNtlmAuthentication;
+ UseDefaultWebProxy = b.UseDefaultWebProxy;
}
-
}
}
diff --git a/mcs/class/System.ServiceModel/System.ServiceModel.Configuration/HttpsTransportElement.cs b/mcs/class/System.ServiceModel/System.ServiceModel.Configuration/HttpsTransportElement.cs
index c844e56dc94..a2de9b75550 100644
--- a/mcs/class/System.ServiceModel/System.ServiceModel.Configuration/HttpsTransportElement.cs
+++ b/mcs/class/System.ServiceModel/System.ServiceModel.Configuration/HttpsTransportElement.cs
@@ -4,7 +4,7 @@
// Author:
// Atsushi Enomoto <atsushi@ximian.com>
//
-// Copyright (C) 2006 Novell, Inc. http://www.novell.com
+// Copyright (C) 2006,2010 Novell, Inc. http://www.novell.com
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
@@ -87,7 +87,31 @@ namespace System.ServiceModel.Configuration
set { base ["requireClientCertificate"] = value; }
}
+ public override void ApplyConfiguration (BindingElement bindingElement)
+ {
+ var b = (HttpsTransportBindingElement) bindingElement;
+ base.ApplyConfiguration (b);
+ b.RequireClientCertificate = RequireClientCertificate;
+ }
+
+ public override void CopyFrom (ServiceModelExtensionElement from)
+ {
+ var e = (HttpsTransportElement) from;
+ base.CopyFrom (from);
+ RequireClientCertificate = e.RequireClientCertificate;
+ }
+ protected override TransportBindingElement CreateDefaultBindingElement ()
+ {
+ return new HttpsTransportBindingElement ();
+ }
+
+ protected internal override void InitializeFrom (BindingElement bindingElement)
+ {
+ var b = (HttpsTransportBindingElement) bindingElement;
+ base.InitializeFrom (b);
+ RequireClientCertificate = b.RequireClientCertificate;
+ }
}
}
diff --git a/mcs/class/System.ServiceModel/System.ServiceModel.Configuration/IssuedTokenClientElement.cs b/mcs/class/System.ServiceModel/System.ServiceModel.Configuration/IssuedTokenClientElement.cs
index 064d26ceb16..a6d8f55d8c3 100644
--- a/mcs/class/System.ServiceModel/System.ServiceModel.Configuration/IssuedTokenClientElement.cs
+++ b/mcs/class/System.ServiceModel/System.ServiceModel.Configuration/IssuedTokenClientElement.cs
@@ -96,7 +96,7 @@ namespace System.ServiceModel.Configuration
ConfigurationPropertyOptions.None);
max_issued_token_caching_time = new ConfigurationProperty ("maxIssuedTokenCachingTime",
- typeof (TimeSpan), "10675199.02:48:05.4775807", null/* FIXME: get converter for TimeSpan*/, null,
+ typeof (TimeSpan), "10675199.02:48:05.4775807", new TimeSpanConverter (), null,
ConfigurationPropertyOptions.None);
properties.Add (cache_issued_tokens);
@@ -168,6 +168,7 @@ namespace System.ServiceModel.Configuration
[ConfigurationProperty ("maxIssuedTokenCachingTime",
Options = ConfigurationPropertyOptions.None,
DefaultValue = "10675199.02:48:05.4775807")]
+ [TypeConverter (typeof (TimeSpanConverter))]
public TimeSpan MaxIssuedTokenCachingTime {
get { return (TimeSpan) base [max_issued_token_caching_time]; }
set { base [max_issued_token_caching_time] = value; }
diff --git a/mcs/class/System.ServiceModel/System.ServiceModel.Configuration/LocalServiceSecuritySettingsElement.cs b/mcs/class/System.ServiceModel/System.ServiceModel.Configuration/LocalServiceSecuritySettingsElement.cs
index 99d7649518e..cf4bc33dc38 100644
--- a/mcs/class/System.ServiceModel/System.ServiceModel.Configuration/LocalServiceSecuritySettingsElement.cs
+++ b/mcs/class/System.ServiceModel/System.ServiceModel.Configuration/LocalServiceSecuritySettingsElement.cs
@@ -83,11 +83,11 @@ namespace System.ServiceModel.Configuration
ConfigurationPropertyOptions.None);
inactivity_timeout = new ConfigurationProperty ("inactivityTimeout",
- typeof (TimeSpan), "00:02:00", null/* FIXME: get converter for TimeSpan*/, null,
+ typeof (TimeSpan), "00:02:00", new TimeSpanConverter (), null,
ConfigurationPropertyOptions.None);
issued_cookie_lifetime = new ConfigurationProperty ("issuedCookieLifetime",
- typeof (TimeSpan), "10:00:00", null/* FIXME: get converter for TimeSpan*/, null,
+ typeof (TimeSpan), "10:00:00", new TimeSpanConverter (), null,
ConfigurationPropertyOptions.None);
max_cached_cookies = new ConfigurationProperty ("maxCachedCookies",
@@ -95,7 +95,7 @@ namespace System.ServiceModel.Configuration
ConfigurationPropertyOptions.None);
max_clock_skew = new ConfigurationProperty ("maxClockSkew",
- typeof (TimeSpan), "00:05:00", null/* FIXME: get converter for TimeSpan*/, null,
+ typeof (TimeSpan), "00:05:00", new TimeSpanConverter (), null,
ConfigurationPropertyOptions.None);
max_pending_sessions = new ConfigurationProperty ("maxPendingSessions",
@@ -107,7 +107,7 @@ namespace System.ServiceModel.Configuration
ConfigurationPropertyOptions.None);
negotiation_timeout = new ConfigurationProperty ("negotiationTimeout",
- typeof (TimeSpan), "00:01:00", null/* FIXME: get converter for TimeSpan*/, null,
+ typeof (TimeSpan), "00:01:00", new TimeSpanConverter (), null,
ConfigurationPropertyOptions.None);
reconnect_transport_on_failure = new ConfigurationProperty ("reconnectTransportOnFailure",
@@ -119,19 +119,19 @@ namespace System.ServiceModel.Configuration
ConfigurationPropertyOptions.None);
replay_window = new ConfigurationProperty ("replayWindow",
- typeof (TimeSpan), "00:05:00", null/* FIXME: get converter for TimeSpan*/, null,
+ typeof (TimeSpan), "00:05:00", new TimeSpanConverter (), null,
ConfigurationPropertyOptions.None);
session_key_renewal_interval = new ConfigurationProperty ("sessionKeyRenewalInterval",
- typeof (TimeSpan), "15:00:00", null/* FIXME: get converter for TimeSpan*/, null,
+ typeof (TimeSpan), "15:00:00", new TimeSpanConverter (), null,
ConfigurationPropertyOptions.None);
session_key_rollover_interval = new ConfigurationProperty ("sessionKeyRolloverInterval",
- typeof (TimeSpan), "00:05:00", null/* FIXME: get converter for TimeSpan*/, null,
+ typeof (TimeSpan), "00:05:00", new TimeSpanConverter (), null,
ConfigurationPropertyOptions.None);
timestamp_validity_duration = new ConfigurationProperty ("timestampValidityDuration",
- typeof (TimeSpan), "00:05:00", null/* FIXME: get converter for TimeSpan*/, null,
+ typeof (TimeSpan), "00:05:00", new TimeSpanConverter (), null,
ConfigurationPropertyOptions.None);
properties.Add (detect_replays);
@@ -168,6 +168,7 @@ namespace System.ServiceModel.Configuration
[ConfigurationProperty ("inactivityTimeout",
Options = ConfigurationPropertyOptions.None,
DefaultValue = "00:02:00")]
+ [TypeConverter (typeof (TimeSpanConverter))]
public TimeSpan InactivityTimeout {
get { return (TimeSpan) base [inactivity_timeout]; }
set { base [inactivity_timeout] = value; }
@@ -176,6 +177,7 @@ namespace System.ServiceModel.Configuration
[ConfigurationProperty ("issuedCookieLifetime",
Options = ConfigurationPropertyOptions.None,
DefaultValue = "10:00:00")]
+ [TypeConverter (typeof (TimeSpanConverter))]
public TimeSpan IssuedCookieLifetime {
get { return (TimeSpan) base [issued_cookie_lifetime]; }
set { base [issued_cookie_lifetime] = value; }
@@ -195,6 +197,7 @@ namespace System.ServiceModel.Configuration
[ConfigurationProperty ("maxClockSkew",
Options = ConfigurationPropertyOptions.None,
DefaultValue = "00:05:00")]
+ [TypeConverter (typeof (TimeSpanConverter))]
public TimeSpan MaxClockSkew {
get { return (TimeSpan) base [max_clock_skew]; }
set { base [max_clock_skew] = value; }
@@ -225,6 +228,7 @@ namespace System.ServiceModel.Configuration
[ConfigurationProperty ("negotiationTimeout",
Options = ConfigurationPropertyOptions.None,
DefaultValue = "00:01:00")]
+ [TypeConverter (typeof (TimeSpanConverter))]
public TimeSpan NegotiationTimeout {
get { return (TimeSpan) base [negotiation_timeout]; }
set { base [negotiation_timeout] = value; }
@@ -256,6 +260,7 @@ namespace System.ServiceModel.Configuration
[ConfigurationProperty ("replayWindow",
Options = ConfigurationPropertyOptions.None,
DefaultValue = "00:05:00")]
+ [TypeConverter (typeof (TimeSpanConverter))]
public TimeSpan ReplayWindow {
get { return (TimeSpan) base [replay_window]; }
set { base [replay_window] = value; }
@@ -264,6 +269,7 @@ namespace System.ServiceModel.Configuration
[ConfigurationProperty ("sessionKeyRenewalInterval",
Options = ConfigurationPropertyOptions.None,
DefaultValue = "15:00:00")]
+ [TypeConverter (typeof (TimeSpanConverter))]
public TimeSpan SessionKeyRenewalInterval {
get { return (TimeSpan) base [session_key_renewal_interval]; }
set { base [session_key_renewal_interval] = value; }
@@ -272,6 +278,7 @@ namespace System.ServiceModel.Configuration
[ConfigurationProperty ("sessionKeyRolloverInterval",
Options = ConfigurationPropertyOptions.None,
DefaultValue = "00:05:00")]
+ [TypeConverter (typeof (TimeSpanConverter))]
public TimeSpan SessionKeyRolloverInterval {
get { return (TimeSpan) base [session_key_rollover_interval]; }
set { base [session_key_rollover_interval] = value; }
@@ -280,6 +287,7 @@ namespace System.ServiceModel.Configuration
[ConfigurationProperty ("timestampValidityDuration",
Options = ConfigurationPropertyOptions.None,
DefaultValue = "00:05:00")]
+ [TypeConverter (typeof (TimeSpanConverter))]
public TimeSpan TimestampValidityDuration {
get { return (TimeSpan) base [timestamp_validity_duration]; }
set { base [timestamp_validity_duration] = value; }
diff --git a/mcs/class/System.ServiceModel/System.ServiceModel.Configuration/MsmqBindingElementBase.cs b/mcs/class/System.ServiceModel/System.ServiceModel.Configuration/MsmqBindingElementBase.cs
index d4f5d86d72b..a88ce844e65 100644
--- a/mcs/class/System.ServiceModel/System.ServiceModel.Configuration/MsmqBindingElementBase.cs
+++ b/mcs/class/System.ServiceModel/System.ServiceModel.Configuration/MsmqBindingElementBase.cs
@@ -109,11 +109,11 @@ namespace System.ServiceModel.Configuration
ConfigurationPropertyOptions.None);
retry_cycle_delay = new ConfigurationProperty ("retryCycleDelay",
- typeof (TimeSpan), "00:30:00", null/* FIXME: get converter for TimeSpan*/, null,
+ typeof (TimeSpan), "00:30:00", new TimeSpanConverter (), null,
ConfigurationPropertyOptions.None);
time_to_live = new ConfigurationProperty ("timeToLive",
- typeof (TimeSpan), "1.00:00:00", null/* FIXME: get converter for TimeSpan*/, null,
+ typeof (TimeSpan), "1.00:00:00", new TimeSpanConverter (), null,
ConfigurationPropertyOptions.None);
use_msmq_tracing = new ConfigurationProperty ("useMsmqTracing",
@@ -225,6 +225,7 @@ namespace System.ServiceModel.Configuration
[ConfigurationProperty ("retryCycleDelay",
Options = ConfigurationPropertyOptions.None,
DefaultValue = "00:30:00")]
+ [TypeConverter (typeof (TimeSpanConverter))]
public TimeSpan RetryCycleDelay {
get { return (TimeSpan) base [retry_cycle_delay]; }
set { base [retry_cycle_delay] = value; }
@@ -233,6 +234,7 @@ namespace System.ServiceModel.Configuration
[ConfigurationProperty ("timeToLive",
Options = ConfigurationPropertyOptions.None,
DefaultValue = "1.00:00:00")]
+ [TypeConverter (typeof (TimeSpanConverter))]
public TimeSpan TimeToLive {
get { return (TimeSpan) base [time_to_live]; }
set { base [time_to_live] = value; }
diff --git a/mcs/class/System.ServiceModel/System.ServiceModel.Configuration/MsmqElementBase.cs b/mcs/class/System.ServiceModel/System.ServiceModel.Configuration/MsmqElementBase.cs
index 81d05957c9f..1ad2b2a1f3f 100644
--- a/mcs/class/System.ServiceModel/System.ServiceModel.Configuration/MsmqElementBase.cs
+++ b/mcs/class/System.ServiceModel/System.ServiceModel.Configuration/MsmqElementBase.cs
@@ -4,7 +4,7 @@
// Author:
// Atsushi Enomoto <atsushi@ximian.com>
//
-// Copyright (C) 2006 Novell, Inc. http://www.novell.com
+// Copyright (C) 2006,2010 Novell, Inc. http://www.novell.com
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
@@ -186,7 +186,77 @@ namespace System.ServiceModel.Configuration
set { base ["useSourceJournal"] = value; }
}
+ public override void ApplyConfiguration (BindingElement bindingElement)
+ {
+ var b = (System.ServiceModel.Channels.MsmqBindingElementBase) bindingElement;
+ base.ApplyConfiguration (b);
+ b.CustomDeadLetterQueue = CustomDeadLetterQueue;
+ b.DeadLetterQueue = DeadLetterQueue;
+ b.Durable = Durable;
+ b.ExactlyOnce = ExactlyOnce;
+ b.MaxRetryCycles = MaxRetryCycles;
+ b.ReceiveErrorHandling = ReceiveErrorHandling;
+ b.ReceiveRetryCount = ReceiveRetryCount;
+ b.RetryCycleDelay = RetryCycleDelay;
+ b.TimeToLive = TimeToLive;
+ b.UseMsmqTracing = UseMsmqTracing;
+ b.UseSourceJournal = UseSourceJournal;
+ var bs = b.MsmqTransportSecurity;
+ var cs = MsmqTransportSecurity;
+ bs.MsmqAuthenticationMode = cs.MsmqAuthenticationMode;
+ bs.MsmqEncryptionAlgorithm = cs.MsmqEncryptionAlgorithm;
+ bs.MsmqProtectionLevel = cs.MsmqProtectionLevel;
+ bs.MsmqSecureHashAlgorithm = cs.MsmqSecureHashAlgorithm;
+ }
+
+ public override void CopyFrom (ServiceModelExtensionElement from)
+ {
+ var e = (MsmqElementBase) from;
+ base.CopyFrom (from);
+ CustomDeadLetterQueue = e.CustomDeadLetterQueue;
+ DeadLetterQueue = e.DeadLetterQueue;
+ Durable = e.Durable;
+ ExactlyOnce = e.ExactlyOnce;
+ MaxRetryCycles = e.MaxRetryCycles;
+ ReceiveErrorHandling = e.ReceiveErrorHandling;
+ ReceiveRetryCount = e.ReceiveRetryCount;
+ RetryCycleDelay = e.RetryCycleDelay;
+ TimeToLive = e.TimeToLive;
+ UseMsmqTracing = e.UseMsmqTracing;
+ UseSourceJournal = e.UseSourceJournal;
+
+ var es = e.MsmqTransportSecurity;
+ var cs = MsmqTransportSecurity;
+ cs.MsmqAuthenticationMode = es.MsmqAuthenticationMode;
+ cs.MsmqEncryptionAlgorithm = es.MsmqEncryptionAlgorithm;
+ cs.MsmqProtectionLevel = es.MsmqProtectionLevel;
+ cs.MsmqSecureHashAlgorithm = es.MsmqSecureHashAlgorithm;
+ }
+
+ protected internal override void InitializeFrom (BindingElement bindingElement)
+ {
+ var b = (System.ServiceModel.Channels.MsmqBindingElementBase) bindingElement;
+ base.InitializeFrom (b);
+ CustomDeadLetterQueue = b.CustomDeadLetterQueue;
+ DeadLetterQueue = b.DeadLetterQueue;
+ Durable = b.Durable;
+ ExactlyOnce = b.ExactlyOnce;
+ MaxRetryCycles = b.MaxRetryCycles;
+ ReceiveErrorHandling = b.ReceiveErrorHandling;
+ ReceiveRetryCount = b.ReceiveRetryCount;
+ RetryCycleDelay = b.RetryCycleDelay;
+ TimeToLive = b.TimeToLive;
+ UseMsmqTracing = b.UseMsmqTracing;
+ UseSourceJournal = b.UseSourceJournal;
+
+ var bs = b.MsmqTransportSecurity;
+ var cs = MsmqTransportSecurity;
+ cs.MsmqAuthenticationMode = bs.MsmqAuthenticationMode;
+ cs.MsmqEncryptionAlgorithm = bs.MsmqEncryptionAlgorithm;
+ cs.MsmqProtectionLevel = bs.MsmqProtectionLevel;
+ cs.MsmqSecureHashAlgorithm = bs.MsmqSecureHashAlgorithm;
+ }
}
}
diff --git a/mcs/class/System.ServiceModel/System.ServiceModel.Configuration/MsmqIntegrationElement.cs b/mcs/class/System.ServiceModel/System.ServiceModel.Configuration/MsmqIntegrationElement.cs
index 06876fdac3d..f1acfbd36df 100644
--- a/mcs/class/System.ServiceModel/System.ServiceModel.Configuration/MsmqIntegrationElement.cs
+++ b/mcs/class/System.ServiceModel/System.ServiceModel.Configuration/MsmqIntegrationElement.cs
@@ -4,7 +4,7 @@
// Author:
// Atsushi Enomoto <atsushi@ximian.com>
//
-// Copyright (C) 2006 Novell, Inc. http://www.novell.com
+// Copyright (C) 2006,2010 Novell, Inc. http://www.novell.com
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
@@ -86,12 +86,31 @@ namespace System.ServiceModel.Configuration
set { base ["serializationFormat"] = value; }
}
+ public override void ApplyConfiguration (BindingElement bindingElement)
+ {
+ var b = (System.ServiceModel.MsmqIntegration.MsmqIntegrationBindingElement) bindingElement;
+ base.ApplyConfiguration (b);
+ b.SerializationFormat = SerializationFormat;
+ }
+
+ public override void CopyFrom (ServiceModelExtensionElement from)
+ {
+ var e = (MsmqIntegrationElement) from;
+ base.CopyFrom (from);
+ SerializationFormat = e.SerializationFormat;
+ }
- [MonoTODO]
- protected internal override BindingElement CreateBindingElement () {
- throw new NotImplementedException ();
+ protected override TransportBindingElement CreateDefaultBindingElement ()
+ {
+ return new System.ServiceModel.MsmqIntegration.MsmqIntegrationBindingElement ();
}
+ protected internal override void InitializeFrom (BindingElement bindingElement)
+ {
+ var b = (System.ServiceModel.MsmqIntegration.MsmqIntegrationBindingElement) bindingElement;
+ base.InitializeFrom (b);
+ SerializationFormat = b.SerializationFormat;
+ }
}
}
diff --git a/mcs/class/System.ServiceModel/System.ServiceModel.Configuration/MsmqTransportElement.cs b/mcs/class/System.ServiceModel/System.ServiceModel.Configuration/MsmqTransportElement.cs
index 59bb88b8139..f4235e85cf5 100644
--- a/mcs/class/System.ServiceModel/System.ServiceModel.Configuration/MsmqTransportElement.cs
+++ b/mcs/class/System.ServiceModel/System.ServiceModel.Configuration/MsmqTransportElement.cs
@@ -4,7 +4,7 @@
// Author:
// Atsushi Enomoto <atsushi@ximian.com>
//
-// Copyright (C) 2006 Novell, Inc. http://www.novell.com
+// Copyright (C) 2006,2010 Novell, Inc. http://www.novell.com
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
@@ -108,11 +108,37 @@ namespace System.ServiceModel.Configuration
set { base ["useActiveDirectory"] = value; }
}
- [MonoTODO]
- protected internal override BindingElement CreateBindingElement () {
- throw new NotImplementedException ();
+ public override void ApplyConfiguration (BindingElement bindingElement)
+ {
+ var b = (MsmqTransportBindingElement) bindingElement;
+ base.ApplyConfiguration (b);
+ b.MaxPoolSize = MaxPoolSize;
+ b.QueueTransferProtocol = QueueTransferProtocol;
+ b.UseActiveDirectory = UseActiveDirectory;
}
+ public override void CopyFrom (ServiceModelExtensionElement from)
+ {
+ var e = (MsmqTransportElement) from;
+ base.CopyFrom (from);
+ MaxPoolSize = e.MaxPoolSize;
+ QueueTransferProtocol = e.QueueTransferProtocol;
+ UseActiveDirectory = e.UseActiveDirectory;
+ }
+
+ protected override TransportBindingElement CreateDefaultBindingElement ()
+ {
+ return new MsmqTransportBindingElement ();
+ }
+
+ protected internal override void InitializeFrom (BindingElement bindingElement)
+ {
+ var b = (MsmqTransportBindingElement) bindingElement;
+ base.InitializeFrom (b);
+ MaxPoolSize = b.MaxPoolSize;
+ QueueTransferProtocol = b.QueueTransferProtocol;
+ UseActiveDirectory = b.UseActiveDirectory;
+ }
}
}
diff --git a/mcs/class/System.ServiceModel/System.ServiceModel.Configuration/NamedPipeConnectionPoolSettingsElement.cs b/mcs/class/System.ServiceModel/System.ServiceModel.Configuration/NamedPipeConnectionPoolSettingsElement.cs
index e66eee7836a..7d29fdf1fab 100644
--- a/mcs/class/System.ServiceModel/System.ServiceModel.Configuration/NamedPipeConnectionPoolSettingsElement.cs
+++ b/mcs/class/System.ServiceModel/System.ServiceModel.Configuration/NamedPipeConnectionPoolSettingsElement.cs
@@ -72,7 +72,7 @@ namespace System.ServiceModel.Configuration
ConfigurationPropertyOptions.None);
idle_timeout = new ConfigurationProperty ("idleTimeout",
- typeof (TimeSpan), "00:02:00", null/* FIXME: get converter for TimeSpan*/, null,
+ typeof (TimeSpan), "00:02:00", new TimeSpanConverter (), null,
ConfigurationPropertyOptions.None);
max_outbound_connections_per_endpoint = new ConfigurationProperty ("maxOutboundConnectionsPerEndpoint",
@@ -105,6 +105,7 @@ namespace System.ServiceModel.Configuration
[ConfigurationProperty ("idleTimeout",
Options = ConfigurationPropertyOptions.None,
DefaultValue = "00:02:00")]
+ [TypeConverter (typeof (TimeSpanConverter))]
public TimeSpan IdleTimeout {
get { return (TimeSpan) base [idle_timeout]; }
set { base [idle_timeout] = value; }
diff --git a/mcs/class/System.ServiceModel/System.ServiceModel.Configuration/NamedPipeTransportElement.cs b/mcs/class/System.ServiceModel/System.ServiceModel.Configuration/NamedPipeTransportElement.cs
index d8ba18ec6b5..82f25055d41 100644
--- a/mcs/class/System.ServiceModel/System.ServiceModel.Configuration/NamedPipeTransportElement.cs
+++ b/mcs/class/System.ServiceModel/System.ServiceModel.Configuration/NamedPipeTransportElement.cs
@@ -4,7 +4,7 @@
// Author:
// Atsushi Enomoto <atsushi@ximian.com>
//
-// Copyright (C) 2006 Novell, Inc. http://www.novell.com
+// Copyright (C) 2006,2010 Novell, Inc. http://www.novell.com
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
@@ -54,7 +54,6 @@ using System.Xml;
namespace System.ServiceModel.Configuration
{
- [MonoTODO]
public sealed partial class NamedPipeTransportElement
: ConnectionOrientedTransportElement
{
@@ -87,11 +86,46 @@ namespace System.ServiceModel.Configuration
}
}
- [MonoTODO]
- protected internal override BindingElement CreateBindingElement () {
- throw new NotImplementedException ();
+ public override void ApplyConfiguration (BindingElement bindingElement)
+ {
+ var b = (NamedPipeTransportBindingElement) bindingElement;
+ base.ApplyConfiguration (b);
+
+ var bs = b.ConnectionPoolSettings;
+ var cs = ConnectionPoolSettings;
+ bs.GroupName = cs.GroupName;
+ bs.IdleTimeout = cs.IdleTimeout;
+ bs.MaxOutboundConnectionsPerEndpoint = cs.MaxOutboundConnectionsPerEndpoint;
+ }
+
+ public override void CopyFrom (ServiceModelExtensionElement from)
+ {
+ var e = (NamedPipeTransportElement) from;
+ base.CopyFrom (from);
+
+ var es = e.ConnectionPoolSettings;
+ var cs = ConnectionPoolSettings;
+ cs.GroupName = es.GroupName;
+ cs.IdleTimeout = es.IdleTimeout;
+ cs.MaxOutboundConnectionsPerEndpoint = es.MaxOutboundConnectionsPerEndpoint;
}
+ protected override TransportBindingElement CreateDefaultBindingElement ()
+ {
+ return new NamedPipeTransportBindingElement ();
+ }
+
+ protected internal override void InitializeFrom (BindingElement bindingElement)
+ {
+ var b = (NamedPipeTransportBindingElement) bindingElement;
+ base.InitializeFrom (b);
+
+ var bs = b.ConnectionPoolSettings;
+ var cs = ConnectionPoolSettings;
+ cs.GroupName = bs.GroupName;
+ cs.IdleTimeout = bs.IdleTimeout;
+ cs.MaxOutboundConnectionsPerEndpoint = bs.MaxOutboundConnectionsPerEndpoint;
+ }
}
}
diff --git a/mcs/class/System.ServiceModel/System.ServiceModel.Configuration/ServiceDebugElement.cs b/mcs/class/System.ServiceModel/System.ServiceModel.Configuration/ServiceDebugElement.cs
index b211ad9b580..41c8682689e 100644
--- a/mcs/class/System.ServiceModel/System.ServiceModel.Configuration/ServiceDebugElement.cs
+++ b/mcs/class/System.ServiceModel/System.ServiceModel.Configuration/ServiceDebugElement.cs
@@ -146,9 +146,9 @@ namespace System.ServiceModel.Configuration
HttpsHelpPageUrl = HttpsHelpPageUrl,
IncludeExceptionDetailInFaults = IncludeExceptionDetailInFaults,
};
- if (HttpHelpPageBinding != null)
+ if (!String.IsNullOrEmpty (HttpHelpPageBinding))
ret.HttpHelpPageBinding = ConfigUtil.CreateBinding (HttpHelpPageBinding, HttpHelpPageBindingConfiguration);
- if (HttpsHelpPageBinding != null)
+ if (!String.IsNullOrEmpty (HttpsHelpPageBinding))
ret.HttpsHelpPageBinding = ConfigUtil.CreateBinding (HttpsHelpPageBinding, HttpsHelpPageBindingConfiguration);
return ret;
}
diff --git a/mcs/class/System.ServiceModel/System.ServiceModel.Configuration/StandardBindingReliableSessionElement.cs b/mcs/class/System.ServiceModel/System.ServiceModel.Configuration/StandardBindingReliableSessionElement.cs
index ce9d811ae68..f6e42409f29 100644
--- a/mcs/class/System.ServiceModel/System.ServiceModel.Configuration/StandardBindingReliableSessionElement.cs
+++ b/mcs/class/System.ServiceModel/System.ServiceModel.Configuration/StandardBindingReliableSessionElement.cs
@@ -74,7 +74,7 @@ namespace System.ServiceModel.Configuration
{
properties = new ConfigurationPropertyCollection ();
inactivity_timeout = new ConfigurationProperty ("inactivityTimeout",
- typeof (TimeSpan), "00:10:00", null/* FIXME: get converter for TimeSpan*/, null,
+ typeof (TimeSpan), "00:10:00", new TimeSpanConverter (), null,
ConfigurationPropertyOptions.None);
ordered = new ConfigurationProperty ("ordered",
@@ -95,6 +95,7 @@ namespace System.ServiceModel.Configuration
[ConfigurationProperty ("inactivityTimeout",
Options = ConfigurationPropertyOptions.None,
DefaultValue = "00:10:00")]
+ [TypeConverter (typeof (TimeSpanConverter))]
public TimeSpan InactivityTimeout {
get { return (TimeSpan) base [inactivity_timeout]; }
set { base [inactivity_timeout] = value; }
diff --git a/mcs/class/System.ServiceModel/System.ServiceModel.Configuration/TcpConnectionPoolSettingsElement.cs b/mcs/class/System.ServiceModel/System.ServiceModel.Configuration/TcpConnectionPoolSettingsElement.cs
index c5509bbb1c2..ac48e04ca54 100644
--- a/mcs/class/System.ServiceModel/System.ServiceModel.Configuration/TcpConnectionPoolSettingsElement.cs
+++ b/mcs/class/System.ServiceModel/System.ServiceModel.Configuration/TcpConnectionPoolSettingsElement.cs
@@ -73,11 +73,11 @@ namespace System.ServiceModel.Configuration
ConfigurationPropertyOptions.None);
idle_timeout = new ConfigurationProperty ("idleTimeout",
- typeof (TimeSpan), "00:02:00", null/* FIXME: get converter for TimeSpan*/, null,
+ typeof (TimeSpan), "00:02:00", new TimeSpanConverter (), null,
ConfigurationPropertyOptions.None);
lease_timeout = new ConfigurationProperty ("leaseTimeout",
- typeof (TimeSpan), "00:05:00", null/* FIXME: get converter for TimeSpan*/, null,
+ typeof (TimeSpan), "00:05:00", new TimeSpanConverter (), null,
ConfigurationPropertyOptions.None);
max_outbound_connections_per_endpoint = new ConfigurationProperty ("maxOutboundConnectionsPerEndpoint",
@@ -111,6 +111,7 @@ namespace System.ServiceModel.Configuration
[ConfigurationProperty ("idleTimeout",
Options = ConfigurationPropertyOptions.None,
DefaultValue = "00:02:00")]
+ [TypeConverter (typeof (TimeSpanConverter))]
public TimeSpan IdleTimeout {
get { return (TimeSpan) base [idle_timeout]; }
set { base [idle_timeout] = value; }
@@ -119,6 +120,7 @@ namespace System.ServiceModel.Configuration
[ConfigurationProperty ("leaseTimeout",
Options = ConfigurationPropertyOptions.None,
DefaultValue = "00:05:00")]
+ [TypeConverter (typeof (TimeSpanConverter))]
public TimeSpan LeaseTimeout {
get { return (TimeSpan) base [lease_timeout]; }
set { base [lease_timeout] = value; }
diff --git a/mcs/class/System.ServiceModel/System.ServiceModel.Configuration/TcpTransportElement.cs b/mcs/class/System.ServiceModel/System.ServiceModel.Configuration/TcpTransportElement.cs
index c75adfb8e1a..e11ebbebf8b 100644
--- a/mcs/class/System.ServiceModel/System.ServiceModel.Configuration/TcpTransportElement.cs
+++ b/mcs/class/System.ServiceModel/System.ServiceModel.Configuration/TcpTransportElement.cs
@@ -4,7 +4,7 @@
// Author:
// Atsushi Enomoto <atsushi@ximian.com>
//
-// Copyright (C) 2006 Novell, Inc. http://www.novell.com
+// Copyright (C) 2006,2010 Novell, Inc. http://www.novell.com
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
@@ -116,12 +116,37 @@ namespace System.ServiceModel.Configuration
set { base ["teredoEnabled"] = value; }
}
+ public override void ApplyConfiguration (BindingElement bindingElement)
+ {
+ var b = (TcpTransportBindingElement) bindingElement;
+ base.ApplyConfiguration (b);
+ b.ListenBacklog = ListenBacklog;
+ b.PortSharingEnabled = PortSharingEnabled;
+ b.TeredoEnabled = TeredoEnabled;
+ }
+
+ public override void CopyFrom (ServiceModelExtensionElement from)
+ {
+ var e = (TcpTransportElement) from;
+ base.CopyFrom (from);
+ ListenBacklog = e.ListenBacklog;
+ PortSharingEnabled = e.PortSharingEnabled;
+ TeredoEnabled = e.TeredoEnabled;
+ }
- [MonoTODO]
- protected internal override BindingElement CreateBindingElement () {
- throw new NotImplementedException ();
+ protected override TransportBindingElement CreateDefaultBindingElement ()
+ {
+ return new TcpTransportBindingElement ();
}
+ protected internal override void InitializeFrom (BindingElement bindingElement)
+ {
+ var b = (TcpTransportBindingElement) bindingElement;
+ base.InitializeFrom (b);
+ ListenBacklog = b.ListenBacklog;
+ PortSharingEnabled = b.PortSharingEnabled;
+ TeredoEnabled = b.TeredoEnabled;
+ }
}
}
diff --git a/mcs/class/System.ServiceModel/System.ServiceModel.Configuration/TransportElement.cs b/mcs/class/System.ServiceModel/System.ServiceModel.Configuration/TransportElement.cs
index 081e2b137e3..0ac73504b83 100644
--- a/mcs/class/System.ServiceModel/System.ServiceModel.Configuration/TransportElement.cs
+++ b/mcs/class/System.ServiceModel/System.ServiceModel.Configuration/TransportElement.cs
@@ -4,7 +4,7 @@
// Author:
// Atsushi Enomoto <atsushi@ximian.com>
//
-// Copyright (C) 2006 Novell, Inc. http://www.novell.com
+// Copyright (C) 2006,2010 Novell, Inc. http://www.novell.com
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
@@ -108,7 +108,41 @@ namespace System.ServiceModel.Configuration
}
}
+ public override void ApplyConfiguration (BindingElement bindingElement)
+ {
+ var b = (TransportBindingElement) bindingElement;
+ base.ApplyConfiguration (b);
+ b.ManualAddressing = ManualAddressing;
+ b.MaxBufferPoolSize = MaxBufferPoolSize;
+ b.MaxReceivedMessageSize = MaxReceivedMessageSize;
+ }
+
+ public override void CopyFrom (ServiceModelExtensionElement from)
+ {
+ var e = (TransportElement) from;
+ base.CopyFrom (from);
+ ManualAddressing = e.ManualAddressing;
+ MaxBufferPoolSize = e.MaxBufferPoolSize;
+ MaxReceivedMessageSize = e.MaxReceivedMessageSize;
+ }
+ protected internal override BindingElement CreateBindingElement ()
+ {
+ var b = CreateDefaultBindingElement ();
+ ApplyConfiguration (b);
+ return b;
+ }
+
+ protected abstract TransportBindingElement CreateDefaultBindingElement ();
+
+ protected internal override void InitializeFrom (BindingElement bindingElement)
+ {
+ var b = (TransportBindingElement) bindingElement;
+ base.InitializeFrom (b);
+ ManualAddressing = b.ManualAddressing;
+ MaxBufferPoolSize = b.MaxBufferPoolSize;
+ MaxReceivedMessageSize = b.MaxReceivedMessageSize;
+ }
}
}
diff --git a/mcs/class/System.ServiceModel/System.ServiceModel.Description/ChangeLog b/mcs/class/System.ServiceModel/System.ServiceModel.Description/ChangeLog
index 13309137d54..26fc2493868 100644
--- a/mcs/class/System.ServiceModel/System.ServiceModel.Description/ChangeLog
+++ b/mcs/class/System.ServiceModel/System.ServiceModel.Description/ChangeLog
@@ -1,3 +1,8 @@
+2010-03-15 Atsushi Enomoto <atsushi@ximian.com>
+
+ * ServiceEndpointCollection.cs : those overrides are rather to check
+ null arguments, not to skip contract duplicates.
+
2010-03-12 Atsushi Enomoto <atsushi@ximian.com>
* MessageDescription.cs : implement MessageName.
diff --git a/mcs/class/System.ServiceModel/System.ServiceModel.Description/ServiceEndpointCollection.cs b/mcs/class/System.ServiceModel/System.ServiceModel.Description/ServiceEndpointCollection.cs
index 58d0024e3de..191d411ed41 100644
--- a/mcs/class/System.ServiceModel/System.ServiceModel.Description/ServiceEndpointCollection.cs
+++ b/mcs/class/System.ServiceModel/System.ServiceModel.Description/ServiceEndpointCollection.cs
@@ -107,20 +107,18 @@ namespace System.ServiceModel.Description
return list;
}
- [MonoTODO]
protected override void InsertItem (int index, ServiceEndpoint item)
{
- if (Find (new XmlQualifiedName (item.Contract.Name, item.Contract.Namespace)) == null)
- base.InsertItem (index, item);
+ if (item == null)
+ throw new ArgumentNullException ("item");
+ base.InsertItem (index, item);
}
- [MonoTODO]
protected override void SetItem (int index, ServiceEndpoint item)
{
- if (Find (new XmlQualifiedName (item.Contract.Name, item.Contract.Namespace)) == null)
- base.SetItem (index, item);
- else
- base.RemoveItem (index);
+ if (item == null)
+ throw new ArgumentNullException ("item");
+ base.SetItem (index, item);
}
}
}
diff --git a/mcs/class/System.ServiceModel/System.ServiceModel/ChangeLog b/mcs/class/System.ServiceModel/System.ServiceModel/ChangeLog
index d2d6c934443..cd6ccd2586d 100755
--- a/mcs/class/System.ServiceModel/System.ServiceModel/ChangeLog
+++ b/mcs/class/System.ServiceModel/System.ServiceModel/ChangeLog
@@ -1,3 +1,9 @@
+2010-03-15 Atsushi Enomoto <atsushi@ximian.com>
+
+ * ServiceHostBase.cs : do not reject endpoints with an identical
+ contract to existing ones but with different binding, address or
+ listen URI.
+
2010-03-09 Atsushi Enomoto <atsushi@ximian.com>
* HttpTransportSecurity.cs : remove MonoTODOs.
diff --git a/mcs/class/System.ServiceModel/System.ServiceModel/ServiceHostBase.cs b/mcs/class/System.ServiceModel/System.ServiceModel/ServiceHostBase.cs
index f799f9a3cd7..a964fb88250 100644
--- a/mcs/class/System.ServiceModel/System.ServiceModel/ServiceHostBase.cs
+++ b/mcs/class/System.ServiceModel/System.ServiceModel/ServiceHostBase.cs
@@ -279,7 +279,7 @@ namespace System.ServiceModel
ContractDescription cd, Binding binding, EndpointAddress address, Uri listenUri)
{
foreach (ServiceEndpoint e in Description.Endpoints)
- if (e.Contract == cd)
+ if (e.Contract == cd && e.Binding == binding && e.Address == address && e.ListenUri.Equals (listenUri))
return e;
ServiceEndpoint se = new ServiceEndpoint (cd, binding, address);
se.ListenUri = listenUri.IsAbsoluteUri ? listenUri : new Uri (address.Uri, listenUri);
@@ -287,7 +287,6 @@ namespace System.ServiceModel
return se;
}
- [MonoTODO]
protected virtual void ApplyConfiguration ()
{
if (Description == null)
@@ -316,7 +315,6 @@ namespace System.ServiceModel
// services
foreach (ServiceEndpointElement endpoint in service.Endpoints) {
- // FIXME: consider BindingName as well
ServiceEndpoint se = AddServiceEndpoint (
endpoint.Contract,
ConfigUtil.CreateBinding (endpoint.Binding, endpoint.BindingConfiguration),
diff --git a/mcs/class/System.ServiceModel/monotouch_System.ServiceModel.dll.sources b/mcs/class/System.ServiceModel/monotouch_System.ServiceModel.dll.sources
index 2870b466997..fbaa524eab3 100644
--- a/mcs/class/System.ServiceModel/monotouch_System.ServiceModel.dll.sources
+++ b/mcs/class/System.ServiceModel/monotouch_System.ServiceModel.dll.sources
@@ -1,3 +1,3 @@
../../build/common/Consts.cs
../../build/common/MonoTODOAttribute.cs
-#include net_2_1_raw_System.ServiceModel.dll.sources
+#include moonlight_raw_System.ServiceModel.dll.sources
diff --git a/mcs/class/System.ServiceModel/net_2_1_raw_System.ServiceModel.dll.sources b/mcs/class/System.ServiceModel/moonlight_raw_System.ServiceModel.dll.sources
index 010821335ea..010821335ea 100755
--- a/mcs/class/System.ServiceModel/net_2_1_raw_System.ServiceModel.dll.sources
+++ b/mcs/class/System.ServiceModel/moonlight_raw_System.ServiceModel.dll.sources
diff --git a/mcs/class/System.ServiceModel/net_4_0_System.ServiceModel.dll.sources b/mcs/class/System.ServiceModel/net_4_0_System.ServiceModel.dll.sources
index 550ccf05bd4..f8b48c78c83 100644
--- a/mcs/class/System.ServiceModel/net_4_0_System.ServiceModel.dll.sources
+++ b/mcs/class/System.ServiceModel/net_4_0_System.ServiceModel.dll.sources
@@ -38,3 +38,8 @@
../System.ServiceModel.Web/System.ServiceModel.Syndication/UrlSyndicationContent.cs
../System.ServiceModel.Web/System.ServiceModel.Syndication/Workspace.cs
../System.ServiceModel.Web/System.ServiceModel.Syndication/XmlSyndicationContent.cs
+../System.ServiceModel.Web/System/UriTemplate.cs
+../System.ServiceModel.Web/System/UriTemplateEquivalenceComparer.cs
+../System.ServiceModel.Web/System/UriTemplateMatch.cs
+../System.ServiceModel.Web/System/UriTemplateMatchException.cs
+../System.ServiceModel.Web/System/UriTemplateTable.cs
diff --git a/mcs/class/System.Web.Mvc/ChangeLog b/mcs/class/System.Web.Mvc/ChangeLog
index 171a479d7fa..3815b79811d 100644
--- a/mcs/class/System.Web.Mvc/ChangeLog
+++ b/mcs/class/System.Web.Mvc/ChangeLog
@@ -1,3 +1,8 @@
+2010-03-15 Marek Habersack <mhabersack@novell.com>
+
+ * Makefile (LIBRARY_COMPAT): added - this lib is installed in the
+ compat dir.
+
2010-02-17 Marek Habersack <mhabersack@novell.com>
* Makefile: RESX_RES is no more, it was replaced with
diff --git a/mcs/class/System.Web.Mvc/Makefile b/mcs/class/System.Web.Mvc/Makefile
index bc679372257..8f8ce46a161 100644
--- a/mcs/class/System.Web.Mvc/Makefile
+++ b/mcs/class/System.Web.Mvc/Makefile
@@ -4,6 +4,7 @@ include ../../build/rules.make
LIBRARY = System.Web.Mvc.dll
LIBRARY_USE_INTERMEDIATE_FILE = yes
+LIBRARY_COMPAT = yes
RESX_DIST = System.Web.Mvc/Resources/MvcResources.resx
diff --git a/mcs/class/System.Web.Mvc2/ChangeLog b/mcs/class/System.Web.Mvc2/ChangeLog
new file mode 100644
index 00000000000..f05ed3499a3
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/ChangeLog
@@ -0,0 +1,5 @@
+2010-02-09 Marek Habersack <mhabersack@novell.com>
+
+ * Makefile: added references to
+ System.ComponentModel.DataAnnotations and System.Data.Linq
+
diff --git a/mcs/class/System.Web.Mvc2/GlobalSuppressions.cs b/mcs/class/System.Web.Mvc2/GlobalSuppressions.cs
new file mode 100644
index 00000000000..71130ae5011
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/GlobalSuppressions.cs
@@ -0,0 +1,26 @@
+// This file is used by Code Analysis to maintain SuppressMessage
+// attributes that are applied to this project.
+// Project-level suppressions either have no target or are given
+// a specific target and scoped to a namespace, type, member, etc.
+//
+// To add a suppression to this file, right-click the message in the
+// Error List, point to "Suppress Message(s)", and click
+// "In Project Suppression File".
+// You do not need to add suppressions to this file manually.
+
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Web.Mvc.TempDataDictionary.#System.Collections.Generic.ICollection`1<System.Collections.Generic.KeyValuePair`2<System.String,System.Object>>.Contains(System.Collections.Generic.KeyValuePair`2<System.String,System.Object>)",
+ Justification = "There are no defined scenarios for wanting to derive from this class, but we don't want to prevent it either.")]
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Web.Mvc.TempDataDictionary.#System.Collections.Generic.ICollection`1<System.Collections.Generic.KeyValuePair`2<System.String,System.Object>>.CopyTo(System.Collections.Generic.KeyValuePair`2<System.String,System.Object>[],System.Int32)",
+ Justification = "There are no defined scenarios for wanting to derive from this class, but we don't want to prevent it either.")]
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Web.Mvc.TempDataDictionary.#System.Collections.Generic.ICollection`1<System.Collections.Generic.KeyValuePair`2<System.String,System.Object>>.IsReadOnly",
+ Justification = "There are no defined scenarios for wanting to derive from this class, but we don't want to prevent it either.")]
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "System.Web.Mvc.Ajax",
+ Justification = "Helpers reside within a separate namespace to support alternate helper classes.")]
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Portability", "CA1903:UseOnlyApiFromTargetedFramework", MessageId = "System.Web.Abstractions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35",
+ Justification = "MVC has a .NET Framework 3.5 SP1 dependency.")]
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Portability", "CA1903:UseOnlyApiFromTargetedFramework", MessageId = "System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35",
+ Justification = "MVC has a .NET Framework 3.5 SP1 dependency.")]
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Portability", "CA1903:UseOnlyApiFromTargetedFramework", MessageId = "System.ComponentModel.DataAnnotations, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35",
+ Justification = "MVC has a .NET Framework 3.5 SP1 dependency.")]
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Portability", "CA1903:UseOnlyApiFromTargetedFramework", MessageId = "System.Data.Entity, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089",
+ Justification = "MVC has a .NET Framework 3.5 SP1 dependency.")]
diff --git a/mcs/class/System.Web.Mvc2/Makefile b/mcs/class/System.Web.Mvc2/Makefile
new file mode 100644
index 00000000000..d6ac2835d43
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/Makefile
@@ -0,0 +1,50 @@
+thisdir = class/System.Web.Mvc2
+SUBDIRS =
+include ../../build/rules.make
+
+LIBRARY = System.Web.Mvc.dll
+LIBRARY_USE_INTERMEDIATE_FILE = yes
+
+RESX_DIST = System.Web.Mvc/Resources/MvcResources.resx
+
+LIB_MCS_FLAGS = \
+ /warnaserror- \
+ /noconfig \
+ /keyfile:../winfx.pub \
+ /r:System.dll \
+ /r:System.Core.dll \
+ /r:System.Configuration.dll \
+ /r:System.Data.dll \
+ /r:System.Xml.dll \
+ /r:System.Web.dll \
+ /r:System.Web.Abstractions.dll \
+ /r:System.Web.Routing.dll \
+ /r:System.Web.Extensions.dll \
+ /r:System.ComponentModel.DataAnnotations.dll \
+ /r:System.Data.Linq.dll \
+ $(foreach r, $(RESX_RES), /resource:$(r),System.Web.Mvc.Resources.$(notdir $(r)))
+
+ifeq (2.0, $(FRAMEWORK_VERSION))
+# This is a .NET 3.5 only assembly, but built during the 2.0 build
+LIB_MCS_FLAGS += -d:NET_3_5 -d:MONO
+endif
+
+EXTRA_DISTFILES = $(RESX_DIST)
+
+# This is a .NET 3.5+ assembly - it must be built ONLY in the 2.0 profile
+VALID_PROFILE := $(filter net_2_0, $(PROFILE))
+ifndef VALID_PROFILE
+LIBRARY_NAME = dummy-System.Web.Mvc.dll
+NO_INSTALL = yes
+NO_SIGN_ASSEMBLY = yes
+NO_TEST = yes
+else
+RESOURCES = $(RESX_DIST:.resx=.resources)
+endif
+
+include ../../build/library.make
+
+$(the_lib): $(RESOURCES)
+
+$(RESOURCES): %.resources: %.resx
+ $(RESGEN) `echo $< | $(PLATFORM_CHANGE_SEPARATOR_CMD)`
diff --git a/mcs/class/System.Web.Mvc2/Properties/AssemblyInfo.cs b/mcs/class/System.Web.Mvc2/Properties/AssemblyInfo.cs
new file mode 100644
index 00000000000..4a06eb8c083
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/Properties/AssemblyInfo.cs
@@ -0,0 +1,63 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+using System;
+using System.Diagnostics.CodeAnalysis;
+using System.Reflection;
+using System.Resources;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Security;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("System.Web.Mvc.dll")]
+[assembly: AssemblyDescription("System.Web.Mvc.dll")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Microsoft Corporation")]
+[assembly: AssemblyProduct("Microsoft® .NET Framework")]
+[assembly: AssemblyCopyright("© Microsoft Corporation. All rights reserved.")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("4b5f4208-c6b0-4c37-9a41-63325ffa52ad")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("2.0.0.0")]
+[assembly: AssemblyFileVersion("2.0.50217.0")]
+
+[assembly: AllowPartiallyTrustedCallers]
+[assembly: SecurityTransparent]
+[assembly: CLSCompliant(true)]
+[assembly: NeutralResourcesLanguage("en-US")]
+
+[assembly: SuppressMessage("Microsoft.Design", "CA2210:AssembliesShouldHaveValidStrongNames",
+ Justification = "Assembly is delay-signed.")]
+
+[assembly: AssemblyDelaySign (true)]
+[assembly: AssemblyKeyFile("../winfx.pub")]
diff --git a/mcs/class/System.Web.Mvc2/Properties/ChangeLog b/mcs/class/System.Web.Mvc2/Properties/ChangeLog
new file mode 100644
index 00000000000..6e4c8c6ef29
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/Properties/ChangeLog
@@ -0,0 +1,5 @@
+2010-02-09 Marek Habersack <mhabersack@novell.com>
+
+ * AssemblyInfo.cs: added Mono-specific attributes to delay-sign
+ the assembly and use the winfs.pub key
+
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc.csproj b/mcs/class/System.Web.Mvc2/System.Web.Mvc.csproj
new file mode 100644
index 00000000000..a47c38242a9
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc.csproj
@@ -0,0 +1,381 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProductVersion>9.0.30729</ProductVersion>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{3D3FFD8A-624D-4E9B-954B-E1C105507975}</ProjectGuid>
+ <OutputType>Library</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <RootNamespace>System.Web</RootNamespace>
+ <AssemblyName>System.Web.Mvc</AssemblyName>
+ <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
+ <FileAlignment>512</FileAlignment>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
+ <BaseAddress>1609891840</BaseAddress>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>..\..\bin\Debug\</OutputPath>
+ <DefineConstants>DEBUG;TRACE</DefineConstants>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>..\..\bin\Release\</OutputPath>
+ <DefineConstants>TRACE</DefineConstants>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="System" />
+ <Reference Include="System.ComponentModel.DataAnnotations">
+ <RequiredTargetFramework>3.5</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="System.configuration" />
+ <Reference Include="System.Core">
+ <RequiredTargetFramework>3.5</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="System.Data.Entity">
+ <RequiredTargetFramework>3.5</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="System.Data.Linq">
+ <RequiredTargetFramework>3.5</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="System.Web" />
+ <Reference Include="System.Data" />
+ <Reference Include="System.Web.Abstractions">
+ <RequiredTargetFramework>3.5</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="System.Web.Extensions">
+ <RequiredTargetFramework>3.5</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="System.Web.Routing">
+ <RequiredTargetFramework>3.5</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="System.Xml" />
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="Mvc\AssociatedMetadataProvider.cs" />
+ <Compile Include="Mvc\ActionExecutedContext.cs" />
+ <Compile Include="Mvc\ActionExecutingContext.cs" />
+ <Compile Include="Mvc\AntiForgeryData.cs" />
+ <Compile Include="Mvc\AntiForgeryDataSerializer.cs" />
+ <Compile Include="Mvc\ClientDataTypeModelValidatorProvider.cs" />
+ <Compile Include="Mvc\AssociatedValidatorProvider.cs" />
+ <Compile Include="Mvc\Async\ActionDescriptorCreator.cs" />
+ <Compile Include="Mvc\Async\AsyncActionDescriptor.cs" />
+ <Compile Include="Mvc\Async\AsyncActionMethodSelector.cs" />
+ <Compile Include="Mvc\Async\AsyncControllerActionInvoker.cs" />
+ <Compile Include="Mvc\Async\SynchronousOperationException.cs" />
+ <Compile Include="Mvc\Async\AsyncManager.cs" />
+ <Compile Include="Mvc\AsyncTimeoutAttribute.cs" />
+ <Compile Include="Mvc\Async\BeginInvokeDelegate.cs" />
+ <Compile Include="Mvc\Async\AsyncResultWrapper.cs" />
+ <Compile Include="Mvc\Async\AsyncVoid.cs" />
+ <Compile Include="Mvc\AsyncController.cs" />
+ <Compile Include="Mvc\Async\AsyncUtil.cs" />
+ <Compile Include="Mvc\Async\IAsyncController.cs" />
+ <Compile Include="Mvc\Async\IAsyncActionInvoker.cs" />
+ <Compile Include="Mvc\Async\IAsyncManagerContainer.cs" />
+ <Compile Include="Mvc\UrlParameter.cs" />
+ <Compile Include="Mvc\FormValueProvider.cs" />
+ <Compile Include="Mvc\FormValueProviderFactory.cs" />
+ <Compile Include="Mvc\HttpFileCollectionValueProvider.cs" />
+ <Compile Include="Mvc\HttpFileCollectionValueProviderFactory.cs" />
+ <Compile Include="Mvc\QueryStringValueProvider.cs" />
+ <Compile Include="Mvc\QueryStringValueProviderFactory.cs" />
+ <Compile Include="Mvc\RangeAttributeAdapter.cs" />
+ <Compile Include="Mvc\RegularExpressionAttributeAdapter.cs" />
+ <Compile Include="Mvc\RequiredAttributeAdapter.cs" />
+ <Compile Include="Mvc\RouteDataValueProvider.cs" />
+ <Compile Include="Mvc\RouteDataValueProviderFactory.cs" />
+ <Compile Include="Mvc\StringLengthAttributeAdapter.cs" />
+ <Compile Include="Mvc\TypeCacheUtil.cs" />
+ <Compile Include="Mvc\TypeCacheSerializer.cs" />
+ <Compile Include="Mvc\Html\DisplayTextExtensions.cs" />
+ <Compile Include="Mvc\NoAsyncTimeoutAttribute.cs" />
+ <Compile Include="Mvc\Async\OperationCounter.cs" />
+ <Compile Include="Mvc\Async\ReflectedAsyncActionDescriptor.cs" />
+ <Compile Include="Mvc\Async\ReflectedAsyncControllerDescriptor.cs" />
+ <Compile Include="Mvc\Async\Trigger.cs" />
+ <Compile Include="Mvc\Async\TriggerListener.cs" />
+ <Compile Include="Mvc\Async\SimpleAsyncResult.cs" />
+ <Compile Include="Mvc\Async\EndInvokeDelegate.cs" />
+ <Compile Include="Mvc\Async\EndInvokeDelegate`1.cs" />
+ <Compile Include="Mvc\Async\SynchronizationContextUtil.cs" />
+ <Compile Include="Mvc\AuthorizationContext.cs" />
+ <Compile Include="Mvc\ByteArrayModelBinder.cs" />
+ <Compile Include="Mvc\ControllerContext.cs" />
+ <Compile Include="Mvc\Html\ChildActionExtensions.cs" />
+ <Compile Include="Mvc\ParameterInfoUtil.cs" />
+ <Compile Include="Mvc\HttpHandlerUtil.cs" />
+ <Compile Include="Mvc\ChildActionOnlyAttribute.cs" />
+ <Compile Include="Mvc\TypeDescriptorHelper.cs" />
+ <Compile Include="Mvc\ValueProviderFactories.cs" />
+ <Compile Include="Mvc\ValueProviderFactory.cs" />
+ <Compile Include="Mvc\ValueProviderFactoryCollection.cs" />
+ <Compile Include="Mvc\ValueProviderCollection.cs" />
+ <Compile Include="Mvc\DictionaryValueProvider`1.cs" />
+ <Compile Include="Mvc\NameValueCollectionValueProvider.cs" />
+ <Compile Include="Mvc\ValueProviderUtil.cs" />
+ <Compile Include="Mvc\IValueProvider.cs" />
+ <Compile Include="Mvc\DataErrorInfoModelValidatorProvider.cs" />
+ <Compile Include="Mvc\ModelValidatorProviderCollection.cs" />
+ <Compile Include="Mvc\DataAnnotationsModelMetadata.cs" />
+ <Compile Include="Mvc\HiddenInputAttribute.cs" />
+ <Compile Include="Mvc\HttpGetAttribute.cs" />
+ <Compile Include="Mvc\HttpPutAttribute.cs" />
+ <Compile Include="Mvc\HttpDeleteAttribute.cs" />
+ <Compile Include="Mvc\DynamicTypeGenerator.cs" />
+ <Compile Include="Mvc\ModelClientValidationRequiredRule.cs" />
+ <Compile Include="Mvc\ModelClientValidationRangeRule.cs" />
+ <Compile Include="Mvc\ModelClientValidationStringLengthRule.cs" />
+ <Compile Include="Mvc\MvcHtmlString.cs" />
+ <Compile Include="Mvc\DataAnnotationsModelValidator.cs" />
+ <Compile Include="Mvc\DataAnnotationsModelValidatorProvider.cs" />
+ <Compile Include="Mvc\DataAnnotationsModelValidator`1.cs" />
+ <Compile Include="Mvc\EmptyModelValidatorProvider.cs" />
+ <Compile Include="Mvc\ExpressionHelper.cs" />
+ <Compile Include="Mvc\FieldValidationMetadata.cs" />
+ <Compile Include="Mvc\FormContext.cs" />
+ <Compile Include="Mvc\JsonRequestBehavior.cs" />
+ <Compile Include="Mvc\ModelClientValidationRegexRule.cs" />
+ <Compile Include="Mvc\ModelClientValidationRule.cs" />
+ <Compile Include="Mvc\ModelValidationResult.cs" />
+ <Compile Include="Mvc\ModelValidator.cs" />
+ <Compile Include="Mvc\ModelValidatorProvider.cs" />
+ <Compile Include="Mvc\ModelValidatorProviders.cs" />
+ <Compile Include="Mvc\RequireHttpsAttribute.cs" />
+ <Compile Include="Mvc\HttpRequestExtensions.cs" />
+ <Compile Include="Mvc\DataAnnotationsModelMetadataProvider.cs" />
+ <Compile Include="Mvc\EmptyModelMetadataProvider.cs" />
+ <Compile Include="Mvc\ExpressionUtil\BinaryExpressionFingerprint.cs" />
+ <Compile Include="Mvc\ExpressionUtil\CachedExpressionCompiler.cs" />
+ <Compile Include="Mvc\ExpressionUtil\ExpressionParser.cs" />
+ <Compile Include="Mvc\ModelMetadata.cs" />
+ <Compile Include="Mvc\ModelMetadataProvider.cs" />
+ <Compile Include="Mvc\ModelMetadataProviders.cs" />
+ <Compile Include="Mvc\ExpressionUtil\CompiledExpressionDelegate`2.cs" />
+ <Compile Include="Mvc\ExpressionUtil\ConstantExpressionFingerprint.cs" />
+ <Compile Include="Mvc\ExpressionUtil\ConditionalExpressionFingerprint.cs" />
+ <Compile Include="Mvc\ExpressionUtil\MethodCallExpressionFingerprint.cs" />
+ <Compile Include="Mvc\ExpressionUtil\MemberExpressionFingerprint.cs" />
+ <Compile Include="Mvc\ExpressionUtil\UnaryExpressionFingerprint.cs" />
+ <Compile Include="Mvc\ExpressionUtil\ParserContext.cs" />
+ <Compile Include="Mvc\ExpressionUtil\ParameterExpressionFingerprint.cs" />
+ <Compile Include="Mvc\ExpressionUtil\HashCodeCombiner.cs" />
+ <Compile Include="Mvc\ExpressionUtil\ExpressionFingerprint.cs" />
+ <Compile Include="Mvc\ExpressionUtil\FastTrack`2.cs" />
+ <Compile Include="Mvc\AreaHelpers.cs" />
+ <Compile Include="Mvc\AreaRegistration.cs" />
+ <Compile Include="Mvc\AreaRegistrationContext.cs" />
+ <Compile Include="Mvc\Error.cs" />
+ <Compile Include="Mvc\IRouteWithArea.cs" />
+ <Compile Include="Mvc\Async\SingleEntryGate.cs" />
+ <Compile Include="Mvc\Html\PartialExtensions.cs" />
+ <Compile Include="Mvc\LinqBinaryModelBinder.cs" />
+ <Compile Include="Mvc\TryGetValueDelegate.cs" />
+ <Compile Include="Mvc\ViewDataInfo.cs" />
+ <Compile Include="Mvc\Html\DefaultDisplayTemplates.cs" />
+ <Compile Include="Mvc\Html\DefaultEditorTemplates.cs" />
+ <Compile Include="Mvc\Html\DisplayExtensions.cs" />
+ <Compile Include="Mvc\Html\EditorExtensions.cs" />
+ <Compile Include="Mvc\Html\LabelExtensions.cs" />
+ <Compile Include="Mvc\Html\TemplateHelpers.cs" />
+ <Compile Include="Mvc\HttpPostAttribute.cs" />
+ <Compile Include="Mvc\PathHelpers.cs" />
+ <Compile Include="Mvc\ExceptionContext.cs" />
+ <Compile Include="Mvc\ResultExecutedContext.cs" />
+ <Compile Include="Mvc\ResultExecutingContext.cs" />
+ <Compile Include="Mvc\TemplateInfo.cs" />
+ <Compile Include="Mvc\ValidateAntiForgeryTokenAttribute.cs" />
+ <Compile Include="Mvc\HttpAntiForgeryException.cs" />
+ <Compile Include="Mvc\JavaScriptResult.cs" />
+ <Compile Include="Mvc\ActionDescriptor.cs" />
+ <Compile Include="Mvc\ActionMethodDispatcher.cs" />
+ <Compile Include="Mvc\ActionMethodSelector.cs" />
+ <Compile Include="Mvc\ActionMethodSelectorAttribute.cs" />
+ <Compile Include="Mvc\ActionNameSelectorAttribute.cs" />
+ <Compile Include="Mvc\AuthorizeAttribute.cs" />
+ <Compile Include="Mvc\Ajax\AjaxOptions.cs" />
+ <Compile Include="Mvc\Ajax\AjaxExtensions.cs" />
+ <Compile Include="Mvc\ActionMethodDispatcherCache.cs" />
+ <Compile Include="Mvc\BindAttribute.cs" />
+ <Compile Include="Mvc\ControllerBase.cs" />
+ <Compile Include="Mvc\ActionNameAttribute.cs" />
+ <Compile Include="Mvc\AcceptVerbsAttribute.cs" />
+ <Compile Include="Mvc\AjaxHelper`1.cs" />
+ <Compile Include="Mvc\HtmlHelper`1.cs" />
+ <Compile Include="Mvc\DictionaryHelpers.cs" />
+ <Compile Include="Mvc\AjaxRequestExtensions.cs" />
+ <Compile Include="Mvc\ModelBinderDictionary.cs" />
+ <Compile Include="Mvc\ValueProviderDictionary.cs" />
+ <Compile Include="Mvc\ViewContext.cs" />
+ <Compile Include="Mvc\ViewTemplateUserControl.cs">
+ <SubType>ASPXCodeBehind</SubType>
+ </Compile>
+ <Compile Include="Mvc\ViewTemplateUserControl`1.cs">
+ <SubType>ASPXCodeBehind</SubType>
+ </Compile>
+ <Compile Include="Mvc\ViewType.cs" />
+ <Compile Include="Mvc\ViewTypeControlBuilder.cs" />
+ <Compile Include="Mvc\ViewUserControlControlBuilder.cs">
+ </Compile>
+ <Compile Include="Mvc\ViewPageControlBuilder.cs">
+ </Compile>
+ <Compile Include="Mvc\ViewTypeParserFilter.cs" />
+ <Compile Include="Mvc\DefaultViewLocationCache.cs" />
+ <Compile Include="Mvc\FormCollection.cs" />
+ <Compile Include="Mvc\HttpPostedFileBaseModelBinder.cs" />
+ <Compile Include="Mvc\NullViewLocationCache.cs" />
+ <Compile Include="Mvc\ValidateInputAttribute.cs" />
+ <Compile Include="Mvc\FileContentResult.cs" />
+ <Compile Include="Mvc\FilePathResult.cs" />
+ <Compile Include="Mvc\FileResult.cs" />
+ <Compile Include="Mvc\FileStreamResult.cs" />
+ <Compile Include="Mvc\InputType.cs" />
+ <Compile Include="Mvc\ControllerDescriptorCache.cs" />
+ <Compile Include="Mvc\ReflectedParameterBindingInfo.cs" />
+ <Compile Include="Mvc\ParameterBindingInfo.cs" />
+ <Compile Include="Mvc\ReaderWriterCache`2.cs" />
+ <Compile Include="Mvc\DescriptorUtil.cs" />
+ <Compile Include="Mvc\ReflectedControllerDescriptor.cs" />
+ <Compile Include="Mvc\ControllerDescriptor.cs" />
+ <Compile Include="Mvc\ActionSelector.cs" />
+ <Compile Include="Mvc\ReflectedActionDescriptor.cs" />
+ <Compile Include="Mvc\Html\MvcForm.cs" />
+ <Compile Include="Mvc\HttpVerbs.cs" />
+ <Compile Include="Mvc\DefaultModelBinder.cs" />
+ <Compile Include="Mvc\ModelBindingContext.cs" />
+ <Compile Include="Mvc\ParameterDescriptor.cs" />
+ <Compile Include="Mvc\RouteValuesHelpers.cs" />
+ <Compile Include="Mvc\SelectListItem.cs" />
+ <Compile Include="Mvc\TagRenderMode.cs" />
+ <Compile Include="Mvc\ReflectedParameterDescriptor.cs" />
+ <Compile Include="Mvc\ValueProviderResult.cs" />
+ <Compile Include="Mvc\CustomModelBinderAttribute.cs" />
+ <Compile Include="Mvc\FormMethod.cs" />
+ <Compile Include="Mvc\Html\FormExtensions.cs" />
+ <Compile Include="Mvc\Html\InputExtensions.cs">
+ <SubType>Code</SubType>
+ </Compile>
+ <Compile Include="Mvc\Html\RenderPartialExtensions.cs" />
+ <Compile Include="Mvc\Html\SelectExtensions.cs" />
+ <Compile Include="Mvc\Html\TextAreaExtensions.cs" />
+ <Compile Include="Mvc\Html\ValidationExtensions.cs" />
+ <Compile Include="Mvc\IModelBinder.cs" />
+ <Compile Include="Mvc\Html\LinkExtensions.cs" />
+ <Compile Include="Mvc\ModelBinderAttribute.cs" />
+ <Compile Include="Mvc\ModelBinders.cs" />
+ <Compile Include="Mvc\ModelStateDictionary.cs" />
+ <Compile Include="Mvc\ModelState.cs" />
+ <Compile Include="Mvc\ModelErrorCollection.cs" />
+ <Compile Include="Mvc\ModelError.cs" />
+ <Compile Include="Mvc\Ajax\InsertionMode.cs" />
+ <Compile Include="Mvc\HandleErrorAttribute.cs" />
+ <Compile Include="Mvc\HandleErrorInfo.cs" />
+ <Compile Include="Mvc\HttpUnauthorizedResult.cs" />
+ <Compile Include="Mvc\IActionInvoker.cs" />
+ <Compile Include="Mvc\IView.cs" />
+ <Compile Include="Mvc\IViewLocationCache.cs" />
+ <Compile Include="Mvc\MvcHttpHandler.cs" />
+ <Compile Include="Mvc\PartialViewResult.cs" />
+ <Compile Include="Mvc\SessionStateTempDataProvider.cs" />
+ <Compile Include="Mvc\ITempDataProvider.cs" />
+ <Compile Include="Mvc\OutputCacheAttribute.cs" />
+ <Compile Include="Mvc\FilterInfo.cs" />
+ <Compile Include="GlobalSuppressions.cs" />
+ <Compile Include="Mvc\ActionFilterAttribute.cs" />
+ <Compile Include="Mvc\ActionResult.cs" />
+ <Compile Include="Mvc\AjaxHelper.cs" />
+ <Compile Include="Mvc\BuildManagerWrapper.cs" />
+ <Compile Include="Mvc\Controller.cs" />
+ <Compile Include="Mvc\ControllerActionInvoker.cs" />
+ <Compile Include="Mvc\ControllerBuilder.cs" />
+ <Compile Include="Mvc\ControllerTypeCache.cs" />
+ <Compile Include="Mvc\ContentResult.cs" />
+ <Compile Include="Mvc\FilterAttribute.cs" />
+ <Compile Include="Mvc\IResultFilter.cs" />
+ <Compile Include="Mvc\IExceptionFilter.cs" />
+ <Compile Include="Mvc\IAuthorizationFilter.cs" />
+ <Compile Include="Mvc\JsonResult.cs" />
+ <Compile Include="Mvc\NameValueCollectionExtensions.cs" />
+ <Compile Include="Mvc\ViewDataDictionary`1.cs" />
+ <Compile Include="Mvc\EmptyResult.cs" />
+ <Compile Include="Mvc\MultiSelectList.cs" />
+ <Compile Include="Mvc\RedirectResult.cs" />
+ <Compile Include="Mvc\RedirectToRouteResult.cs" />
+ <Compile Include="Mvc\DefaultControllerFactory.cs" />
+ <Compile Include="Mvc\HtmlHelper.cs" />
+ <Compile Include="Mvc\IActionFilter.cs" />
+ <Compile Include="Mvc\IBuildManager.cs" />
+ <Compile Include="Mvc\IController.cs" />
+ <Compile Include="Mvc\IControllerFactory.cs" />
+ <Compile Include="Mvc\IViewDataContainer.cs" />
+ <Compile Include="Mvc\IViewEngine.cs" />
+ <Compile Include="Mvc\MvcHandler.cs" />
+ <Compile Include="Mvc\MvcRouteHandler.cs" />
+ <Compile Include="Mvc\NonActionAttribute.cs" />
+ <Compile Include="Mvc\RouteCollectionExtensions.cs" />
+ <Compile Include="Mvc\SelectList.cs" />
+ <Compile Include="Mvc\TagBuilder.cs" />
+ <Compile Include="Mvc\TempDataDictionary.cs" />
+ <Compile Include="Mvc\TypeHelpers.cs" />
+ <Compile Include="Mvc\UrlHelper.cs" />
+ <Compile Include="Mvc\ViewDataDictionary.cs" />
+ <Compile Include="Mvc\ViewEngineCollection.cs" />
+ <Compile Include="Mvc\ViewEngineResult.cs" />
+ <Compile Include="Mvc\ViewEngines.cs" />
+ <Compile Include="Mvc\ViewMasterPage.cs">
+ <SubType>ASPXCodeBehind</SubType>
+ </Compile>
+ <Compile Include="Mvc\ViewMasterPage`1.cs">
+ <SubType>ASPXCodeBehind</SubType>
+ </Compile>
+ <Compile Include="Mvc\ViewPage.cs">
+ <SubType>ASPXCodeBehind</SubType>
+ </Compile>
+ <Compile Include="Mvc\ViewPage`1.cs">
+ <SubType>ASPXCodeBehind</SubType>
+ </Compile>
+ <Compile Include="Mvc\ViewResult.cs" />
+ <Compile Include="Mvc\ViewResultBase.cs" />
+ <Compile Include="Mvc\ViewUserControl.cs">
+ <SubType>ASPXCodeBehind</SubType>
+ </Compile>
+ <Compile Include="Mvc\ViewUserControl`1.cs">
+ <SubType>ASPXCodeBehind</SubType>
+ </Compile>
+ <Compile Include="Mvc\VirtualPathProviderViewEngine.cs" />
+ <Compile Include="Mvc\WebFormView.cs" />
+ <Compile Include="Mvc\WebFormViewEngine.cs" />
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ <Compile Include="Mvc\Resources\MvcResources.Designer.cs">
+ <AutoGen>True</AutoGen>
+ <DesignTime>True</DesignTime>
+ <DependentUpon>MvcResources.resx</DependentUpon>
+ </Compile>
+ </ItemGroup>
+ <ItemGroup>
+ <EmbeddedResource Include="Mvc\Resources\MvcResources.resx">
+ <Generator>ResXFileCodeGenerator</Generator>
+ <LastGenOutput>MvcResources.Designer.cs</LastGenOutput>
+ <SubType>Designer</SubType>
+ </EmbeddedResource>
+ </ItemGroup>
+ <ItemGroup>
+ <CodeAnalysisDictionary Include="..\CustomDictionary.xml" />
+ </ItemGroup>
+ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+ <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
+ Other similar extension points exist, see Microsoft.Common.targets.
+ <Target Name="BeforeBuild">
+ </Target>
+ <Target Name="AfterBuild">
+ </Target>
+ -->
+</Project> \ No newline at end of file
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc.dll.sources b/mcs/class/System.Web.Mvc2/System.Web.Mvc.dll.sources
new file mode 100644
index 00000000000..9ecb0aa8b11
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc.dll.sources
@@ -0,0 +1,279 @@
+../../build/common/Consts.cs
+../../build/common/MonoTODOAttribute.cs
+
+GlobalSuppressions.cs
+Properties/AssemblyInfo.cs
+System.Web.Mvc/AcceptVerbsAttribute.cs
+System.Web.Mvc/ActionDescriptor.cs
+System.Web.Mvc/ActionExecutedContext.cs
+System.Web.Mvc/ActionExecutingContext.cs
+System.Web.Mvc/ActionFilterAttribute.cs
+System.Web.Mvc/ActionMethodDispatcherCache.cs
+System.Web.Mvc/ActionMethodDispatcher.cs
+System.Web.Mvc/ActionMethodSelectorAttribute.cs
+System.Web.Mvc/ActionMethodSelector.cs
+System.Web.Mvc/ActionNameAttribute.cs
+System.Web.Mvc/ActionNameSelectorAttribute.cs
+System.Web.Mvc/ActionResult.cs
+System.Web.Mvc/ActionSelector.cs
+System.Web.Mvc/Ajax/AjaxExtensions.cs
+System.Web.Mvc/Ajax/AjaxOptions.cs
+System.Web.Mvc/AjaxHelper`1.cs
+System.Web.Mvc/AjaxHelper.cs
+System.Web.Mvc/Ajax/InsertionMode.cs
+System.Web.Mvc/AjaxRequestExtensions.cs
+System.Web.Mvc/AntiForgeryData.cs
+System.Web.Mvc/AntiForgeryDataSerializer.cs
+System.Web.Mvc/AreaHelpers.cs
+System.Web.Mvc/AreaRegistrationContext.cs
+System.Web.Mvc/AreaRegistration.cs
+System.Web.Mvc/AssociatedMetadataProvider.cs
+System.Web.Mvc/AssociatedValidatorProvider.cs
+System.Web.Mvc/Async/ActionDescriptorCreator.cs
+System.Web.Mvc/Async/AsyncActionDescriptor.cs
+System.Web.Mvc/Async/AsyncActionMethodSelector.cs
+System.Web.Mvc/Async/AsyncControllerActionInvoker.cs
+System.Web.Mvc/Async/AsyncManager.cs
+System.Web.Mvc/Async/AsyncResultWrapper.cs
+System.Web.Mvc/Async/AsyncUtil.cs
+System.Web.Mvc/Async/AsyncVoid.cs
+System.Web.Mvc/Async/BeginInvokeDelegate.cs
+System.Web.Mvc/AsyncController.cs
+System.Web.Mvc/Async/EndInvokeDelegate`1.cs
+System.Web.Mvc/Async/EndInvokeDelegate.cs
+System.Web.Mvc/Async/IAsyncActionInvoker.cs
+System.Web.Mvc/Async/IAsyncController.cs
+System.Web.Mvc/Async/IAsyncManagerContainer.cs
+System.Web.Mvc/Async/OperationCounter.cs
+System.Web.Mvc/Async/ReflectedAsyncActionDescriptor.cs
+System.Web.Mvc/Async/ReflectedAsyncControllerDescriptor.cs
+System.Web.Mvc/Async/SimpleAsyncResult.cs
+System.Web.Mvc/Async/SingleEntryGate.cs
+System.Web.Mvc/Async/SynchronizationContextUtil.cs
+System.Web.Mvc/Async/SynchronousOperationException.cs
+System.Web.Mvc/AsyncTimeoutAttribute.cs
+System.Web.Mvc/Async/Trigger.cs
+System.Web.Mvc/Async/TriggerListener.cs
+System.Web.Mvc/AuthorizationContext.cs
+System.Web.Mvc/AuthorizeAttribute.cs
+System.Web.Mvc/BindAttribute.cs
+System.Web.Mvc/BuildManagerWrapper.cs
+System.Web.Mvc/ByteArrayModelBinder.cs
+System.Web.Mvc/ChildActionOnlyAttribute.cs
+System.Web.Mvc/ClientDataTypeModelValidatorProvider.cs
+System.Web.Mvc/ContentResult.cs
+System.Web.Mvc/ControllerActionInvoker.cs
+System.Web.Mvc/ControllerBase.cs
+System.Web.Mvc/ControllerBuilder.cs
+System.Web.Mvc/ControllerContext.cs
+System.Web.Mvc/Controller.cs
+System.Web.Mvc/ControllerDescriptorCache.cs
+System.Web.Mvc/ControllerDescriptor.cs
+System.Web.Mvc/ControllerTypeCache.cs
+System.Web.Mvc/CustomModelBinderAttribute.cs
+System.Web.Mvc/DataAnnotationsModelMetadata.cs
+System.Web.Mvc/DataAnnotationsModelMetadataProvider.cs
+System.Web.Mvc/DataAnnotationsModelValidator`1.cs
+System.Web.Mvc/DataAnnotationsModelValidator.cs
+System.Web.Mvc/DataAnnotationsModelValidatorProvider.cs
+System.Web.Mvc/DataErrorInfoModelValidatorProvider.cs
+System.Web.Mvc/DefaultControllerFactory.cs
+System.Web.Mvc/DefaultModelBinder.cs
+System.Web.Mvc/DefaultViewLocationCache.cs
+System.Web.Mvc/DescriptorUtil.cs
+System.Web.Mvc/DictionaryHelpers.cs
+System.Web.Mvc/DictionaryValueProvider`1.cs
+System.Web.Mvc/DynamicTypeGenerator.cs
+System.Web.Mvc/EmptyModelMetadataProvider.cs
+System.Web.Mvc/EmptyModelValidatorProvider.cs
+System.Web.Mvc/EmptyResult.cs
+System.Web.Mvc/Error.cs
+System.Web.Mvc/ExceptionContext.cs
+System.Web.Mvc/ExpressionHelper.cs
+System.Web.Mvc/ExpressionUtil/BinaryExpressionFingerprint.cs
+System.Web.Mvc/ExpressionUtil/CachedExpressionCompiler.cs
+System.Web.Mvc/ExpressionUtil/CompiledExpressionDelegate`2.cs
+System.Web.Mvc/ExpressionUtil/ConditionalExpressionFingerprint.cs
+System.Web.Mvc/ExpressionUtil/ConstantExpressionFingerprint.cs
+System.Web.Mvc/ExpressionUtil/ExpressionFingerprint.cs
+System.Web.Mvc/ExpressionUtil/ExpressionParser.cs
+System.Web.Mvc/ExpressionUtil/FastTrack`2.cs
+System.Web.Mvc/ExpressionUtil/HashCodeCombiner.cs
+System.Web.Mvc/ExpressionUtil/MemberExpressionFingerprint.cs
+System.Web.Mvc/ExpressionUtil/MethodCallExpressionFingerprint.cs
+System.Web.Mvc/ExpressionUtil/ParameterExpressionFingerprint.cs
+System.Web.Mvc/ExpressionUtil/ParserContext.cs
+System.Web.Mvc/ExpressionUtil/UnaryExpressionFingerprint.cs
+System.Web.Mvc/FieldValidationMetadata.cs
+System.Web.Mvc/FileContentResult.cs
+System.Web.Mvc/FilePathResult.cs
+System.Web.Mvc/FileResult.cs
+System.Web.Mvc/FileStreamResult.cs
+System.Web.Mvc/FilterAttribute.cs
+System.Web.Mvc/FilterInfo.cs
+System.Web.Mvc/FormCollection.cs
+System.Web.Mvc/FormContext.cs
+System.Web.Mvc/FormMethod.cs
+System.Web.Mvc/FormValueProvider.cs
+System.Web.Mvc/FormValueProviderFactory.cs
+System.Web.Mvc/HandleErrorAttribute.cs
+System.Web.Mvc/HandleErrorInfo.cs
+System.Web.Mvc/HiddenInputAttribute.cs
+System.Web.Mvc/Html/ChildActionExtensions.cs
+System.Web.Mvc/Html/DefaultDisplayTemplates.cs
+System.Web.Mvc/Html/DefaultEditorTemplates.cs
+System.Web.Mvc/Html/DisplayExtensions.cs
+System.Web.Mvc/Html/DisplayTextExtensions.cs
+System.Web.Mvc/Html/EditorExtensions.cs
+System.Web.Mvc/Html/FormExtensions.cs
+System.Web.Mvc/HtmlHelper`1.cs
+System.Web.Mvc/HtmlHelper.cs
+System.Web.Mvc/Html/InputExtensions.cs
+System.Web.Mvc/Html/LabelExtensions.cs
+System.Web.Mvc/Html/LinkExtensions.cs
+System.Web.Mvc/Html/MvcForm.cs
+System.Web.Mvc/Html/PartialExtensions.cs
+System.Web.Mvc/Html/RenderPartialExtensions.cs
+System.Web.Mvc/Html/SelectExtensions.cs
+System.Web.Mvc/Html/TemplateHelpers.cs
+System.Web.Mvc/Html/TextAreaExtensions.cs
+System.Web.Mvc/Html/ValidationExtensions.cs
+System.Web.Mvc/HttpAntiForgeryException.cs
+System.Web.Mvc/HttpDeleteAttribute.cs
+System.Web.Mvc/HttpFileCollectionValueProvider.cs
+System.Web.Mvc/HttpFileCollectionValueProviderFactory.cs
+System.Web.Mvc/HttpGetAttribute.cs
+System.Web.Mvc/HttpHandlerUtil.cs
+System.Web.Mvc/HttpPostAttribute.cs
+System.Web.Mvc/HttpPostedFileBaseModelBinder.cs
+System.Web.Mvc/HttpPutAttribute.cs
+System.Web.Mvc/HttpRequestExtensions.cs
+System.Web.Mvc/HttpUnauthorizedResult.cs
+System.Web.Mvc/HttpVerbs.cs
+System.Web.Mvc/IActionFilter.cs
+System.Web.Mvc/IActionInvoker.cs
+System.Web.Mvc/IAuthorizationFilter.cs
+System.Web.Mvc/IBuildManager.cs
+System.Web.Mvc/IController.cs
+System.Web.Mvc/IControllerFactory.cs
+System.Web.Mvc/IExceptionFilter.cs
+System.Web.Mvc/IModelBinder.cs
+System.Web.Mvc/InputType.cs
+System.Web.Mvc/IResultFilter.cs
+System.Web.Mvc/IRouteWithArea.cs
+System.Web.Mvc/ITempDataProvider.cs
+System.Web.Mvc/IValueProvider.cs
+System.Web.Mvc/IView.cs
+System.Web.Mvc/IViewDataContainer.cs
+System.Web.Mvc/IViewEngine.cs
+System.Web.Mvc/IViewLocationCache.cs
+System.Web.Mvc/JavaScriptResult.cs
+System.Web.Mvc/JsonRequestBehavior.cs
+System.Web.Mvc/JsonResult.cs
+System.Web.Mvc/LinqBinaryModelBinder.cs
+System.Web.Mvc/ModelBinderAttribute.cs
+System.Web.Mvc/ModelBinderDictionary.cs
+System.Web.Mvc/ModelBinders.cs
+System.Web.Mvc/ModelBindingContext.cs
+System.Web.Mvc/ModelClientValidationRangeRule.cs
+System.Web.Mvc/ModelClientValidationRegexRule.cs
+System.Web.Mvc/ModelClientValidationRequiredRule.cs
+System.Web.Mvc/ModelClientValidationRule.cs
+System.Web.Mvc/ModelClientValidationStringLengthRule.cs
+System.Web.Mvc/ModelErrorCollection.cs
+System.Web.Mvc/ModelError.cs
+System.Web.Mvc/ModelMetadata.cs
+System.Web.Mvc/ModelMetadataProvider.cs
+System.Web.Mvc/ModelMetadataProviders.cs
+System.Web.Mvc/ModelState.cs
+System.Web.Mvc/ModelStateDictionary.cs
+System.Web.Mvc/ModelValidationResult.cs
+System.Web.Mvc/ModelValidator.cs
+System.Web.Mvc/ModelValidatorProviderCollection.cs
+System.Web.Mvc/ModelValidatorProvider.cs
+System.Web.Mvc/ModelValidatorProviders.cs
+System.Web.Mvc/MultiSelectList.cs
+System.Web.Mvc/MvcHandler.cs
+System.Web.Mvc/MvcHtmlString.cs
+System.Web.Mvc/MvcHttpHandler.cs
+System.Web.Mvc/MvcRouteHandler.cs
+System.Web.Mvc/NameValueCollectionExtensions.cs
+System.Web.Mvc/NameValueCollectionValueProvider.cs
+System.Web.Mvc/NoAsyncTimeoutAttribute.cs
+System.Web.Mvc/NonActionAttribute.cs
+System.Web.Mvc/NullViewLocationCache.cs
+System.Web.Mvc/OutputCacheAttribute.cs
+System.Web.Mvc/ParameterBindingInfo.cs
+System.Web.Mvc/ParameterDescriptor.cs
+System.Web.Mvc/ParameterInfoUtil.cs
+System.Web.Mvc/PartialViewResult.cs
+System.Web.Mvc/PathHelpers.cs
+System.Web.Mvc/QueryStringValueProvider.cs
+System.Web.Mvc/QueryStringValueProviderFactory.cs
+System.Web.Mvc/RangeAttributeAdapter.cs
+System.Web.Mvc/ReaderWriterCache`2.cs
+System.Web.Mvc/RedirectResult.cs
+System.Web.Mvc/RedirectToRouteResult.cs
+System.Web.Mvc/ReflectedActionDescriptor.cs
+System.Web.Mvc/ReflectedControllerDescriptor.cs
+System.Web.Mvc/ReflectedParameterBindingInfo.cs
+System.Web.Mvc/ReflectedParameterDescriptor.cs
+System.Web.Mvc/RegularExpressionAttributeAdapter.cs
+System.Web.Mvc/RequiredAttributeAdapter.cs
+System.Web.Mvc/RequireHttpsAttribute.cs
+System.Web.Mvc/Resources/MvcResources.Designer.cs
+System.Web.Mvc/ResultExecutedContext.cs
+System.Web.Mvc/ResultExecutingContext.cs
+System.Web.Mvc/RouteCollectionExtensions.cs
+System.Web.Mvc/RouteDataValueProvider.cs
+System.Web.Mvc/RouteDataValueProviderFactory.cs
+System.Web.Mvc/RouteValuesHelpers.cs
+System.Web.Mvc/SelectList.cs
+System.Web.Mvc/SelectListItem.cs
+System.Web.Mvc/SessionStateTempDataProvider.cs
+System.Web.Mvc/StringLengthAttributeAdapter.cs
+System.Web.Mvc/TagBuilder.cs
+System.Web.Mvc/TagRenderMode.cs
+System.Web.Mvc/TempDataDictionary.cs
+System.Web.Mvc/TemplateInfo.cs
+System.Web.Mvc/TryGetValueDelegate.cs
+System.Web.Mvc/TypeCacheSerializer.cs
+System.Web.Mvc/TypeCacheUtil.cs
+System.Web.Mvc/TypeDescriptorHelper.cs
+System.Web.Mvc/TypeHelpers.cs
+System.Web.Mvc/UrlHelper.cs
+System.Web.Mvc/UrlParameter.cs
+System.Web.Mvc/ValidateAntiForgeryTokenAttribute.cs
+System.Web.Mvc/ValidateInputAttribute.cs
+System.Web.Mvc/ValueProviderCollection.cs
+System.Web.Mvc/ValueProviderDictionary.cs
+System.Web.Mvc/ValueProviderFactories.cs
+System.Web.Mvc/ValueProviderFactoryCollection.cs
+System.Web.Mvc/ValueProviderFactory.cs
+System.Web.Mvc/ValueProviderResult.cs
+System.Web.Mvc/ValueProviderUtil.cs
+System.Web.Mvc/ViewContext.cs
+System.Web.Mvc/ViewDataDictionary`1.cs
+System.Web.Mvc/ViewDataDictionary.cs
+System.Web.Mvc/ViewDataInfo.cs
+System.Web.Mvc/ViewEngineCollection.cs
+System.Web.Mvc/ViewEngineResult.cs
+System.Web.Mvc/ViewEngines.cs
+System.Web.Mvc/ViewMasterPage`1.cs
+System.Web.Mvc/ViewMasterPage.cs
+System.Web.Mvc/ViewPage`1.cs
+System.Web.Mvc/ViewPageControlBuilder.cs
+System.Web.Mvc/ViewPage.cs
+System.Web.Mvc/ViewResultBase.cs
+System.Web.Mvc/ViewResult.cs
+System.Web.Mvc/ViewTemplateUserControl`1.cs
+System.Web.Mvc/ViewTemplateUserControl.cs
+System.Web.Mvc/ViewTypeControlBuilder.cs
+System.Web.Mvc/ViewType.cs
+System.Web.Mvc/ViewTypeParserFilter.cs
+System.Web.Mvc/ViewUserControl`1.cs
+System.Web.Mvc/ViewUserControlControlBuilder.cs
+System.Web.Mvc/ViewUserControl.cs
+System.Web.Mvc/VirtualPathProviderViewEngine.cs
+System.Web.Mvc/WebFormView.cs
+System.Web.Mvc/WebFormViewEngine.cs
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/AcceptVerbsAttribute.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/AcceptVerbsAttribute.cs
new file mode 100644
index 00000000000..c0924a424ce
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/AcceptVerbsAttribute.cs
@@ -0,0 +1,71 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Collections.Generic;
+ using System.Collections.ObjectModel;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Linq;
+ using System.Reflection;
+ using System.Web.Mvc.Resources;
+
+ [SuppressMessage("Microsoft.Design", "CA1019:DefineAccessorsForAttributeArguments",
+ Justification = "The accessor is exposed as an ICollection<string>.")]
+ [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
+ public sealed class AcceptVerbsAttribute : ActionMethodSelectorAttribute {
+ public AcceptVerbsAttribute(HttpVerbs verbs)
+ : this(EnumToArray(verbs)) {
+ }
+
+ public AcceptVerbsAttribute(params string[] verbs) {
+ if (verbs == null || verbs.Length == 0) {
+ throw new ArgumentException(MvcResources.Common_NullOrEmpty, "verbs");
+ }
+
+ Verbs = new ReadOnlyCollection<string>(verbs);
+ }
+
+ public ICollection<string> Verbs {
+ get;
+ private set;
+ }
+
+ private static void AddEntryToList(HttpVerbs verbs, HttpVerbs match, List<string> verbList, string entryText) {
+ if ((verbs & match) != 0) {
+ verbList.Add(entryText);
+ }
+ }
+
+ internal static string[] EnumToArray(HttpVerbs verbs) {
+ List<string> verbList = new List<string>();
+
+ AddEntryToList(verbs, HttpVerbs.Get, verbList, "GET");
+ AddEntryToList(verbs, HttpVerbs.Post, verbList, "POST");
+ AddEntryToList(verbs, HttpVerbs.Put, verbList, "PUT");
+ AddEntryToList(verbs, HttpVerbs.Delete, verbList, "DELETE");
+ AddEntryToList(verbs, HttpVerbs.Head, verbList, "HEAD");
+
+ return verbList.ToArray();
+ }
+
+ public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo) {
+ if (controllerContext == null) {
+ throw new ArgumentNullException("controllerContext");
+ }
+
+ string incomingVerb = controllerContext.HttpContext.Request.GetHttpMethodOverride();
+
+ return Verbs.Contains(incomingVerb, StringComparer.OrdinalIgnoreCase);
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ActionDescriptor.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ActionDescriptor.cs
new file mode 100644
index 00000000000..4041d790747
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ActionDescriptor.cs
@@ -0,0 +1,230 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Globalization;
+ using System.Linq;
+ using System.Reflection;
+ using System.Web.Mvc.Resources;
+
+ public abstract class ActionDescriptor : ICustomAttributeProvider {
+
+ private readonly static AllowMultipleAttributesCache _allowMultiplAttributesCache = new AllowMultipleAttributesCache();
+ private readonly static ActionMethodDispatcherCache _staticDispatcherCache = new ActionMethodDispatcherCache();
+ private ActionMethodDispatcherCache _instanceDispatcherCache;
+
+ private static readonly ActionSelector[] _emptySelectors = new ActionSelector[0];
+
+ public abstract string ActionName {
+ get;
+ }
+
+ public abstract ControllerDescriptor ControllerDescriptor {
+ get;
+ }
+
+ internal ActionMethodDispatcherCache DispatcherCache {
+ get {
+ if (_instanceDispatcherCache == null) {
+ _instanceDispatcherCache = _staticDispatcherCache;
+ }
+ return _instanceDispatcherCache;
+ }
+ set {
+ _instanceDispatcherCache = value;
+ }
+ }
+
+ public abstract object Execute(ControllerContext controllerContext, IDictionary<string, object> parameters);
+
+ internal static object ExtractParameterFromDictionary(ParameterInfo parameterInfo, IDictionary<string, object> parameters, MethodInfo methodInfo) {
+ object value;
+
+ if (!parameters.TryGetValue(parameterInfo.Name, out value)) {
+ // the key should always be present, even if the parameter value is null
+ string message = String.Format(CultureInfo.CurrentUICulture, MvcResources.ReflectedActionDescriptor_ParameterNotInDictionary,
+ parameterInfo.Name, parameterInfo.ParameterType, methodInfo, methodInfo.DeclaringType);
+ throw new ArgumentException(message, "parameters");
+ }
+
+ if (value == null && !TypeHelpers.TypeAllowsNullValue(parameterInfo.ParameterType)) {
+ // tried to pass a null value for a non-nullable parameter type
+ string message = String.Format(CultureInfo.CurrentUICulture, MvcResources.ReflectedActionDescriptor_ParameterCannotBeNull,
+ parameterInfo.Name, parameterInfo.ParameterType, methodInfo, methodInfo.DeclaringType);
+ throw new ArgumentException(message, "parameters");
+ }
+
+ if (value != null && !parameterInfo.ParameterType.IsInstanceOfType(value)) {
+ // value was supplied but is not of the proper type
+ string message = String.Format(CultureInfo.CurrentUICulture, MvcResources.ReflectedActionDescriptor_ParameterValueHasWrongType,
+ parameterInfo.Name, methodInfo, methodInfo.DeclaringType, value.GetType(), parameterInfo.ParameterType);
+ throw new ArgumentException(message, "parameters");
+ }
+
+ return value;
+ }
+
+ internal static object ExtractParameterOrDefaultFromDictionary(ParameterInfo parameterInfo, IDictionary<string, object> parameters) {
+ Type parameterType = parameterInfo.ParameterType;
+
+ object value;
+ parameters.TryGetValue(parameterInfo.Name, out value);
+
+ // if wrong type, replace with default instance
+ if (parameterType.IsInstanceOfType(value)) {
+ return value;
+ }
+ else {
+ object defaultValue;
+ if (ParameterInfoUtil.TryGetDefaultValue(parameterInfo, out defaultValue)) {
+ return defaultValue;
+ }
+ else {
+ return TypeHelpers.GetDefaultValue(parameterType);
+ }
+ }
+ }
+
+ public virtual object[] GetCustomAttributes(bool inherit) {
+ return GetCustomAttributes(typeof(object), inherit);
+ }
+
+ public virtual object[] GetCustomAttributes(Type attributeType, bool inherit) {
+ if (attributeType == null) {
+ throw new ArgumentNullException("attributeType");
+ }
+
+ return (object[])Array.CreateInstance(attributeType, 0);
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate",
+ Justification = "This method may perform non-trivial work.")]
+ public virtual FilterInfo GetFilters() {
+ return new FilterInfo();
+ }
+
+ internal static FilterInfo GetFilters(MethodInfo methodInfo) {
+ // Enumerable.OrderBy() is a stable sort, so this method preserves scope ordering.
+ FilterAttribute[] typeFilters = (FilterAttribute[])methodInfo.ReflectedType.GetCustomAttributes(typeof(FilterAttribute), true /* inherit */);
+ FilterAttribute[] methodFilters = (FilterAttribute[])methodInfo.GetCustomAttributes(typeof(FilterAttribute), true /* inherit */);
+ List<FilterAttribute> orderedFilters = RemoveOverriddenFilters(typeFilters.Concat(methodFilters)).OrderBy(attr => attr.Order).ToList();
+
+ FilterInfo filterInfo = new FilterInfo();
+ MergeFiltersIntoList(orderedFilters, filterInfo.ActionFilters);
+ MergeFiltersIntoList(orderedFilters, filterInfo.AuthorizationFilters);
+ MergeFiltersIntoList(orderedFilters, filterInfo.ExceptionFilters);
+ MergeFiltersIntoList(orderedFilters, filterInfo.ResultFilters);
+ return filterInfo;
+ }
+
+ public abstract ParameterDescriptor[] GetParameters();
+
+ [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate",
+ Justification = "This method may perform non-trivial work.")]
+ public virtual ICollection<ActionSelector> GetSelectors() {
+ return _emptySelectors;
+ }
+
+ public virtual bool IsDefined(Type attributeType, bool inherit) {
+ if (attributeType == null) {
+ throw new ArgumentNullException("attributeType");
+ }
+
+ return false;
+ }
+
+ internal static void MergeFiltersIntoList<TFilter>(IList<FilterAttribute> allFilters, IList<TFilter> destFilters) where TFilter : class {
+ foreach (FilterAttribute filter in allFilters) {
+ TFilter castFilter = filter as TFilter;
+ if (castFilter != null) {
+ destFilters.Add(castFilter);
+ }
+ }
+ }
+
+ internal static IEnumerable<FilterAttribute> RemoveOverriddenFilters(IEnumerable<FilterAttribute> filters) {
+ // If an attribute is declared on both the controller and on an action method and that attribute's
+ // type has AllowMultiple = false (which is the default for attributes), we should ignore the attributes
+ // declared on the controller. The CLR's reflection implementation follows a similar algorithm when it
+ // encounters an overridden virtual method where both the base and the override contain some
+ // AllowMultiple = false attribute.
+
+ // Key = attribute type
+ // Value = -1 if AllowMultiple true, last index of this attribute type if AllowMultiple false
+ Dictionary<Type, int> attrsIndexes = new Dictionary<Type, int>();
+
+ FilterAttribute[] filtersList = filters.ToArray();
+ for (int i = 0; i < filtersList.Length; i++) {
+ FilterAttribute filter = filtersList[i];
+ Type filterType = filter.GetType();
+
+ int lastIndex;
+ if (attrsIndexes.TryGetValue(filterType, out lastIndex)) {
+ if (lastIndex >= 0) {
+ // this filter already exists and AllowMultiple = false, so clear last entry
+ filtersList[lastIndex] = null;
+ attrsIndexes[filterType] = i;
+ }
+ }
+ else {
+ // not found - add to dictionary
+ // exactly one AttributeUsageAttribute will always be present
+ bool allowMultiple = _allowMultiplAttributesCache.IsMultiUseAttribute(filterType);
+ attrsIndexes[filterType] = (allowMultiple) ? -1 : i;
+ }
+ }
+
+ // any duplicated attributes have now been nulled out, so just return remaining attributes
+ return filtersList.Where(attr => attr != null);
+ }
+
+ internal static string VerifyActionMethodIsCallable(MethodInfo methodInfo) {
+ // we can't call instance methods where the 'this' parameter is a type other than ControllerBase
+ if (!methodInfo.IsStatic && !typeof(ControllerBase).IsAssignableFrom(methodInfo.ReflectedType)) {
+ return String.Format(CultureInfo.CurrentUICulture, MvcResources.ReflectedActionDescriptor_CannotCallInstanceMethodOnNonControllerType,
+ methodInfo, methodInfo.ReflectedType.FullName);
+ }
+
+ // we can't call methods with open generic type parameters
+ if (methodInfo.ContainsGenericParameters) {
+ return String.Format(CultureInfo.CurrentUICulture, MvcResources.ReflectedActionDescriptor_CannotCallOpenGenericMethods,
+ methodInfo, methodInfo.ReflectedType.FullName);
+ }
+
+ // we can't call methods with ref/out parameters
+ ParameterInfo[] parameterInfos = methodInfo.GetParameters();
+ foreach (ParameterInfo parameterInfo in parameterInfos) {
+ if (parameterInfo.IsOut || parameterInfo.ParameterType.IsByRef) {
+ return String.Format(CultureInfo.CurrentUICulture, MvcResources.ReflectedActionDescriptor_CannotCallMethodsWithOutOrRefParameters,
+ methodInfo, methodInfo.ReflectedType.FullName, parameterInfo);
+ }
+ }
+
+ // we can call this method
+ return null;
+ }
+
+ private sealed class AllowMultipleAttributesCache : ReaderWriterCache<Type, bool> {
+ public bool IsMultiUseAttribute(Type attributeType) {
+ return FetchOrCreateItem(attributeType, () => AttributeUsageAllowsMultiple(attributeType));
+ }
+
+ private static bool AttributeUsageAllowsMultiple(Type type) {
+ return (((AttributeUsageAttribute[])type.GetCustomAttributes(typeof(AttributeUsageAttribute), true))[0]).AllowMultiple;
+ }
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ActionExecutedContext.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ActionExecutedContext.cs
new file mode 100644
index 00000000000..1dfaa174b16
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ActionExecutedContext.cs
@@ -0,0 +1,68 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Diagnostics.CodeAnalysis;
+
+ public class ActionExecutedContext : ControllerContext {
+
+ private ActionResult _result;
+
+ // parameterless constructor used for mocking
+ public ActionExecutedContext() {
+ }
+
+ [SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors",
+ Justification = "The virtual property setters are only to support mocking frameworks, in which case this constructor shouldn't be called anyway.")]
+ public ActionExecutedContext(ControllerContext controllerContext, ActionDescriptor actionDescriptor, bool canceled, Exception exception)
+ : base(controllerContext) {
+ if (actionDescriptor == null) {
+ throw new ArgumentNullException("actionDescriptor");
+ }
+
+ ActionDescriptor = actionDescriptor;
+ Canceled = canceled;
+ Exception = exception;
+ }
+
+ public virtual ActionDescriptor ActionDescriptor {
+ get;
+ set;
+ }
+
+ public virtual bool Canceled {
+ get;
+ set;
+ }
+
+ public virtual Exception Exception {
+ get;
+ set;
+ }
+
+ public bool ExceptionHandled {
+ get;
+ set;
+ }
+
+ public ActionResult Result {
+ get {
+ return _result ?? EmptyResult.Instance;
+ }
+ set {
+ _result = value;
+ }
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ActionExecutingContext.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ActionExecutingContext.cs
new file mode 100644
index 00000000000..4cc21e0999b
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ActionExecutingContext.cs
@@ -0,0 +1,57 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics.CodeAnalysis;
+
+ public class ActionExecutingContext : ControllerContext {
+
+ // parameterless constructor used for mocking
+ public ActionExecutingContext() {
+ }
+
+ [SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors",
+ Justification = "The virtual property setters are only to support mocking frameworks, in which case this constructor shouldn't be called anyway.")]
+ public ActionExecutingContext(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary<string, object> actionParameters)
+ : base(controllerContext) {
+ if (actionDescriptor == null) {
+ throw new ArgumentNullException("actionDescriptor");
+ }
+ if (actionParameters == null) {
+ throw new ArgumentNullException("actionParameters");
+ }
+
+ ActionDescriptor = actionDescriptor;
+ ActionParameters = actionParameters;
+ }
+
+ public virtual ActionDescriptor ActionDescriptor {
+ get;
+ set;
+ }
+
+ [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly",
+ Justification = "The property setter is only here to support mocking this type and should not be called at runtime.")]
+ public virtual IDictionary<string, object> ActionParameters {
+ get;
+ set;
+ }
+
+ public ActionResult Result {
+ get;
+ set;
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ActionFilterAttribute.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ActionFilterAttribute.cs
new file mode 100644
index 00000000000..1069ccc7d63
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ActionFilterAttribute.cs
@@ -0,0 +1,34 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+
+ [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
+ public abstract class ActionFilterAttribute : FilterAttribute, IActionFilter, IResultFilter {
+
+ // The OnXxx() methods are virtual rather than abstract so that a developer need override
+ // only the ones that interest him.
+
+ public virtual void OnActionExecuting(ActionExecutingContext filterContext) {
+ }
+
+ public virtual void OnActionExecuted(ActionExecutedContext filterContext) {
+ }
+
+ public virtual void OnResultExecuting(ResultExecutingContext filterContext) {
+ }
+
+ public virtual void OnResultExecuted(ResultExecutedContext filterContext) {
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ActionMethodDispatcher.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ActionMethodDispatcher.cs
new file mode 100644
index 00000000000..0adafe2b695
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ActionMethodDispatcher.cs
@@ -0,0 +1,86 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq.Expressions;
+ using System.Reflection;
+
+ // The methods in this class don't perform error checking; that is the responsibility of the
+ // caller.
+ internal sealed class ActionMethodDispatcher {
+
+ private delegate object ActionExecutor(ControllerBase controller, object[] parameters);
+ private delegate void VoidActionExecutor(ControllerBase controller, object[] parameters);
+
+ private ActionExecutor _executor;
+
+ public ActionMethodDispatcher(MethodInfo methodInfo) {
+ _executor = GetExecutor(methodInfo);
+ MethodInfo = methodInfo;
+ }
+
+ public MethodInfo MethodInfo {
+ get;
+ private set;
+ }
+
+ public object Execute(ControllerBase controller, object[] parameters) {
+ return _executor(controller, parameters);
+ }
+
+ private static ActionExecutor GetExecutor(MethodInfo methodInfo) {
+ // Parameters to executor
+ ParameterExpression controllerParameter = Expression.Parameter(typeof(ControllerBase), "controller");
+ ParameterExpression parametersParameter = Expression.Parameter(typeof(object[]), "parameters");
+
+ // Build parameter list
+ List<Expression> parameters = new List<Expression>();
+ ParameterInfo[] paramInfos = methodInfo.GetParameters();
+ for (int i = 0; i < paramInfos.Length; i++) {
+ ParameterInfo paramInfo = paramInfos[i];
+ BinaryExpression valueObj = Expression.ArrayIndex(parametersParameter, Expression.Constant(i));
+ UnaryExpression valueCast = Expression.Convert(valueObj, paramInfo.ParameterType);
+
+ // valueCast is "(Ti) parameters[i]"
+ parameters.Add(valueCast);
+ }
+
+ // Call method
+ UnaryExpression instanceCast = (!methodInfo.IsStatic) ? Expression.Convert(controllerParameter, methodInfo.ReflectedType) : null;
+ MethodCallExpression methodCall = methodCall = Expression.Call(instanceCast, methodInfo, parameters);
+
+ // methodCall is "((TController) controller) method((T0) parameters[0], (T1) parameters[1], ...)"
+ // Create function
+ if (methodCall.Type == typeof(void)) {
+ Expression<VoidActionExecutor> lambda = Expression.Lambda<VoidActionExecutor>(methodCall, controllerParameter, parametersParameter);
+ VoidActionExecutor voidExecutor = lambda.Compile();
+ return WrapVoidAction(voidExecutor);
+ }
+ else {
+ // must coerce methodCall to match ActionExecutor signature
+ UnaryExpression castMethodCall = Expression.Convert(methodCall, typeof(object));
+ Expression<ActionExecutor> lambda = Expression.Lambda<ActionExecutor>(castMethodCall, controllerParameter, parametersParameter);
+ return lambda.Compile();
+ }
+ }
+
+ private static ActionExecutor WrapVoidAction(VoidActionExecutor executor) {
+ return delegate(ControllerBase controller, object[] parameters) {
+ executor(controller, parameters);
+ return null;
+ };
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ActionMethodDispatcherCache.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ActionMethodDispatcherCache.cs
new file mode 100644
index 00000000000..61b5333367c
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ActionMethodDispatcherCache.cs
@@ -0,0 +1,27 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Reflection;
+
+ internal sealed class ActionMethodDispatcherCache : ReaderWriterCache<MethodInfo,ActionMethodDispatcher> {
+
+ public ActionMethodDispatcherCache() {
+ }
+
+ public ActionMethodDispatcher GetDispatcher(MethodInfo methodInfo) {
+ return FetchOrCreateItem(methodInfo, () => new ActionMethodDispatcher(methodInfo));
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ActionMethodSelector.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ActionMethodSelector.cs
new file mode 100644
index 00000000000..4b765dba1ed
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ActionMethodSelector.cs
@@ -0,0 +1,125 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Collections.Generic;
+ using System.Globalization;
+ using System.Linq;
+ using System.Reflection;
+ using System.Text;
+ using System.Web.Mvc.Resources;
+
+ internal sealed class ActionMethodSelector {
+
+ public ActionMethodSelector(Type controllerType) {
+ ControllerType = controllerType;
+ PopulateLookupTables();
+ }
+
+ public Type ControllerType {
+ get;
+ private set;
+ }
+
+ public MethodInfo[] AliasedMethods {
+ get;
+ private set;
+ }
+
+ public ILookup<string, MethodInfo> NonAliasedMethods {
+ get;
+ private set;
+ }
+
+ private AmbiguousMatchException CreateAmbiguousMatchException(List<MethodInfo> ambiguousMethods, string actionName) {
+ StringBuilder exceptionMessageBuilder = new StringBuilder();
+ foreach (MethodInfo methodInfo in ambiguousMethods) {
+ string controllerAction = Convert.ToString(methodInfo, CultureInfo.CurrentUICulture);
+ string controllerType = methodInfo.DeclaringType.FullName;
+ exceptionMessageBuilder.AppendLine();
+ exceptionMessageBuilder.AppendFormat(CultureInfo.CurrentUICulture, MvcResources.ActionMethodSelector_AmbiguousMatchType, controllerAction, controllerType);
+ }
+ string message = String.Format(CultureInfo.CurrentUICulture, MvcResources.ActionMethodSelector_AmbiguousMatch,
+ actionName, ControllerType.Name, exceptionMessageBuilder);
+ return new AmbiguousMatchException(message);
+ }
+
+ public MethodInfo FindActionMethod(ControllerContext controllerContext, string actionName) {
+ List<MethodInfo> methodsMatchingName = GetMatchingAliasedMethods(controllerContext, actionName);
+ methodsMatchingName.AddRange(NonAliasedMethods[actionName]);
+ List<MethodInfo> finalMethods = RunSelectionFilters(controllerContext, methodsMatchingName);
+
+ switch (finalMethods.Count) {
+ case 0:
+ return null;
+
+ case 1:
+ return finalMethods[0];
+
+ default:
+ throw CreateAmbiguousMatchException(finalMethods, actionName);
+ }
+ }
+
+ internal List<MethodInfo> GetMatchingAliasedMethods(ControllerContext controllerContext, string actionName) {
+ // find all aliased methods which are opting in to this request
+ // to opt in, all attributes defined on the method must return true
+
+ var methods = from methodInfo in AliasedMethods
+ let attrs = (ActionNameSelectorAttribute[])methodInfo.GetCustomAttributes(typeof(ActionNameSelectorAttribute), true /* inherit */)
+ where attrs.All(attr => attr.IsValidName(controllerContext, actionName, methodInfo))
+ select methodInfo;
+ return methods.ToList();
+ }
+
+ private static bool IsMethodDecoratedWithAliasingAttribute(MethodInfo methodInfo) {
+ return methodInfo.IsDefined(typeof(ActionNameSelectorAttribute), true /* inherit */);
+ }
+
+ private static bool IsValidActionMethod(MethodInfo methodInfo) {
+ return !(methodInfo.IsSpecialName ||
+ methodInfo.GetBaseDefinition().DeclaringType.IsAssignableFrom(typeof(Controller)));
+ }
+
+ private void PopulateLookupTables() {
+ MethodInfo[] allMethods = ControllerType.GetMethods(BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.Public);
+ MethodInfo[] actionMethods = Array.FindAll(allMethods, IsValidActionMethod);
+
+ AliasedMethods = Array.FindAll(actionMethods, IsMethodDecoratedWithAliasingAttribute);
+ NonAliasedMethods = actionMethods.Except(AliasedMethods).ToLookup(method => method.Name, StringComparer.OrdinalIgnoreCase);
+ }
+
+ private static List<MethodInfo> RunSelectionFilters(ControllerContext controllerContext, List<MethodInfo> methodInfos) {
+ // remove all methods which are opting out of this request
+ // to opt out, at least one attribute defined on the method must return false
+
+ List<MethodInfo> matchesWithSelectionAttributes = new List<MethodInfo>();
+ List<MethodInfo> matchesWithoutSelectionAttributes = new List<MethodInfo>();
+
+ foreach (MethodInfo methodInfo in methodInfos) {
+ ActionMethodSelectorAttribute[] attrs = (ActionMethodSelectorAttribute[])methodInfo.GetCustomAttributes(typeof(ActionMethodSelectorAttribute), true /* inherit */);
+ if (attrs.Length == 0) {
+ matchesWithoutSelectionAttributes.Add(methodInfo);
+ }
+ else if (attrs.All(attr => attr.IsValidForRequest(controllerContext, methodInfo))) {
+ matchesWithSelectionAttributes.Add(methodInfo);
+ }
+ }
+
+ // if a matching action method had a selection attribute, consider it more specific than a matching action method
+ // without a selection attribute
+ return (matchesWithSelectionAttributes.Count > 0) ? matchesWithSelectionAttributes : matchesWithoutSelectionAttributes;
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ActionMethodSelectorAttribute.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ActionMethodSelectorAttribute.cs
new file mode 100644
index 00000000000..0a2fd297f99
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ActionMethodSelectorAttribute.cs
@@ -0,0 +1,21 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Reflection;
+
+ [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
+ public abstract class ActionMethodSelectorAttribute : Attribute {
+ public abstract bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo);
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ActionNameAttribute.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ActionNameAttribute.cs
new file mode 100644
index 00000000000..46ef66a39cd
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ActionNameAttribute.cs
@@ -0,0 +1,39 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Reflection;
+ using System.Web.Mvc.Resources;
+
+ [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
+ public sealed class ActionNameAttribute : ActionNameSelectorAttribute {
+
+ public ActionNameAttribute(string name) {
+ if (String.IsNullOrEmpty(name)) {
+ throw new ArgumentException(MvcResources.Common_NullOrEmpty, "name");
+ }
+
+ Name = name;
+ }
+
+ public string Name {
+ get;
+ private set;
+ }
+
+ public override bool IsValidName(ControllerContext controllerContext, string actionName, MethodInfo methodInfo) {
+ return String.Equals(actionName, Name, StringComparison.OrdinalIgnoreCase);
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ActionNameSelectorAttribute.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ActionNameSelectorAttribute.cs
new file mode 100644
index 00000000000..5ec4134dacc
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ActionNameSelectorAttribute.cs
@@ -0,0 +1,21 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Reflection;
+
+ [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
+ public abstract class ActionNameSelectorAttribute : Attribute {
+ public abstract bool IsValidName(ControllerContext controllerContext, string actionName, MethodInfo methodInfo);
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ActionResult.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ActionResult.cs
new file mode 100644
index 00000000000..11bc79c84c0
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ActionResult.cs
@@ -0,0 +1,21 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+
+ public abstract class ActionResult {
+
+ public abstract void ExecuteResult(ControllerContext context);
+
+ }
+
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ActionSelector.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ActionSelector.cs
new file mode 100644
index 00000000000..59891d6cf81
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ActionSelector.cs
@@ -0,0 +1,17 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+
+ public delegate bool ActionSelector(ControllerContext controllerContext);
+
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/Ajax/AjaxExtensions.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Ajax/AjaxExtensions.cs
new file mode 100644
index 00000000000..a34122fd330
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Ajax/AjaxExtensions.cs
@@ -0,0 +1,322 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc.Ajax {
+ using System;
+ using System.Collections.Generic;
+ using System.ComponentModel;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Globalization;
+ using System.Web;
+ using System.Web.Mvc;
+ using System.Web.Mvc.Html;
+ using System.Web.Mvc.Resources;
+ using System.Web.Routing;
+
+ public static class AjaxExtensions {
+ private const string LinkOnClickFormat = "Sys.Mvc.AsyncHyperlink.handleClick(this, new Sys.UI.DomEvent(event), {0});";
+ private const string FormOnClickValue = "Sys.Mvc.AsyncForm.handleClick(this, new Sys.UI.DomEvent(event));";
+ private const string FormOnSubmitFormat = "Sys.Mvc.AsyncForm.handleSubmit(this, new Sys.UI.DomEvent(event), {0});";
+ private const string _globalizationScript = @"<script type=""text/javascript"" src=""{0}""></script>";
+
+ public static MvcHtmlString ActionLink(this AjaxHelper ajaxHelper, string linkText, string actionName, AjaxOptions ajaxOptions) {
+ return ActionLink(ajaxHelper, linkText, actionName, (string)null /* controllerName */, ajaxOptions);
+ }
+
+ public static MvcHtmlString ActionLink(this AjaxHelper ajaxHelper, string linkText, string actionName, object routeValues, AjaxOptions ajaxOptions) {
+ return ActionLink(ajaxHelper, linkText, actionName, (string)null /* controllerName */, routeValues, ajaxOptions);
+ }
+
+ public static MvcHtmlString ActionLink(this AjaxHelper ajaxHelper, string linkText, string actionName, object routeValues, AjaxOptions ajaxOptions, object htmlAttributes) {
+ return ActionLink(ajaxHelper, linkText, actionName, (string)null /* controllerName */, routeValues, ajaxOptions, htmlAttributes);
+ }
+
+ public static MvcHtmlString ActionLink(this AjaxHelper ajaxHelper, string linkText, string actionName, RouteValueDictionary routeValues, AjaxOptions ajaxOptions) {
+ return ActionLink(ajaxHelper, linkText, actionName, (string)null /* controllerName */, routeValues, ajaxOptions);
+ }
+
+ public static MvcHtmlString ActionLink(this AjaxHelper ajaxHelper, string linkText, string actionName, RouteValueDictionary routeValues, AjaxOptions ajaxOptions, IDictionary<string, object> htmlAttributes) {
+ return ActionLink(ajaxHelper, linkText, actionName, (string)null /* controllerName */, routeValues, ajaxOptions, htmlAttributes);
+ }
+
+ public static MvcHtmlString ActionLink(this AjaxHelper ajaxHelper, string linkText, string actionName, string controllerName, AjaxOptions ajaxOptions) {
+ return ActionLink(ajaxHelper, linkText, actionName, controllerName, null /* values */, ajaxOptions, null /* htmlAttributes */);
+ }
+
+ public static MvcHtmlString ActionLink(this AjaxHelper ajaxHelper, string linkText, string actionName, string controllerName, object routeValues, AjaxOptions ajaxOptions) {
+ return ActionLink(ajaxHelper, linkText, actionName, controllerName, routeValues, ajaxOptions, null /* htmlAttributes */);
+ }
+
+ public static MvcHtmlString ActionLink(this AjaxHelper ajaxHelper, string linkText, string actionName, string controllerName, object routeValues, AjaxOptions ajaxOptions, object htmlAttributes) {
+ RouteValueDictionary newValues = new RouteValueDictionary(routeValues);
+ Dictionary<string, object> newAttributes = ObjectToCaseSensitiveDictionary(htmlAttributes);
+ return ActionLink(ajaxHelper, linkText, actionName, controllerName, newValues, ajaxOptions, newAttributes);
+ }
+
+ public static MvcHtmlString ActionLink(this AjaxHelper ajaxHelper, string linkText, string actionName, string controllerName, RouteValueDictionary routeValues, AjaxOptions ajaxOptions) {
+ return ActionLink(ajaxHelper, linkText, actionName, controllerName, routeValues, ajaxOptions, null /* htmlAttributes */);
+ }
+
+ public static MvcHtmlString ActionLink(this AjaxHelper ajaxHelper, string linkText, string actionName, string controllerName, RouteValueDictionary routeValues, AjaxOptions ajaxOptions, IDictionary<string, object> htmlAttributes) {
+ if (String.IsNullOrEmpty(linkText)) {
+ throw new ArgumentException(MvcResources.Common_NullOrEmpty, "linkText");
+ }
+
+ string targetUrl = UrlHelper.GenerateUrl(null, actionName, controllerName, routeValues, ajaxHelper.RouteCollection, ajaxHelper.ViewContext.RequestContext, true /* includeImplicitMvcValues */);
+
+ return MvcHtmlString.Create(GenerateLink(linkText, targetUrl, GetAjaxOptions(ajaxOptions), htmlAttributes));
+ }
+
+ public static MvcHtmlString ActionLink(this AjaxHelper ajaxHelper, string linkText, string actionName, string controllerName, string protocol, string hostName, string fragment, object routeValues, AjaxOptions ajaxOptions, object htmlAttributes) {
+ RouteValueDictionary newValues = new RouteValueDictionary(routeValues);
+ Dictionary<string, object> newAttributes = ObjectToCaseSensitiveDictionary(htmlAttributes);
+ return ActionLink(ajaxHelper, linkText, actionName, controllerName, protocol, hostName, fragment, newValues, ajaxOptions, newAttributes);
+ }
+
+ public static MvcHtmlString ActionLink(this AjaxHelper ajaxHelper, string linkText, string actionName, string controllerName, string protocol, string hostName, string fragment, RouteValueDictionary routeValues, AjaxOptions ajaxOptions, IDictionary<string, object> htmlAttributes) {
+ if (String.IsNullOrEmpty(linkText)) {
+ throw new ArgumentException(MvcResources.Common_NullOrEmpty, "linkText");
+ }
+
+ string targetUrl = UrlHelper.GenerateUrl(null /* routeName */, actionName, controllerName, protocol, hostName, fragment, routeValues, ajaxHelper.RouteCollection, ajaxHelper.ViewContext.RequestContext, true /* includeImplicitMvcValues */);
+
+ return MvcHtmlString.Create(GenerateLink(linkText, targetUrl, ajaxOptions, htmlAttributes));
+ }
+
+ public static MvcForm BeginForm(this AjaxHelper ajaxHelper, AjaxOptions ajaxOptions) {
+ string formAction = ajaxHelper.ViewContext.HttpContext.Request.RawUrl;
+ return FormHelper(ajaxHelper, formAction, ajaxOptions, new RouteValueDictionary());
+ }
+
+ public static MvcForm BeginForm(this AjaxHelper ajaxHelper, string actionName, AjaxOptions ajaxOptions) {
+ return BeginForm(ajaxHelper, actionName, (string)null /* controllerName */, ajaxOptions);
+ }
+
+ public static MvcForm BeginForm(this AjaxHelper ajaxHelper, string actionName, object routeValues, AjaxOptions ajaxOptions) {
+ return BeginForm(ajaxHelper, actionName, (string)null /* controllerName */, routeValues, ajaxOptions);
+ }
+
+ public static MvcForm BeginForm(this AjaxHelper ajaxHelper, string actionName, object routeValues, AjaxOptions ajaxOptions, object htmlAttributes) {
+ return BeginForm(ajaxHelper, actionName, (string)null /* controllerName */, routeValues, ajaxOptions, htmlAttributes);
+ }
+
+ public static MvcForm BeginForm(this AjaxHelper ajaxHelper, string actionName, RouteValueDictionary routeValues, AjaxOptions ajaxOptions) {
+ return BeginForm(ajaxHelper, actionName, (string)null /* controllerName */, routeValues, ajaxOptions);
+ }
+
+ public static MvcForm BeginForm(this AjaxHelper ajaxHelper, string actionName, RouteValueDictionary routeValues, AjaxOptions ajaxOptions, IDictionary<string, object> htmlAttributes) {
+ return BeginForm(ajaxHelper, actionName, (string)null /* controllerName */, routeValues, ajaxOptions, htmlAttributes);
+ }
+
+ public static MvcForm BeginForm(this AjaxHelper ajaxHelper, string actionName, string controllerName, AjaxOptions ajaxOptions) {
+ return BeginForm(ajaxHelper, actionName, controllerName, null /* values */, ajaxOptions, null /* htmlAttributes */);
+ }
+
+ public static MvcForm BeginForm(this AjaxHelper ajaxHelper, string actionName, string controllerName, object routeValues, AjaxOptions ajaxOptions) {
+ return BeginForm(ajaxHelper, actionName, controllerName, routeValues, ajaxOptions, null /* htmlAttributes */);
+ }
+
+ public static MvcForm BeginForm(this AjaxHelper ajaxHelper, string actionName, string controllerName, object routeValues, AjaxOptions ajaxOptions, object htmlAttributes) {
+ RouteValueDictionary newValues = new RouteValueDictionary(routeValues);
+ Dictionary<string, object> newAttributes = ObjectToCaseSensitiveDictionary(htmlAttributes);
+ return BeginForm(ajaxHelper, actionName, controllerName, newValues, ajaxOptions, newAttributes);
+ }
+
+ public static MvcForm BeginForm(this AjaxHelper ajaxHelper, string actionName, string controllerName, RouteValueDictionary routeValues, AjaxOptions ajaxOptions) {
+ return BeginForm(ajaxHelper, actionName, controllerName, routeValues, ajaxOptions, null /* htmlAttributes */);
+ }
+
+ public static MvcForm BeginForm(this AjaxHelper ajaxHelper, string actionName, string controllerName, RouteValueDictionary routeValues, AjaxOptions ajaxOptions, IDictionary<string, object> htmlAttributes) {
+ // get target URL
+ string formAction = UrlHelper.GenerateUrl(null, actionName, controllerName, routeValues ?? new RouteValueDictionary(), ajaxHelper.RouteCollection, ajaxHelper.ViewContext.RequestContext, true /* includeImplicitMvcValues */);
+ return FormHelper(ajaxHelper, formAction, ajaxOptions, htmlAttributes);
+ }
+
+ public static MvcForm BeginRouteForm(this AjaxHelper ajaxHelper, string routeName, AjaxOptions ajaxOptions) {
+ return BeginRouteForm(ajaxHelper, routeName, null /* routeValues */, ajaxOptions, null /* htmlAttributes */);
+ }
+
+ public static MvcForm BeginRouteForm(this AjaxHelper ajaxHelper, string routeName, object routeValues, AjaxOptions ajaxOptions) {
+ return BeginRouteForm(ajaxHelper, routeName, (object)routeValues, ajaxOptions, null /* htmlAttributes */);
+ }
+
+ public static MvcForm BeginRouteForm(this AjaxHelper ajaxHelper, string routeName, object routeValues, AjaxOptions ajaxOptions, object htmlAttributes) {
+ Dictionary<string, object> newAttributes = ObjectToCaseSensitiveDictionary(htmlAttributes);
+ return BeginRouteForm(ajaxHelper, routeName, new RouteValueDictionary(routeValues), ajaxOptions, newAttributes);
+ }
+
+ public static MvcForm BeginRouteForm(this AjaxHelper ajaxHelper, string routeName, RouteValueDictionary routeValues, AjaxOptions ajaxOptions) {
+ return BeginRouteForm(ajaxHelper, routeName, routeValues, ajaxOptions, null /* htmlAttributes */);
+ }
+
+ public static MvcForm BeginRouteForm(this AjaxHelper ajaxHelper, string routeName, RouteValueDictionary routeValues, AjaxOptions ajaxOptions, IDictionary<string, object> htmlAttributes) {
+ string formAction = UrlHelper.GenerateUrl(routeName, null /* actionName */, null /* controllerName */, routeValues ?? new RouteValueDictionary(), ajaxHelper.RouteCollection, ajaxHelper.ViewContext.RequestContext, false /* includeImplicitMvcValues */);
+ return FormHelper(ajaxHelper, formAction, ajaxOptions, htmlAttributes);
+ }
+
+ private static MvcForm FormHelper(this AjaxHelper ajaxHelper, string formAction, AjaxOptions ajaxOptions, IDictionary<string, object> htmlAttributes) {
+ TagBuilder builder = new TagBuilder("form");
+ builder.MergeAttributes(htmlAttributes);
+ builder.MergeAttribute("action", formAction);
+ builder.MergeAttribute("method", "post");
+ builder.MergeAttribute("onclick", FormOnClickValue);
+ builder.MergeAttribute("onsubmit", GenerateAjaxScript(GetAjaxOptions(ajaxOptions), FormOnSubmitFormat));
+
+ if (ajaxHelper.ViewContext.ClientValidationEnabled) {
+ // forms must have an ID for client validation
+ builder.GenerateId(ajaxHelper.ViewContext.FormIdGenerator());
+ }
+
+ ajaxHelper.ViewContext.Writer.Write(builder.ToString(TagRenderMode.StartTag));
+ MvcForm theForm = new MvcForm(ajaxHelper.ViewContext);
+
+ if (ajaxHelper.ViewContext.ClientValidationEnabled) {
+ ajaxHelper.ViewContext.FormContext.FormId = builder.Attributes["id"];
+ }
+
+ return theForm;
+ }
+
+ public static MvcHtmlString GlobalizationScript(this AjaxHelper ajaxHelper) {
+ return GlobalizationScript(ajaxHelper, CultureInfo.CurrentCulture);
+ }
+
+ [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "ajaxHelper",
+ Justification = "This is an extension method")]
+ public static MvcHtmlString GlobalizationScript(this AjaxHelper ajaxHelper, CultureInfo cultureInfo) {
+ return GlobalizationScriptHelper(AjaxHelper.GlobalizationScriptPath, cultureInfo);
+ }
+
+ private static MvcHtmlString GlobalizationScriptHelper(string scriptPath, CultureInfo cultureInfo) {
+ if (cultureInfo == null) {
+ throw new ArgumentNullException("cultureInfo");
+ }
+
+ string src = VirtualPathUtility.AppendTrailingSlash(scriptPath) + cultureInfo.Name + ".js";
+ string scriptWithCorrectNewLines = _globalizationScript.Replace("\r\n", Environment.NewLine);
+ string formatted = String.Format(CultureInfo.InvariantCulture, scriptWithCorrectNewLines, src);
+
+ return MvcHtmlString.Create(formatted);
+ }
+
+ public static MvcHtmlString RouteLink(this AjaxHelper ajaxHelper, string linkText, object routeValues, AjaxOptions ajaxOptions) {
+ return RouteLink(ajaxHelper, linkText, null /* routeName */, new RouteValueDictionary(routeValues), ajaxOptions,
+ new Dictionary<string, object>());
+ }
+
+ public static MvcHtmlString RouteLink(this AjaxHelper ajaxHelper, string linkText, object routeValues, AjaxOptions ajaxOptions, object htmlAttributes) {
+ return RouteLink(ajaxHelper, linkText, null /* routeName */, new RouteValueDictionary(routeValues), ajaxOptions,
+ ObjectToCaseSensitiveDictionary(htmlAttributes));
+ }
+
+ public static MvcHtmlString RouteLink(this AjaxHelper ajaxHelper, string linkText, RouteValueDictionary routeValues, AjaxOptions ajaxOptions) {
+ return RouteLink(ajaxHelper, linkText, null /* routeName */, routeValues, ajaxOptions,
+ new Dictionary<string, object>());
+ }
+
+ public static MvcHtmlString RouteLink(this AjaxHelper ajaxHelper, string linkText, RouteValueDictionary routeValues, AjaxOptions ajaxOptions, IDictionary<string, object> htmlAttributes) {
+ return RouteLink(ajaxHelper, linkText, null /* routeName */, routeValues, ajaxOptions, htmlAttributes);
+ }
+
+ public static MvcHtmlString RouteLink(this AjaxHelper ajaxHelper, string linkText, string routeName, AjaxOptions ajaxOptions) {
+ return RouteLink(ajaxHelper, linkText, routeName, new RouteValueDictionary(), ajaxOptions,
+ new Dictionary<string, object>());
+ }
+
+ public static MvcHtmlString RouteLink(this AjaxHelper ajaxHelper, string linkText, string routeName, AjaxOptions ajaxOptions, object htmlAttributes) {
+ return RouteLink(ajaxHelper, linkText, routeName, new RouteValueDictionary(), ajaxOptions, ObjectToCaseSensitiveDictionary(htmlAttributes));
+ }
+
+ public static MvcHtmlString RouteLink(this AjaxHelper ajaxHelper, string linkText, string routeName, AjaxOptions ajaxOptions, IDictionary<string, object> htmlAttributes) {
+ return RouteLink(ajaxHelper, linkText, routeName, new RouteValueDictionary(), ajaxOptions, htmlAttributes);
+ }
+
+ public static MvcHtmlString RouteLink(this AjaxHelper ajaxHelper, string linkText, string routeName, object routeValues, AjaxOptions ajaxOptions) {
+ return RouteLink(ajaxHelper, linkText, routeName, new RouteValueDictionary(routeValues), ajaxOptions,
+ new Dictionary<string, object>());
+ }
+
+ public static MvcHtmlString RouteLink(this AjaxHelper ajaxHelper, string linkText, string routeName, object routeValues, AjaxOptions ajaxOptions, object htmlAttributes) {
+ return RouteLink(ajaxHelper, linkText, routeName, new RouteValueDictionary(routeValues), ajaxOptions,
+ ObjectToCaseSensitiveDictionary(htmlAttributes));
+ }
+
+ public static MvcHtmlString RouteLink(this AjaxHelper ajaxHelper, string linkText, string routeName, RouteValueDictionary routeValues, AjaxOptions ajaxOptions) {
+ return RouteLink(ajaxHelper, linkText, routeName, routeValues, ajaxOptions, new Dictionary<string, object>());
+ }
+
+ public static MvcHtmlString RouteLink(this AjaxHelper ajaxHelper, string linkText, string routeName, RouteValueDictionary routeValues, AjaxOptions ajaxOptions, IDictionary<string, object> htmlAttributes) {
+ if (String.IsNullOrEmpty(linkText)) {
+ throw new ArgumentException(MvcResources.Common_NullOrEmpty, "linkText");
+ }
+
+ string targetUrl = UrlHelper.GenerateUrl(routeName, null /* actionName */, null /* controllerName */, routeValues ?? new RouteValueDictionary(), ajaxHelper.RouteCollection, ajaxHelper.ViewContext.RequestContext, false /* includeImplicitMvcValues */);
+
+ return MvcHtmlString.Create(GenerateLink(linkText, targetUrl, GetAjaxOptions(ajaxOptions), htmlAttributes));
+ }
+
+ public static MvcHtmlString RouteLink(this AjaxHelper ajaxHelper, string linkText, string routeName, string protocol, string hostName, string fragment, RouteValueDictionary routeValues, AjaxOptions ajaxOptions, IDictionary<string, object> htmlAttributes) {
+ if (String.IsNullOrEmpty(linkText)) {
+ throw new ArgumentException(MvcResources.Common_NullOrEmpty, "linkText");
+ }
+
+ string targetUrl = UrlHelper.GenerateUrl(routeName, null /* actionName */, null /* controllerName */, protocol, hostName, fragment, routeValues ?? new RouteValueDictionary(), ajaxHelper.RouteCollection, ajaxHelper.ViewContext.RequestContext, false /* includeImplicitMvcValues */);
+
+ return MvcHtmlString.Create(GenerateLink(linkText, targetUrl, GetAjaxOptions(ajaxOptions), htmlAttributes));
+ }
+
+ internal static string InsertionModeToString(InsertionMode insertionMode) {
+ switch (insertionMode) {
+ case InsertionMode.Replace:
+ return "Sys.Mvc.InsertionMode.replace";
+ case InsertionMode.InsertBefore:
+ return "Sys.Mvc.InsertionMode.insertBefore";
+ case InsertionMode.InsertAfter:
+ return "Sys.Mvc.InsertionMode.insertAfter";
+ default:
+ return ((int)insertionMode).ToString(CultureInfo.InvariantCulture);
+ }
+ }
+
+ private static Dictionary<string, object> ObjectToCaseSensitiveDictionary(object values) {
+ Dictionary<string, object> dict = new Dictionary<string, object>(StringComparer.Ordinal);
+ if (values != null) {
+ foreach (PropertyDescriptor prop in TypeDescriptor.GetProperties(values)) {
+ object val = prop.GetValue(values);
+ dict[prop.Name] = val;
+ }
+ }
+ return dict;
+ }
+
+ private static string GenerateLink(string linkText, string targetUrl, AjaxOptions ajaxOptions, IDictionary<string, object> htmlAttributes) {
+ TagBuilder tag = new TagBuilder("a") {
+ InnerHtml = HttpUtility.HtmlEncode(linkText)
+ };
+
+ tag.MergeAttributes(htmlAttributes);
+ tag.MergeAttribute("href", targetUrl);
+ tag.MergeAttribute("onclick", GenerateAjaxScript(ajaxOptions, LinkOnClickFormat));
+
+ return tag.ToString(TagRenderMode.Normal);
+ }
+
+ private static string GenerateAjaxScript(AjaxOptions ajaxOptions, string scriptFormat) {
+ string optionsString = ajaxOptions.ToJavascriptString();
+ return String.Format(CultureInfo.InvariantCulture, scriptFormat, optionsString);
+ }
+
+ private static AjaxOptions GetAjaxOptions(AjaxOptions ajaxOptions) {
+ return (ajaxOptions != null) ? ajaxOptions : new AjaxOptions();
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/Ajax/AjaxOptions.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Ajax/AjaxOptions.cs
new file mode 100644
index 00000000000..ca04c64f007
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Ajax/AjaxOptions.cs
@@ -0,0 +1,166 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc.Ajax {
+ using System;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Globalization;
+ using System.Text;
+ using System.Web.Mvc.Resources;
+
+ public class AjaxOptions {
+ private string _confirm;
+ private string _httpMethod;
+ private InsertionMode _insertionMode = InsertionMode.Replace;
+ private string _loadingElementId;
+ private string _onBegin;
+ private string _onComplete;
+ private string _onFailure;
+ private string _onSuccess;
+ private string _updateTargetId;
+ private string _url;
+
+ public string Confirm {
+ get {
+ return _confirm ?? String.Empty;
+ }
+ set {
+ _confirm = value;
+ }
+ }
+
+ public string HttpMethod {
+ get {
+ return _httpMethod ?? String.Empty;
+ }
+ set {
+ _httpMethod = value;
+ }
+ }
+
+ public InsertionMode InsertionMode {
+ get {
+ return _insertionMode;
+ }
+ set {
+ switch (value) {
+ case InsertionMode.Replace:
+ case InsertionMode.InsertAfter:
+ case InsertionMode.InsertBefore:
+ _insertionMode = value;
+ return;
+
+ default:
+ throw new ArgumentOutOfRangeException("value");
+ }
+ }
+ }
+
+ public string LoadingElementId {
+ get {
+ return _loadingElementId ?? String.Empty;
+ }
+ set {
+ _loadingElementId = value;
+ }
+ }
+
+ public string OnBegin {
+ get {
+ return _onBegin ?? String.Empty;
+ }
+ set {
+ _onBegin = value;
+ }
+ }
+
+ public string OnComplete {
+ get {
+ return _onComplete ?? String.Empty;
+ }
+ set {
+ _onComplete = value;
+ }
+ }
+
+ public string OnFailure {
+ get {
+ return _onFailure ?? String.Empty;
+ }
+ set {
+ _onFailure = value;
+ }
+ }
+
+ public string OnSuccess {
+ get {
+ return _onSuccess ?? String.Empty;
+ }
+ set {
+ _onSuccess = value;
+ }
+ }
+
+ public string UpdateTargetId {
+ get {
+ return _updateTargetId ?? String.Empty;
+ }
+ set {
+ _updateTargetId = value;
+ }
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1056:UriPropertiesShouldNotBeStrings",
+ Justification = "This property is used by the optionsBuilder which always accepts a string.")]
+ public string Url {
+ get {
+ return _url ?? String.Empty;
+ }
+ set {
+ _url = value;
+ }
+ }
+
+ internal string ToJavascriptString() {
+ // creates a string of the form { key1: value1, key2 : value2, ... }
+ StringBuilder optionsBuilder = new StringBuilder("{");
+ optionsBuilder.Append(String.Format(CultureInfo.InvariantCulture, " insertionMode: {0},", AjaxExtensions.InsertionModeToString(InsertionMode)));
+ optionsBuilder.Append(PropertyStringIfSpecified("confirm", Confirm));
+ optionsBuilder.Append(PropertyStringIfSpecified("httpMethod", HttpMethod));
+ optionsBuilder.Append(PropertyStringIfSpecified("loadingElementId", LoadingElementId));
+ optionsBuilder.Append(PropertyStringIfSpecified("updateTargetId", UpdateTargetId));
+ optionsBuilder.Append(PropertyStringIfSpecified("url", Url));
+ optionsBuilder.Append(EventStringIfSpecified("onBegin", OnBegin));
+ optionsBuilder.Append(EventStringIfSpecified("onComplete", OnComplete));
+ optionsBuilder.Append(EventStringIfSpecified("onFailure", OnFailure));
+ optionsBuilder.Append(EventStringIfSpecified("onSuccess", OnSuccess));
+ optionsBuilder.Length--;
+ optionsBuilder.Append(" }");
+ return optionsBuilder.ToString();
+ }
+
+ private static string EventStringIfSpecified(string propertyName, string handler) {
+ if (!String.IsNullOrEmpty(handler)) {
+ return String.Format(CultureInfo.InvariantCulture, " {0}: Function.createDelegate(this, {1}),", propertyName, handler.ToString());
+ }
+ return String.Empty;
+ }
+
+ private static string PropertyStringIfSpecified(string propertyName, string propertyValue) {
+ if (!String.IsNullOrEmpty(propertyValue)) {
+ string escapedPropertyValue = propertyValue.Replace("'", @"\'");
+ return String.Format(CultureInfo.InvariantCulture, " {0}: '{1}',", propertyName, escapedPropertyValue);
+ }
+ return String.Empty;
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/Ajax/InsertionMode.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Ajax/InsertionMode.cs
new file mode 100644
index 00000000000..de0eb4e24f5
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Ajax/InsertionMode.cs
@@ -0,0 +1,19 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc.Ajax {
+ public enum InsertionMode {
+ Replace = 0,
+ InsertBefore = 1,
+ InsertAfter = 2
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/AjaxHelper.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/AjaxHelper.cs
new file mode 100644
index 00000000000..7324a52d204
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/AjaxHelper.cs
@@ -0,0 +1,89 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Text;
+ using System.Web.Routing;
+ using System.Web.Script.Serialization;
+
+ public class AjaxHelper {
+
+ private static string _globalizationScriptPath;
+
+ public AjaxHelper(ViewContext viewContext, IViewDataContainer viewDataContainer)
+ : this(viewContext, viewDataContainer, RouteTable.Routes) {
+ }
+
+ public AjaxHelper(ViewContext viewContext, IViewDataContainer viewDataContainer, RouteCollection routeCollection) {
+ if (viewContext == null) {
+ throw new ArgumentNullException("viewContext");
+ }
+ if (viewDataContainer == null) {
+ throw new ArgumentNullException("viewDataContainer");
+ }
+ if (routeCollection == null) {
+ throw new ArgumentNullException("routeCollection");
+ }
+ ViewContext = viewContext;
+ ViewDataContainer = viewDataContainer;
+ RouteCollection = routeCollection;
+ }
+
+ public static string GlobalizationScriptPath {
+ get {
+ if (String.IsNullOrEmpty(_globalizationScriptPath)) {
+ _globalizationScriptPath = "~/Scripts/Globalization";
+ }
+ return _globalizationScriptPath;
+ }
+ set {
+ _globalizationScriptPath = value;
+ }
+ }
+
+ public RouteCollection RouteCollection {
+ get;
+ private set;
+ }
+
+ public ViewContext ViewContext {
+ get;
+ private set;
+ }
+
+ public ViewDataDictionary ViewData {
+ get {
+ return ViewDataContainer.ViewData;
+ }
+ }
+
+ public IViewDataContainer ViewDataContainer {
+ get;
+ private set;
+ }
+
+ [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic",
+ Justification = "Instance method for consistency with other helpers.")]
+ public string JavaScriptStringEncode(string message) {
+ if (String.IsNullOrEmpty(message)) {
+ return message;
+ }
+
+ StringBuilder builder = new StringBuilder();
+ JavaScriptSerializer serializer = new JavaScriptSerializer();
+ serializer.Serialize(message, builder);
+ return builder.ToString(1, builder.Length - 2); // remove first + last quote
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/AjaxHelper`1.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/AjaxHelper`1.cs
new file mode 100644
index 00000000000..1e1f2b95660
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/AjaxHelper`1.cs
@@ -0,0 +1,35 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System.Web.Routing;
+
+ public class AjaxHelper<TModel> : AjaxHelper {
+ private ViewDataDictionary<TModel> _viewData;
+
+ public AjaxHelper(ViewContext viewContext, IViewDataContainer viewDataContainer)
+ : this(viewContext, viewDataContainer, RouteTable.Routes) {
+ }
+
+ public AjaxHelper(ViewContext viewContext, IViewDataContainer viewDataContainer, RouteCollection routeCollection)
+ : base(viewContext, viewDataContainer, routeCollection) {
+
+ _viewData = new ViewDataDictionary<TModel>(viewDataContainer.ViewData);
+ }
+
+ public new ViewDataDictionary<TModel> ViewData {
+ get {
+ return _viewData;
+ }
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/AjaxRequestExtensions.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/AjaxRequestExtensions.cs
new file mode 100644
index 00000000000..411c25092c2
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/AjaxRequestExtensions.cs
@@ -0,0 +1,26 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+
+ public static class AjaxRequestExtensions {
+
+ public static bool IsAjaxRequest(this HttpRequestBase request) {
+ if (request == null) {
+ throw new ArgumentNullException("request");
+ }
+
+ return (request["X-Requested-With"] == "XMLHttpRequest") || ((request.Headers != null) && (request.Headers["X-Requested-With"] == "XMLHttpRequest"));
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/AntiForgeryData.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/AntiForgeryData.cs
new file mode 100644
index 00000000000..82252d44102
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/AntiForgeryData.cs
@@ -0,0 +1,129 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Security.Cryptography;
+ using System.Security.Principal;
+ using System.Text;
+
+ internal sealed class AntiForgeryData {
+
+ private const string AntiForgeryTokenFieldName = "__RequestVerificationToken";
+
+ private const int TokenLength = 128 / 8;
+ private readonly static RNGCryptoServiceProvider _prng = new RNGCryptoServiceProvider();
+
+ private DateTime _creationDate = DateTime.UtcNow;
+ private string _salt;
+ private string _username;
+ private string _value;
+
+ public AntiForgeryData() {
+ }
+
+ // copy constructor
+ public AntiForgeryData(AntiForgeryData token) {
+ if (token == null) {
+ throw new ArgumentNullException("token");
+ }
+
+ CreationDate = token.CreationDate;
+ Salt = token.Salt;
+ Username = token.Username;
+ Value = token.Value;
+ }
+
+ public DateTime CreationDate {
+ get {
+ return _creationDate;
+ }
+ set {
+ _creationDate = value;
+ }
+ }
+
+ public string Salt {
+ get {
+ return _salt ?? String.Empty;
+ }
+ set {
+ _salt = value;
+ }
+ }
+
+ public string Username {
+ get {
+ return _username ?? String.Empty;
+ }
+ set {
+ _username = value;
+ }
+ }
+
+ public string Value {
+ get {
+ return _value ?? String.Empty;
+ }
+ set {
+ _value = value;
+ }
+ }
+
+ private static string Base64EncodeForCookieName(string s) {
+ byte[] rawBytes = Encoding.UTF8.GetBytes(s);
+ string base64String = Convert.ToBase64String(rawBytes);
+
+ // replace base64-specific characters with characters that are safe for a cookie name
+ return base64String.Replace('+', '.').Replace('/', '-').Replace('=', '_');
+ }
+
+ private static string GenerateRandomTokenString() {
+ byte[] tokenBytes = new byte[TokenLength];
+ _prng.GetBytes(tokenBytes);
+
+ string token = Convert.ToBase64String(tokenBytes);
+ return token;
+ }
+
+ // If the app path is provided, we're generating a cookie name rather than a field name, and the cookie names should
+ // be unique so that a development server cookie and an IIS cookie - both running on localhost - don't stomp on
+ // each other.
+ internal static string GetAntiForgeryTokenName(string appPath) {
+ if (String.IsNullOrEmpty(appPath)) {
+ return AntiForgeryTokenFieldName;
+ }
+ else {
+ return AntiForgeryTokenFieldName + "_" + Base64EncodeForCookieName(appPath);
+ }
+ }
+
+ internal static string GetUsername(IPrincipal user) {
+ if (user != null) {
+ IIdentity identity = user.Identity;
+ if (identity != null && identity.IsAuthenticated) {
+ return identity.Name;
+ }
+ }
+
+ return String.Empty;
+ }
+
+ public static AntiForgeryData NewToken() {
+ string tokenString = GenerateRandomTokenString();
+ return new AntiForgeryData() {
+ Value = tokenString
+ };
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/AntiForgeryDataSerializer.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/AntiForgeryDataSerializer.cs
new file mode 100644
index 00000000000..cb068317e13
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/AntiForgeryDataSerializer.cs
@@ -0,0 +1,128 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Diagnostics.CodeAnalysis;
+ using System.IO;
+ using System.Web;
+ using System.Web.Mvc.Resources;
+ using System.Web.UI;
+
+ internal class AntiForgeryDataSerializer {
+
+ private IStateFormatter _formatter;
+
+ protected internal IStateFormatter Formatter {
+ get {
+ if (_formatter == null) {
+ _formatter = FormatterGenerator.GetFormatter();
+ }
+ return _formatter;
+ }
+ set {
+ _formatter = value;
+ }
+ }
+
+ private static HttpAntiForgeryException CreateValidationException(Exception innerException) {
+ return new HttpAntiForgeryException(MvcResources.AntiForgeryToken_ValidationFailed, innerException);
+ }
+
+ public virtual AntiForgeryData Deserialize(string serializedToken) {
+ if (String.IsNullOrEmpty(serializedToken)) {
+ throw new ArgumentException(MvcResources.Common_NullOrEmpty, "serializedToken");
+ }
+
+ // call property getter outside try { } block so that exceptions bubble up for debugging
+ IStateFormatter formatter = Formatter;
+
+ try {
+ object[] deserializedObj = (object[])formatter.Deserialize(serializedToken);
+ return new AntiForgeryData() {
+ Salt = (string)deserializedObj[0],
+ Value = (string)deserializedObj[1],
+ CreationDate = (DateTime)deserializedObj[2],
+ Username = (string)deserializedObj[3]
+ };
+ }
+ catch (Exception ex) {
+ throw CreateValidationException(ex);
+ }
+ }
+
+ public virtual string Serialize(AntiForgeryData token) {
+ if (token == null) {
+ throw new ArgumentNullException("token");
+ }
+
+ object[] objToSerialize = new object[] {
+ token.Salt,
+ token.Value,
+ token.CreationDate,
+ token.Username
+ };
+
+ string serializedValue = Formatter.Serialize(objToSerialize);
+ return serializedValue;
+ }
+
+ // See http://www.yoda.arachsys.com/csharp/singleton.html (fifth version - fully lazy) for the singleton pattern
+ // used here. We need to defer the call to TokenPersister.CreateFormatterGenerator() until we're actually
+ // servicing a request, else HttpContext.Current might be invalid in TokenPersister.CreateFormatterGenerator().
+ private static class FormatterGenerator {
+
+ public static readonly Func<IStateFormatter> GetFormatter = TokenPersister.CreateFormatterGenerator();
+
+ [SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline",
+ Justification = "This type must not be marked 'beforefieldinit'.")]
+ static FormatterGenerator() {
+ }
+
+ // This type is very difficult to unit-test because Page.ProcessRequest() requires mocking
+ // much of the hosting environment. For now, we can perform functional tests of this feature.
+ private sealed class TokenPersister : PageStatePersister {
+ private TokenPersister(Page page)
+ : base(page) {
+ }
+
+ public static Func<IStateFormatter> CreateFormatterGenerator() {
+ // This code instantiates a page and tricks it into thinking that it's servicing
+ // a postback scenario with encrypted ViewState, which is required to make the
+ // StateFormatter properly decrypt data. Specifically, this code sets the
+ // internal Page.ContainsEncryptedViewState flag.
+ TextWriter writer = TextWriter.Null;
+ HttpResponse response = new HttpResponse(writer);
+ HttpRequest request = new HttpRequest("DummyFile.aspx", HttpContext.Current.Request.Url.ToString(), "__EVENTTARGET=true&__VIEWSTATEENCRYPTED=true");
+ HttpContext context = new HttpContext(request, response);
+
+ Page page = new Page() {
+ EnableViewStateMac = true,
+ ViewStateEncryptionMode = ViewStateEncryptionMode.Always
+ };
+ page.ProcessRequest(context);
+
+ return () => new TokenPersister(page).StateFormatter;
+ }
+
+ public override void Load() {
+ throw new NotImplementedException();
+ }
+
+ public override void Save() {
+ throw new NotImplementedException();
+ }
+ }
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/AreaHelpers.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/AreaHelpers.cs
new file mode 100644
index 00000000000..723f834e15b
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/AreaHelpers.cs
@@ -0,0 +1,43 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Web.Routing;
+
+ internal static class AreaHelpers {
+
+ public static string GetAreaName(RouteBase route) {
+ IRouteWithArea routeWithArea = route as IRouteWithArea;
+ if (routeWithArea != null) {
+ return routeWithArea.Area;
+ }
+
+ Route castRoute = route as Route;
+ if (castRoute != null && castRoute.DataTokens != null) {
+ return castRoute.DataTokens["area"] as string;
+ }
+
+ return null;
+ }
+
+ public static string GetAreaName(RouteData routeData) {
+ object area;
+ if (routeData.DataTokens.TryGetValue("area", out area)) {
+ return area as string;
+ }
+
+ return GetAreaName(routeData.Route);
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/AreaRegistration.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/AreaRegistration.cs
new file mode 100644
index 00000000000..5725ddf7066
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/AreaRegistration.cs
@@ -0,0 +1,62 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Collections.Generic;
+ using System.Web.Routing;
+
+ public abstract class AreaRegistration {
+
+ private const string _typeCacheName = "MVC-AreaRegistrationTypeCache.xml";
+
+ public abstract string AreaName {
+ get;
+ }
+
+ internal void CreateContextAndRegister(RouteCollection routes, object state) {
+ AreaRegistrationContext context = new AreaRegistrationContext(AreaName, routes, state);
+
+ string thisNamespace = GetType().Namespace;
+ if (thisNamespace != null) {
+ context.Namespaces.Add(thisNamespace + ".*");
+ }
+
+ RegisterArea(context);
+ }
+
+ private static bool IsAreaRegistrationType(Type type) {
+ return
+ typeof(AreaRegistration).IsAssignableFrom(type) &&
+ type.GetConstructor(Type.EmptyTypes) != null;
+ }
+
+ public static void RegisterAllAreas() {
+ RegisterAllAreas(null);
+ }
+
+ public static void RegisterAllAreas(object state) {
+ RegisterAllAreas(RouteTable.Routes, new BuildManagerWrapper(), state);
+ }
+
+ internal static void RegisterAllAreas(RouteCollection routes, IBuildManager buildManager, object state) {
+ List<Type> areaRegistrationTypes = TypeCacheUtil.GetFilteredTypesFromAssemblies(_typeCacheName, IsAreaRegistrationType, buildManager);
+ foreach (Type areaRegistrationType in areaRegistrationTypes) {
+ AreaRegistration registration = (AreaRegistration)Activator.CreateInstance(areaRegistrationType);
+ registration.CreateContextAndRegister(routes, state);
+ }
+ }
+
+ public abstract void RegisterArea(AreaRegistrationContext context);
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/AreaRegistrationContext.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/AreaRegistrationContext.cs
new file mode 100644
index 00000000000..869c1ac6dd4
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/AreaRegistrationContext.cs
@@ -0,0 +1,111 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Linq;
+ using System.Web.Routing;
+
+ public class AreaRegistrationContext {
+
+ private readonly HashSet<string> _namespaces = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
+
+ public AreaRegistrationContext(string areaName, RouteCollection routes)
+ : this(areaName, routes, null) {
+ }
+
+ public AreaRegistrationContext(string areaName, RouteCollection routes, object state) {
+ if (String.IsNullOrEmpty(areaName)) {
+ throw Error.ParameterCannotBeNullOrEmpty("areaName");
+ }
+ if (routes == null) {
+ throw new ArgumentNullException("routes");
+ }
+
+ AreaName = areaName;
+ Routes = routes;
+ State = state;
+ }
+
+ public string AreaName {
+ get;
+ private set;
+ }
+
+ public ICollection<string> Namespaces {
+ get {
+ return _namespaces;
+ }
+ }
+
+ public RouteCollection Routes {
+ get;
+ private set;
+ }
+
+ public object State {
+ get;
+ private set;
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "1#",
+ Justification = "This is not a regular URL as it may contain special routing characters.")]
+ public Route MapRoute(string name, string url) {
+ return MapRoute(name, url, (object)null /* defaults */);
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "1#",
+ Justification = "This is not a regular URL as it may contain special routing characters.")]
+ public Route MapRoute(string name, string url, object defaults) {
+ return MapRoute(name, url, defaults, (object)null /* constraints */);
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "1#",
+ Justification = "This is not a regular URL as it may contain special routing characters.")]
+ public Route MapRoute(string name, string url, object defaults, object constraints) {
+ return MapRoute(name, url, defaults, constraints, null /* namespaces */);
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "1#",
+ Justification = "This is not a regular URL as it may contain special routing characters.")]
+ public Route MapRoute(string name, string url, string[] namespaces) {
+ return MapRoute(name, url, (object)null /* defaults */, namespaces);
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "1#",
+ Justification = "This is not a regular URL as it may contain special routing characters.")]
+ public Route MapRoute(string name, string url, object defaults, string[] namespaces) {
+ return MapRoute(name, url, defaults, null /* constraints */, namespaces);
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "1#",
+ Justification = "This is not a regular URL as it may contain special routing characters.")]
+ public Route MapRoute(string name, string url, object defaults, object constraints, string[] namespaces) {
+ if (namespaces == null && Namespaces != null) {
+ namespaces = Namespaces.ToArray();
+ }
+
+ Route route = Routes.MapRoute(name, url, defaults, constraints, namespaces);
+ route.DataTokens["area"] = AreaName;
+
+ // disabling the namespace lookup fallback mechanism keeps this areas from accidentally picking up
+ // controllers belonging to other areas
+ bool useNamespaceFallback = (namespaces == null || namespaces.Length == 0);
+ route.DataTokens["UseNamespaceFallback"] = useNamespaceFallback;
+
+ return route;
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/AssociatedMetadataProvider.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/AssociatedMetadataProvider.cs
new file mode 100644
index 00000000000..bd9248f811f
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/AssociatedMetadataProvider.cs
@@ -0,0 +1,95 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Collections.Generic;
+ using System.ComponentModel;
+ using System.ComponentModel.DataAnnotations;
+ using System.Globalization;
+ using System.Linq;
+ using System.Web.Mvc.Resources;
+
+ // This class provides a good implementation of ModelMetadataProvider for people who will be
+ // using traditional classes with properties. It uses the buddy class support from
+ // DataAnnotations, and consolidates the three operations down to a single override
+ // for reading the attribute values and creating the metadata class.
+ public abstract class AssociatedMetadataProvider : ModelMetadataProvider {
+ protected abstract ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName);
+
+ protected virtual IEnumerable<Attribute> FilterAttributes(Type containerType, PropertyDescriptor propertyDescriptor, IEnumerable<Attribute> attributes) {
+ if (typeof(ViewPage).IsAssignableFrom(containerType) || typeof(ViewUserControl).IsAssignableFrom(containerType)) {
+ return attributes.Where(a => !(a is ReadOnlyAttribute));
+ }
+
+ return attributes;
+ }
+
+ public override IEnumerable<ModelMetadata> GetMetadataForProperties(object container, Type containerType) {
+ if (containerType == null) {
+ throw new ArgumentNullException("containerType");
+ }
+
+ return GetMetadataForPropertiesImpl(container, containerType);
+ }
+
+ private IEnumerable<ModelMetadata> GetMetadataForPropertiesImpl(object container, Type containerType) {
+ foreach (PropertyDescriptor property in GetTypeDescriptor(containerType).GetProperties()) {
+ Func<object> modelAccessor = container == null ? null : GetPropertyValueAccessor(container, property);
+ yield return GetMetadataForProperty(modelAccessor, containerType, property);
+ }
+ }
+
+ public override ModelMetadata GetMetadataForProperty(Func<object> modelAccessor, Type containerType, string propertyName) {
+ if (containerType == null) {
+ throw new ArgumentNullException("containerType");
+ }
+ if (String.IsNullOrEmpty(propertyName)) {
+ throw new ArgumentException(MvcResources.Common_NullOrEmpty, "propertyName");
+ }
+
+ ICustomTypeDescriptor typeDescriptor = GetTypeDescriptor(containerType);
+ PropertyDescriptor property = typeDescriptor.GetProperties().Find(propertyName, true);
+ if (property == null) {
+ throw new ArgumentException(
+ String.Format(
+ CultureInfo.CurrentCulture,
+ MvcResources.Common_PropertyNotFound,
+ containerType.FullName, propertyName));
+ }
+
+ return GetMetadataForProperty(modelAccessor, containerType, property);
+ }
+
+ protected virtual ModelMetadata GetMetadataForProperty(Func<object> modelAccessor, Type containerType, PropertyDescriptor propertyDescriptor) {
+ IEnumerable<Attribute> attributes = FilterAttributes(containerType, propertyDescriptor, propertyDescriptor.Attributes.Cast<Attribute>());
+ return CreateMetadata(attributes, containerType, modelAccessor, propertyDescriptor.PropertyType, propertyDescriptor.Name);
+ }
+
+ public override ModelMetadata GetMetadataForType(Func<object> modelAccessor, Type modelType) {
+ if (modelType == null) {
+ throw new ArgumentNullException("modelType");
+ }
+
+ IEnumerable<Attribute> attributes = GetTypeDescriptor(modelType).GetAttributes().Cast<Attribute>();
+ return CreateMetadata(attributes, null /* containerType */, modelAccessor, modelType, null /* propertyName */);
+ }
+
+ private static Func<object> GetPropertyValueAccessor(object container, PropertyDescriptor property) {
+ return () => property.GetValue(container);
+ }
+
+ protected virtual ICustomTypeDescriptor GetTypeDescriptor(Type type) {
+ return TypeDescriptorHelper.Get(type);
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/AssociatedValidatorProvider.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/AssociatedValidatorProvider.cs
new file mode 100644
index 00000000000..f7b280ab39e
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/AssociatedValidatorProvider.cs
@@ -0,0 +1,63 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Collections.Generic;
+ using System.ComponentModel;
+ using System.ComponentModel.DataAnnotations;
+ using System.Globalization;
+ using System.Linq;
+ using System.Web.Mvc.Resources;
+
+ public abstract class AssociatedValidatorProvider : ModelValidatorProvider {
+ protected virtual ICustomTypeDescriptor GetTypeDescriptor(Type type) {
+ return TypeDescriptorHelper.Get(type);
+ }
+
+ public override sealed IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, ControllerContext context) {
+ if (metadata == null) {
+ throw new ArgumentNullException("metadata");
+ }
+ if (context == null) {
+ throw new ArgumentNullException("context");
+ }
+
+ if (metadata.ContainerType != null && !String.IsNullOrEmpty(metadata.PropertyName)) {
+ return GetValidatorsForProperty(metadata, context);
+ }
+
+ return GetValidatorsForType(metadata, context);
+ }
+
+ protected abstract IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, ControllerContext context, IEnumerable<Attribute> attributes);
+
+ private IEnumerable<ModelValidator> GetValidatorsForProperty(ModelMetadata metadata, ControllerContext context) {
+ ICustomTypeDescriptor typeDescriptor = GetTypeDescriptor(metadata.ContainerType);
+ PropertyDescriptor property = typeDescriptor.GetProperties().Find(metadata.PropertyName, true);
+ if (property == null) {
+ throw new ArgumentException(
+ String.Format(
+ CultureInfo.CurrentCulture,
+ MvcResources.Common_PropertyNotFound,
+ metadata.ContainerType.FullName, metadata.PropertyName),
+ "metadata");
+ }
+
+ return GetValidators(metadata, context, property.Attributes.OfType<Attribute>());
+ }
+
+ private IEnumerable<ModelValidator> GetValidatorsForType(ModelMetadata metadata, ControllerContext context) {
+ return GetValidators(metadata, context, GetTypeDescriptor(metadata.ModelType).GetAttributes().Cast<Attribute>());
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/ActionDescriptorCreator.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/ActionDescriptorCreator.cs
new file mode 100644
index 00000000000..19e1179a4a0
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/ActionDescriptorCreator.cs
@@ -0,0 +1,18 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc.Async {
+ using System;
+
+ internal delegate ActionDescriptor ActionDescriptorCreator(string actionName, ControllerDescriptor controllerDescriptor);
+
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/AsyncActionDescriptor.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/AsyncActionDescriptor.cs
new file mode 100644
index 00000000000..2b9ff2fe406
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/AsyncActionDescriptor.cs
@@ -0,0 +1,40 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc.Async {
+ using System;
+ using System.Collections.Generic;
+
+ public abstract class AsyncActionDescriptor : ActionDescriptor {
+
+ public abstract IAsyncResult BeginExecute(ControllerContext controllerContext, IDictionary<string, object> parameters, AsyncCallback callback, object state);
+
+ public abstract object EndExecute(IAsyncResult asyncResult);
+
+ public override object Execute(ControllerContext controllerContext, IDictionary<string, object> parameters) {
+ // execute an asynchronous task synchronously
+ IAsyncResult asyncResult = BeginExecute(controllerContext, parameters, null, null);
+ AsyncUtil.WaitForAsyncResultCompletion(asyncResult, controllerContext.HttpContext.ApplicationInstance); // blocks
+ return EndExecute(asyncResult);
+ }
+
+ internal static AsyncManager GetAsyncManager(ControllerBase controller) {
+ IAsyncManagerContainer helperContainer = controller as IAsyncManagerContainer;
+ if (helperContainer == null) {
+ throw Error.AsyncCommon_ControllerMustImplementIAsyncManagerContainer(controller.GetType());
+ }
+
+ return helperContainer.AsyncManager;
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/AsyncActionMethodSelector.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/AsyncActionMethodSelector.cs
new file mode 100644
index 00000000000..efd00b153c8
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/AsyncActionMethodSelector.cs
@@ -0,0 +1,206 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc.Async {
+ using System;
+ using System.Collections.Generic;
+ using System.Globalization;
+ using System.Linq;
+ using System.Reflection;
+ using System.Text;
+ using System.Web.Mvc.Resources;
+
+ internal sealed class AsyncActionMethodSelector {
+
+ public AsyncActionMethodSelector(Type controllerType) {
+ ControllerType = controllerType;
+ PopulateLookupTables();
+ }
+
+ public Type ControllerType {
+ get;
+ private set;
+ }
+
+ public MethodInfo[] AliasedMethods {
+ get;
+ private set;
+ }
+
+ public ILookup<string, MethodInfo> NonAliasedMethods {
+ get;
+ private set;
+ }
+
+ private AmbiguousMatchException CreateAmbiguousActionMatchException(IEnumerable<MethodInfo> ambiguousMethods, string actionName) {
+ string ambiguityList = CreateAmbiguousMatchList(ambiguousMethods);
+ string message = String.Format(CultureInfo.CurrentUICulture, MvcResources.ActionMethodSelector_AmbiguousMatch,
+ actionName, ControllerType.Name, ambiguityList);
+ return new AmbiguousMatchException(message);
+ }
+
+ private AmbiguousMatchException CreateAmbiguousMethodMatchException(IEnumerable<MethodInfo> ambiguousMethods, string methodName) {
+ string ambiguityList = CreateAmbiguousMatchList(ambiguousMethods);
+ string message = String.Format(CultureInfo.CurrentUICulture, MvcResources.AsyncActionMethodSelector_AmbiguousMethodMatch,
+ methodName, ControllerType.Name, ambiguityList);
+ return new AmbiguousMatchException(message);
+ }
+
+ private static string CreateAmbiguousMatchList(IEnumerable<MethodInfo> ambiguousMethods) {
+ StringBuilder exceptionMessageBuilder = new StringBuilder();
+ foreach (MethodInfo methodInfo in ambiguousMethods) {
+ exceptionMessageBuilder.AppendLine();
+ exceptionMessageBuilder.AppendFormat(CultureInfo.CurrentUICulture, MvcResources.ActionMethodSelector_AmbiguousMatchType, methodInfo, methodInfo.DeclaringType.FullName);
+ }
+
+ return exceptionMessageBuilder.ToString();
+ }
+
+ public ActionDescriptorCreator FindAction(ControllerContext controllerContext, string actionName) {
+ List<MethodInfo> methodsMatchingName = GetMatchingAliasedMethods(controllerContext, actionName);
+ methodsMatchingName.AddRange(NonAliasedMethods[actionName]);
+ List<MethodInfo> finalMethods = RunSelectionFilters(controllerContext, methodsMatchingName);
+
+ switch (finalMethods.Count) {
+ case 0:
+ return null;
+
+ case 1:
+ MethodInfo entryMethod = finalMethods[0];
+ return GetActionDescriptorDelegate(entryMethod);
+
+ default:
+ throw CreateAmbiguousActionMatchException(finalMethods, actionName);
+ }
+ }
+
+ private ActionDescriptorCreator GetActionDescriptorDelegate(MethodInfo entryMethod) {
+ // Is this the FooAsync() / FooCompleted() pattern?
+ if (IsAsyncSuffixedMethod(entryMethod)) {
+ string completionMethodName = entryMethod.Name.Substring(0, entryMethod.Name.Length - "Async".Length) + "Completed";
+ MethodInfo completionMethod = GetMethodByName(completionMethodName);
+ if (completionMethod != null) {
+ return (actionName, controllerDescriptor) => new ReflectedAsyncActionDescriptor(entryMethod, completionMethod, actionName, controllerDescriptor);
+ }
+ else {
+ throw Error.AsyncActionMethodSelector_CouldNotFindMethod(completionMethodName, ControllerType);
+ }
+ }
+
+ // Fallback to synchronous method
+ return (actionName, controllerDescriptor) => new ReflectedActionDescriptor(entryMethod, actionName, controllerDescriptor);
+ }
+
+ private static string GetCanonicalMethodName(MethodInfo methodInfo) {
+ string methodName = methodInfo.Name;
+ return (IsAsyncSuffixedMethod(methodInfo))
+ ? methodName.Substring(0, methodName.Length - "Async".Length)
+ : methodName;
+ }
+
+ internal List<MethodInfo> GetMatchingAliasedMethods(ControllerContext controllerContext, string actionName) {
+ // find all aliased methods which are opting in to this request
+ // to opt in, all attributes defined on the method must return true
+
+ var methods = from methodInfo in AliasedMethods
+ let attrs = (ActionNameSelectorAttribute[])methodInfo.GetCustomAttributes(typeof(ActionNameSelectorAttribute), true /* inherit */)
+ where attrs.All(attr => attr.IsValidName(controllerContext, actionName, methodInfo))
+ select methodInfo;
+ return methods.ToList();
+ }
+
+ private static bool IsAsyncSuffixedMethod(MethodInfo methodInfo) {
+ return methodInfo.Name.EndsWith("Async", StringComparison.OrdinalIgnoreCase);
+ }
+
+ private static bool IsCompletedSuffixedMethod(MethodInfo methodInfo) {
+ return methodInfo.Name.EndsWith("Completed", StringComparison.OrdinalIgnoreCase);
+ }
+
+ private static bool IsMethodDecoratedWithAliasingAttribute(MethodInfo methodInfo) {
+ return methodInfo.IsDefined(typeof(ActionNameSelectorAttribute), true /* inherit */);
+ }
+
+ private MethodInfo GetMethodByName(string methodName) {
+ List<MethodInfo> methods = (from MethodInfo methodInfo in ControllerType.GetMember(methodName, MemberTypes.Method, BindingFlags.Instance | BindingFlags.Public | BindingFlags.InvokeMethod | BindingFlags.IgnoreCase)
+ where IsValidActionMethod(methodInfo, false /* stripInfrastructureMethods */)
+ select methodInfo).ToList();
+
+ switch (methods.Count) {
+ case 0:
+ return null;
+
+ case 1:
+ return methods[0];
+
+ default:
+ throw CreateAmbiguousMethodMatchException(methods, methodName);
+ }
+ }
+
+ private static bool IsValidActionMethod(MethodInfo methodInfo) {
+ return IsValidActionMethod(methodInfo, true /* stripInfrastructureMethods */);
+ }
+
+ private static bool IsValidActionMethod(MethodInfo methodInfo, bool stripInfrastructureMethods) {
+ if (methodInfo.IsSpecialName) {
+ // not a normal method, e.g. a constructor or an event
+ return false;
+ }
+
+ if (methodInfo.GetBaseDefinition().DeclaringType.IsAssignableFrom(typeof(AsyncController))) {
+ // is a method on Object, ControllerBase, Controller, or AsyncController
+ return false;
+ };
+
+ if (stripInfrastructureMethods) {
+ if (IsCompletedSuffixedMethod(methodInfo)) {
+ // do not match FooCompleted() methods, as these are infrastructure methods
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private void PopulateLookupTables() {
+ MethodInfo[] allMethods = ControllerType.GetMethods(BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.Public);
+ MethodInfo[] actionMethods = Array.FindAll(allMethods, IsValidActionMethod);
+
+ AliasedMethods = Array.FindAll(actionMethods, IsMethodDecoratedWithAliasingAttribute);
+ NonAliasedMethods = actionMethods.Except(AliasedMethods).ToLookup(GetCanonicalMethodName, StringComparer.OrdinalIgnoreCase);
+ }
+
+ private static List<MethodInfo> RunSelectionFilters(ControllerContext controllerContext, List<MethodInfo> methodInfos) {
+ // remove all methods which are opting out of this request
+ // to opt out, at least one attribute defined on the method must return false
+
+ List<MethodInfo> matchesWithSelectionAttributes = new List<MethodInfo>();
+ List<MethodInfo> matchesWithoutSelectionAttributes = new List<MethodInfo>();
+
+ foreach (MethodInfo methodInfo in methodInfos) {
+ ActionMethodSelectorAttribute[] attrs = (ActionMethodSelectorAttribute[])methodInfo.GetCustomAttributes(typeof(ActionMethodSelectorAttribute), true /* inherit */);
+ if (attrs.Length == 0) {
+ matchesWithoutSelectionAttributes.Add(methodInfo);
+ }
+ else if (attrs.All(attr => attr.IsValidForRequest(controllerContext, methodInfo))) {
+ matchesWithSelectionAttributes.Add(methodInfo);
+ }
+ }
+
+ // if a matching action method had a selection attribute, consider it more specific than a matching action method
+ // without a selection attribute
+ return (matchesWithSelectionAttributes.Count > 0) ? matchesWithSelectionAttributes : matchesWithoutSelectionAttributes;
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/AsyncControllerActionInvoker.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/AsyncControllerActionInvoker.cs
new file mode 100644
index 00000000000..cf1349ec52d
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/AsyncControllerActionInvoker.cs
@@ -0,0 +1,282 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc.Async {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Threading;
+
+ public class AsyncControllerActionInvoker : ControllerActionInvoker, IAsyncActionInvoker {
+
+ private static readonly object _invokeActionTag = new object();
+ private static readonly object _invokeActionMethodTag = new object();
+ private static readonly object _invokeActionMethodWithFiltersTag = new object();
+
+ public virtual IAsyncResult BeginInvokeAction(ControllerContext controllerContext, string actionName, AsyncCallback callback, object state) {
+ if (controllerContext == null) {
+ throw new ArgumentNullException("controllerContext");
+ }
+ if (String.IsNullOrEmpty(actionName)) {
+ throw Error.ParameterCannotBeNullOrEmpty("actionName");
+ }
+
+ ControllerDescriptor controllerDescriptor = GetControllerDescriptor(controllerContext);
+ ActionDescriptor actionDescriptor = controllerDescriptor.FindAction(controllerContext, actionName);
+ if (actionDescriptor != null) {
+ FilterInfo filterInfo = GetFilters(controllerContext, actionDescriptor);
+ Action continuation = null;
+
+ BeginInvokeDelegate beginDelegate = delegate(AsyncCallback asyncCallback, object asyncState) {
+ try {
+ AuthorizationContext authContext = InvokeAuthorizationFilters(controllerContext, filterInfo.AuthorizationFilters, actionDescriptor);
+ if (authContext.Result != null) {
+ // the auth filter signaled that we should let it short-circuit the request
+ continuation = () => InvokeActionResult(controllerContext, authContext.Result);
+ }
+ else {
+ if (controllerContext.Controller.ValidateRequest) {
+ ValidateRequest(controllerContext);
+ }
+
+ IDictionary<string, object> parameters = GetParameterValues(controllerContext, actionDescriptor);
+ IAsyncResult asyncResult = BeginInvokeActionMethodWithFilters(controllerContext, filterInfo.ActionFilters, actionDescriptor, parameters, asyncCallback, asyncState);
+ continuation = () => {
+ ActionExecutedContext postActionContext = EndInvokeActionMethodWithFilters(asyncResult);
+ InvokeActionResultWithFilters(controllerContext, filterInfo.ResultFilters, postActionContext.Result);
+ };
+ return asyncResult;
+ }
+ }
+ catch (ThreadAbortException) {
+ // This type of exception occurs as a result of Response.Redirect(), but we special-case so that
+ // the filters don't see this as an error.
+ throw;
+ }
+ catch (Exception ex) {
+ // something blew up, so execute the exception filters
+ ExceptionContext exceptionContext = InvokeExceptionFilters(controllerContext, filterInfo.ExceptionFilters, ex);
+ if (!exceptionContext.ExceptionHandled) {
+ throw;
+ }
+
+ continuation = () => InvokeActionResult(controllerContext, exceptionContext.Result);
+ }
+
+ return BeginInvokeAction_MakeSynchronousAsyncResult(asyncCallback, asyncState);
+ };
+
+ EndInvokeDelegate<bool> endDelegate = delegate(IAsyncResult asyncResult) {
+ try {
+ continuation();
+ }
+ catch (ThreadAbortException) {
+ // This type of exception occurs as a result of Response.Redirect(), but we special-case so that
+ // the filters don't see this as an error.
+ throw;
+ }
+ catch (Exception ex) {
+ // something blew up, so execute the exception filters
+ ExceptionContext exceptionContext = InvokeExceptionFilters(controllerContext, filterInfo.ExceptionFilters, ex);
+ if (!exceptionContext.ExceptionHandled) {
+ throw;
+ }
+ InvokeActionResult(controllerContext, exceptionContext.Result);
+ }
+
+ return true;
+ };
+
+ return AsyncResultWrapper.Begin(callback, state, beginDelegate, endDelegate, _invokeActionTag);
+ }
+ else {
+ // Notify the controller that no action was found.
+ return BeginInvokeAction_ActionNotFound(callback, state);
+ }
+ }
+
+ private static IAsyncResult BeginInvokeAction_ActionNotFound(AsyncCallback callback, object state) {
+ BeginInvokeDelegate beginDelegate = BeginInvokeAction_MakeSynchronousAsyncResult;
+
+ EndInvokeDelegate<bool> endDelegate = delegate(IAsyncResult asyncResult) {
+ return false;
+ };
+
+ return AsyncResultWrapper.Begin(callback, state, beginDelegate, endDelegate, _invokeActionTag);
+ }
+
+ private static IAsyncResult BeginInvokeAction_MakeSynchronousAsyncResult(AsyncCallback callback, object state) {
+ SimpleAsyncResult asyncResult = new SimpleAsyncResult(state);
+ asyncResult.MarkCompleted(true /* completedSynchronously */, callback);
+ return asyncResult;
+ }
+
+ protected internal virtual IAsyncResult BeginInvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary<string, object> parameters, AsyncCallback callback, object state) {
+ AsyncActionDescriptor asyncActionDescriptor = actionDescriptor as AsyncActionDescriptor;
+ if (asyncActionDescriptor != null) {
+ return BeginInvokeAsynchronousActionMethod(controllerContext, asyncActionDescriptor, parameters, callback, state);
+ }
+ else {
+ return BeginInvokeSynchronousActionMethod(controllerContext, actionDescriptor, parameters, callback, state);
+ }
+ }
+
+ protected internal virtual IAsyncResult BeginInvokeActionMethodWithFilters(ControllerContext controllerContext, IList<IActionFilter> filters, ActionDescriptor actionDescriptor, IDictionary<string, object> parameters, AsyncCallback callback, object state) {
+ Func<ActionExecutedContext> endContinuation = null;
+
+ BeginInvokeDelegate beginDelegate = delegate(AsyncCallback asyncCallback, object asyncState) {
+ ActionExecutingContext preContext = new ActionExecutingContext(controllerContext, actionDescriptor, parameters);
+ IAsyncResult innerAsyncResult = null;
+
+ Func<Func<ActionExecutedContext>> beginContinuation = () => {
+ innerAsyncResult = BeginInvokeActionMethod(controllerContext, actionDescriptor, parameters, asyncCallback, asyncState);
+ return () =>
+ new ActionExecutedContext(controllerContext, actionDescriptor, false /* canceled */, null /* exception */) {
+ Result = EndInvokeActionMethod(innerAsyncResult)
+ };
+ };
+
+ // need to reverse the filter list because the continuations are built up backward
+ Func<Func<ActionExecutedContext>> thunk = filters.Reverse().Aggregate(beginContinuation,
+ (next, filter) => () => InvokeActionMethodFilterAsynchronously(filter, preContext, next));
+ endContinuation = thunk();
+
+ if (innerAsyncResult != null) {
+ // we're just waiting for the inner result to complete
+ return innerAsyncResult;
+ }
+ else {
+ // something was short-circuited and the action was not called, so this was a synchronous operation
+ SimpleAsyncResult newAsyncResult = new SimpleAsyncResult(asyncState);
+ newAsyncResult.MarkCompleted(true /* completedSynchronously */, asyncCallback);
+ return newAsyncResult;
+ }
+ };
+
+ EndInvokeDelegate<ActionExecutedContext> endDelegate = delegate(IAsyncResult asyncResult) {
+ return endContinuation();
+ };
+
+ return AsyncResultWrapper.Begin(callback, state, beginDelegate, endDelegate, _invokeActionMethodWithFiltersTag);
+ }
+
+ private IAsyncResult BeginInvokeAsynchronousActionMethod(ControllerContext controllerContext, AsyncActionDescriptor actionDescriptor, IDictionary<string, object> parameters, AsyncCallback callback, object state) {
+ BeginInvokeDelegate beginDelegate = delegate(AsyncCallback asyncCallback, object asyncState) {
+ return actionDescriptor.BeginExecute(controllerContext, parameters, asyncCallback, asyncState);
+ };
+
+ EndInvokeDelegate<ActionResult> endDelegate = delegate(IAsyncResult asyncResult) {
+ object returnValue = actionDescriptor.EndExecute(asyncResult);
+ ActionResult result = CreateActionResult(controllerContext, actionDescriptor, returnValue);
+ return result;
+ };
+
+ return AsyncResultWrapper.Begin(callback, state, beginDelegate, endDelegate, _invokeActionMethodTag);
+ }
+
+ private IAsyncResult BeginInvokeSynchronousActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary<string, object> parameters, AsyncCallback callback, object state) {
+ return AsyncResultWrapper.BeginSynchronous(callback, state,
+ () => InvokeSynchronousActionMethod(controllerContext, actionDescriptor, parameters),
+ _invokeActionMethodTag);
+ }
+
+ public virtual bool EndInvokeAction(IAsyncResult asyncResult) {
+ return AsyncResultWrapper.End<bool>(asyncResult, _invokeActionTag);
+ }
+
+ protected internal virtual ActionResult EndInvokeActionMethod(IAsyncResult asyncResult) {
+ return AsyncResultWrapper.End<ActionResult>(asyncResult, _invokeActionMethodTag);
+ }
+
+ protected internal virtual ActionExecutedContext EndInvokeActionMethodWithFilters(IAsyncResult asyncResult) {
+ return AsyncResultWrapper.End<ActionExecutedContext>(asyncResult, _invokeActionMethodWithFiltersTag);
+ }
+
+ protected override ControllerDescriptor GetControllerDescriptor(ControllerContext controllerContext) {
+ Type controllerType = controllerContext.Controller.GetType();
+ ControllerDescriptor controllerDescriptor = DescriptorCache.GetDescriptor(controllerType, () => new ReflectedAsyncControllerDescriptor(controllerType));
+ return controllerDescriptor;
+ }
+
+ internal static Func<ActionExecutedContext> InvokeActionMethodFilterAsynchronously(IActionFilter filter, ActionExecutingContext preContext, Func<Func<ActionExecutedContext>> nextInChain) {
+ filter.OnActionExecuting(preContext);
+ if (preContext.Result != null) {
+ ActionExecutedContext shortCircuitedPostContext = new ActionExecutedContext(preContext, preContext.ActionDescriptor, true /* canceled */, null /* exception */) {
+ Result = preContext.Result
+ };
+ return () => shortCircuitedPostContext;
+ }
+
+ // There is a nested try / catch block here that contains much the same logic as the outer block.
+ // Since an exception can occur on either side of the asynchronous invocation, we need guards on
+ // on both sides. In the code below, the second side is represented by the nested delegate. This
+ // is really just a parallel of the synchronous ControllerActionInvoker.InvokeActionMethodFilter()
+ // method.
+
+ try {
+ Func<ActionExecutedContext> continuation = nextInChain();
+
+ // add our own continuation, then return the new function
+ return () => {
+ ActionExecutedContext postContext;
+ bool wasError = true;
+
+ try {
+ postContext = continuation();
+ wasError = false;
+ }
+ catch (ThreadAbortException) {
+ // This type of exception occurs as a result of Response.Redirect(), but we special-case so that
+ // the filters don't see this as an error.
+ postContext = new ActionExecutedContext(preContext, preContext.ActionDescriptor, false /* canceled */, null /* exception */);
+ filter.OnActionExecuted(postContext);
+ throw;
+ }
+ catch (Exception ex) {
+ postContext = new ActionExecutedContext(preContext, preContext.ActionDescriptor, false /* canceled */, ex);
+ filter.OnActionExecuted(postContext);
+ if (!postContext.ExceptionHandled) {
+ throw;
+ }
+ }
+ if (!wasError) {
+ filter.OnActionExecuted(postContext);
+ }
+
+ return postContext;
+ };
+ }
+ catch (ThreadAbortException) {
+ // This type of exception occurs as a result of Response.Redirect(), but we special-case so that
+ // the filters don't see this as an error.
+ ActionExecutedContext postContext = new ActionExecutedContext(preContext, preContext.ActionDescriptor, false /* canceled */, null /* exception */);
+ filter.OnActionExecuted(postContext);
+ throw;
+ }
+ catch (Exception ex) {
+ ActionExecutedContext postContext = new ActionExecutedContext(preContext, preContext.ActionDescriptor, false /* canceled */, ex);
+ filter.OnActionExecuted(postContext);
+ if (postContext.ExceptionHandled) {
+ return () => postContext;
+ }
+ else {
+ throw;
+ }
+ }
+ }
+
+ private ActionResult InvokeSynchronousActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary<string, object> parameters) {
+ return base.InvokeActionMethod(controllerContext, actionDescriptor, parameters);
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/AsyncManager.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/AsyncManager.cs
new file mode 100644
index 00000000000..082f460e7ec
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/AsyncManager.cs
@@ -0,0 +1,79 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc.Async {
+ using System;
+ using System.Collections.Generic;
+ using System.Threading;
+
+ public class AsyncManager {
+
+ private readonly SynchronizationContext _syncContext;
+
+ // default timeout is 45 sec
+ // from: http://msdn.microsoft.com/en-us/library/system.web.ui.page.asynctimeout.aspx
+ private int _timeout = 45 * 1000;
+
+ public AsyncManager()
+ : this(null /* syncContext */) {
+ }
+
+ public AsyncManager(SynchronizationContext syncContext) {
+ _syncContext = syncContext ?? SynchronizationContextUtil.GetSynchronizationContext();
+
+ OutstandingOperations = new OperationCounter();
+ OutstandingOperations.Completed += delegate { Finish(); };
+
+ Parameters = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
+ }
+
+ public OperationCounter OutstandingOperations {
+ get;
+ private set;
+ }
+
+ public IDictionary<string, object> Parameters {
+ get;
+ private set;
+ }
+
+ public event EventHandler Finished;
+
+ // the developer may call this function to signal that all operations are complete instead of
+ // waiting for the operation counter to reach zero
+ public virtual void Finish() {
+ EventHandler handler = Finished;
+ if (handler != null) {
+ handler(this, EventArgs.Empty);
+ }
+ }
+
+ // executes a callback in the current synchronization context, which gives access to HttpContext and related items
+ public virtual void Sync(Action action) {
+ _syncContext.Sync(action);
+ }
+
+ // measured in milliseconds, Timeout.Infinite means 'no timeout'
+ public int Timeout {
+ get {
+ return _timeout;
+ }
+ set {
+ if (value < -1) {
+ throw Error.AsyncCommon_InvalidTimeout("value");
+ }
+ _timeout = value;
+ }
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/AsyncResultWrapper.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/AsyncResultWrapper.cs
new file mode 100644
index 00000000000..1f42f516388
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/AsyncResultWrapper.cs
@@ -0,0 +1,265 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc.Async {
+ using System;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Threading;
+
+ // This class is used for the following pattern:
+
+ // public IAsyncResult BeginInner(..., callback, state);
+ // public TInnerResult EndInner(asyncResult);
+ // public IAsyncResult BeginOuter(..., callback, state);
+ // public TOuterResult EndOuter(asyncResult);
+
+ // That is, Begin/EndOuter() wrap Begin/EndInner(), potentially with pre- and post-processing.
+
+ internal static class AsyncResultWrapper {
+
+ // helper methods
+
+ private static Func<AsyncVoid> MakeVoidDelegate(Action action) {
+ return () => {
+ action();
+ return default(AsyncVoid);
+ };
+ }
+
+ private static EndInvokeDelegate<AsyncVoid> MakeVoidDelegate(EndInvokeDelegate endDelegate) {
+ return ar => {
+ endDelegate(ar);
+ return default(AsyncVoid);
+ };
+ }
+
+ // kicks off an asynchronous operation
+
+ public static IAsyncResult Begin<TResult>(AsyncCallback callback, object state, BeginInvokeDelegate beginDelegate, EndInvokeDelegate<TResult> endDelegate) {
+ return Begin<TResult>(callback, state, beginDelegate, endDelegate, null /* tag */);
+ }
+
+ public static IAsyncResult Begin<TResult>(AsyncCallback callback, object state, BeginInvokeDelegate beginDelegate, EndInvokeDelegate<TResult> endDelegate, object tag) {
+ return Begin<TResult>(callback, state, beginDelegate, endDelegate, tag, Timeout.Infinite);
+ }
+
+ public static IAsyncResult Begin<TResult>(AsyncCallback callback, object state, BeginInvokeDelegate beginDelegate, EndInvokeDelegate<TResult> endDelegate, object tag, int timeout) {
+ WrappedAsyncResult<TResult> asyncResult = new WrappedAsyncResult<TResult>(beginDelegate, endDelegate, tag);
+ asyncResult.Begin(callback, state, timeout);
+ return asyncResult;
+ }
+
+ public static IAsyncResult Begin(AsyncCallback callback, object state, BeginInvokeDelegate beginDelegate, EndInvokeDelegate endDelegate) {
+ return Begin(callback, state, beginDelegate, endDelegate, null /* tag */);
+ }
+
+ public static IAsyncResult Begin(AsyncCallback callback, object state, BeginInvokeDelegate beginDelegate, EndInvokeDelegate endDelegate, object tag) {
+ return Begin(callback, state, beginDelegate, endDelegate, tag, Timeout.Infinite);
+ }
+
+ public static IAsyncResult Begin(AsyncCallback callback, object state, BeginInvokeDelegate beginDelegate, EndInvokeDelegate endDelegate, object tag, int timeout) {
+ return Begin<AsyncVoid>(callback, state, beginDelegate, MakeVoidDelegate(endDelegate), tag, timeout);
+ }
+
+ // wraps a synchronous operation in an asynchronous wrapper, but still completes synchronously
+
+ public static IAsyncResult BeginSynchronous<TResult>(AsyncCallback callback, object state, Func<TResult> func) {
+ return BeginSynchronous<TResult>(callback, state, func, null /* tag */);
+ }
+
+ public static IAsyncResult BeginSynchronous<TResult>(AsyncCallback callback, object state, Func<TResult> func, object tag) {
+ // Begin() doesn't perform any work on its own and returns immediately.
+ BeginInvokeDelegate beginDelegate = (asyncCallback, asyncState) => {
+ SimpleAsyncResult innerAsyncResult = new SimpleAsyncResult(asyncState);
+ innerAsyncResult.MarkCompleted(true /* completedSynchronously */, asyncCallback);
+ return innerAsyncResult;
+ };
+
+ // The End() method blocks.
+ EndInvokeDelegate<TResult> endDelegate = _ => {
+ return func();
+ };
+
+ WrappedAsyncResult<TResult> asyncResult = new WrappedAsyncResult<TResult>(beginDelegate, endDelegate, tag);
+ asyncResult.Begin(callback, state, Timeout.Infinite);
+ return asyncResult;
+ }
+
+ public static IAsyncResult BeginSynchronous(AsyncCallback callback, object state, Action action) {
+ return BeginSynchronous(callback, state, action, null /* tag */);
+ }
+
+ public static IAsyncResult BeginSynchronous(AsyncCallback callback, object state, Action action, object tag) {
+ return BeginSynchronous<AsyncVoid>(callback, state, MakeVoidDelegate(action), tag);
+ }
+
+ // completes an asynchronous operation
+
+ public static TResult End<TResult>(IAsyncResult asyncResult) {
+ return End<TResult>(asyncResult, null /* tag */);
+ }
+
+ public static TResult End<TResult>(IAsyncResult asyncResult, object tag) {
+ return WrappedAsyncResult<TResult>.Cast(asyncResult, tag).End();
+ }
+
+ public static void End(IAsyncResult asyncResult) {
+ End(asyncResult, null /* tag */);
+ }
+
+ public static void End(IAsyncResult asyncResult, object tag) {
+ End<AsyncVoid>(asyncResult, tag);
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1001:TypesThatOwnDisposableFieldsShouldBeDisposable",
+ Justification = "The Timer will be disposed of either when it fires or when the operation completes successfully.")]
+ private sealed class WrappedAsyncResult<TResult> : IAsyncResult {
+
+ private readonly BeginInvokeDelegate _beginDelegate;
+ private readonly object _beginDelegateLockObj = new object();
+ private readonly EndInvokeDelegate<TResult> _endDelegate;
+ private readonly SingleEntryGate _endExecutedGate = new SingleEntryGate(); // prevent End() from being called twice
+ private readonly SingleEntryGate _handleCallbackGate = new SingleEntryGate(); // prevent callback from being handled multiple times
+ private IAsyncResult _innerAsyncResult;
+ private AsyncCallback _originalCallback;
+ private readonly object _tag; // prevent an instance of this type from being passed to the wrong End() method
+ private volatile bool _timedOut;
+ private Timer _timer;
+
+ public WrappedAsyncResult(BeginInvokeDelegate beginDelegate, EndInvokeDelegate<TResult> endDelegate, object tag) {
+ _beginDelegate = beginDelegate;
+ _endDelegate = endDelegate;
+ _tag = tag;
+ }
+
+ public object AsyncState {
+ get {
+ return _innerAsyncResult.AsyncState;
+ }
+ }
+
+ public WaitHandle AsyncWaitHandle {
+ get {
+ return _innerAsyncResult.AsyncWaitHandle;
+ }
+ }
+
+ public bool CompletedSynchronously {
+ get {
+ return _innerAsyncResult.CompletedSynchronously;
+ }
+ }
+
+ public bool IsCompleted {
+ get {
+ return _innerAsyncResult.IsCompleted;
+ }
+ }
+
+ // kicks off the process, instantiates a timer if requested
+ public void Begin(AsyncCallback callback, object state, int timeout) {
+ _originalCallback = callback;
+ bool completedSynchronously;
+
+ // Force the target Begin() operation to complete before the callback can continue,
+ // since the target operation might perform post-processing of the data.
+ lock (_beginDelegateLockObj) {
+ _innerAsyncResult = _beginDelegate(HandleAsynchronousCompletion, state);
+
+ completedSynchronously = _innerAsyncResult.CompletedSynchronously;
+ if (!completedSynchronously) {
+ if (timeout > Timeout.Infinite) {
+ CreateTimer(timeout);
+ }
+ }
+ }
+
+ if (completedSynchronously) {
+ if (callback != null) {
+ callback(this);
+ }
+ }
+ }
+
+ public static WrappedAsyncResult<TResult> Cast(IAsyncResult asyncResult, object tag) {
+ if (asyncResult == null) {
+ throw new ArgumentNullException("asyncResult");
+ }
+
+ WrappedAsyncResult<TResult> castResult = asyncResult as WrappedAsyncResult<TResult>;
+ if (castResult != null && Object.Equals(castResult._tag, tag)) {
+ return castResult;
+ }
+ else {
+ throw Error.AsyncCommon_InvalidAsyncResult("asyncResult");
+ }
+ }
+
+ private void CreateTimer(int timeout) {
+ // this method should be called within a lock(_beginDelegateLockObj)
+ _timer = new Timer(HandleTimeout, null, timeout, Timeout.Infinite /* disable periodic signaling */);
+ }
+
+ public TResult End() {
+ if (!_endExecutedGate.TryEnter()) {
+ throw Error.AsyncCommon_AsyncResultAlreadyConsumed();
+ }
+
+ if (_timedOut) {
+ throw new TimeoutException();
+ }
+ WaitForBeginToCompleteAndDestroyTimer();
+
+ return _endDelegate(_innerAsyncResult);
+ }
+
+ private void ExecuteAsynchronousCallback(bool timedOut) {
+ WaitForBeginToCompleteAndDestroyTimer();
+
+ if (_handleCallbackGate.TryEnter()) {
+ _timedOut = timedOut;
+ if (_originalCallback != null) {
+ _originalCallback(this);
+ }
+ }
+ }
+
+ private void HandleAsynchronousCompletion(IAsyncResult asyncResult) {
+ if (asyncResult.CompletedSynchronously) {
+ // If the operation completed synchronously, the WrappedAsyncResult.Begin() method will handle it.
+ return;
+ }
+
+ ExecuteAsynchronousCallback(false /* timedOut */);
+ }
+
+ private void HandleTimeout(object state) {
+ ExecuteAsynchronousCallback(true /* timedOut */);
+ }
+
+ private void WaitForBeginToCompleteAndDestroyTimer() {
+ lock (_beginDelegateLockObj) {
+ // Wait for the target Begin() method to complete, as it might be performing
+ // post-processing. This also forces a memory barrier, so _innerAsyncResult
+ // is guaranteed to be non-null at this point.
+
+ if (_timer != null) {
+ _timer.Dispose();
+ }
+ _timer = null;
+ }
+ }
+
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/AsyncUtil.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/AsyncUtil.cs
new file mode 100644
index 00000000000..64484cf325d
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/AsyncUtil.cs
@@ -0,0 +1,75 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc.Async {
+ using System;
+ using System.Threading;
+
+ internal static class AsyncUtil {
+
+ public static void WaitForAsyncResultCompletion(IAsyncResult asyncResult, HttpApplication app) {
+ // based on HttpServerUtility.ExecuteInternal()
+
+ if (!asyncResult.IsCompleted) {
+ // suspend app lock while waiting, else might deadlock
+ bool needToRelock = false;
+
+ try {
+ // .NET 2.0+ will not allow a ThreadAbortException to be thrown while a
+ // thread is inside a finally block, so this pattern ensures that the
+ // value of 'needToRelock' is correct.
+ try { }
+ finally {
+ Monitor.Exit(app);
+ needToRelock = true;
+ }
+
+ WaitHandle waitHandle = asyncResult.AsyncWaitHandle;
+
+ if (waitHandle != null) {
+ waitHandle.WaitOne();
+ }
+ else {
+ while (!asyncResult.IsCompleted) {
+ Thread.Sleep(1);
+ }
+ }
+ }
+ finally {
+ if (needToRelock) {
+ Monitor.Enter(app);
+ }
+ }
+ }
+ }
+
+ public static AsyncCallback WrapCallbackForSynchronizedExecution(AsyncCallback callback, SynchronizationContext syncContext) {
+ if (callback == null || syncContext == null) {
+ return callback;
+ }
+
+ AsyncCallback newCallback = delegate(IAsyncResult asyncResult) {
+ if (asyncResult.CompletedSynchronously) {
+ callback(asyncResult);
+ }
+ else {
+ // Only take the application lock if this request completed asynchronously,
+ // else we might end up in a deadlock situation.
+ syncContext.Sync(() => callback(asyncResult));
+ }
+ };
+
+ return newCallback;
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/AsyncVoid.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/AsyncVoid.cs
new file mode 100644
index 00000000000..95a81ce7f2b
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/AsyncVoid.cs
@@ -0,0 +1,19 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc.Async {
+ using System;
+
+ // Dummy type used for passing something resembling 'void' to the async delegate functions
+ internal struct AsyncVoid {
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/BeginInvokeDelegate.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/BeginInvokeDelegate.cs
new file mode 100644
index 00000000000..5d568f4a3b7
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/BeginInvokeDelegate.cs
@@ -0,0 +1,17 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc.Async {
+ using System;
+
+ internal delegate IAsyncResult BeginInvokeDelegate(AsyncCallback callback, object state);
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/EndInvokeDelegate.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/EndInvokeDelegate.cs
new file mode 100644
index 00000000000..11308dff6a7
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/EndInvokeDelegate.cs
@@ -0,0 +1,17 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc.Async {
+ using System;
+
+ internal delegate void EndInvokeDelegate(IAsyncResult asyncResult);
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/EndInvokeDelegate`1.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/EndInvokeDelegate`1.cs
new file mode 100644
index 00000000000..9e838960afd
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/EndInvokeDelegate`1.cs
@@ -0,0 +1,17 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc.Async {
+ using System;
+
+ internal delegate TResult EndInvokeDelegate<TResult>(IAsyncResult asyncResult);
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/IAsyncActionInvoker.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/IAsyncActionInvoker.cs
new file mode 100644
index 00000000000..acd624bf7ef
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/IAsyncActionInvoker.cs
@@ -0,0 +1,20 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc.Async {
+ using System;
+
+ public interface IAsyncActionInvoker : IActionInvoker {
+ IAsyncResult BeginInvokeAction(ControllerContext controllerContext, string actionName, AsyncCallback callback, object state);
+ bool EndInvokeAction(IAsyncResult asyncResult);
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/IAsyncController.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/IAsyncController.cs
new file mode 100644
index 00000000000..fe256e84c26
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/IAsyncController.cs
@@ -0,0 +1,20 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc.Async {
+ using System.Web.Routing;
+
+ public interface IAsyncController : IController {
+ IAsyncResult BeginExecute(RequestContext requestContext, AsyncCallback callback, object state);
+ void EndExecute(IAsyncResult asyncResult);
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/IAsyncManagerContainer.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/IAsyncManagerContainer.cs
new file mode 100644
index 00000000000..f9aa6adbced
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/IAsyncManagerContainer.cs
@@ -0,0 +1,22 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc.Async {
+
+ public interface IAsyncManagerContainer {
+
+ AsyncManager AsyncManager {
+ get;
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/OperationCounter.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/OperationCounter.cs
new file mode 100644
index 00000000000..519f6ed2714
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/OperationCounter.cs
@@ -0,0 +1,62 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc.Async {
+ using System;
+ using System.Threading;
+
+ public sealed class OperationCounter {
+
+ private int _count;
+
+ public int Count {
+ get {
+ return Thread.VolatileRead(ref _count);
+ }
+ }
+
+ public event EventHandler Completed;
+
+ private int AddAndExecuteCallbackIfCompleted(int value) {
+ int newCount = Interlocked.Add(ref _count, value);
+ if (newCount == 0) {
+ OnCompleted();
+ }
+
+ return newCount;
+ }
+
+ public int Decrement() {
+ return AddAndExecuteCallbackIfCompleted(-1);
+ }
+
+ public int Decrement(int value) {
+ return AddAndExecuteCallbackIfCompleted(-value);
+ }
+
+ public int Increment() {
+ return AddAndExecuteCallbackIfCompleted(1);
+ }
+
+ public int Increment(int value) {
+ return AddAndExecuteCallbackIfCompleted(value);
+ }
+
+ private void OnCompleted() {
+ EventHandler handler = Completed;
+ if (handler != null) {
+ handler(this, EventArgs.Empty);
+ }
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/ReflectedAsyncActionDescriptor.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/ReflectedAsyncActionDescriptor.cs
new file mode 100644
index 00000000000..397a5a24ff8
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/ReflectedAsyncActionDescriptor.cs
@@ -0,0 +1,183 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc.Async {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Reflection;
+ using System.Threading;
+
+ public class ReflectedAsyncActionDescriptor : AsyncActionDescriptor {
+
+ private readonly object _executeTag = new object();
+
+ private readonly string _actionName;
+ private readonly ControllerDescriptor _controllerDescriptor;
+ private ParameterDescriptor[] _parametersCache;
+
+ public ReflectedAsyncActionDescriptor(MethodInfo asyncMethodInfo, MethodInfo completedMethodInfo, string actionName, ControllerDescriptor controllerDescriptor)
+ : this(asyncMethodInfo, completedMethodInfo, actionName, controllerDescriptor, true /* validateMethods */) {
+ }
+
+ internal ReflectedAsyncActionDescriptor(MethodInfo asyncMethodInfo, MethodInfo completedMethodInfo, string actionName, ControllerDescriptor controllerDescriptor, bool validateMethods) {
+ if (asyncMethodInfo == null) {
+ throw new ArgumentNullException("asyncMethodInfo");
+ }
+ if (completedMethodInfo == null) {
+ throw new ArgumentNullException("completedMethodInfo");
+ }
+ if (String.IsNullOrEmpty(actionName)) {
+ throw Error.ParameterCannotBeNullOrEmpty("actionName");
+ }
+ if (controllerDescriptor == null) {
+ throw new ArgumentNullException("controllerDescriptor");
+ }
+
+ if (validateMethods) {
+ string asyncFailedMessage = VerifyActionMethodIsCallable(asyncMethodInfo);
+ if (asyncFailedMessage != null) {
+ throw new ArgumentException(asyncFailedMessage, "asyncMethodInfo");
+ }
+
+ string completedFailedMessage = VerifyActionMethodIsCallable(completedMethodInfo);
+ if (completedFailedMessage != null) {
+ throw new ArgumentException(completedFailedMessage, "completedMethodInfo");
+ }
+ }
+
+ AsyncMethodInfo = asyncMethodInfo;
+ CompletedMethodInfo = completedMethodInfo;
+ _actionName = actionName;
+ _controllerDescriptor = controllerDescriptor;
+ }
+
+ public override string ActionName {
+ get {
+ return _actionName;
+ }
+ }
+
+ public MethodInfo AsyncMethodInfo {
+ get;
+ private set;
+ }
+
+ public MethodInfo CompletedMethodInfo {
+ get;
+ private set;
+ }
+
+ public override ControllerDescriptor ControllerDescriptor {
+ get {
+ return _controllerDescriptor;
+ }
+ }
+
+ public override IAsyncResult BeginExecute(ControllerContext controllerContext, IDictionary<string, object> parameters, AsyncCallback callback, object state) {
+ if (controllerContext == null) {
+ throw new ArgumentNullException("controllerContext");
+ }
+ if (parameters == null) {
+ throw new ArgumentNullException("parameters");
+ }
+
+ AsyncManager asyncManager = GetAsyncManager(controllerContext.Controller);
+
+ BeginInvokeDelegate beginDelegate = delegate(AsyncCallback asyncCallback, object asyncState) {
+ // call the XxxAsync() method
+ ParameterInfo[] parameterInfos = AsyncMethodInfo.GetParameters();
+ var rawParameterValues = from parameterInfo in parameterInfos
+ select ExtractParameterFromDictionary(parameterInfo, parameters, AsyncMethodInfo);
+ object[] parametersArray = rawParameterValues.ToArray();
+
+ TriggerListener listener = new TriggerListener();
+ SimpleAsyncResult asyncResult = new SimpleAsyncResult(asyncState);
+
+ // hook the Finished event to notify us upon completion
+ Trigger finishTrigger = listener.CreateTrigger();
+ asyncManager.Finished += delegate { finishTrigger.Fire(); };
+ asyncManager.OutstandingOperations.Increment();
+
+ // to simplify the logic, force the rest of the pipeline to execute in an asynchronous callback
+ listener.SetContinuation(() => ThreadPool.QueueUserWorkItem(_ => asyncResult.MarkCompleted(false /* completedSynchronously */, asyncCallback)));
+
+ // the inner operation might complete synchronously, so all setup work has to be done before this point
+ ActionMethodDispatcher dispatcher = DispatcherCache.GetDispatcher(AsyncMethodInfo);
+ dispatcher.Execute(controllerContext.Controller, parametersArray); // ignore return value from this method
+
+ // now that the XxxAsync() method has completed, kick off any pending operations
+ asyncManager.OutstandingOperations.Decrement();
+ listener.Activate();
+ return asyncResult;
+ };
+
+ EndInvokeDelegate<object> endDelegate = delegate(IAsyncResult asyncResult) {
+ // call the XxxCompleted() method
+ ParameterInfo[] completionParametersInfos = CompletedMethodInfo.GetParameters();
+ var rawCompletionParameterValues = from parameterInfo in completionParametersInfos
+ select ExtractParameterOrDefaultFromDictionary(parameterInfo, asyncManager.Parameters);
+ object[] completionParametersArray = rawCompletionParameterValues.ToArray();
+
+ ActionMethodDispatcher dispatcher = DispatcherCache.GetDispatcher(CompletedMethodInfo);
+ object actionReturnValue = dispatcher.Execute(controllerContext.Controller, completionParametersArray);
+ return actionReturnValue;
+ };
+
+ return AsyncResultWrapper.Begin(callback, state, beginDelegate, endDelegate, _executeTag, asyncManager.Timeout);
+ }
+
+ public override object EndExecute(IAsyncResult asyncResult) {
+ return AsyncResultWrapper.End<object>(asyncResult, _executeTag);
+ }
+
+ public override object[] GetCustomAttributes(bool inherit) {
+ return AsyncMethodInfo.GetCustomAttributes(inherit);
+ }
+
+ public override object[] GetCustomAttributes(Type attributeType, bool inherit) {
+ return AsyncMethodInfo.GetCustomAttributes(attributeType, inherit);
+ }
+
+ public override FilterInfo GetFilters() {
+ // By default, we only look at filters on the XxxAsync() method.
+ return GetFilters(AsyncMethodInfo);
+ }
+
+ public override ParameterDescriptor[] GetParameters() {
+ ParameterDescriptor[] parameters = LazilyFetchParametersCollection();
+
+ // need to clone array so that user modifications aren't accidentally stored
+ return (ParameterDescriptor[])parameters.Clone();
+ }
+
+ public override ICollection<ActionSelector> GetSelectors() {
+ // By default, we only look at filters on the XxxAsync() method.
+
+ ActionMethodSelectorAttribute[] attrs = (ActionMethodSelectorAttribute[])AsyncMethodInfo.GetCustomAttributes(typeof(ActionMethodSelectorAttribute), true /* inherit */);
+ ActionSelector[] selectors = Array.ConvertAll(attrs, attr => (ActionSelector)(controllerContext => attr.IsValidForRequest(controllerContext, AsyncMethodInfo)));
+ return selectors;
+ }
+
+ public override bool IsDefined(Type attributeType, bool inherit) {
+ return AsyncMethodInfo.IsDefined(attributeType, inherit);
+ }
+
+ private ParameterDescriptor[] LazilyFetchParametersCollection() {
+ return DescriptorUtil.LazilyFetchOrCreateDescriptors<ParameterInfo, ParameterDescriptor>(
+ ref _parametersCache /* cacheLocation */,
+ AsyncMethodInfo.GetParameters /* initializer */,
+ parameterInfo => new ReflectedParameterDescriptor(parameterInfo, this) /* converter */);
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/ReflectedAsyncControllerDescriptor.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/ReflectedAsyncControllerDescriptor.cs
new file mode 100644
index 00000000000..ebbc938a300
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/ReflectedAsyncControllerDescriptor.cs
@@ -0,0 +1,72 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc.Async {
+ using System;
+
+ public class ReflectedAsyncControllerDescriptor : ControllerDescriptor {
+
+ private static readonly ActionDescriptor[] _emptyCanonicalActions = new ActionDescriptor[0];
+
+ private readonly Type _controllerType;
+ private readonly AsyncActionMethodSelector _selector;
+
+ public ReflectedAsyncControllerDescriptor(Type controllerType) {
+ if (controllerType == null) {
+ throw new ArgumentNullException("controllerType");
+ }
+
+ _controllerType = controllerType;
+ _selector = new AsyncActionMethodSelector(_controllerType);
+ }
+
+ public sealed override Type ControllerType {
+ get {
+ return _controllerType;
+ }
+ }
+
+ public override ActionDescriptor FindAction(ControllerContext controllerContext, string actionName) {
+ if (controllerContext == null) {
+ throw new ArgumentNullException("controllerContext");
+ }
+ if (String.IsNullOrEmpty(actionName)) {
+ throw Error.ParameterCannotBeNullOrEmpty("actionName");
+ }
+
+ ActionDescriptorCreator creator = _selector.FindAction(controllerContext, actionName);
+ if (creator == null) {
+ return null;
+ }
+
+ return creator(actionName, this);
+ }
+
+ public override ActionDescriptor[] GetCanonicalActions() {
+ // everything is looked up dymanically, so there are no 'canonical' actions
+ return _emptyCanonicalActions;
+ }
+
+ public override object[] GetCustomAttributes(bool inherit) {
+ return ControllerType.GetCustomAttributes(inherit);
+ }
+
+ public override object[] GetCustomAttributes(Type attributeType, bool inherit) {
+ return ControllerType.GetCustomAttributes(attributeType, inherit);
+ }
+
+ public override bool IsDefined(Type attributeType, bool inherit) {
+ return ControllerType.IsDefined(attributeType, inherit);
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/SimpleAsyncResult.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/SimpleAsyncResult.cs
new file mode 100644
index 00000000000..74f39b51f77
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/SimpleAsyncResult.cs
@@ -0,0 +1,67 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc.Async {
+ using System;
+ using System.Threading;
+
+ internal sealed class SimpleAsyncResult : IAsyncResult {
+
+ private readonly object _asyncState;
+ private bool _completedSynchronously;
+ private volatile bool _isCompleted;
+
+ public SimpleAsyncResult(object asyncState) {
+ _asyncState = asyncState;
+ }
+
+ public object AsyncState {
+ get {
+ return _asyncState;
+ }
+ }
+
+ // ASP.NET IAsyncResult objects should never expose a WaitHandle due to potential deadlocking
+ public WaitHandle AsyncWaitHandle {
+ get {
+ return null;
+ }
+ }
+
+ public bool CompletedSynchronously {
+ get {
+ return _completedSynchronously;
+ }
+ }
+
+ public bool IsCompleted {
+ get {
+ return _isCompleted;
+ }
+ }
+
+ // Proper order of execution:
+ // 1. Set the CompletedSynchronously property to the correct value
+ // 2. Set the IsCompleted flag
+ // 3. Execute the callback
+ // 4. Signal the WaitHandle (which we don't have)
+ public void MarkCompleted(bool completedSynchronously, AsyncCallback callback) {
+ _completedSynchronously = completedSynchronously;
+ _isCompleted = true;
+
+ if (callback != null) {
+ callback(this);
+ }
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/SingleEntryGate.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/SingleEntryGate.cs
new file mode 100644
index 00000000000..c3e63a155c6
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/SingleEntryGate.cs
@@ -0,0 +1,32 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc.Async {
+ using System;
+ using System.Threading;
+
+ // used to synchronize access to a single-use consumable resource
+ internal sealed class SingleEntryGate {
+
+ private const int NOT_ENTERED = 0;
+ private const int ENTERED = 1;
+
+ private int _status;
+
+ // returns true if this is the first call to TryEnter(), false otherwise
+ public bool TryEnter() {
+ int oldStatus = Interlocked.Exchange(ref _status, ENTERED);
+ return (oldStatus == NOT_ENTERED);
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/SynchronizationContextUtil.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/SynchronizationContextUtil.cs
new file mode 100644
index 00000000000..5988091f8c4
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/SynchronizationContextUtil.cs
@@ -0,0 +1,55 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc.Async {
+ using System;
+ using System.Threading;
+
+ internal static class SynchronizationContextUtil {
+
+ public static SynchronizationContext GetSynchronizationContext() {
+ // In a runtime environment, SynchronizationContext.Current will be set to an instance
+ // of AspNetSynchronizationContext. In a unit test environment, the Current property
+ // won't be set and we have to create one on the fly.
+ return SynchronizationContext.Current ?? new SynchronizationContext();
+ }
+
+ public static T Sync<T>(this SynchronizationContext syncContext, Func<T> func) {
+ T theValue = default(T);
+ Exception thrownException = null;
+
+ syncContext.Send(o => {
+ try {
+ theValue = func();
+ }
+ catch (Exception ex) {
+ // by default, the AspNetSynchronizationContext type will swallow thrown exceptions,
+ // so we need to save and propagate them
+ thrownException = ex;
+ }
+ }, null);
+
+ if (thrownException != null) {
+ throw Error.SynchronizationContextUtil_ExceptionThrown(thrownException);
+ }
+ return theValue;
+ }
+
+ public static void Sync(this SynchronizationContext syncContext, Action action) {
+ Sync<AsyncVoid>(syncContext, () => {
+ action();
+ return default(AsyncVoid);
+ });
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/SynchronousOperationException.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/SynchronousOperationException.cs
new file mode 100644
index 00000000000..8836e26137e
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/SynchronousOperationException.cs
@@ -0,0 +1,39 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc.Async {
+ using System;
+ using System.Runtime.Serialization;
+
+ // This exception type is thrown by the SynchronizationContextUtil helper class since the AspNetSynchronizationContext
+ // type swallows exceptions. The inner exception contains the data the user cares about.
+
+ [Serializable]
+ public sealed class SynchronousOperationException : HttpException {
+
+ public SynchronousOperationException() {
+ }
+
+ private SynchronousOperationException(SerializationInfo info, StreamingContext context)
+ : base(info, context) {
+ }
+
+ public SynchronousOperationException(string message)
+ : base(message) {
+ }
+
+ public SynchronousOperationException(string message, Exception innerException)
+ : base(message, innerException) {
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/Trigger.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/Trigger.cs
new file mode 100644
index 00000000000..3ac944c503e
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/Trigger.cs
@@ -0,0 +1,33 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc.Async {
+ using System;
+ using System.Runtime.Serialization;
+
+ // Provides a trigger for the TriggerListener class.
+
+ internal sealed class Trigger {
+
+ private readonly Action _fireAction;
+
+ // Constructor should only be called by TriggerListener.
+ internal Trigger(Action fireAction) {
+ _fireAction = fireAction;
+ }
+
+ public void Fire() {
+ _fireAction();
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/TriggerListener.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/TriggerListener.cs
new file mode 100644
index 00000000000..eed509691ca
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Async/TriggerListener.cs
@@ -0,0 +1,68 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc.Async {
+ using System;
+ using System.Threading;
+
+ // This class is used to wait for triggers and a continuation. When the continuation has been provded
+ // and all triggers have been fired, the continuation is called. Similar to WaitHandle.WaitAll().
+ // New instances of this type are initially in the inactive state; activation is enabled by a call
+ // to Activate().
+
+ // This class is thread-safe.
+
+ internal sealed class TriggerListener {
+
+ private readonly Trigger _activateTrigger;
+ private volatile Action _continuation;
+ private readonly SingleEntryGate _continuationFiredGate = new SingleEntryGate();
+ private int _outstandingTriggers;
+ private readonly Trigger _setContinuationTrigger;
+
+ public TriggerListener() {
+ _activateTrigger = CreateTrigger();
+ _setContinuationTrigger = CreateTrigger();
+ }
+
+ public void Activate() {
+ _activateTrigger.Fire();
+ }
+
+ public Trigger CreateTrigger() {
+ Interlocked.Increment(ref _outstandingTriggers);
+
+ SingleEntryGate triggerFiredGate = new SingleEntryGate();
+ return new Trigger(() => {
+ if (triggerFiredGate.TryEnter()) {
+ HandleTriggerFired();
+ }
+ });
+ }
+
+ private void HandleTriggerFired() {
+ if (Interlocked.Decrement(ref _outstandingTriggers) == 0) {
+ if (_continuationFiredGate.TryEnter()) {
+ _continuation();
+ }
+ }
+ }
+
+ public void SetContinuation(Action continuation) {
+ if (continuation != null) {
+ _continuation = continuation;
+ _setContinuationTrigger.Fire();
+ }
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/AsyncController.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/AsyncController.cs
new file mode 100644
index 00000000000..975ce4fc3c3
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/AsyncController.cs
@@ -0,0 +1,111 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Web.Mvc.Async;
+ using System.Web.Routing;
+
+ public abstract class AsyncController : Controller, IAsyncManagerContainer, IAsyncController {
+
+ private static readonly object _executeTag = new object();
+ private static readonly object _executeCoreTag = new object();
+
+ private readonly AsyncManager _asyncManager = new AsyncManager();
+
+ public AsyncManager AsyncManager {
+ get {
+ return _asyncManager;
+ }
+ }
+
+ protected virtual IAsyncResult BeginExecute(RequestContext requestContext, AsyncCallback callback, object state) {
+ if (requestContext == null) {
+ throw new ArgumentNullException("requestContext");
+ }
+
+ VerifyExecuteCalledOnce();
+ Initialize(requestContext);
+ return AsyncResultWrapper.Begin(callback, state, BeginExecuteCore, EndExecuteCore, _executeTag);
+ }
+
+ protected virtual IAsyncResult BeginExecuteCore(AsyncCallback callback, object state) {
+ // If code in this method needs to be updated, please also check the ExecuteCore() method
+ // of Controller to see if that code also must be updated.
+
+ PossiblyLoadTempData();
+ try {
+ string actionName = RouteData.GetRequiredString("action");
+ IActionInvoker invoker = ActionInvoker;
+ IAsyncActionInvoker asyncInvoker = invoker as IAsyncActionInvoker;
+ if (asyncInvoker != null) {
+ // asynchronous invocation
+ BeginInvokeDelegate beginDelegate = delegate(AsyncCallback asyncCallback, object asyncState) {
+ return asyncInvoker.BeginInvokeAction(ControllerContext, actionName, asyncCallback, asyncState);
+ };
+
+ EndInvokeDelegate endDelegate = delegate(IAsyncResult asyncResult) {
+ if (!asyncInvoker.EndInvokeAction(asyncResult)) {
+ HandleUnknownAction(actionName);
+ }
+ };
+
+ return AsyncResultWrapper.Begin(callback, state, beginDelegate, endDelegate, _executeCoreTag);
+ }
+ else {
+ // synchronous invocation
+ Action action = () => {
+ if (!invoker.InvokeAction(ControllerContext, actionName)) {
+ HandleUnknownAction(actionName);
+ }
+ };
+ return AsyncResultWrapper.BeginSynchronous(callback, state, action, _executeCoreTag);
+ }
+ }
+ catch {
+ PossiblySaveTempData();
+ throw;
+ }
+ }
+
+ protected override IActionInvoker CreateActionInvoker() {
+ return new AsyncControllerActionInvoker();
+ }
+
+ protected virtual void EndExecute(IAsyncResult asyncResult) {
+ AsyncResultWrapper.End(asyncResult, _executeTag);
+ }
+
+ protected virtual void EndExecuteCore(IAsyncResult asyncResult) {
+ // If code in this method needs to be updated, please also check the ExecuteCore() method
+ // of Controller to see if that code also must be updated.
+
+ try {
+ AsyncResultWrapper.End(asyncResult, _executeCoreTag);
+ }
+ finally {
+ PossiblySaveTempData();
+ }
+ }
+
+ #region IAsyncController Members
+ IAsyncResult IAsyncController.BeginExecute(RequestContext requestContext, AsyncCallback callback, object state) {
+ return BeginExecute(requestContext, callback, state);
+ }
+
+ void IAsyncController.EndExecute(IAsyncResult asyncResult) {
+ EndExecute(asyncResult);
+ }
+ #endregion
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/AsyncTimeoutAttribute.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/AsyncTimeoutAttribute.cs
new file mode 100644
index 00000000000..9622e25ea64
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/AsyncTimeoutAttribute.cs
@@ -0,0 +1,53 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Web.Mvc.Async;
+
+ [SuppressMessage("Microsoft.Performance", "CA1813:AvoidUnsealedAttributes",
+ Justification = "Unsealed so that subclassed types can set properties in the default constructor.")]
+ [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
+ public class AsyncTimeoutAttribute : ActionFilterAttribute {
+
+ // duration is specified in milliseconds
+ public AsyncTimeoutAttribute(int duration) {
+ if (duration < -1) {
+ throw Error.AsyncCommon_InvalidTimeout("duration");
+ }
+
+ Duration = duration;
+ }
+
+ public int Duration {
+ get;
+ private set;
+ }
+
+ public override void OnActionExecuting(ActionExecutingContext filterContext) {
+ if (filterContext == null) {
+ throw new ArgumentNullException("filterContext");
+ }
+
+ IAsyncManagerContainer container = filterContext.Controller as IAsyncManagerContainer;
+ if (container == null) {
+ throw Error.AsyncCommon_ControllerMustImplementIAsyncManagerContainer(filterContext.Controller.GetType());
+ }
+
+ container.AsyncManager.Timeout = Duration;
+
+ base.OnActionExecuting(filterContext);
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/AuthorizationContext.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/AuthorizationContext.cs
new file mode 100644
index 00000000000..8cd68084b46
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/AuthorizationContext.cs
@@ -0,0 +1,50 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Diagnostics.CodeAnalysis;
+
+ public class AuthorizationContext : ControllerContext {
+
+ // parameterless constructor used for mocking
+ public AuthorizationContext() {
+ }
+
+ [Obsolete("The recommended alternative is the constructor AuthorizationContext(ControllerContext controllerContext, ActionDescriptor actionDescriptor).")]
+ public AuthorizationContext(ControllerContext controllerContext)
+ : base(controllerContext) {
+ }
+
+ [SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors",
+ Justification = "The virtual property setters are only to support mocking frameworks, in which case this constructor shouldn't be called anyway.")]
+ public AuthorizationContext(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
+ : base(controllerContext) {
+ if (actionDescriptor == null) {
+ throw new ArgumentNullException("actionDescriptor");
+ }
+
+ ActionDescriptor = actionDescriptor;
+ }
+
+ public virtual ActionDescriptor ActionDescriptor {
+ get;
+ set;
+ }
+
+ public ActionResult Result {
+ get;
+ set;
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/AuthorizeAttribute.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/AuthorizeAttribute.cs
new file mode 100644
index 00000000000..3b07bcaf50d
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/AuthorizeAttribute.cs
@@ -0,0 +1,135 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Linq;
+ using System.Security.Principal;
+ using System.Web;
+
+ [SuppressMessage("Microsoft.Performance", "CA1813:AvoidUnsealedAttributes",
+ Justification = "Unsealed so that subclassed types can set properties in the default constructor or override our behavior.")]
+ [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
+ public class AuthorizeAttribute : FilterAttribute, IAuthorizationFilter {
+
+ private readonly object _typeId = new object();
+
+ private string _roles;
+ private string[] _rolesSplit = new string[0];
+ private string _users;
+ private string[] _usersSplit = new string[0];
+
+ public string Roles {
+ get {
+ return _roles ?? String.Empty;
+ }
+ set {
+ _roles = value;
+ _rolesSplit = SplitString(value);
+ }
+ }
+
+ public override object TypeId {
+ get {
+ return _typeId;
+ }
+ }
+
+ public string Users {
+ get {
+ return _users ?? String.Empty;
+ }
+ set {
+ _users = value;
+ _usersSplit = SplitString(value);
+ }
+ }
+
+ // This method must be thread-safe since it is called by the thread-safe OnCacheAuthorization() method.
+ protected virtual bool AuthorizeCore(HttpContextBase httpContext) {
+ if (httpContext == null) {
+ throw new ArgumentNullException("httpContext");
+ }
+
+ IPrincipal user = httpContext.User;
+ if (!user.Identity.IsAuthenticated) {
+ return false;
+ }
+
+ if (_usersSplit.Length > 0 && !_usersSplit.Contains(user.Identity.Name, StringComparer.OrdinalIgnoreCase)) {
+ return false;
+ }
+
+ if (_rolesSplit.Length > 0 && !_rolesSplit.Any(user.IsInRole)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ private void CacheValidateHandler(HttpContext context, object data, ref HttpValidationStatus validationStatus) {
+ validationStatus = OnCacheAuthorization(new HttpContextWrapper(context));
+ }
+
+ public virtual void OnAuthorization(AuthorizationContext filterContext) {
+ if (filterContext == null) {
+ throw new ArgumentNullException("filterContext");
+ }
+
+ if (AuthorizeCore(filterContext.HttpContext)) {
+ // ** IMPORTANT **
+ // Since we're performing authorization at the action level, the authorization code runs
+ // after the output caching module. In the worst case this could allow an authorized user
+ // to cause the page to be cached, then an unauthorized user would later be served the
+ // cached page. We work around this by telling proxies not to cache the sensitive page,
+ // then we hook our custom authorization code into the caching mechanism so that we have
+ // the final say on whether a page should be served from the cache.
+
+ HttpCachePolicyBase cachePolicy = filterContext.HttpContext.Response.Cache;
+ cachePolicy.SetProxyMaxAge(new TimeSpan(0));
+ cachePolicy.AddValidationCallback(CacheValidateHandler, null /* data */);
+ }
+ else {
+ HandleUnauthorizedRequest(filterContext);
+ }
+ }
+
+ protected virtual void HandleUnauthorizedRequest(AuthorizationContext filterContext) {
+ // Returns HTTP 401 - see comment in HttpUnauthorizedResult.cs.
+ filterContext.Result = new HttpUnauthorizedResult();
+ }
+
+ // This method must be thread-safe since it is called by the caching module.
+ protected virtual HttpValidationStatus OnCacheAuthorization(HttpContextBase httpContext) {
+ if (httpContext == null) {
+ throw new ArgumentNullException("httpContext");
+ }
+
+ bool isAuthorized = AuthorizeCore(httpContext);
+ return (isAuthorized) ? HttpValidationStatus.Valid : HttpValidationStatus.IgnoreThisRequest;
+ }
+
+ internal static string[] SplitString(string original) {
+ if (String.IsNullOrEmpty(original)) {
+ return new string[0];
+ }
+
+ var split = from piece in original.Split(',')
+ let trimmed = piece.Trim()
+ where !String.IsNullOrEmpty(trimmed)
+ select trimmed;
+ return split.ToArray();
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/BindAttribute.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/BindAttribute.cs
new file mode 100644
index 00000000000..5f3b1bee55a
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/BindAttribute.cs
@@ -0,0 +1,63 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Linq;
+
+ [AttributeUsage(AttributeTargets.Class | AttributeTargets.Parameter, AllowMultiple = false, Inherited = true)]
+ public sealed class BindAttribute : Attribute {
+
+ private string _exclude;
+ private string[] _excludeSplit = new string[0];
+ private string _include;
+ private string[] _includeSplit = new string[0];
+
+ public string Exclude {
+ get {
+ return _exclude ?? String.Empty;
+ }
+ set {
+ _exclude = value;
+ _excludeSplit = AuthorizeAttribute.SplitString(value);
+ }
+ }
+
+ public string Include {
+ get {
+ return _include ?? String.Empty;
+ }
+ set {
+ _include = value;
+ _includeSplit = AuthorizeAttribute.SplitString(value);
+ }
+ }
+
+ public string Prefix {
+ get;
+ set;
+ }
+
+ internal static bool IsPropertyAllowed(string propertyName, string[] includeProperties, string[] excludeProperties) {
+ // We allow a property to be bound if its both in the include list AND not in the exclude list.
+ // An empty include list implies all properties are allowed.
+ // An empty exclude list implies no properties are disallowed.
+ bool includeProperty = (includeProperties == null) || (includeProperties.Length == 0) || includeProperties.Contains(propertyName, StringComparer.OrdinalIgnoreCase);
+ bool excludeProperty = (excludeProperties != null) && excludeProperties.Contains(propertyName, StringComparer.OrdinalIgnoreCase);
+ return includeProperty && !excludeProperty;
+ }
+
+ public bool IsPropertyAllowed(string propertyName) {
+ return IsPropertyAllowed(propertyName, _includeSplit, _excludeSplit);
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/BuildManagerWrapper.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/BuildManagerWrapper.cs
new file mode 100644
index 00000000000..f8959ecd1e0
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/BuildManagerWrapper.cs
@@ -0,0 +1,43 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System.Collections;
+ using System.IO;
+ using System.Web.Compilation;
+
+ internal sealed class BuildManagerWrapper : IBuildManager {
+ private static readonly Func<string, Stream> _readCachedFileDelegate =
+ TypeHelpers.CreateDelegate<Func<string, Stream>>(typeof(BuildManager), "ReadCachedFile", null /* thisParameter */);
+ private static readonly Func<string, Stream> _createCachedFileDelegate =
+ TypeHelpers.CreateDelegate<Func<string, Stream>>(typeof(BuildManager), "CreateCachedFile", null /* thisParameter */);
+
+ #region IBuildManager Members
+ object IBuildManager.CreateInstanceFromVirtualPath(string virtualPath, Type requiredBaseType) {
+ return BuildManager.CreateInstanceFromVirtualPath(virtualPath, requiredBaseType);
+ }
+
+ ICollection IBuildManager.GetReferencedAssemblies() {
+ return BuildManager.GetReferencedAssemblies();
+ }
+
+ // ASP.NET 4 methods
+ Stream IBuildManager.ReadCachedFile(string fileName) {
+ return (_readCachedFileDelegate != null) ? _readCachedFileDelegate(fileName) : null;
+ }
+
+ Stream IBuildManager.CreateCachedFile(string fileName) {
+ return (_createCachedFileDelegate != null) ? _createCachedFileDelegate(fileName) : null;
+ }
+ #endregion
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ByteArrayModelBinder.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ByteArrayModelBinder.cs
new file mode 100644
index 00000000000..ad62a139d65
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ByteArrayModelBinder.cs
@@ -0,0 +1,43 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+
+ public class ByteArrayModelBinder : IModelBinder {
+ public virtual object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) {
+ if (bindingContext == null) {
+ throw new ArgumentNullException("bindingContext");
+ }
+
+ ValueProviderResult valueResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
+
+ // case 1: there was no <input ... /> element containing this data
+ if (valueResult == null) {
+ return null;
+ }
+
+ string value = valueResult.AttemptedValue;
+
+ // case 2: there was an <input ... /> element but it was left blank
+ if (String.IsNullOrEmpty(value)) {
+ return null;
+ }
+
+ // Future proofing. If the byte array is actually an instance of System.Data.Linq.Binary
+ // then we need to remove these quotes put in place by the ToString() method.
+ string realValue = value.Replace("\"", String.Empty);
+ return Convert.FromBase64String(realValue);
+ }
+ }
+
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ChildActionOnlyAttribute.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ChildActionOnlyAttribute.cs
new file mode 100644
index 00000000000..398546bfc08
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ChildActionOnlyAttribute.cs
@@ -0,0 +1,30 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+
+ [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
+ public sealed class ChildActionOnlyAttribute : FilterAttribute, IAuthorizationFilter {
+
+ public void OnAuthorization(AuthorizationContext filterContext) {
+ if (filterContext == null) {
+ throw new ArgumentNullException("filterContext");
+ }
+
+ if (!filterContext.IsChildAction) {
+ throw Error.ChildActionOnlyAttribute_MustBeInChildRequest(filterContext.ActionDescriptor);
+ }
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ClientDataTypeModelValidatorProvider.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ClientDataTypeModelValidatorProvider.cs
new file mode 100644
index 00000000000..54c2519aa02
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ClientDataTypeModelValidatorProvider.cs
@@ -0,0 +1,79 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Collections.Generic;
+ using System.Globalization;
+ using System.Linq;
+ using System.Web.Mvc.Resources;
+
+ public class ClientDataTypeModelValidatorProvider : ModelValidatorProvider {
+
+ private static readonly HashSet<Type> _numericTypes = new HashSet<Type>(new Type[] {
+ typeof(byte), typeof(sbyte),
+ typeof(short), typeof(ushort),
+ typeof(int), typeof(uint),
+ typeof(long), typeof(ulong),
+ typeof(float), typeof(double), typeof(decimal)
+ });
+
+ public override IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, ControllerContext context) {
+ if (metadata == null) {
+ throw new ArgumentNullException("metadata");
+ }
+ if (context == null) {
+ throw new ArgumentNullException("context");
+ }
+
+ return GetValidatorsImpl(metadata, context);
+ }
+
+ private static IEnumerable<ModelValidator> GetValidatorsImpl(ModelMetadata metadata, ControllerContext context) {
+ Type type = metadata.ModelType;
+ if (IsNumericType(type)) {
+ yield return new NumericModelValidator(metadata, context);
+ }
+ }
+
+ private static bool IsNumericType(Type type) {
+ Type underlyingType = Nullable.GetUnderlyingType(type); // strip off the Nullable<>
+ return _numericTypes.Contains(underlyingType ?? type);
+ }
+
+ internal sealed class NumericModelValidator : ModelValidator {
+ public NumericModelValidator(ModelMetadata metadata, ControllerContext controllerContext)
+ : base(metadata, controllerContext) {
+ }
+
+ public override IEnumerable<ModelClientValidationRule> GetClientValidationRules() {
+ ModelClientValidationRule rule = new ModelClientValidationRule() {
+ ValidationType = "number",
+ ErrorMessage = MakeErrorString(Metadata.GetDisplayName())
+ };
+
+ return new ModelClientValidationRule[] { rule };
+ }
+
+ private static string MakeErrorString(string displayName) {
+ // use CurrentCulture since this message is intended for the site visitor
+ return String.Format(CultureInfo.CurrentCulture, MvcResources.ClientDataTypeModelValidatorProvider_FieldMustBeNumeric, displayName);
+ }
+
+ public override IEnumerable<ModelValidationResult> Validate(object container) {
+ // this is not a server-side validator
+ return Enumerable.Empty<ModelValidationResult>();
+ }
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ContentResult.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ContentResult.cs
new file mode 100644
index 00000000000..37ecb71a70c
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ContentResult.cs
@@ -0,0 +1,53 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Text;
+ using System.Web;
+
+ public class ContentResult : ActionResult {
+
+ public string Content {
+ get;
+ set;
+ }
+
+ public Encoding ContentEncoding {
+ get;
+ set;
+ }
+
+ public string ContentType {
+ get;
+ set;
+ }
+
+ public override void ExecuteResult(ControllerContext context) {
+ if (context == null) {
+ throw new ArgumentNullException("context");
+ }
+
+ HttpResponseBase response = context.HttpContext.Response;
+
+ if (!String.IsNullOrEmpty(ContentType)) {
+ response.ContentType = ContentType;
+ }
+ if (ContentEncoding != null) {
+ response.ContentEncoding = ContentEncoding;
+ }
+ if (Content != null) {
+ response.Write(Content);
+ }
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/Controller.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Controller.cs
new file mode 100644
index 00000000000..04f4bc41280
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Controller.cs
@@ -0,0 +1,599 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Globalization;
+ using System.IO;
+ using System.Security.Principal;
+ using System.Text;
+ using System.Web;
+ using System.Web.Mvc.Resources;
+ using System.Web.Routing;
+
+ public abstract class Controller : ControllerBase, IActionFilter, IAuthorizationFilter, IDisposable, IExceptionFilter, IResultFilter {
+
+ private IActionInvoker _actionInvoker;
+ private ModelBinderDictionary _binders;
+ private RouteCollection _routeCollection;
+ private ITempDataProvider _tempDataProvider;
+
+ public IActionInvoker ActionInvoker {
+ get {
+ if (_actionInvoker == null) {
+ _actionInvoker = CreateActionInvoker();
+ }
+ return _actionInvoker;
+ }
+ set {
+ _actionInvoker = value;
+ }
+ }
+
+ [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly",
+ Justification = "Property is settable so that the dictionary can be provided for unit testing purposes.")]
+ protected internal ModelBinderDictionary Binders {
+ get {
+ if (_binders == null) {
+ _binders = ModelBinders.Binders;
+ }
+ return _binders;
+ }
+ set {
+ _binders = value;
+ }
+ }
+
+ public HttpContextBase HttpContext {
+ get {
+ return ControllerContext == null ? null : ControllerContext.HttpContext;
+ }
+ }
+
+ public ModelStateDictionary ModelState {
+ get {
+ return ViewData.ModelState;
+ }
+ }
+
+ public HttpRequestBase Request {
+ get {
+ return HttpContext == null ? null : HttpContext.Request;
+ }
+ }
+
+ public HttpResponseBase Response {
+ get {
+ return HttpContext == null ? null : HttpContext.Response;
+ }
+ }
+
+ internal RouteCollection RouteCollection {
+ get {
+ if (_routeCollection == null) {
+ _routeCollection = RouteTable.Routes;
+ }
+ return _routeCollection;
+ }
+ set {
+ _routeCollection = value;
+ }
+ }
+
+ public RouteData RouteData {
+ get {
+ return ControllerContext == null ? null : ControllerContext.RouteData;
+ }
+ }
+
+ public HttpServerUtilityBase Server {
+ get {
+ return HttpContext == null ? null : HttpContext.Server;
+ }
+ }
+
+ public HttpSessionStateBase Session {
+ get {
+ return HttpContext == null ? null : HttpContext.Session;
+ }
+ }
+
+ public ITempDataProvider TempDataProvider {
+ get {
+ if (_tempDataProvider == null) {
+ _tempDataProvider = CreateTempDataProvider();
+ }
+ return _tempDataProvider;
+ }
+ set {
+ _tempDataProvider = value;
+ }
+ }
+
+ public UrlHelper Url {
+ get;
+ set;
+ }
+
+ public IPrincipal User {
+ get {
+ return HttpContext == null ? null : HttpContext.User;
+ }
+ }
+
+ [SuppressMessage("Microsoft.Naming", "CA1719:ParameterNamesShouldNotMatchMemberNames", MessageId = "0#",
+ Justification = "'Content' refers to ContentResult type; 'content' refers to ContentResult.Content property.")]
+ protected internal ContentResult Content(string content) {
+ return Content(content, null /* contentType */);
+ }
+
+ [SuppressMessage("Microsoft.Naming", "CA1719:ParameterNamesShouldNotMatchMemberNames", MessageId = "0#",
+ Justification = "'Content' refers to ContentResult type; 'content' refers to ContentResult.Content property.")]
+ protected internal ContentResult Content(string content, string contentType) {
+ return Content(content, contentType, null /* contentEncoding */);
+ }
+
+ [SuppressMessage("Microsoft.Naming", "CA1719:ParameterNamesShouldNotMatchMemberNames", MessageId = "0#",
+ Justification = "'Content' refers to ContentResult type; 'content' refers to ContentResult.Content property.")]
+ protected internal virtual ContentResult Content(string content, string contentType, Encoding contentEncoding) {
+ return new ContentResult {
+ Content = content,
+ ContentType = contentType,
+ ContentEncoding = contentEncoding
+ };
+ }
+
+ protected virtual IActionInvoker CreateActionInvoker() {
+ return new ControllerActionInvoker();
+ }
+
+ protected virtual ITempDataProvider CreateTempDataProvider() {
+ return new SessionStateTempDataProvider();
+ }
+
+ // The default invoker will never match methods defined on the Controller type, so
+ // the Dispose() method is not web-callable. However, in general, since implicitly-
+ // implemented interface methods are public, they are web-callable unless decorated with
+ // [NonAction].
+ public void Dispose() {
+ Dispose(true /* disposing */);
+ GC.SuppressFinalize(this);
+ }
+
+ protected virtual void Dispose(bool disposing) {
+ }
+
+ protected override void ExecuteCore() {
+ // If code in this method needs to be updated, please also check the BeginExecuteCore() and
+ // EndExecuteCore() methods of AsyncController to see if that code also must be updated.
+
+ PossiblyLoadTempData();
+ try {
+ string actionName = RouteData.GetRequiredString("action");
+ if (!ActionInvoker.InvokeAction(ControllerContext, actionName)) {
+ HandleUnknownAction(actionName);
+ }
+ }
+ finally {
+ PossiblySaveTempData();
+ }
+ }
+
+ protected internal FileContentResult File(byte[] fileContents, string contentType) {
+ return File(fileContents, contentType, null /* fileDownloadName */);
+ }
+
+ protected internal virtual FileContentResult File(byte[] fileContents, string contentType, string fileDownloadName) {
+ return new FileContentResult(fileContents, contentType) { FileDownloadName = fileDownloadName };
+ }
+
+ protected internal FileStreamResult File(Stream fileStream, string contentType) {
+ return File(fileStream, contentType, null /* fileDownloadName */);
+ }
+
+ protected internal virtual FileStreamResult File(Stream fileStream, string contentType, string fileDownloadName) {
+ return new FileStreamResult(fileStream, contentType) { FileDownloadName = fileDownloadName };
+ }
+
+ protected internal FilePathResult File(string fileName, string contentType) {
+ return File(fileName, contentType, null /* fileDownloadName */);
+ }
+
+ protected internal virtual FilePathResult File(string fileName, string contentType, string fileDownloadName) {
+ return new FilePathResult(fileName, contentType) { FileDownloadName = fileDownloadName };
+ }
+
+ protected virtual void HandleUnknownAction(string actionName) {
+ throw new HttpException(404, String.Format(CultureInfo.CurrentUICulture,
+ MvcResources.Controller_UnknownAction, actionName, GetType().FullName));
+ }
+
+ protected internal virtual JavaScriptResult JavaScript(string script) {
+ return new JavaScriptResult { Script = script };
+ }
+
+ protected internal JsonResult Json(object data) {
+ return Json(data, null /* contentType */, null /* contentEncoding */, JsonRequestBehavior.DenyGet);
+ }
+
+ protected internal JsonResult Json(object data, string contentType) {
+ return Json(data, contentType, null /* contentEncoding */, JsonRequestBehavior.DenyGet);
+ }
+
+ protected internal virtual JsonResult Json(object data, string contentType, Encoding contentEncoding) {
+ return Json(data, contentType, contentEncoding, JsonRequestBehavior.DenyGet);
+ }
+
+ protected internal JsonResult Json(object data, JsonRequestBehavior behavior) {
+ return Json(data, null /* contentType */, null /* contentEncoding */, behavior);
+ }
+
+ protected internal JsonResult Json(object data, string contentType, JsonRequestBehavior behavior) {
+ return Json(data, contentType, null /* contentEncoding */, behavior);
+ }
+
+ protected internal virtual JsonResult Json(object data, string contentType, Encoding contentEncoding, JsonRequestBehavior behavior) {
+ return new JsonResult {
+ Data = data,
+ ContentType = contentType,
+ ContentEncoding = contentEncoding,
+ JsonRequestBehavior = behavior
+ };
+ }
+
+ protected override void Initialize(RequestContext requestContext) {
+ base.Initialize(requestContext);
+ Url = new UrlHelper(requestContext);
+ }
+
+ protected virtual void OnActionExecuting(ActionExecutingContext filterContext) {
+ }
+
+ protected virtual void OnActionExecuted(ActionExecutedContext filterContext) {
+ }
+
+ protected virtual void OnAuthorization(AuthorizationContext filterContext) {
+ }
+
+ protected virtual void OnException(ExceptionContext filterContext) {
+ }
+
+ protected virtual void OnResultExecuted(ResultExecutedContext filterContext) {
+ }
+
+ protected virtual void OnResultExecuting(ResultExecutingContext filterContext) {
+ }
+
+ protected internal PartialViewResult PartialView() {
+ return PartialView(null /* viewName */, null /* model */);
+ }
+
+ protected internal PartialViewResult PartialView(object model) {
+ return PartialView(null /* viewName */, model);
+ }
+
+ protected internal PartialViewResult PartialView(string viewName) {
+ return PartialView(viewName, null /* model */);
+ }
+
+ protected internal virtual PartialViewResult PartialView(string viewName, object model) {
+ if (model != null) {
+ ViewData.Model = model;
+ }
+
+ return new PartialViewResult {
+ ViewName = viewName,
+ ViewData = ViewData,
+ TempData = TempData
+ };
+ }
+
+ internal void PossiblyLoadTempData() {
+ if (!ControllerContext.IsChildAction) {
+ TempData.Load(ControllerContext, TempDataProvider);
+ }
+ }
+
+ internal void PossiblySaveTempData() {
+ if (!ControllerContext.IsChildAction) {
+ TempData.Save(ControllerContext, TempDataProvider);
+ }
+ }
+
+ [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic",
+ Justification = "Instance method for consistency with other helpers.")]
+ [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "0#",
+ Justification = "Response.Redirect() takes its URI as a string parameter.")]
+ protected internal virtual RedirectResult Redirect(string url) {
+ if (String.IsNullOrEmpty(url)) {
+ throw new ArgumentException(MvcResources.Common_NullOrEmpty, "url");
+ }
+ return new RedirectResult(url);
+ }
+
+ protected internal RedirectToRouteResult RedirectToAction(string actionName) {
+ return RedirectToAction(actionName, (RouteValueDictionary)null);
+ }
+
+ protected internal RedirectToRouteResult RedirectToAction(string actionName, object routeValues) {
+ return RedirectToAction(actionName, new RouteValueDictionary(routeValues));
+ }
+
+ protected internal RedirectToRouteResult RedirectToAction(string actionName, RouteValueDictionary routeValues) {
+ return RedirectToAction(actionName, null /* controllerName */, routeValues);
+ }
+
+ protected internal RedirectToRouteResult RedirectToAction(string actionName, string controllerName) {
+ return RedirectToAction(actionName, controllerName, (RouteValueDictionary)null);
+ }
+
+ protected internal RedirectToRouteResult RedirectToAction(string actionName, string controllerName, object routeValues) {
+ return RedirectToAction(actionName, controllerName, new RouteValueDictionary(routeValues));
+ }
+
+ protected internal virtual RedirectToRouteResult RedirectToAction(string actionName, string controllerName, RouteValueDictionary routeValues) {
+ RouteValueDictionary mergedRouteValues;
+
+ if (RouteData == null) {
+ mergedRouteValues = RouteValuesHelpers.MergeRouteValues(actionName, controllerName, null, routeValues, true /* includeImplicitMvcValues */);
+ }
+ else {
+ mergedRouteValues = RouteValuesHelpers.MergeRouteValues(actionName, controllerName, RouteData.Values, routeValues, true /* includeImplicitMvcValues */);
+ }
+
+ return new RedirectToRouteResult(mergedRouteValues);
+ }
+
+ protected internal RedirectToRouteResult RedirectToRoute(object routeValues) {
+ return RedirectToRoute(new RouteValueDictionary(routeValues));
+ }
+
+ protected internal RedirectToRouteResult RedirectToRoute(RouteValueDictionary routeValues) {
+ return RedirectToRoute(null /* routeName */, routeValues);
+ }
+
+ protected internal RedirectToRouteResult RedirectToRoute(string routeName) {
+ return RedirectToRoute(routeName, (RouteValueDictionary)null);
+ }
+
+ protected internal RedirectToRouteResult RedirectToRoute(string routeName, object routeValues) {
+ return RedirectToRoute(routeName, new RouteValueDictionary(routeValues));
+ }
+
+ protected internal virtual RedirectToRouteResult RedirectToRoute(string routeName, RouteValueDictionary routeValues) {
+ return new RedirectToRouteResult(routeName, RouteValuesHelpers.GetRouteValues(routeValues));
+ }
+
+ protected internal bool TryUpdateModel<TModel>(TModel model) where TModel : class {
+ return TryUpdateModel(model, null, null, null, ValueProvider);
+ }
+
+ protected internal bool TryUpdateModel<TModel>(TModel model, string prefix) where TModel : class {
+ return TryUpdateModel(model, prefix, null, null, ValueProvider);
+ }
+
+ protected internal bool TryUpdateModel<TModel>(TModel model, string[] includeProperties) where TModel : class {
+ return TryUpdateModel(model, null, includeProperties, null, ValueProvider);
+ }
+
+ protected internal bool TryUpdateModel<TModel>(TModel model, string prefix, string[] includeProperties) where TModel : class {
+ return TryUpdateModel(model, prefix, includeProperties, null, ValueProvider);
+ }
+
+ protected internal bool TryUpdateModel<TModel>(TModel model, string prefix, string[] includeProperties, string[] excludeProperties) where TModel : class {
+ return TryUpdateModel(model, prefix, includeProperties, excludeProperties, ValueProvider);
+ }
+
+ protected internal bool TryUpdateModel<TModel>(TModel model, IValueProvider valueProvider) where TModel : class {
+ return TryUpdateModel(model, null, null, null, valueProvider);
+ }
+
+ protected internal bool TryUpdateModel<TModel>(TModel model, string prefix, IValueProvider valueProvider) where TModel : class {
+ return TryUpdateModel(model, prefix, null, null, valueProvider);
+ }
+
+ protected internal bool TryUpdateModel<TModel>(TModel model, string[] includeProperties, IValueProvider valueProvider) where TModel : class {
+ return TryUpdateModel(model, null, includeProperties, null, valueProvider);
+ }
+
+ protected internal bool TryUpdateModel<TModel>(TModel model, string prefix, string[] includeProperties, IValueProvider valueProvider) where TModel : class {
+ return TryUpdateModel(model, prefix, includeProperties, null, valueProvider);
+ }
+
+ protected internal bool TryUpdateModel<TModel>(TModel model, string prefix, string[] includeProperties, string[] excludeProperties, IValueProvider valueProvider) where TModel : class {
+ if (model == null) {
+ throw new ArgumentNullException("model");
+ }
+ if (valueProvider == null) {
+ throw new ArgumentNullException("valueProvider");
+ }
+
+ Predicate<string> propertyFilter = propertyName => BindAttribute.IsPropertyAllowed(propertyName, includeProperties, excludeProperties);
+ IModelBinder binder = Binders.GetBinder(typeof(TModel));
+
+ ModelBindingContext bindingContext = new ModelBindingContext() {
+ ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, typeof(TModel)),
+ ModelName = prefix,
+ ModelState = ModelState,
+ PropertyFilter = propertyFilter,
+ ValueProvider = valueProvider
+ };
+ binder.BindModel(ControllerContext, bindingContext);
+ return ModelState.IsValid;
+ }
+
+ protected internal bool TryValidateModel(object model) {
+ return TryValidateModel(model, null /* prefix */);
+ }
+
+ protected internal bool TryValidateModel(object model, string prefix) {
+ if (model == null) {
+ throw new ArgumentNullException("model");
+ }
+
+ ModelMetadata metadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, model.GetType());
+
+ foreach (ModelValidationResult validationResult in ModelValidator.GetModelValidator(metadata, ControllerContext).Validate(null)) {
+ ModelState.AddModelError(DefaultModelBinder.CreateSubPropertyName(prefix, validationResult.MemberName), validationResult.Message);
+ }
+
+ return ModelState.IsValid;
+ }
+
+ protected internal void UpdateModel<TModel>(TModel model) where TModel : class {
+ UpdateModel(model, null, null, null, ValueProvider);
+ }
+
+ protected internal void UpdateModel<TModel>(TModel model, string prefix) where TModel : class {
+ UpdateModel(model, prefix, null, null, ValueProvider);
+ }
+
+ protected internal void UpdateModel<TModel>(TModel model, string[] includeProperties) where TModel : class {
+ UpdateModel(model, null, includeProperties, null, ValueProvider);
+ }
+
+ protected internal void UpdateModel<TModel>(TModel model, string prefix, string[] includeProperties) where TModel : class {
+ UpdateModel(model, prefix, includeProperties, null, ValueProvider);
+ }
+
+ protected internal void UpdateModel<TModel>(TModel model, string prefix, string[] includeProperties, string[] excludeProperties) where TModel : class {
+ UpdateModel(model, prefix, includeProperties, excludeProperties, ValueProvider);
+ }
+
+ protected internal void UpdateModel<TModel>(TModel model, IValueProvider valueProvider) where TModel : class {
+ UpdateModel(model, null, null, null, valueProvider);
+ }
+
+ protected internal void UpdateModel<TModel>(TModel model, string prefix, IValueProvider valueProvider) where TModel : class {
+ UpdateModel(model, prefix, null, null, valueProvider);
+ }
+
+ protected internal void UpdateModel<TModel>(TModel model, string[] includeProperties, IValueProvider valueProvider) where TModel : class {
+ UpdateModel(model, null, includeProperties, null, valueProvider);
+ }
+
+ protected internal void UpdateModel<TModel>(TModel model, string prefix, string[] includeProperties, IValueProvider valueProvider) where TModel : class {
+ UpdateModel(model, prefix, includeProperties, null, valueProvider);
+ }
+
+ protected internal void UpdateModel<TModel>(TModel model, string prefix, string[] includeProperties, string[] excludeProperties, IValueProvider valueProvider) where TModel : class {
+ bool success = TryUpdateModel(model, prefix, includeProperties, excludeProperties, valueProvider);
+ if (!success) {
+ string message = String.Format(CultureInfo.CurrentUICulture, MvcResources.Controller_UpdateModel_UpdateUnsuccessful,
+ typeof(TModel).FullName);
+ throw new InvalidOperationException(message);
+ }
+ }
+
+ protected internal void ValidateModel(object model) {
+ ValidateModel(model, null /* prefix */);
+ }
+
+ protected internal void ValidateModel(object model, string prefix) {
+ if (!TryValidateModel(model, prefix)) {
+ throw new InvalidOperationException(
+ String.Format(
+ CultureInfo.CurrentUICulture,
+ MvcResources.Controller_Validate_ValidationFailed,
+ model.GetType().FullName
+ )
+ );
+ }
+ }
+
+ protected internal ViewResult View() {
+ return View(null /* viewName */, null /* masterName */, null /* model */);
+ }
+
+ protected internal ViewResult View(object model) {
+ return View(null /* viewName */, null /* masterName */, model);
+ }
+
+ protected internal ViewResult View(string viewName) {
+ return View(viewName, null /* masterName */, null /* model */);
+ }
+
+ protected internal ViewResult View(string viewName, string masterName) {
+ return View(viewName, masterName, null /* model */);
+ }
+
+ protected internal ViewResult View(string viewName, object model) {
+ return View(viewName, null /* masterName */, model);
+ }
+
+ protected internal virtual ViewResult View(string viewName, string masterName, object model) {
+ if (model != null) {
+ ViewData.Model = model;
+ }
+
+ return new ViewResult {
+ ViewName = viewName,
+ MasterName = masterName,
+ ViewData = ViewData,
+ TempData = TempData
+ };
+ }
+
+ [SuppressMessage("Microsoft.Naming", "CA1719:ParameterNamesShouldNotMatchMemberNames", MessageId = "0#",
+ Justification = "The method name 'View' is a convenient shorthand for 'CreateViewResult'.")]
+ protected internal ViewResult View(IView view) {
+ return View(view, null /* model */);
+ }
+
+ [SuppressMessage("Microsoft.Naming", "CA1719:ParameterNamesShouldNotMatchMemberNames", MessageId = "0#",
+ Justification = "The method name 'View' is a convenient shorthand for 'CreateViewResult'.")]
+ protected internal virtual ViewResult View(IView view, object model) {
+ if (model != null) {
+ ViewData.Model = model;
+ }
+
+ return new ViewResult {
+ View = view,
+ ViewData = ViewData,
+ TempData = TempData
+ };
+ }
+
+ #region IActionFilter Members
+ void IActionFilter.OnActionExecuting(ActionExecutingContext filterContext) {
+ OnActionExecuting(filterContext);
+ }
+
+ void IActionFilter.OnActionExecuted(ActionExecutedContext filterContext) {
+ OnActionExecuted(filterContext);
+ }
+ #endregion
+
+ #region IAuthorizationFilter Members
+ void IAuthorizationFilter.OnAuthorization(AuthorizationContext filterContext) {
+ OnAuthorization(filterContext);
+ }
+ #endregion
+
+ #region IExceptionFilter Members
+ void IExceptionFilter.OnException(ExceptionContext filterContext) {
+ OnException(filterContext);
+ }
+ #endregion
+
+ #region IResultFilter Members
+ void IResultFilter.OnResultExecuting(ResultExecutingContext filterContext) {
+ OnResultExecuting(filterContext);
+ }
+
+ void IResultFilter.OnResultExecuted(ResultExecutedContext filterContext) {
+ OnResultExecuted(filterContext);
+ }
+ #endregion
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ControllerActionInvoker.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ControllerActionInvoker.cs
new file mode 100644
index 00000000000..aad3e814bc1
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ControllerActionInvoker.cs
@@ -0,0 +1,333 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Globalization;
+ using System.Linq;
+ using System.Threading;
+ using System.Web;
+ using System.Web.Mvc.Resources;
+
+ public class ControllerActionInvoker : IActionInvoker {
+
+ private readonly static ControllerDescriptorCache _staticDescriptorCache = new ControllerDescriptorCache();
+
+ private ModelBinderDictionary _binders;
+ private ControllerDescriptorCache _instanceDescriptorCache;
+
+ [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly",
+ Justification = "Property is settable so that the dictionary can be provided for unit testing purposes.")]
+ protected internal ModelBinderDictionary Binders {
+ get {
+ if (_binders == null) {
+ _binders = ModelBinders.Binders;
+ }
+ return _binders;
+ }
+ set {
+ _binders = value;
+ }
+ }
+
+ internal ControllerDescriptorCache DescriptorCache {
+ get {
+ if (_instanceDescriptorCache == null) {
+ _instanceDescriptorCache = _staticDescriptorCache;
+ }
+ return _instanceDescriptorCache;
+ }
+ set {
+ _instanceDescriptorCache = value;
+ }
+ }
+
+ private static void AddControllerToFilterList<TFilter>(ControllerBase controller, IList<TFilter> filterList) where TFilter : class {
+ TFilter controllerAsFilter = controller as TFilter;
+ if (controllerAsFilter != null) {
+ filterList.Insert(0, controllerAsFilter);
+ }
+ }
+
+ protected virtual ActionResult CreateActionResult(ControllerContext controllerContext, ActionDescriptor actionDescriptor, object actionReturnValue) {
+ if (actionReturnValue == null) {
+ return new EmptyResult();
+ }
+
+ ActionResult actionResult = (actionReturnValue as ActionResult) ??
+ new ContentResult { Content = Convert.ToString(actionReturnValue, CultureInfo.InvariantCulture) };
+ return actionResult;
+ }
+
+ protected virtual ControllerDescriptor GetControllerDescriptor(ControllerContext controllerContext) {
+ Type controllerType = controllerContext.Controller.GetType();
+ ControllerDescriptor controllerDescriptor = DescriptorCache.GetDescriptor(controllerType, () => new ReflectedControllerDescriptor(controllerType));
+ return controllerDescriptor;
+ }
+
+ protected virtual ActionDescriptor FindAction(ControllerContext controllerContext, ControllerDescriptor controllerDescriptor, string actionName) {
+ ActionDescriptor actionDescriptor = controllerDescriptor.FindAction(controllerContext, actionName);
+ return actionDescriptor;
+ }
+
+ protected virtual FilterInfo GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor) {
+ FilterInfo filters = actionDescriptor.GetFilters();
+
+ // if the current controller implements one of the filter interfaces, it should be added to the list at position 0
+ ControllerBase controller = controllerContext.Controller;
+ AddControllerToFilterList(controller, filters.ActionFilters);
+ AddControllerToFilterList(controller, filters.ResultFilters);
+ AddControllerToFilterList(controller, filters.AuthorizationFilters);
+ AddControllerToFilterList(controller, filters.ExceptionFilters);
+
+ return filters;
+ }
+
+ private IModelBinder GetModelBinder(ParameterDescriptor parameterDescriptor) {
+ // look on the parameter itself, then look in the global table
+ return parameterDescriptor.BindingInfo.Binder ?? Binders.GetBinder(parameterDescriptor.ParameterType);
+ }
+
+ protected virtual object GetParameterValue(ControllerContext controllerContext, ParameterDescriptor parameterDescriptor) {
+ // collect all of the necessary binding properties
+ Type parameterType = parameterDescriptor.ParameterType;
+ IModelBinder binder = GetModelBinder(parameterDescriptor);
+ IValueProvider valueProvider = controllerContext.Controller.ValueProvider;
+ string parameterName = parameterDescriptor.BindingInfo.Prefix ?? parameterDescriptor.ParameterName;
+ Predicate<string> propertyFilter = GetPropertyFilter(parameterDescriptor);
+
+ // finally, call into the binder
+ ModelBindingContext bindingContext = new ModelBindingContext() {
+ FallbackToEmptyPrefix = (parameterDescriptor.BindingInfo.Prefix == null), // only fall back if prefix not specified
+ ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, parameterType),
+ ModelName = parameterName,
+ ModelState = controllerContext.Controller.ViewData.ModelState,
+ PropertyFilter = propertyFilter,
+ ValueProvider = valueProvider
+ };
+
+ object result = binder.BindModel(controllerContext, bindingContext);
+ return result ?? parameterDescriptor.DefaultValue;
+ }
+
+ protected virtual IDictionary<string, object> GetParameterValues(ControllerContext controllerContext, ActionDescriptor actionDescriptor) {
+ Dictionary<string, object> parametersDict = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
+ ParameterDescriptor[] parameterDescriptors = actionDescriptor.GetParameters();
+
+ foreach (ParameterDescriptor parameterDescriptor in parameterDescriptors) {
+ parametersDict[parameterDescriptor.ParameterName] = GetParameterValue(controllerContext, parameterDescriptor);
+ }
+ return parametersDict;
+ }
+
+ private static Predicate<string> GetPropertyFilter(ParameterDescriptor parameterDescriptor) {
+ ParameterBindingInfo bindingInfo = parameterDescriptor.BindingInfo;
+ return propertyName => BindAttribute.IsPropertyAllowed(propertyName, bindingInfo.Include.ToArray(), bindingInfo.Exclude.ToArray());
+ }
+
+ public virtual bool InvokeAction(ControllerContext controllerContext, string actionName) {
+ if (controllerContext == null) {
+ throw new ArgumentNullException("controllerContext");
+ }
+ if (String.IsNullOrEmpty(actionName)) {
+ throw new ArgumentException(MvcResources.Common_NullOrEmpty, "actionName");
+ }
+
+ ControllerDescriptor controllerDescriptor = GetControllerDescriptor(controllerContext);
+ ActionDescriptor actionDescriptor = FindAction(controllerContext, controllerDescriptor, actionName);
+ if (actionDescriptor != null) {
+ FilterInfo filterInfo = GetFilters(controllerContext, actionDescriptor);
+
+ try {
+ AuthorizationContext authContext = InvokeAuthorizationFilters(controllerContext, filterInfo.AuthorizationFilters, actionDescriptor);
+ if (authContext.Result != null) {
+ // the auth filter signaled that we should let it short-circuit the request
+ InvokeActionResult(controllerContext, authContext.Result);
+ }
+ else {
+ if (controllerContext.Controller.ValidateRequest) {
+ ValidateRequest(controllerContext);
+ }
+
+ IDictionary<string, object> parameters = GetParameterValues(controllerContext, actionDescriptor);
+ ActionExecutedContext postActionContext = InvokeActionMethodWithFilters(controllerContext, filterInfo.ActionFilters, actionDescriptor, parameters);
+ InvokeActionResultWithFilters(controllerContext, filterInfo.ResultFilters, postActionContext.Result);
+ }
+ }
+ catch (ThreadAbortException) {
+ // This type of exception occurs as a result of Response.Redirect(), but we special-case so that
+ // the filters don't see this as an error.
+ throw;
+ }
+ catch (Exception ex) {
+ // something blew up, so execute the exception filters
+ ExceptionContext exceptionContext = InvokeExceptionFilters(controllerContext, filterInfo.ExceptionFilters, ex);
+ if (!exceptionContext.ExceptionHandled) {
+ throw;
+ }
+ InvokeActionResult(controllerContext, exceptionContext.Result);
+ }
+
+ return true;
+ }
+
+ // notify controller that no method matched
+ return false;
+ }
+
+ protected virtual ActionResult InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary<string, object> parameters) {
+ object returnValue = actionDescriptor.Execute(controllerContext, parameters);
+ ActionResult result = CreateActionResult(controllerContext, actionDescriptor, returnValue);
+ return result;
+ }
+
+ internal static ActionExecutedContext InvokeActionMethodFilter(IActionFilter filter, ActionExecutingContext preContext, Func<ActionExecutedContext> continuation) {
+ filter.OnActionExecuting(preContext);
+ if (preContext.Result != null) {
+ return new ActionExecutedContext(preContext, preContext.ActionDescriptor, true /* canceled */, null /* exception */) {
+ Result = preContext.Result
+ };
+ }
+
+ bool wasError = false;
+ ActionExecutedContext postContext = null;
+ try {
+ postContext = continuation();
+ }
+ catch (ThreadAbortException) {
+ // This type of exception occurs as a result of Response.Redirect(), but we special-case so that
+ // the filters don't see this as an error.
+ postContext = new ActionExecutedContext(preContext, preContext.ActionDescriptor, false /* canceled */, null /* exception */);
+ filter.OnActionExecuted(postContext);
+ throw;
+ }
+ catch (Exception ex) {
+ wasError = true;
+ postContext = new ActionExecutedContext(preContext, preContext.ActionDescriptor, false /* canceled */, ex);
+ filter.OnActionExecuted(postContext);
+ if (!postContext.ExceptionHandled) {
+ throw;
+ }
+ }
+ if (!wasError) {
+ filter.OnActionExecuted(postContext);
+ }
+ return postContext;
+ }
+
+ protected virtual ActionExecutedContext InvokeActionMethodWithFilters(ControllerContext controllerContext, IList<IActionFilter> filters, ActionDescriptor actionDescriptor, IDictionary<string, object> parameters) {
+ ActionExecutingContext preContext = new ActionExecutingContext(controllerContext, actionDescriptor, parameters);
+ Func<ActionExecutedContext> continuation = () =>
+ new ActionExecutedContext(controllerContext, actionDescriptor, false /* canceled */, null /* exception */) {
+ Result = InvokeActionMethod(controllerContext, actionDescriptor, parameters)
+ };
+
+ // need to reverse the filter list because the continuations are built up backward
+ Func<ActionExecutedContext> thunk = filters.Reverse().Aggregate(continuation,
+ (next, filter) => () => InvokeActionMethodFilter(filter, preContext, next));
+ return thunk();
+ }
+
+ protected virtual void InvokeActionResult(ControllerContext controllerContext, ActionResult actionResult) {
+ actionResult.ExecuteResult(controllerContext);
+ }
+
+ internal static ResultExecutedContext InvokeActionResultFilter(IResultFilter filter, ResultExecutingContext preContext, Func<ResultExecutedContext> continuation) {
+ filter.OnResultExecuting(preContext);
+ if (preContext.Cancel) {
+ return new ResultExecutedContext(preContext, preContext.Result, true /* canceled */, null /* exception */);
+ }
+
+ bool wasError = false;
+ ResultExecutedContext postContext = null;
+ try {
+ postContext = continuation();
+ }
+ catch (ThreadAbortException) {
+ // This type of exception occurs as a result of Response.Redirect(), but we special-case so that
+ // the filters don't see this as an error.
+ postContext = new ResultExecutedContext(preContext, preContext.Result, false /* canceled */, null /* exception */);
+ filter.OnResultExecuted(postContext);
+ throw;
+ }
+ catch (Exception ex) {
+ wasError = true;
+ postContext = new ResultExecutedContext(preContext, preContext.Result, false /* canceled */, ex);
+ filter.OnResultExecuted(postContext);
+ if (!postContext.ExceptionHandled) {
+ throw;
+ }
+ }
+ if (!wasError) {
+ filter.OnResultExecuted(postContext);
+ }
+ return postContext;
+ }
+
+ protected virtual ResultExecutedContext InvokeActionResultWithFilters(ControllerContext controllerContext, IList<IResultFilter> filters, ActionResult actionResult) {
+ ResultExecutingContext preContext = new ResultExecutingContext(controllerContext, actionResult);
+ Func<ResultExecutedContext> continuation = delegate {
+ InvokeActionResult(controllerContext, actionResult);
+ return new ResultExecutedContext(controllerContext, actionResult, false /* canceled */, null /* exception */);
+ };
+
+ // need to reverse the filter list because the continuations are built up backward
+ Func<ResultExecutedContext> thunk = filters.Reverse().Aggregate(continuation,
+ (next, filter) => () => InvokeActionResultFilter(filter, preContext, next));
+ return thunk();
+ }
+
+ protected virtual AuthorizationContext InvokeAuthorizationFilters(ControllerContext controllerContext, IList<IAuthorizationFilter> filters, ActionDescriptor actionDescriptor) {
+ AuthorizationContext context = new AuthorizationContext(controllerContext, actionDescriptor);
+ foreach (IAuthorizationFilter filter in filters) {
+ filter.OnAuthorization(context);
+ // short-circuit evaluation
+ if (context.Result != null) {
+ break;
+ }
+ }
+
+ return context;
+ }
+
+ protected virtual ExceptionContext InvokeExceptionFilters(ControllerContext controllerContext, IList<IExceptionFilter> filters, Exception exception) {
+ ExceptionContext context = new ExceptionContext(controllerContext, exception);
+ foreach (IExceptionFilter filter in filters) {
+ filter.OnException(context);
+ }
+
+ return context;
+ }
+
+ [SuppressMessage("Microsoft.Performance", "CA1804:RemoveUnusedLocals", MessageId = "rawUrl",
+ Justification = "We only care about the property getter's side effects, not the returned value.")]
+ internal static void ValidateRequest(ControllerContext controllerContext) {
+ if (controllerContext.IsChildAction) {
+ return;
+ }
+
+ // DevDiv 214040: Enable Request Validation by default for all controller requests
+ //
+ // Note that we grab the Request's RawUrl to force it to be validated. Calling ValidateInput()
+ // doesn't actually validate anything. It just sets flags indicating that on the next usage of
+ // certain inputs that they should be validated. We special case RawUrl because the URL has already
+ // been consumed by routing and thus might contain dangerous data. By forcing the RawUrl to be
+ // re-read we're making sure that it gets validated by ASP.NET.
+
+ controllerContext.HttpContext.Request.ValidateInput();
+ string rawUrl = controllerContext.HttpContext.Request.RawUrl;
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ControllerBase.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ControllerBase.cs
new file mode 100644
index 00000000000..b5902bd3a66
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ControllerBase.cs
@@ -0,0 +1,116 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Globalization;
+ using System.Web.Mvc.Async;
+ using System.Web.Mvc.Resources;
+ using System.Web.Routing;
+
+ public abstract class ControllerBase : IController {
+
+ private readonly SingleEntryGate _executeWasCalledGate = new SingleEntryGate();
+
+ private TempDataDictionary _tempDataDictionary;
+ private bool _validateRequest = true;
+ private IValueProvider _valueProvider;
+ private ViewDataDictionary _viewDataDictionary;
+
+ public ControllerContext ControllerContext {
+ get;
+ set;
+ }
+
+ [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly",
+ Justification = "This property is settable so that unit tests can provide mock implementations.")]
+ public TempDataDictionary TempData {
+ get {
+ if (ControllerContext != null && ControllerContext.IsChildAction) {
+ return ControllerContext.ParentActionViewContext.TempData;
+ }
+ if (_tempDataDictionary == null) {
+ _tempDataDictionary = new TempDataDictionary();
+ }
+ return _tempDataDictionary;
+ }
+ set {
+ _tempDataDictionary = value;
+ }
+ }
+
+ public bool ValidateRequest {
+ get {
+ return _validateRequest;
+ }
+ set {
+ _validateRequest = value;
+ }
+ }
+
+ public IValueProvider ValueProvider {
+ get {
+ if (_valueProvider == null) {
+ _valueProvider = ValueProviderFactories.Factories.GetValueProvider(ControllerContext);
+ }
+ return _valueProvider;
+ }
+ set {
+ _valueProvider = value;
+ }
+ }
+
+ [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly",
+ Justification = "This property is settable so that unit tests can provide mock implementations.")]
+ public ViewDataDictionary ViewData {
+ get {
+ if (_viewDataDictionary == null) {
+ _viewDataDictionary = new ViewDataDictionary();
+ }
+ return _viewDataDictionary;
+ }
+ set {
+ _viewDataDictionary = value;
+ }
+ }
+
+ protected virtual void Execute(RequestContext requestContext) {
+ if (requestContext == null) {
+ throw new ArgumentNullException("requestContext");
+ }
+
+ VerifyExecuteCalledOnce();
+ Initialize(requestContext);
+ ExecuteCore();
+ }
+
+ protected abstract void ExecuteCore();
+
+ protected virtual void Initialize(RequestContext requestContext) {
+ ControllerContext = new ControllerContext(requestContext, this);
+ }
+
+ internal void VerifyExecuteCalledOnce() {
+ if (!_executeWasCalledGate.TryEnter()) {
+ string message = String.Format(CultureInfo.CurrentUICulture, MvcResources.ControllerBase_CannotHandleMultipleRequests, GetType());
+ throw new InvalidOperationException(message);
+ }
+ }
+
+ #region IController Members
+ void IController.Execute(RequestContext requestContext) {
+ Execute(requestContext);
+ }
+ #endregion
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ControllerBuilder.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ControllerBuilder.cs
new file mode 100644
index 00000000000..c86f740fd42
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ControllerBuilder.cs
@@ -0,0 +1,87 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Globalization;
+ using System.Web.Mvc.Resources;
+
+ public class ControllerBuilder {
+
+ private Func<IControllerFactory> _factoryThunk;
+ private static ControllerBuilder _instance = new ControllerBuilder();
+ private HashSet<string> _namespaces = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
+
+ public ControllerBuilder() {
+ SetControllerFactory(new DefaultControllerFactory() {
+ ControllerBuilder = this
+ });
+ }
+
+ public static ControllerBuilder Current {
+ get {
+ return _instance;
+ }
+ }
+
+ public HashSet<string> DefaultNamespaces {
+ get {
+ return _namespaces;
+ }
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate",
+ Justification = "Calling method multiple times might return different objects.")]
+ public IControllerFactory GetControllerFactory() {
+ IControllerFactory controllerFactoryInstance = _factoryThunk();
+ return controllerFactoryInstance;
+ }
+
+ public void SetControllerFactory(IControllerFactory controllerFactory) {
+ if (controllerFactory == null) {
+ throw new ArgumentNullException("controllerFactory");
+ }
+
+ _factoryThunk = () => controllerFactory;
+ }
+
+ public void SetControllerFactory(Type controllerFactoryType) {
+ if (controllerFactoryType == null) {
+ throw new ArgumentNullException("controllerFactoryType");
+ }
+ if (!typeof(IControllerFactory).IsAssignableFrom(controllerFactoryType)) {
+ throw new ArgumentException(
+ String.Format(
+ CultureInfo.CurrentUICulture,
+ MvcResources.ControllerBuilder_MissingIControllerFactory,
+ controllerFactoryType),
+ "controllerFactoryType");
+ }
+
+ _factoryThunk = delegate() {
+ try {
+ return (IControllerFactory)Activator.CreateInstance(controllerFactoryType);
+ }
+ catch (Exception ex) {
+ throw new InvalidOperationException(
+ String.Format(
+ CultureInfo.CurrentUICulture,
+ MvcResources.ControllerBuilder_ErrorCreatingControllerFactory,
+ controllerFactoryType),
+ ex);
+ }
+ };
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ControllerContext.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ControllerContext.cs
new file mode 100644
index 00000000000..22625019d48
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ControllerContext.cs
@@ -0,0 +1,134 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Web;
+ using System.Web.Routing;
+ using System.Web.Mvc.Html;
+
+ // Though many of the properties on ControllerContext and its subclassed types are virtual, there are still sealed
+ // properties (like ControllerContext.RequestContext, ActionExecutingContext.Result, etc.). If these properties
+ // were virtual, a mocking framework might override them with incorrect behavior (property getters would return
+ // null, property setters would be no-ops). By sealing these properties, we are forcing them to have the default
+ // "get or store a value" semantics that they were intended to have.
+
+ public class ControllerContext {
+
+ private HttpContextBase _httpContext;
+ private RequestContext _requestContext;
+ private RouteData _routeData;
+
+ internal const string PARENT_ACTION_VIEWCONTEXT = "ParentActionViewContext";
+
+ // parameterless constructor used for mocking
+ public ControllerContext() {
+ }
+
+ // copy constructor - allows for subclassed types to take an existing ControllerContext as a parameter
+ // and we'll automatically set the appropriate properties
+ [SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors",
+ Justification = "The virtual property setters are only to support mocking frameworks, in which case this constructor shouldn't be called anyway.")]
+ protected ControllerContext(ControllerContext controllerContext) {
+ if (controllerContext == null) {
+ throw new ArgumentNullException("controllerContext");
+ }
+
+ Controller = controllerContext.Controller;
+ RequestContext = controllerContext.RequestContext;
+ }
+
+ public ControllerContext(HttpContextBase httpContext, RouteData routeData, ControllerBase controller)
+ : this(new RequestContext(httpContext, routeData), controller) {
+ }
+
+ [SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors",
+ Justification = "The virtual property setters are only to support mocking frameworks, in which case this constructor shouldn't be called anyway.")]
+ public ControllerContext(RequestContext requestContext, ControllerBase controller) {
+ if (requestContext == null) {
+ throw new ArgumentNullException("requestContext");
+ }
+ if (controller == null) {
+ throw new ArgumentNullException("controller");
+ }
+
+ RequestContext = requestContext;
+ Controller = controller;
+ }
+
+ public virtual ControllerBase Controller {
+ get;
+ set;
+ }
+
+ public virtual HttpContextBase HttpContext {
+ get {
+ if (_httpContext == null) {
+ _httpContext = (_requestContext != null) ? _requestContext.HttpContext : new EmptyHttpContext();
+ }
+ return _httpContext;
+ }
+ set {
+ _httpContext = value;
+ }
+ }
+
+ public virtual bool IsChildAction {
+ get {
+ RouteData routeData = RouteData;
+ if (routeData == null) {
+ return false;
+ }
+ return routeData.DataTokens.ContainsKey(PARENT_ACTION_VIEWCONTEXT);
+ }
+ }
+
+ public ViewContext ParentActionViewContext {
+ get {
+ return RouteData.DataTokens[PARENT_ACTION_VIEWCONTEXT] as ViewContext;
+ }
+ }
+
+ public RequestContext RequestContext {
+ get {
+ if (_requestContext == null) {
+ // still need explicit calls to constructors since the property getters are virtual and might return null
+ HttpContextBase httpContext = HttpContext ?? new EmptyHttpContext();
+ RouteData routeData = RouteData ?? new RouteData();
+
+ _requestContext = new RequestContext(httpContext, routeData);
+ }
+ return _requestContext;
+ }
+ set {
+ _requestContext = value;
+ }
+ }
+
+ public virtual RouteData RouteData {
+ get {
+ if (_routeData == null) {
+ _routeData = (_requestContext != null) ? _requestContext.RouteData : new RouteData();
+ }
+ return _routeData;
+ }
+ set {
+ _routeData = value;
+ }
+ }
+
+ private sealed class EmptyHttpContext : HttpContextBase {
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ControllerDescriptor.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ControllerDescriptor.cs
new file mode 100644
index 00000000000..abb7c236c47
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ControllerDescriptor.cs
@@ -0,0 +1,59 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Reflection;
+
+ public abstract class ControllerDescriptor : ICustomAttributeProvider {
+
+ public virtual string ControllerName {
+ get {
+ string typeName = ControllerType.Name;
+ if (typeName.EndsWith("Controller", StringComparison.OrdinalIgnoreCase)) {
+ return typeName.Substring(0, typeName.Length - "Controller".Length);
+ }
+
+ return typeName;
+ }
+ }
+
+ public abstract Type ControllerType {
+ get;
+ }
+
+ public abstract ActionDescriptor FindAction(ControllerContext controllerContext, string actionName);
+
+ public abstract ActionDescriptor[] GetCanonicalActions();
+
+ public virtual object[] GetCustomAttributes(bool inherit) {
+ return GetCustomAttributes(typeof(object), inherit);
+ }
+
+ public virtual object[] GetCustomAttributes(Type attributeType, bool inherit) {
+ if (attributeType == null) {
+ throw new ArgumentNullException("attributeType");
+ }
+
+ return (object[])Array.CreateInstance(attributeType, 0);
+ }
+
+ public virtual bool IsDefined(Type attributeType, bool inherit) {
+ if (attributeType == null) {
+ throw new ArgumentNullException("attributeType");
+ }
+
+ return false;
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ControllerDescriptorCache.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ControllerDescriptorCache.cs
new file mode 100644
index 00000000000..43ed25ee0db
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ControllerDescriptorCache.cs
@@ -0,0 +1,26 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+
+ internal sealed class ControllerDescriptorCache : ReaderWriterCache<Type, ControllerDescriptor> {
+
+ public ControllerDescriptorCache() {
+ }
+
+ public ControllerDescriptor GetDescriptor(Type controllerType, Func<ControllerDescriptor> creator) {
+ return FetchOrCreateItem(controllerType, creator);
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ControllerTypeCache.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ControllerTypeCache.cs
new file mode 100644
index 00000000000..bea8005bb74
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ControllerTypeCache.cs
@@ -0,0 +1,127 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Collections;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Reflection;
+
+ internal sealed class ControllerTypeCache {
+
+ private const string _typeCacheName = "MVC-ControllerTypeCache.xml";
+
+ private Dictionary<string, ILookup<string, Type>> _cache;
+ private object _lockObj = new object();
+
+ internal int Count {
+ get {
+ int count = 0;
+ foreach (var lookup in _cache.Values) {
+ foreach (var grouping in lookup) {
+ count += grouping.Count();
+ }
+ }
+ return count;
+ }
+ }
+
+ public void EnsureInitialized(IBuildManager buildManager) {
+ if (_cache == null) {
+ lock (_lockObj) {
+ if (_cache == null) {
+ List<Type> controllerTypes = TypeCacheUtil.GetFilteredTypesFromAssemblies(_typeCacheName, IsControllerType, buildManager);
+ var groupedByName = controllerTypes.GroupBy(
+ t => t.Name.Substring(0, t.Name.Length - "Controller".Length),
+ StringComparer.OrdinalIgnoreCase);
+ _cache = groupedByName.ToDictionary(
+ g => g.Key,
+ g => g.ToLookup(t => t.Namespace ?? String.Empty, StringComparer.OrdinalIgnoreCase),
+ StringComparer.OrdinalIgnoreCase);
+ }
+ }
+ }
+ }
+
+ public ICollection<Type> GetControllerTypes(string controllerName, HashSet<string> namespaces) {
+ HashSet<Type> matchingTypes = new HashSet<Type>();
+
+ ILookup<string, Type> nsLookup;
+ if (_cache.TryGetValue(controllerName, out nsLookup)) {
+ // this friendly name was located in the cache, now cycle through namespaces
+ if (namespaces != null) {
+ foreach (string requestedNamespace in namespaces) {
+ foreach (var targetNamespaceGrouping in nsLookup) {
+ if (IsNamespaceMatch(requestedNamespace, targetNamespaceGrouping.Key)) {
+ matchingTypes.UnionWith(targetNamespaceGrouping);
+ }
+ }
+ }
+ }
+ else {
+ // if the namespaces parameter is null, search *every* namespace
+ foreach (var nsGroup in nsLookup) {
+ matchingTypes.UnionWith(nsGroup);
+ }
+ }
+ }
+
+ return matchingTypes;
+ }
+
+ internal static bool IsControllerType(Type t) {
+ return
+ t != null &&
+ t.IsPublic &&
+ t.Name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase) &&
+ !t.IsAbstract &&
+ typeof(IController).IsAssignableFrom(t);
+ }
+
+ internal static bool IsNamespaceMatch(string requestedNamespace, string targetNamespace) {
+ // degenerate cases
+ if (requestedNamespace == null) {
+ return false;
+ }
+ else if (requestedNamespace.Length == 0) {
+ return true;
+ }
+
+ if (!requestedNamespace.EndsWith(".*", StringComparison.OrdinalIgnoreCase)) {
+ // looking for exact namespace match
+ return String.Equals(requestedNamespace, targetNamespace, StringComparison.OrdinalIgnoreCase);
+ }
+ else {
+ // looking for exact or sub-namespace match
+ requestedNamespace = requestedNamespace.Substring(0, requestedNamespace.Length - ".*".Length);
+ if (!targetNamespace.StartsWith(requestedNamespace, StringComparison.OrdinalIgnoreCase)) {
+ return false;
+ }
+
+ if (requestedNamespace.Length == targetNamespace.Length) {
+ // exact match
+ return true;
+ }
+ else if (targetNamespace[requestedNamespace.Length] == '.') {
+ // good prefix match, e.g. requestedNamespace = "Foo.Bar" and targetNamespace = "Foo.Bar.Baz"
+ return true;
+ }
+ else {
+ // bad prefix match, e.g. requestedNamespace = "Foo.Bar" and targetNamespace = "Foo.Bar2"
+ return false;
+ }
+ }
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/CustomModelBinderAttribute.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/CustomModelBinderAttribute.cs
new file mode 100644
index 00000000000..c086781f1f3
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/CustomModelBinderAttribute.cs
@@ -0,0 +1,27 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Diagnostics.CodeAnalysis;
+
+ [AttributeUsage(ValidTargets, AllowMultiple = false, Inherited = false)]
+ public abstract class CustomModelBinderAttribute : Attribute {
+
+ internal const AttributeTargets ValidTargets = AttributeTargets.Class | AttributeTargets.Enum | AttributeTargets.Interface | AttributeTargets.Parameter | AttributeTargets.Struct;
+
+ [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate",
+ Justification = "This method can potentially perform a non-trivial amount of work.")]
+ public abstract IModelBinder GetBinder();
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/DataAnnotationsModelMetadata.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/DataAnnotationsModelMetadata.cs
new file mode 100644
index 00000000000..aba69faa9f7
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/DataAnnotationsModelMetadata.cs
@@ -0,0 +1,63 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.ComponentModel.DataAnnotations;
+ using System.Globalization;
+ using System.Reflection;
+ using System.Web.Mvc.Resources;
+
+ public class DataAnnotationsModelMetadata : ModelMetadata {
+ private DisplayColumnAttribute _displayColumnAttribute;
+
+ public DataAnnotationsModelMetadata(DataAnnotationsModelMetadataProvider provider, Type containerType,
+ Func<object> modelAccessor, Type modelType, string propertyName,
+ DisplayColumnAttribute displayColumnAttribute)
+ : base(provider, containerType, modelAccessor, modelType, propertyName) {
+ _displayColumnAttribute = displayColumnAttribute;
+ }
+
+ protected override string GetSimpleDisplayText() {
+ if (Model != null) {
+ if (_displayColumnAttribute != null && !String.IsNullOrEmpty(_displayColumnAttribute.DisplayColumn)) {
+ PropertyInfo displayColumnProperty = ModelType.GetProperty(_displayColumnAttribute.DisplayColumn, BindingFlags.Public | BindingFlags.IgnoreCase | BindingFlags.Instance);
+ ValidateDisplayColumnAttribute(_displayColumnAttribute, displayColumnProperty, ModelType);
+
+ object simpleDisplayTextValue = displayColumnProperty.GetValue(Model, new object[0]);
+ if (simpleDisplayTextValue != null) {
+ return simpleDisplayTextValue.ToString();
+ }
+ }
+ }
+
+ return base.GetSimpleDisplayText();
+ }
+
+ private static void ValidateDisplayColumnAttribute(DisplayColumnAttribute displayColumnAttribute, PropertyInfo displayColumnProperty, Type modelType) {
+ if (displayColumnProperty == null) {
+ throw new InvalidOperationException(
+ String.Format(
+ CultureInfo.CurrentCulture,
+ MvcResources.DataAnnotationsModelMetadataProvider_UnknownProperty,
+ modelType.FullName, displayColumnAttribute.DisplayColumn));
+ }
+ if (displayColumnProperty.GetGetMethod() == null) {
+ throw new InvalidOperationException(
+ String.Format(
+ CultureInfo.CurrentCulture,
+ MvcResources.DataAnnotationsModelMetadataProvider_UnreadableProperty,
+ modelType.FullName, displayColumnAttribute.DisplayColumn));
+ }
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/DataAnnotationsModelMetadataProvider.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/DataAnnotationsModelMetadataProvider.cs
new file mode 100644
index 00000000000..7dffa9b7f39
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/DataAnnotationsModelMetadataProvider.cs
@@ -0,0 +1,83 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Collections.Generic;
+ using System.ComponentModel;
+ using System.ComponentModel.DataAnnotations;
+ using System.Linq;
+
+ public class DataAnnotationsModelMetadataProvider : AssociatedMetadataProvider {
+ protected override ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName) {
+ List<Attribute> attributeList = new List<Attribute>(attributes);
+ DisplayColumnAttribute displayColumnAttribute = attributeList.OfType<DisplayColumnAttribute>().FirstOrDefault();
+ DataAnnotationsModelMetadata result = new DataAnnotationsModelMetadata(this, containerType, modelAccessor, modelType, propertyName, displayColumnAttribute);
+
+ // Do [HiddenInput] before [UIHint], so you can override the template hint
+ HiddenInputAttribute hiddenInputAttribute = attributeList.OfType<HiddenInputAttribute>().FirstOrDefault();
+ if (hiddenInputAttribute != null) {
+ result.TemplateHint = "HiddenInput";
+ result.HideSurroundingHtml = !hiddenInputAttribute.DisplayValue;
+ }
+
+ // We prefer [UIHint("...", PresentationLayer = "MVC")] but will fall back to [UIHint("...")]
+ IEnumerable<UIHintAttribute> uiHintAttributes = attributeList.OfType<UIHintAttribute>();
+ UIHintAttribute uiHintAttribute = uiHintAttributes.FirstOrDefault(a => String.Equals(a.PresentationLayer, "MVC", StringComparison.OrdinalIgnoreCase))
+ ?? uiHintAttributes.FirstOrDefault(a => String.IsNullOrEmpty(a.PresentationLayer));
+ if (uiHintAttribute != null) {
+ result.TemplateHint = uiHintAttribute.UIHint;
+ }
+
+ DataTypeAttribute dataTypeAttribute = attributeList.OfType<DataTypeAttribute>().FirstOrDefault();
+ if (dataTypeAttribute != null) {
+ result.DataTypeName = dataTypeAttribute.GetDataTypeName();
+ }
+
+ ReadOnlyAttribute readOnlyAttribute = attributeList.OfType<ReadOnlyAttribute>().FirstOrDefault();
+ if (readOnlyAttribute != null) {
+ result.IsReadOnly = readOnlyAttribute.IsReadOnly;
+ }
+
+ DisplayFormatAttribute displayFormatAttribute = attributeList.OfType<DisplayFormatAttribute>().FirstOrDefault();
+ if (displayFormatAttribute == null && dataTypeAttribute != null) {
+ displayFormatAttribute = dataTypeAttribute.DisplayFormat;
+ }
+ if (displayFormatAttribute != null) {
+ result.NullDisplayText = displayFormatAttribute.NullDisplayText;
+ result.DisplayFormatString = displayFormatAttribute.DataFormatString;
+ result.ConvertEmptyStringToNull = displayFormatAttribute.ConvertEmptyStringToNull;
+
+ if (displayFormatAttribute.ApplyFormatInEditMode) {
+ result.EditFormatString = displayFormatAttribute.DataFormatString;
+ }
+ }
+
+ ScaffoldColumnAttribute scaffoldColumnAttribute = attributeList.OfType<ScaffoldColumnAttribute>().FirstOrDefault();
+ if (scaffoldColumnAttribute != null) {
+ result.ShowForDisplay = result.ShowForEdit = scaffoldColumnAttribute.Scaffold;
+ }
+
+ DisplayNameAttribute displayNameAttribute = attributeList.OfType<DisplayNameAttribute>().FirstOrDefault();
+ if (displayNameAttribute != null) {
+ result.DisplayName = displayNameAttribute.DisplayName;
+ }
+
+ RequiredAttribute requiredAttribute = attributeList.OfType<RequiredAttribute>().FirstOrDefault();
+ if (requiredAttribute != null) {
+ result.IsRequired = true;
+ }
+
+ return result;
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/DataAnnotationsModelValidator.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/DataAnnotationsModelValidator.cs
new file mode 100644
index 00000000000..7b83c66225b
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/DataAnnotationsModelValidator.cs
@@ -0,0 +1,55 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Collections.Generic;
+ using System.ComponentModel.DataAnnotations;
+
+ public class DataAnnotationsModelValidator : ModelValidator {
+ public DataAnnotationsModelValidator(ModelMetadata metadata, ControllerContext context, ValidationAttribute attribute)
+ : base(metadata, context) {
+
+ if (attribute == null) {
+ throw new ArgumentNullException("attribute");
+ }
+
+ Attribute = attribute;
+ }
+
+ protected internal ValidationAttribute Attribute { get; private set; }
+
+ protected internal string ErrorMessage {
+ get {
+ return Attribute.FormatErrorMessage(Metadata.GetDisplayName());
+ }
+ }
+
+ public override bool IsRequired {
+ get {
+ return Attribute is RequiredAttribute;
+ }
+ }
+
+ internal static ModelValidator Create(ModelMetadata metadata, ControllerContext context, ValidationAttribute attribute) {
+ return new DataAnnotationsModelValidator(metadata, context, attribute);
+ }
+
+ public override IEnumerable<ModelValidationResult> Validate(object container) {
+ if (!Attribute.IsValid(Metadata.Model)) {
+ yield return new ModelValidationResult {
+ Message = ErrorMessage
+ };
+ }
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/DataAnnotationsModelValidatorProvider.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/DataAnnotationsModelValidatorProvider.cs
new file mode 100644
index 00000000000..3e0c97f9ba2
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/DataAnnotationsModelValidatorProvider.cs
@@ -0,0 +1,187 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Collections.Generic;
+ using System.ComponentModel.DataAnnotations;
+ using System.Globalization;
+ using System.Linq;
+ using System.Reflection;
+ using System.Threading;
+ using System.Web.Mvc.Resources;
+
+ public delegate ModelValidator DataAnnotationsModelValidationFactory(ModelMetadata metadata, ControllerContext context, ValidationAttribute attribute);
+
+ public class DataAnnotationsModelValidatorProvider : AssociatedValidatorProvider {
+ private static bool _addImplicitRequiredAttributeForValueTypes = true;
+ private static ReaderWriterLockSlim _adaptersLock = new ReaderWriterLockSlim();
+
+ internal static DataAnnotationsModelValidationFactory DefaultAttributeFactory = DataAnnotationsModelValidator.Create;
+ internal static Dictionary<Type, DataAnnotationsModelValidationFactory> AttributeFactories = new Dictionary<Type, DataAnnotationsModelValidationFactory>() {
+ {
+ typeof(RangeAttribute),
+ (metadata, context, attribute) => new RangeAttributeAdapter(metadata, context, (RangeAttribute)attribute)
+ },
+ {
+ typeof(RegularExpressionAttribute),
+ (metadata, context, attribute) => new RegularExpressionAttributeAdapter(metadata, context, (RegularExpressionAttribute)attribute)
+ },
+ {
+ typeof(RequiredAttribute),
+ (metadata, context, attribute) => new RequiredAttributeAdapter(metadata, context, (RequiredAttribute)attribute)
+ },
+ {
+ typeof(StringLengthAttribute),
+ (metadata, context, attribute) => new StringLengthAttributeAdapter(metadata, context, (StringLengthAttribute)attribute)
+ },
+ };
+
+ public static bool AddImplicitRequiredAttributeForValueTypes {
+ get {
+ return _addImplicitRequiredAttributeForValueTypes;
+ }
+ set {
+ _addImplicitRequiredAttributeForValueTypes = value;
+ }
+ }
+
+ protected override IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, ControllerContext context, IEnumerable<Attribute> attributes) {
+ _adaptersLock.EnterReadLock();
+
+ try {
+ List<ModelValidator> results = new List<ModelValidator>();
+
+ if (AddImplicitRequiredAttributeForValueTypes &&
+ metadata.IsRequired &&
+ !attributes.Any(a => a is RequiredAttribute)) {
+ attributes = attributes.Concat(new[] { new RequiredAttribute() });
+ }
+
+ foreach (ValidationAttribute attribute in attributes.OfType<ValidationAttribute>()) {
+ DataAnnotationsModelValidationFactory factory;
+ if (!AttributeFactories.TryGetValue(attribute.GetType(), out factory)) {
+ factory = DefaultAttributeFactory;
+ }
+ results.Add(factory(metadata, context, attribute));
+ }
+
+ return results;
+ }
+ finally {
+ _adaptersLock.ExitReadLock();
+ }
+ }
+
+ public static void RegisterAdapter(Type attributeType, Type adapterType) {
+ ValidateAttributeType(attributeType);
+ ValidateAdapterType(adapterType);
+ ConstructorInfo constructor = GetAdapterConstructor(attributeType, adapterType);
+
+ _adaptersLock.EnterWriteLock();
+
+ try {
+ AttributeFactories[attributeType] = (metadata, context, attribute) => (ModelValidator)constructor.Invoke(new object[] { metadata, context, attribute });
+ }
+ finally {
+ _adaptersLock.ExitWriteLock();
+ }
+ }
+
+ public static void RegisterAdapterFactory(Type attributeType, DataAnnotationsModelValidationFactory factory) {
+ ValidateAttributeType(attributeType);
+ ValidateFactory(factory);
+
+ _adaptersLock.EnterWriteLock();
+
+ try {
+ AttributeFactories[attributeType] = factory;
+ }
+ finally {
+ _adaptersLock.ExitWriteLock();
+ }
+ }
+
+ public static void RegisterDefaultAdapter(Type adapterType) {
+ ValidateAdapterType(adapterType);
+ ConstructorInfo constructor = GetAdapterConstructor(typeof(ValidationAttribute), adapterType);
+
+ DefaultAttributeFactory = (metadata, context, attribute) => (ModelValidator)constructor.Invoke(new object[] { metadata, context, attribute });
+ }
+
+ public static void RegisterDefaultAdapterFactory(DataAnnotationsModelValidationFactory factory) {
+ ValidateFactory(factory);
+
+ DefaultAttributeFactory = factory;
+ }
+
+ // Helpers
+
+ private static ConstructorInfo GetAdapterConstructor(Type attributeType, Type adapterType) {
+ ConstructorInfo constructor = adapterType.GetConstructor(new[] { typeof(ModelMetadata), typeof(ControllerContext), attributeType });
+ if (constructor == null) {
+ throw new ArgumentException(
+ String.Format(
+ CultureInfo.CurrentCulture,
+ MvcResources.DataAnnotationsModelValidatorProvider_ConstructorRequirements,
+ adapterType.FullName,
+ typeof(ModelMetadata).FullName,
+ typeof(ControllerContext).FullName,
+ attributeType.FullName
+ ),
+ "adapterType"
+ );
+ }
+
+ return constructor;
+ }
+
+ private static void ValidateAdapterType(Type adapterType) {
+ if (adapterType == null) {
+ throw new ArgumentNullException("adapterType");
+ }
+ if (!typeof(ModelValidator).IsAssignableFrom(adapterType)) {
+ throw new ArgumentException(
+ String.Format(
+ CultureInfo.CurrentCulture,
+ MvcResources.Common_TypeMustDriveFromType,
+ adapterType.FullName,
+ typeof(ModelValidator).FullName
+ ),
+ "adapterType"
+ );
+ }
+ }
+
+ private static void ValidateAttributeType(Type attributeType) {
+ if (attributeType == null) {
+ throw new ArgumentNullException("attributeType");
+ }
+ if (!typeof(ValidationAttribute).IsAssignableFrom(attributeType)) {
+ throw new ArgumentException(
+ String.Format(
+ CultureInfo.CurrentCulture,
+ MvcResources.Common_TypeMustDriveFromType,
+ attributeType.FullName,
+ typeof(ValidationAttribute).FullName
+ ),
+ "attributeType");
+ }
+ }
+
+ private static void ValidateFactory(DataAnnotationsModelValidationFactory factory) {
+ if (factory == null) {
+ throw new ArgumentNullException("factory");
+ }
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/DataAnnotationsModelValidator`1.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/DataAnnotationsModelValidator`1.cs
new file mode 100644
index 00000000000..d8d981279ca
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/DataAnnotationsModelValidator`1.cs
@@ -0,0 +1,26 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System.ComponentModel.DataAnnotations;
+
+ public class DataAnnotationsModelValidator<TAttribute> : DataAnnotationsModelValidator where TAttribute : ValidationAttribute {
+ public DataAnnotationsModelValidator(ModelMetadata metadata, ControllerContext context, TAttribute attribute)
+ : base(metadata, context, attribute) { }
+
+ protected new TAttribute Attribute {
+ get {
+ return (TAttribute)base.Attribute;
+ }
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/DataErrorInfoModelValidatorProvider.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/DataErrorInfoModelValidatorProvider.cs
new file mode 100644
index 00000000000..eb10e5a8edb
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/DataErrorInfoModelValidatorProvider.cs
@@ -0,0 +1,87 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Collections.Generic;
+ using System.ComponentModel;
+ using System.Linq;
+
+ public class DataErrorInfoModelValidatorProvider : ModelValidatorProvider {
+
+ public override IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, ControllerContext context) {
+ if (metadata == null) {
+ throw new ArgumentNullException("metadata");
+ }
+ if (context == null) {
+ throw new ArgumentNullException("context");
+ }
+
+ return GetValidatorsImpl(metadata, context);
+ }
+
+ private static IEnumerable<ModelValidator> GetValidatorsImpl(ModelMetadata metadata, ControllerContext context) {
+ // If the metadata describes a model that implements IDataErrorInfo, we should call its
+ // Error property at the appropriate time.
+ if (TypeImplementsIDataErrorInfo(metadata.ModelType)) {
+ yield return new DataErrorInfoClassModelValidator(metadata, context);
+ }
+
+ // If the metadata describes a property of a container that implements IDataErrorInfo,
+ // we should call its Item indexer at the appropriate time.
+ if (TypeImplementsIDataErrorInfo(metadata.ContainerType)) {
+ yield return new DataErrorInfoPropertyModelValidator(metadata, context);
+ }
+ }
+
+ private static bool TypeImplementsIDataErrorInfo(Type type) {
+ return typeof(IDataErrorInfo).IsAssignableFrom(type);
+ }
+
+ internal sealed class DataErrorInfoClassModelValidator : ModelValidator {
+ public DataErrorInfoClassModelValidator(ModelMetadata metadata, ControllerContext controllerContext)
+ : base(metadata, controllerContext) {
+ }
+ public override IEnumerable<ModelValidationResult> Validate(object container) {
+ IDataErrorInfo castModel = Metadata.Model as IDataErrorInfo;
+ if (castModel != null) {
+ string errorMessage = castModel.Error;
+ if (!String.IsNullOrEmpty(errorMessage)) {
+ return new ModelValidationResult[] {
+ new ModelValidationResult() { Message = errorMessage }
+ };
+ }
+ }
+ return Enumerable.Empty<ModelValidationResult>();
+ }
+ }
+
+ internal sealed class DataErrorInfoPropertyModelValidator : ModelValidator {
+ public DataErrorInfoPropertyModelValidator(ModelMetadata metadata, ControllerContext controllerContext)
+ : base(metadata, controllerContext) {
+ }
+ public override IEnumerable<ModelValidationResult> Validate(object container) {
+ IDataErrorInfo castContainer = container as IDataErrorInfo;
+ if (castContainer != null && !String.Equals(Metadata.PropertyName, "error", StringComparison.OrdinalIgnoreCase)) {
+ string errorMessage = castContainer[Metadata.PropertyName];
+ if (!String.IsNullOrEmpty(errorMessage)) {
+ return new ModelValidationResult[] {
+ new ModelValidationResult() { Message = errorMessage }
+ };
+ }
+ }
+ return Enumerable.Empty<ModelValidationResult>();
+ }
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/DefaultControllerFactory.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/DefaultControllerFactory.cs
new file mode 100644
index 00000000000..bb651892279
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/DefaultControllerFactory.cs
@@ -0,0 +1,187 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Collections;
+ using System.Collections.Generic;
+ using System.Globalization;
+ using System.Linq;
+ using System.Text;
+ using System.Web;
+ using System.Web.Mvc.Resources;
+ using System.Web.Routing;
+
+ public class DefaultControllerFactory : IControllerFactory {
+
+ private IBuildManager _buildManager;
+ private ControllerBuilder _controllerBuilder;
+ private ControllerTypeCache _instanceControllerTypeCache;
+ private static ControllerTypeCache _staticControllerTypeCache = new ControllerTypeCache();
+
+ internal IBuildManager BuildManager {
+ get {
+ if (_buildManager == null) {
+ _buildManager = new BuildManagerWrapper();
+ }
+ return _buildManager;
+ }
+ set {
+ _buildManager = value;
+ }
+ }
+
+ internal ControllerBuilder ControllerBuilder {
+ get {
+ return _controllerBuilder ?? ControllerBuilder.Current;
+ }
+ set {
+ _controllerBuilder = value;
+ }
+ }
+
+ internal ControllerTypeCache ControllerTypeCache {
+ get {
+ return _instanceControllerTypeCache ?? _staticControllerTypeCache;
+ }
+ set {
+ _instanceControllerTypeCache = value;
+ }
+ }
+
+ internal static InvalidOperationException CreateAmbiguousControllerException(RouteBase route, string controllerName, ICollection<Type> matchingTypes) {
+ // we need to generate an exception containing all the controller types
+ StringBuilder typeList = new StringBuilder();
+ foreach (Type matchedType in matchingTypes) {
+ typeList.AppendLine();
+ typeList.Append(matchedType.FullName);
+ }
+
+ string errorText;
+ Route castRoute = route as Route;
+ if (castRoute != null) {
+ errorText = String.Format(CultureInfo.CurrentUICulture, MvcResources.DefaultControllerFactory_ControllerNameAmbiguous_WithRouteUrl,
+ controllerName, castRoute.Url, typeList);
+ }
+ else {
+ errorText = String.Format(CultureInfo.CurrentUICulture, MvcResources.DefaultControllerFactory_ControllerNameAmbiguous_WithoutRouteUrl,
+ controllerName, typeList);
+ }
+
+ return new InvalidOperationException(errorText);
+ }
+
+ public virtual IController CreateController(RequestContext requestContext, string controllerName) {
+ if (requestContext == null) {
+ throw new ArgumentNullException("requestContext");
+ }
+ if (String.IsNullOrEmpty(controllerName)) {
+ throw new ArgumentException(MvcResources.Common_NullOrEmpty, "controllerName");
+ }
+ Type controllerType = GetControllerType(requestContext, controllerName);
+ IController controller = GetControllerInstance(requestContext, controllerType);
+ return controller;
+ }
+
+ protected internal virtual IController GetControllerInstance(RequestContext requestContext, Type controllerType) {
+ if (controllerType == null) {
+ throw new HttpException(404,
+ String.Format(
+ CultureInfo.CurrentUICulture,
+ MvcResources.DefaultControllerFactory_NoControllerFound,
+ requestContext.HttpContext.Request.Path));
+ }
+ if (!typeof(IController).IsAssignableFrom(controllerType)) {
+ throw new ArgumentException(
+ String.Format(
+ CultureInfo.CurrentUICulture,
+ MvcResources.DefaultControllerFactory_TypeDoesNotSubclassControllerBase,
+ controllerType),
+ "controllerType");
+ }
+ try {
+ return (IController)Activator.CreateInstance(controllerType);
+ }
+ catch (Exception ex) {
+ throw new InvalidOperationException(
+ String.Format(
+ CultureInfo.CurrentUICulture,
+ MvcResources.DefaultControllerFactory_ErrorCreatingController,
+ controllerType),
+ ex);
+ }
+ }
+
+ protected internal virtual Type GetControllerType(RequestContext requestContext, string controllerName) {
+ if (String.IsNullOrEmpty(controllerName)) {
+ throw new ArgumentException(MvcResources.Common_NullOrEmpty, "controllerName");
+ }
+
+ // first search in the current route's namespace collection
+ object routeNamespacesObj;
+ Type match;
+ if (requestContext != null && requestContext.RouteData.DataTokens.TryGetValue("Namespaces", out routeNamespacesObj)) {
+ IEnumerable<string> routeNamespaces = routeNamespacesObj as IEnumerable<string>;
+ if (routeNamespaces != null && routeNamespaces.Any()) {
+ HashSet<string> nsHash = new HashSet<string>(routeNamespaces, StringComparer.OrdinalIgnoreCase);
+ match = GetControllerTypeWithinNamespaces(requestContext.RouteData.Route, controllerName, nsHash);
+
+ // the UseNamespaceFallback key might not exist, in which case its value is implicitly "true"
+ if (match != null || false.Equals(requestContext.RouteData.DataTokens["UseNamespaceFallback"])) {
+ // got a match or the route requested we stop looking
+ return match;
+ }
+ }
+ }
+
+ // then search in the application's default namespace collection
+ if (ControllerBuilder.DefaultNamespaces.Count > 0) {
+ HashSet<string> nsDefaults = new HashSet<string>(ControllerBuilder.DefaultNamespaces, StringComparer.OrdinalIgnoreCase);
+ match = GetControllerTypeWithinNamespaces(requestContext.RouteData.Route, controllerName, nsDefaults);
+ if (match != null) {
+ return match;
+ }
+ }
+
+ // if all else fails, search every namespace
+ return GetControllerTypeWithinNamespaces(requestContext.RouteData.Route, controllerName, null /* namespaces */);
+ }
+
+ private Type GetControllerTypeWithinNamespaces(RouteBase route, string controllerName, HashSet<string> namespaces) {
+ // Once the master list of controllers has been created we can quickly index into it
+ ControllerTypeCache.EnsureInitialized(BuildManager);
+
+ ICollection<Type> matchingTypes = ControllerTypeCache.GetControllerTypes(controllerName, namespaces);
+ switch (matchingTypes.Count) {
+ case 0:
+ // no matching types
+ return null;
+
+ case 1:
+ // single matching type
+ return matchingTypes.First();
+
+ default:
+ // multiple matching types
+ throw CreateAmbiguousControllerException(route, controllerName, matchingTypes);
+ }
+ }
+
+ public virtual void ReleaseController(IController controller) {
+ IDisposable disposable = controller as IDisposable;
+ if (disposable != null) {
+ disposable.Dispose();
+ }
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/DefaultModelBinder.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/DefaultModelBinder.cs
new file mode 100644
index 00000000000..02507e6056e
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/DefaultModelBinder.cs
@@ -0,0 +1,709 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Collections;
+ using System.Collections.Generic;
+ using System.ComponentModel;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Globalization;
+ using System.Linq;
+ using System.Reflection;
+ using System.Runtime.CompilerServices;
+ using System.Web.Mvc.Resources;
+
+ public class DefaultModelBinder : IModelBinder {
+
+ private ModelBinderDictionary _binders;
+ private static string _resourceClassKey;
+
+ [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly",
+ Justification = "Property is settable so that the dictionary can be provided for unit testing purposes.")]
+ protected internal ModelBinderDictionary Binders {
+ get {
+ if (_binders == null) {
+ _binders = ModelBinders.Binders;
+ }
+ return _binders;
+ }
+ set {
+ _binders = value;
+ }
+ }
+
+ public static string ResourceClassKey {
+ get {
+ return _resourceClassKey ?? String.Empty;
+ }
+ set {
+ _resourceClassKey = value;
+ }
+ }
+
+ private static void AddValueRequiredMessageToModelState(ControllerContext controllerContext, ModelStateDictionary modelState, string modelStateKey, Type elementType, object value) {
+ if (value == null && !TypeHelpers.TypeAllowsNullValue(elementType) && modelState.IsValidField(modelStateKey)) {
+ modelState.AddModelError(modelStateKey, GetValueRequiredResource(controllerContext));
+ }
+ }
+
+ internal void BindComplexElementalModel(ControllerContext controllerContext, ModelBindingContext bindingContext, object model) {
+ // need to replace the property filter + model object and create an inner binding context
+ ModelBindingContext newBindingContext = CreateComplexElementalModelBindingContext(controllerContext, bindingContext, model);
+
+ // validation
+ if (OnModelUpdating(controllerContext, newBindingContext)) {
+ BindProperties(controllerContext, newBindingContext);
+ OnModelUpdated(controllerContext, newBindingContext);
+ }
+ }
+
+ internal object BindComplexModel(ControllerContext controllerContext, ModelBindingContext bindingContext) {
+ object model = bindingContext.Model;
+ Type modelType = bindingContext.ModelType;
+
+ // if we're being asked to create an array, create a list instead, then coerce to an array after the list is created
+ if (model == null && modelType.IsArray) {
+ Type elementType = modelType.GetElementType();
+ Type listType = typeof(List<>).MakeGenericType(elementType);
+ object collection = CreateModel(controllerContext, bindingContext, listType);
+
+ ModelBindingContext arrayBindingContext = new ModelBindingContext() {
+ ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => collection, listType),
+ ModelName = bindingContext.ModelName,
+ ModelState = bindingContext.ModelState,
+ PropertyFilter = bindingContext.PropertyFilter,
+ ValueProvider = bindingContext.ValueProvider
+ };
+ IList list = (IList)UpdateCollection(controllerContext, arrayBindingContext, elementType);
+
+ if (list == null) {
+ return null;
+ }
+
+ Array array = Array.CreateInstance(elementType, list.Count);
+ list.CopyTo(array, 0);
+ return array;
+ }
+
+ if (model == null) {
+ model = CreateModel(controllerContext, bindingContext, modelType);
+ }
+
+ // special-case IDictionary<,> and ICollection<>
+ Type dictionaryType = TypeHelpers.ExtractGenericInterface(modelType, typeof(IDictionary<,>));
+ if (dictionaryType != null) {
+ Type[] genericArguments = dictionaryType.GetGenericArguments();
+ Type keyType = genericArguments[0];
+ Type valueType = genericArguments[1];
+
+ ModelBindingContext dictionaryBindingContext = new ModelBindingContext() {
+ ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, modelType),
+ ModelName = bindingContext.ModelName,
+ ModelState = bindingContext.ModelState,
+ PropertyFilter = bindingContext.PropertyFilter,
+ ValueProvider = bindingContext.ValueProvider
+ };
+ object dictionary = UpdateDictionary(controllerContext, dictionaryBindingContext, keyType, valueType);
+ return dictionary;
+ }
+
+ Type enumerableType = TypeHelpers.ExtractGenericInterface(modelType, typeof(IEnumerable<>));
+ if (enumerableType != null) {
+ Type elementType = enumerableType.GetGenericArguments()[0];
+
+ Type collectionType = typeof(ICollection<>).MakeGenericType(elementType);
+ if (collectionType.IsInstanceOfType(model)) {
+ ModelBindingContext collectionBindingContext = new ModelBindingContext() {
+ ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, modelType),
+ ModelName = bindingContext.ModelName,
+ ModelState = bindingContext.ModelState,
+ PropertyFilter = bindingContext.PropertyFilter,
+ ValueProvider = bindingContext.ValueProvider
+ };
+ object collection = UpdateCollection(controllerContext, collectionBindingContext, elementType);
+ return collection;
+ }
+ }
+
+ // otherwise, just update the properties on the complex type
+ BindComplexElementalModel(controllerContext, bindingContext, model);
+ return model;
+ }
+
+ public virtual object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) {
+ if (bindingContext == null) {
+ throw new ArgumentNullException("bindingContext");
+ }
+
+ bool performedFallback = false;
+
+ if (!String.IsNullOrEmpty(bindingContext.ModelName) && !bindingContext.ValueProvider.ContainsPrefix(bindingContext.ModelName)) {
+ // We couldn't find any entry that began with the prefix. If this is the top-level element, fall back
+ // to the empty prefix.
+ if (bindingContext.FallbackToEmptyPrefix) {
+ bindingContext = new ModelBindingContext() {
+ ModelMetadata = bindingContext.ModelMetadata,
+ ModelState = bindingContext.ModelState,
+ PropertyFilter = bindingContext.PropertyFilter,
+ ValueProvider = bindingContext.ValueProvider
+ };
+ performedFallback = true;
+ }
+ else {
+ return null;
+ }
+ }
+
+ // Simple model = int, string, etc.; determined by calling TypeConverter.CanConvertFrom(typeof(string))
+ // or by seeing if a value in the request exactly matches the name of the model we're binding.
+ // Complex type = everything else.
+ if (!performedFallback) {
+ ValueProviderResult vpResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
+ if (vpResult != null) {
+ return BindSimpleModel(controllerContext, bindingContext, vpResult);
+ }
+ }
+ if (!bindingContext.ModelMetadata.IsComplexType) {
+ return null;
+ }
+
+ return BindComplexModel(controllerContext, bindingContext);
+ }
+
+ private void BindProperties(ControllerContext controllerContext, ModelBindingContext bindingContext) {
+ IEnumerable<PropertyDescriptor> properties = GetFilteredModelProperties(controllerContext, bindingContext);
+ foreach (PropertyDescriptor property in properties) {
+ BindProperty(controllerContext, bindingContext, property);
+ }
+ }
+
+ protected virtual void BindProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor) {
+ // need to skip properties that aren't part of the request, else we might hit a StackOverflowException
+ string fullPropertyKey = CreateSubPropertyName(bindingContext.ModelName, propertyDescriptor.Name);
+ if (!bindingContext.ValueProvider.ContainsPrefix(fullPropertyKey)) {
+ return;
+ }
+
+ // call into the property's model binder
+ IModelBinder propertyBinder = Binders.GetBinder(propertyDescriptor.PropertyType);
+ object originalPropertyValue = propertyDescriptor.GetValue(bindingContext.Model);
+ ModelMetadata propertyMetadata = bindingContext.PropertyMetadata[propertyDescriptor.Name];
+ propertyMetadata.Model = originalPropertyValue;
+ ModelBindingContext innerBindingContext = new ModelBindingContext() {
+ ModelMetadata = propertyMetadata,
+ ModelName = fullPropertyKey,
+ ModelState = bindingContext.ModelState,
+ ValueProvider = bindingContext.ValueProvider
+ };
+ object newPropertyValue = GetPropertyValue(controllerContext, innerBindingContext, propertyDescriptor, propertyBinder);
+ propertyMetadata.Model = newPropertyValue;
+
+ // validation
+ ModelState modelState = bindingContext.ModelState[fullPropertyKey];
+ if (modelState == null || modelState.Errors.Count == 0) {
+ if (OnPropertyValidating(controllerContext, bindingContext, propertyDescriptor, newPropertyValue)) {
+ SetProperty(controllerContext, bindingContext, propertyDescriptor, newPropertyValue);
+ OnPropertyValidated(controllerContext, bindingContext, propertyDescriptor, newPropertyValue);
+ }
+ }
+ else {
+ SetProperty(controllerContext, bindingContext, propertyDescriptor, newPropertyValue);
+
+ // Convert FormatExceptions (type conversion failures) into InvalidValue messages
+ foreach (ModelError error in modelState.Errors.Where(err => String.IsNullOrEmpty(err.ErrorMessage) && err.Exception != null).ToList()) {
+ for (Exception exception = error.Exception; exception != null; exception = exception.InnerException) {
+ if (exception is FormatException) {
+ string displayName = propertyMetadata.GetDisplayName();
+ string errorMessageTemplate = GetValueInvalidResource(controllerContext);
+ string errorMessage = String.Format(CultureInfo.CurrentUICulture, errorMessageTemplate, modelState.Value.AttemptedValue, displayName);
+ modelState.Errors.Remove(error);
+ modelState.Errors.Add(errorMessage);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ internal object BindSimpleModel(ControllerContext controllerContext, ModelBindingContext bindingContext, ValueProviderResult valueProviderResult) {
+ bindingContext.ModelState.SetModelValue(bindingContext.ModelName, valueProviderResult);
+
+ // if the value provider returns an instance of the requested data type, we can just short-circuit
+ // the evaluation and return that instance
+ if (bindingContext.ModelType.IsInstanceOfType(valueProviderResult.RawValue)) {
+ return valueProviderResult.RawValue;
+ }
+
+ // since a string is an IEnumerable<char>, we want it to skip the two checks immediately following
+ if (bindingContext.ModelType != typeof(string)) {
+
+ // conversion results in 3 cases, as below
+ if (bindingContext.ModelType.IsArray) {
+ // case 1: user asked for an array
+ // ValueProviderResult.ConvertTo() understands array types, so pass in the array type directly
+ object modelArray = ConvertProviderResult(bindingContext.ModelState, bindingContext.ModelName, valueProviderResult, bindingContext.ModelType);
+ return modelArray;
+ }
+
+ Type enumerableType = TypeHelpers.ExtractGenericInterface(bindingContext.ModelType, typeof(IEnumerable<>));
+ if (enumerableType != null) {
+ // case 2: user asked for a collection rather than an array
+ // need to call ConvertTo() on the array type, then copy the array to the collection
+ object modelCollection = CreateModel(controllerContext, bindingContext, bindingContext.ModelType);
+ Type elementType = enumerableType.GetGenericArguments()[0];
+ Type arrayType = elementType.MakeArrayType();
+ object modelArray = ConvertProviderResult(bindingContext.ModelState, bindingContext.ModelName, valueProviderResult, arrayType);
+
+ Type collectionType = typeof(ICollection<>).MakeGenericType(elementType);
+ if (collectionType.IsInstanceOfType(modelCollection)) {
+ CollectionHelpers.ReplaceCollection(elementType, modelCollection, modelArray);
+ }
+ return modelCollection;
+ }
+ }
+
+ // case 3: user asked for an individual element
+ object model = ConvertProviderResult(bindingContext.ModelState, bindingContext.ModelName, valueProviderResult, bindingContext.ModelType);
+ return model;
+ }
+
+ private static bool CanUpdateReadonlyTypedReference(Type type) {
+ // value types aren't strictly immutable, but because they have copy-by-value semantics
+ // we can't update a value type that is marked readonly
+ if (type.IsValueType) {
+ return false;
+ }
+
+ // arrays are mutable, but because we can't change their length we shouldn't try
+ // to update an array that is referenced readonly
+ if (type.IsArray) {
+ return false;
+ }
+
+ // special-case known common immutable types
+ if (type == typeof(string)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ [SuppressMessage("Microsoft.Globalization", "CA1304:SpecifyCultureInfo", MessageId = "System.Web.Mvc.ValueProviderResult.ConvertTo(System.Type)",
+ Justification = "The target object should make the correct culture determination, not this method.")]
+ [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes",
+ Justification = "We're recording this exception so that we can act on it later.")]
+ private static object ConvertProviderResult(ModelStateDictionary modelState, string modelStateKey, ValueProviderResult valueProviderResult, Type destinationType) {
+ try {
+ object convertedValue = valueProviderResult.ConvertTo(destinationType);
+ return convertedValue;
+ }
+ catch (Exception ex) {
+ modelState.AddModelError(modelStateKey, ex);
+ return null;
+ }
+ }
+
+ internal ModelBindingContext CreateComplexElementalModelBindingContext(ControllerContext controllerContext, ModelBindingContext bindingContext, object model) {
+ BindAttribute bindAttr = (BindAttribute)GetTypeDescriptor(controllerContext, bindingContext).GetAttributes()[typeof(BindAttribute)];
+ Predicate<string> newPropertyFilter = (bindAttr != null)
+ ? propertyName => bindAttr.IsPropertyAllowed(propertyName) && bindingContext.PropertyFilter(propertyName)
+ : bindingContext.PropertyFilter;
+
+ ModelBindingContext newBindingContext = new ModelBindingContext() {
+ ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, bindingContext.ModelType),
+ ModelName = bindingContext.ModelName,
+ ModelState = bindingContext.ModelState,
+ PropertyFilter = newPropertyFilter,
+ ValueProvider = bindingContext.ValueProvider
+ };
+
+ return newBindingContext;
+ }
+
+ protected virtual object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType) {
+ Type typeToCreate = modelType;
+
+ // we can understand some collection interfaces, e.g. IList<>, IDictionary<,>
+ if (modelType.IsGenericType) {
+ Type genericTypeDefinition = modelType.GetGenericTypeDefinition();
+ if (genericTypeDefinition == typeof(IDictionary<,>)) {
+ typeToCreate = typeof(Dictionary<,>).MakeGenericType(modelType.GetGenericArguments());
+ }
+ else if (genericTypeDefinition == typeof(IEnumerable<>) || genericTypeDefinition == typeof(ICollection<>) || genericTypeDefinition == typeof(IList<>)) {
+ typeToCreate = typeof(List<>).MakeGenericType(modelType.GetGenericArguments());
+ }
+ }
+
+ // fallback to the type's default constructor
+ return Activator.CreateInstance(typeToCreate);
+ }
+
+ protected static string CreateSubIndexName(string prefix, int index) {
+ return String.Format(CultureInfo.InvariantCulture, "{0}[{1}]", prefix, index);
+ }
+
+ protected static string CreateSubIndexName(string prefix, string index) {
+ return String.Format(CultureInfo.InvariantCulture, "{0}[{1}]", prefix, index);
+ }
+
+ protected internal static string CreateSubPropertyName(string prefix, string propertyName) {
+ if (String.IsNullOrEmpty(prefix)) {
+ return propertyName;
+ }
+ else if (String.IsNullOrEmpty(propertyName)) {
+ return prefix;
+ }
+ else {
+ return prefix + "." + propertyName;
+ }
+ }
+
+ protected IEnumerable<PropertyDescriptor> GetFilteredModelProperties(ControllerContext controllerContext, ModelBindingContext bindingContext) {
+ PropertyDescriptorCollection properties = GetModelProperties(controllerContext, bindingContext);
+ Predicate<string> propertyFilter = bindingContext.PropertyFilter;
+
+ return from PropertyDescriptor property in properties
+ where ShouldUpdateProperty(property, propertyFilter)
+ select property;
+ }
+
+ [SuppressMessage("Microsoft.Globalization", "CA1304:SpecifyCultureInfo", MessageId = "System.Web.Mvc.ValueProviderResult.ConvertTo(System.Type)",
+ Justification = "ValueProviderResult already handles culture conversion appropriately.")]
+ private static void GetIndexes(ModelBindingContext bindingContext, out bool stopOnIndexNotFound, out IEnumerable<string> indexes) {
+ string indexKey = CreateSubPropertyName(bindingContext.ModelName, "index");
+ ValueProviderResult vpResult = bindingContext.ValueProvider.GetValue(indexKey);
+
+ if (vpResult != null) {
+ string[] indexesArray = vpResult.ConvertTo(typeof(string[])) as string[];
+ if (indexesArray != null) {
+ stopOnIndexNotFound = false;
+ indexes = indexesArray;
+ return;
+ }
+ }
+
+ // just use a simple zero-based system
+ stopOnIndexNotFound = true;
+ indexes = GetZeroBasedIndexes();
+ }
+
+ protected virtual PropertyDescriptorCollection GetModelProperties(ControllerContext controllerContext, ModelBindingContext bindingContext) {
+ return GetTypeDescriptor(controllerContext, bindingContext).GetProperties();
+ }
+
+ protected virtual object GetPropertyValue(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor, IModelBinder propertyBinder) {
+ object value = propertyBinder.BindModel(controllerContext, bindingContext);
+
+ if (bindingContext.ModelMetadata.ConvertEmptyStringToNull && Object.Equals(value, String.Empty)) {
+ return null;
+ }
+
+ return value;
+ }
+
+ protected virtual ICustomTypeDescriptor GetTypeDescriptor(ControllerContext controllerContext, ModelBindingContext bindingContext) {
+ return TypeDescriptorHelper.Get(bindingContext.ModelType);
+ }
+
+ // If the user specified a ResourceClassKey try to load the resource they specified.
+ // If the class key is invalid, an exception will be thrown.
+ // If the class key is valid but the resource is not found, it returns null, in which
+ // case it will fall back to the MVC default error message.
+ private static string GetUserResourceString(ControllerContext controllerContext, string resourceName) {
+ string result = null;
+
+ if (!String.IsNullOrEmpty(ResourceClassKey) && (controllerContext != null) && (controllerContext.HttpContext != null)) {
+ result = controllerContext.HttpContext.GetGlobalResourceObject(ResourceClassKey, resourceName, CultureInfo.CurrentUICulture) as string;
+ }
+
+ return result;
+ }
+
+ private static string GetValueInvalidResource(ControllerContext controllerContext) {
+ return GetUserResourceString(controllerContext, "PropertyValueInvalid") ?? MvcResources.DefaultModelBinder_ValueInvalid;
+ }
+
+ private static string GetValueRequiredResource(ControllerContext controllerContext) {
+ return GetUserResourceString(controllerContext, "PropertyValueRequired") ?? MvcResources.DefaultModelBinder_ValueRequired;
+ }
+
+ private static IEnumerable<string> GetZeroBasedIndexes() {
+ for (int i = 0; ; i++) {
+ yield return i.ToString(CultureInfo.InvariantCulture);
+ }
+ }
+
+ protected static bool IsModelValid(ModelBindingContext bindingContext) {
+ if (bindingContext == null) {
+ throw new ArgumentNullException("bindingContext");
+ }
+ if (String.IsNullOrEmpty(bindingContext.ModelName)) {
+ return bindingContext.ModelState.IsValid;
+ }
+ return bindingContext.ModelState.IsValidField(bindingContext.ModelName);
+ }
+
+ protected virtual void OnModelUpdated(ControllerContext controllerContext, ModelBindingContext bindingContext) {
+ Dictionary<string, bool> startedValid = new Dictionary<string, bool>(StringComparer.OrdinalIgnoreCase);
+
+ foreach (ModelValidationResult validationResult in ModelValidator.GetModelValidator(bindingContext.ModelMetadata, controllerContext).Validate(null)) {
+ string subPropertyName = CreateSubPropertyName(bindingContext.ModelName, validationResult.MemberName);
+
+ if (!startedValid.ContainsKey(subPropertyName)) {
+ startedValid[subPropertyName] = bindingContext.ModelState.IsValidField(subPropertyName);
+ }
+
+ if (startedValid[subPropertyName]) {
+ bindingContext.ModelState.AddModelError(subPropertyName, validationResult.Message);
+ }
+ }
+ }
+
+ protected virtual bool OnModelUpdating(ControllerContext controllerContext, ModelBindingContext bindingContext) {
+ // default implementation does nothing
+ return true;
+ }
+
+ protected virtual void OnPropertyValidated(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor, object value) {
+ // default implementation does nothing
+ }
+
+ protected virtual bool OnPropertyValidating(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor, object value) {
+ // default implementation does nothing
+ return true;
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes",
+ Justification = "We're recording this exception so that we can act on it later.")]
+ protected virtual void SetProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor, object value) {
+
+ ModelMetadata propertyMetadata = bindingContext.PropertyMetadata[propertyDescriptor.Name];
+ propertyMetadata.Model = value;
+ string modelStateKey = CreateSubPropertyName(bindingContext.ModelName, propertyMetadata.PropertyName);
+
+ // If the value is null, and the validation system can find a Required validator for
+ // us, we'd prefer to run it before we attempt to set the value; otherwise, property
+ // setters which throw on null (f.e., Entity Framework properties which are backed by
+ // non-nullable strings in the DB) will get their error message in ahead of us.
+ //
+ // We are effectively using the special validator -- Required -- as a helper to the
+ // binding system, which is why this code is here instead of in the Validating/Validated
+ // methods, which are really the old-school validation hooks.
+ if (value == null && bindingContext.ModelState.IsValidField(modelStateKey)) {
+ ModelValidator requiredValidator = ModelValidatorProviders.Providers.GetValidators(propertyMetadata, controllerContext).Where(v => v.IsRequired).FirstOrDefault();
+ if (requiredValidator != null) {
+ foreach (ModelValidationResult validationResult in requiredValidator.Validate(bindingContext.Model)) {
+ bindingContext.ModelState.AddModelError(modelStateKey, validationResult.Message);
+ }
+ }
+ }
+
+ bool isNullValueOnNonNullableType =
+ value == null &&
+ !TypeHelpers.TypeAllowsNullValue(propertyDescriptor.PropertyType);
+
+ // Try to set a value into the property unless we know it will fail (read-only
+ // properties and null values with non-nullable types)
+ if (!propertyDescriptor.IsReadOnly && !isNullValueOnNonNullableType) {
+ try {
+ propertyDescriptor.SetValue(bindingContext.Model, value);
+ }
+ catch (Exception ex) {
+ // Only add if we're not already invalid
+ if (bindingContext.ModelState.IsValidField(modelStateKey)) {
+ bindingContext.ModelState.AddModelError(modelStateKey, ex);
+ }
+ }
+ }
+
+ // Last chance for an error on null values with non-nullable types, we'll use
+ // the default "A value is required." message.
+ if (isNullValueOnNonNullableType && bindingContext.ModelState.IsValidField(modelStateKey)) {
+ bindingContext.ModelState.AddModelError(modelStateKey, GetValueRequiredResource(controllerContext));
+ }
+ }
+
+ private static bool ShouldUpdateProperty(PropertyDescriptor property, Predicate<string> propertyFilter) {
+ if (property.IsReadOnly && !CanUpdateReadonlyTypedReference(property.PropertyType)) {
+ return false;
+ }
+
+ // if this property is rejected by the filter, move on
+ if (!propertyFilter(property.Name)) {
+ return false;
+ }
+
+ // otherwise, allow
+ return true;
+ }
+
+ internal object UpdateCollection(ControllerContext controllerContext, ModelBindingContext bindingContext, Type elementType) {
+ bool stopOnIndexNotFound;
+ IEnumerable<string> indexes;
+ GetIndexes(bindingContext, out stopOnIndexNotFound, out indexes);
+ IModelBinder elementBinder = Binders.GetBinder(elementType);
+
+ // build up a list of items from the request
+ List<object> modelList = new List<object>();
+ foreach (string currentIndex in indexes) {
+ string subIndexKey = CreateSubIndexName(bindingContext.ModelName, currentIndex);
+ if (!bindingContext.ValueProvider.ContainsPrefix(subIndexKey)) {
+ if (stopOnIndexNotFound) {
+ // we ran out of elements to pull
+ break;
+ }
+ else {
+ continue;
+ }
+ }
+
+ ModelBindingContext innerContext = new ModelBindingContext() {
+ ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, elementType),
+ ModelName = subIndexKey,
+ ModelState = bindingContext.ModelState,
+ PropertyFilter = bindingContext.PropertyFilter,
+ ValueProvider = bindingContext.ValueProvider
+ };
+ object thisElement = elementBinder.BindModel(controllerContext, innerContext);
+
+ // we need to merge model errors up
+ AddValueRequiredMessageToModelState(controllerContext, bindingContext.ModelState, subIndexKey, elementType, thisElement);
+ modelList.Add(thisElement);
+ }
+
+ // if there weren't any elements at all in the request, just return
+ if (modelList.Count == 0) {
+ return null;
+ }
+
+ // replace the original collection
+ object collection = bindingContext.Model;
+ CollectionHelpers.ReplaceCollection(elementType, collection, modelList);
+ return collection;
+ }
+
+ internal object UpdateDictionary(ControllerContext controllerContext, ModelBindingContext bindingContext, Type keyType, Type valueType) {
+ bool stopOnIndexNotFound;
+ IEnumerable<string> indexes;
+ GetIndexes(bindingContext, out stopOnIndexNotFound, out indexes);
+
+ IModelBinder keyBinder = Binders.GetBinder(keyType);
+ IModelBinder valueBinder = Binders.GetBinder(valueType);
+
+ // build up a list of items from the request
+ List<KeyValuePair<object, object>> modelList = new List<KeyValuePair<object, object>>();
+ foreach (string currentIndex in indexes) {
+ string subIndexKey = CreateSubIndexName(bindingContext.ModelName, currentIndex);
+ string keyFieldKey = CreateSubPropertyName(subIndexKey, "key");
+ string valueFieldKey = CreateSubPropertyName(subIndexKey, "value");
+
+ if (!(bindingContext.ValueProvider.ContainsPrefix(keyFieldKey) && bindingContext.ValueProvider.ContainsPrefix(valueFieldKey))) {
+ if (stopOnIndexNotFound) {
+ // we ran out of elements to pull
+ break;
+ }
+ else {
+ continue;
+ }
+ }
+
+ // bind the key
+ ModelBindingContext keyBindingContext = new ModelBindingContext() {
+ ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, keyType),
+ ModelName = keyFieldKey,
+ ModelState = bindingContext.ModelState,
+ ValueProvider = bindingContext.ValueProvider
+ };
+ object thisKey = keyBinder.BindModel(controllerContext, keyBindingContext);
+
+ // we need to merge model errors up
+ AddValueRequiredMessageToModelState(controllerContext, bindingContext.ModelState, keyFieldKey, keyType, thisKey);
+ if (!keyType.IsInstanceOfType(thisKey)) {
+ // we can't add an invalid key, so just move on
+ continue;
+ }
+
+ // bind the value
+ ModelBindingContext valueBindingContext = new ModelBindingContext() {
+ ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, valueType),
+ ModelName = valueFieldKey,
+ ModelState = bindingContext.ModelState,
+ PropertyFilter = bindingContext.PropertyFilter,
+ ValueProvider = bindingContext.ValueProvider
+ };
+ object thisValue = valueBinder.BindModel(controllerContext, valueBindingContext);
+
+ // we need to merge model errors up
+ AddValueRequiredMessageToModelState(controllerContext, bindingContext.ModelState, valueFieldKey, valueType, thisValue);
+ KeyValuePair<object, object> kvp = new KeyValuePair<object, object>(thisKey, thisValue);
+ modelList.Add(kvp);
+ }
+
+ // if there weren't any elements at all in the request, just return
+ if (modelList.Count == 0) {
+ return null;
+ }
+
+ // replace the original collection
+ object dictionary = bindingContext.Model;
+ CollectionHelpers.ReplaceDictionary(keyType, valueType, dictionary, modelList);
+ return dictionary;
+ }
+
+ // This helper type is used because we're working with strongly-typed collections, but we don't know the Ts
+ // ahead of time. By using the generic methods below, we can consolidate the collection-specific code in a
+ // single helper type rather than having reflection-based calls spread throughout the DefaultModelBinder type.
+ // There is a single point of entry to each of the methods below, so they're fairly simple to maintain.
+
+ private static class CollectionHelpers {
+
+ private static readonly MethodInfo _replaceCollectionMethod = typeof(CollectionHelpers).GetMethod("ReplaceCollectionImpl", BindingFlags.Static | BindingFlags.NonPublic);
+ private static readonly MethodInfo _replaceDictionaryMethod = typeof(CollectionHelpers).GetMethod("ReplaceDictionaryImpl", BindingFlags.Static | BindingFlags.NonPublic);
+
+ [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
+ public static void ReplaceCollection(Type collectionType, object collection, object newContents) {
+ MethodInfo targetMethod = _replaceCollectionMethod.MakeGenericMethod(collectionType);
+ targetMethod.Invoke(null, new object[] { collection, newContents });
+ }
+
+ private static void ReplaceCollectionImpl<T>(ICollection<T> collection, IEnumerable newContents) {
+ collection.Clear();
+ if (newContents != null) {
+ foreach (object item in newContents) {
+ // if the item was not a T, some conversion failed. the error message will be propagated,
+ // but in the meanwhile we need to make a placeholder element in the array.
+ T castItem = (item is T) ? (T)item : default(T);
+ collection.Add(castItem);
+ }
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
+ public static void ReplaceDictionary(Type keyType, Type valueType, object dictionary, object newContents) {
+ MethodInfo targetMethod = _replaceDictionaryMethod.MakeGenericMethod(keyType, valueType);
+ targetMethod.Invoke(null, new object[] { dictionary, newContents });
+ }
+
+ private static void ReplaceDictionaryImpl<TKey, TValue>(IDictionary<TKey, TValue> dictionary, IEnumerable<KeyValuePair<object, object>> newContents) {
+ dictionary.Clear();
+ foreach (KeyValuePair<object, object> item in newContents) {
+ // if the item was not a T, some conversion failed. the error message will be propagated,
+ // but in the meanwhile we need to make a placeholder element in the dictionary.
+ TKey castKey = (TKey)item.Key; // this cast shouldn't fail
+ TValue castValue = (item.Value is TValue) ? (TValue)item.Value : default(TValue);
+ dictionary[castKey] = castValue;
+ }
+ }
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/DefaultViewLocationCache.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/DefaultViewLocationCache.cs
new file mode 100644
index 00000000000..f0479f42123
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/DefaultViewLocationCache.cs
@@ -0,0 +1,60 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Web.Caching;
+ using System.Web.Mvc.Resources;
+
+ [SuppressMessage("Microsoft.Security", "CA2112:SecuredTypesShouldNotExposeFields",
+ Justification = "The Null field does not have access to secure information")]
+ public class DefaultViewLocationCache : IViewLocationCache {
+ private static readonly TimeSpan _defaultTimeSpan = new TimeSpan(0, 15, 0);
+
+ public DefaultViewLocationCache()
+ : this(_defaultTimeSpan) {
+ }
+
+ public DefaultViewLocationCache(TimeSpan timeSpan) {
+ if (timeSpan.Ticks < 0) {
+ throw new InvalidOperationException(MvcResources.DefaultViewLocationCache_NegativeTimeSpan);
+ }
+ TimeSpan = timeSpan;
+ }
+
+ [SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes",
+ Justification = "The reference type is immutable. ")]
+ public static readonly IViewLocationCache Null = new NullViewLocationCache();
+
+ public TimeSpan TimeSpan {
+ get;
+ private set;
+ }
+
+ #region IViewLocationCache Members
+ public string GetViewLocation(HttpContextBase httpContext, string key) {
+ if (httpContext == null) {
+ throw new ArgumentNullException("httpContext");
+ }
+ return (string)httpContext.Cache[key];
+ }
+
+ public void InsertViewLocation(HttpContextBase httpContext, string key, string virtualPath) {
+ if (httpContext == null) {
+ throw new ArgumentNullException("httpContext");
+ }
+ httpContext.Cache.Insert(key, virtualPath, null /* dependencies */, Cache.NoAbsoluteExpiration, TimeSpan);
+ }
+ #endregion
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/DescriptorUtil.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/DescriptorUtil.cs
new file mode 100644
index 00000000000..a19c90f89f3
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/DescriptorUtil.cs
@@ -0,0 +1,34 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Linq;
+ using System.Threading;
+
+ internal static class DescriptorUtil {
+
+ public static TDescriptor[] LazilyFetchOrCreateDescriptors<TReflection, TDescriptor>(ref TDescriptor[] cacheLocation, Func<TReflection[]> initializer, Func<TReflection, TDescriptor> converter) {
+ // did we already calculate this once?
+ TDescriptor[] existingCache = Interlocked.CompareExchange(ref cacheLocation, null, null);
+ if (existingCache != null) {
+ return existingCache;
+ }
+
+ TReflection[] memberInfos = initializer();
+ TDescriptor[] descriptors = memberInfos.Select(converter).Where(descriptor => descriptor != null).ToArray();
+ TDescriptor[] updatedCache = Interlocked.CompareExchange(ref cacheLocation, descriptors, null);
+ return updatedCache ?? descriptors;
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/DictionaryHelpers.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/DictionaryHelpers.cs
new file mode 100644
index 00000000000..7eb761f55fe
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/DictionaryHelpers.cs
@@ -0,0 +1,52 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+
+ internal static class DictionaryHelpers {
+
+ public static IEnumerable<KeyValuePair<string, TValue>> FindKeysWithPrefix<TValue>(IDictionary<string, TValue> dictionary, string prefix) {
+ TValue exactMatchValue;
+ if (dictionary.TryGetValue(prefix, out exactMatchValue)) {
+ yield return new KeyValuePair<string, TValue>(prefix, exactMatchValue);
+ }
+
+ foreach (var entry in dictionary) {
+ string key = entry.Key;
+
+ if (key.Length <= prefix.Length) {
+ continue;
+ }
+
+ if (!key.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) {
+ continue;
+ }
+
+ char charAfterPrefix = key[prefix.Length];
+ switch (charAfterPrefix) {
+ case '[':
+ case '.':
+ yield return entry;
+ break;
+ }
+ }
+ }
+
+ public static bool DoesAnyKeyHavePrefix<TValue>(IDictionary<string, TValue> dictionary, string prefix) {
+ return FindKeysWithPrefix(dictionary, prefix).Any();
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/DictionaryValueProvider`1.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/DictionaryValueProvider`1.cs
new file mode 100644
index 00000000000..b1e8845da46
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/DictionaryValueProvider`1.cs
@@ -0,0 +1,64 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Collections.Generic;
+ using System.Globalization;
+
+ public class DictionaryValueProvider<TValue> : IValueProvider {
+
+ private readonly HashSet<string> _prefixes = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
+ private readonly Dictionary<string, ValueProviderResult> _values = new Dictionary<string, ValueProviderResult>(StringComparer.OrdinalIgnoreCase);
+
+ public DictionaryValueProvider(IDictionary<string, TValue> dictionary, CultureInfo culture) {
+ if (dictionary == null) {
+ throw new ArgumentNullException("dictionary");
+ }
+
+ AddValues(dictionary, culture);
+ }
+
+ private void AddValues(IDictionary<string, TValue> dictionary, CultureInfo culture) {
+ if (dictionary.Count > 0) {
+ _prefixes.Add("");
+ }
+
+ foreach (var entry in dictionary) {
+ _prefixes.UnionWith(ValueProviderUtil.GetPrefixes(entry.Key));
+
+ object rawValue = entry.Value;
+ string attemptedValue = Convert.ToString(rawValue, culture);
+ _values[entry.Key] = new ValueProviderResult(rawValue, attemptedValue, culture);
+ }
+ }
+
+ public virtual bool ContainsPrefix(string prefix) {
+ if (prefix == null) {
+ throw new ArgumentNullException("prefix");
+ }
+
+ return _prefixes.Contains(prefix);
+ }
+
+ public virtual ValueProviderResult GetValue(string key) {
+ if (key == null) {
+ throw new ArgumentNullException("key");
+ }
+
+ ValueProviderResult vpResult;
+ _values.TryGetValue(key, out vpResult);
+ return vpResult;
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/DynamicTypeGenerator.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/DynamicTypeGenerator.cs
new file mode 100644
index 00000000000..eefc9f87b07
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/DynamicTypeGenerator.cs
@@ -0,0 +1,125 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Reflection;
+ using System.Reflection.Emit;
+ using System.Security;
+
+ internal static class DynamicTypeGenerator {
+
+ private static readonly ModuleBuilder _dynamicModule = CreateDynamicModule();
+
+ private static ModuleBuilder CreateDynamicModule() {
+ // DDB 226615 - since MVC is [SecurityTransparent], the dynamic assembly must declare itself likewise
+ CustomAttributeBuilder builder = new CustomAttributeBuilder(
+ typeof(SecurityTransparentAttribute).GetConstructor(Type.EmptyTypes), new object[0]);
+ CustomAttributeBuilder[] assemblyAttributes = new CustomAttributeBuilder[] { builder };
+ AssemblyBuilder dynamicAssembly = AppDomain.CurrentDomain.DefineDynamicAssembly(
+ new AssemblyName("System.Web.Mvc.{Dynamic}"), AssemblyBuilderAccess.Run, assemblyAttributes);
+ ModuleBuilder dynamicModule = dynamicAssembly.DefineDynamicModule("System.Web.Mvc.{Dynamic}.dll");
+ return dynamicModule;
+ }
+
+ // Creates a new dynamic type that is a subclassed type of baseType and also implements methods of the specified
+ // interfaces. The base type must already have method signatures that implicitly implement the given
+ // interfaces. The signatures of all public (e.g. not private / internal) constructors from the baseType
+ // will be duplicated for the subclassed type and the new constructors made public.
+ public static Type GenerateType(string dynamicTypeName, Type baseType, IEnumerable<Type> interfaceTypes) {
+ TypeBuilder newType = _dynamicModule.DefineType(
+ "System.Web.Mvc.{Dynamic}." + dynamicTypeName,
+ TypeAttributes.AutoLayout | TypeAttributes.Public | TypeAttributes.Class,
+ baseType);
+
+ foreach (Type interfaceType in interfaceTypes) {
+ newType.AddInterfaceImplementation(interfaceType);
+ foreach (MethodInfo interfaceMethod in interfaceType.GetMethods()) {
+ ImplementInterfaceMethod(newType, interfaceMethod);
+ }
+ }
+
+ // generate new constructors for each accessible base constructor
+ foreach (ConstructorInfo ctor in baseType.GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)) {
+ switch (ctor.Attributes & MethodAttributes.MemberAccessMask) {
+ case MethodAttributes.Family:
+ case MethodAttributes.Public:
+ case MethodAttributes.FamORAssem:
+ ImplementConstructor(newType, ctor);
+ break;
+ }
+ }
+
+ Type bakedType = newType.CreateType();
+ return bakedType;
+ }
+
+ // generates this constructor:
+ // public NewType(param0, param1, ...) : base(param0, param1, ...) { }
+ private static void ImplementConstructor(TypeBuilder newType, ConstructorInfo baseCtor) {
+ ParameterInfo[] parameters = baseCtor.GetParameters();
+ Type[] parameterTypes = (from p in parameters select p.ParameterType).ToArray();
+
+ ConstructorBuilder newCtor = newType.DefineConstructor(
+ (baseCtor.Attributes & ~MethodAttributes.MemberAccessMask) | MethodAttributes.Public /* force public constructor */,
+ baseCtor.CallingConvention, parameterTypes);
+
+ // parameter 0 is 'this', so we start at index 1
+ for (int i = 0; i < parameters.Length; i++) {
+ newCtor.DefineParameter(i + 1, parameters[i].Attributes, parameters[i].Name);
+ }
+
+ // load all arguments (including 'this') in proper order, then call and return
+ ILGenerator ilGen = newCtor.GetILGenerator();
+ for (int i = 0; i <= parameterTypes.Length; i++) {
+ ilGen.Emit(OpCodes.Ldarg_S, (byte)i);
+ }
+ ilGen.Emit(OpCodes.Call, baseCtor);
+ ilGen.Emit(OpCodes.Ret);
+ }
+
+ // generates this explicit interface method:
+ // public new Interface.Method(param0, param1, ...) {
+ // return base.Method(param0, param1, ...);
+ // }
+ private static void ImplementInterfaceMethod(TypeBuilder newType, MethodInfo interfaceMethod) {
+ ParameterInfo[] parameters = interfaceMethod.GetParameters();
+ Type[] parameterTypes = (from p in parameters select p.ParameterType).ToArray();
+
+ // based on http://msdn.microsoft.com/en-us/library/system.reflection.emit.typebuilder.definemethodoverride.aspx
+ MethodBuilder newMethod = newType.DefineMethod(interfaceMethod.DeclaringType.Name + "." + interfaceMethod.Name,
+ MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual | MethodAttributes.Final,
+ interfaceMethod.ReturnType, parameterTypes);
+
+ MethodInfo baseMethod = newType.BaseType.GetMethod(interfaceMethod.Name, parameterTypes);
+
+ // parameter 0 is 'this', so we start at index 1
+ for (int i = 0; i < parameters.Length; i++) {
+ newMethod.DefineParameter(i + 1, parameters[i].Attributes, parameters[i].Name);
+ }
+
+ // load all arguments (including 'this') in proper order, then call and return
+ ILGenerator ilGen = newMethod.GetILGenerator();
+ for (int i = 0; i <= parameterTypes.Length; i++) {
+ ilGen.Emit(OpCodes.Ldarg_S, (byte)i);
+ }
+ ilGen.Emit(OpCodes.Call, baseMethod);
+ ilGen.Emit(OpCodes.Ret);
+
+ // finally, hook the new method up to the interface mapping
+ newType.DefineMethodOverride(newMethod, interfaceMethod);
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/EmptyModelMetadataProvider.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/EmptyModelMetadataProvider.cs
new file mode 100644
index 00000000000..984924b95ab
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/EmptyModelMetadataProvider.cs
@@ -0,0 +1,23 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Collections.Generic;
+ using System.Reflection;
+
+ public class EmptyModelMetadataProvider : AssociatedMetadataProvider {
+ protected override ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName) {
+ return new ModelMetadata(this, containerType, modelAccessor, modelType, propertyName);
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/EmptyModelValidatorProvider.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/EmptyModelValidatorProvider.cs
new file mode 100644
index 00000000000..63fb1a26bdf
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/EmptyModelValidatorProvider.cs
@@ -0,0 +1,22 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System.Collections.Generic;
+ using System.Linq;
+
+ public class EmptyModelValidatorProvider : ModelValidatorProvider {
+ public override IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, ControllerContext context) {
+ return Enumerable.Empty<ModelValidator>();
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/EmptyResult.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/EmptyResult.cs
new file mode 100644
index 00000000000..3fef8eed4ca
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/EmptyResult.cs
@@ -0,0 +1,29 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+
+ // represents a result that doesn't do anything, like a controller action returning null
+ public class EmptyResult : ActionResult {
+
+ private static readonly EmptyResult _singleton = new EmptyResult();
+
+ internal static EmptyResult Instance {
+ get {
+ return _singleton;
+ }
+ }
+
+ public override void ExecuteResult(ControllerContext context) {
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/Error.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Error.cs
new file mode 100644
index 00000000000..26e86f8748d
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Error.cs
@@ -0,0 +1,84 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Globalization;
+ using System.Web.Mvc.Async;
+ using System.Web.Mvc.Resources;
+
+ internal static class Error {
+
+ public static InvalidOperationException AsyncActionMethodSelector_CouldNotFindMethod(string methodName, Type controllerType) {
+ string message = String.Format(CultureInfo.CurrentUICulture, MvcResources.AsyncActionMethodSelector_CouldNotFindMethod,
+ methodName, controllerType);
+ return new InvalidOperationException(message);
+ }
+
+ public static InvalidOperationException AsyncCommon_AsyncResultAlreadyConsumed() {
+ return new InvalidOperationException(MvcResources.AsyncCommon_AsyncResultAlreadyConsumed);
+ }
+
+ public static InvalidOperationException AsyncCommon_ControllerMustImplementIAsyncManagerContainer(Type actualControllerType) {
+ string message = String.Format(CultureInfo.CurrentUICulture, MvcResources.AsyncCommon_ControllerMustImplementIAsyncManagerContainer,
+ actualControllerType);
+ return new InvalidOperationException(message);
+ }
+
+ public static ArgumentException AsyncCommon_InvalidAsyncResult(string parameterName) {
+ return new ArgumentException(MvcResources.AsyncCommon_InvalidAsyncResult, parameterName);
+ }
+
+ public static ArgumentOutOfRangeException AsyncCommon_InvalidTimeout(string parameterName) {
+ return new ArgumentOutOfRangeException(parameterName, MvcResources.AsyncCommon_InvalidTimeout);
+ }
+
+ public static InvalidOperationException ReflectedAsyncActionDescriptor_CannotExecuteSynchronously(string actionName) {
+ string message = String.Format(CultureInfo.CurrentUICulture, MvcResources.ReflectedAsyncActionDescriptor_CannotExecuteSynchronously,
+ actionName);
+ return new InvalidOperationException(message);
+ }
+
+ public static InvalidOperationException ChildActionOnlyAttribute_MustBeInChildRequest(ActionDescriptor actionDescriptor) {
+ string message = String.Format(CultureInfo.CurrentUICulture, MvcResources.ChildActionOnlyAttribute_MustBeInChildRequest,
+ actionDescriptor.ActionName);
+ return new InvalidOperationException(message);
+ }
+
+ public static ArgumentException ParameterCannotBeNullOrEmpty(string parameterName) {
+ return new ArgumentException(MvcResources.Common_NullOrEmpty, parameterName);
+ }
+
+ public static InvalidOperationException PropertyCannotBeNullOrEmpty(string propertyName) {
+ string message = String.Format(CultureInfo.CurrentUICulture, MvcResources.Common_PropertyCannotBeNullOrEmpty,
+ propertyName);
+ return new InvalidOperationException(message);
+ }
+
+ public static SynchronousOperationException SynchronizationContextUtil_ExceptionThrown(Exception innerException) {
+ return new SynchronousOperationException(MvcResources.SynchronizationContextUtil_ExceptionThrown, innerException);
+ }
+
+ public static InvalidOperationException ViewDataDictionary_WrongTModelType(Type valueType, Type modelType) {
+ string message = String.Format(CultureInfo.CurrentUICulture, MvcResources.ViewDataDictionary_WrongTModelType,
+ valueType, modelType);
+ return new InvalidOperationException(message);
+ }
+
+ public static InvalidOperationException ViewDataDictionary_ModelCannotBeNull(Type modelType) {
+ string message = String.Format(CultureInfo.CurrentUICulture, MvcResources.ViewDataDictionary_ModelCannotBeNull,
+ modelType);
+ return new InvalidOperationException(message);
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ExceptionContext.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ExceptionContext.cs
new file mode 100644
index 00000000000..937dd233c43
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ExceptionContext.cs
@@ -0,0 +1,56 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Diagnostics.CodeAnalysis;
+
+ public class ExceptionContext : ControllerContext {
+
+ private ActionResult _result;
+
+ // parameterless constructor used for mocking
+ public ExceptionContext() {
+ }
+
+ [SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors",
+ Justification = "The virtual property setters are only to support mocking frameworks, in which case this constructor shouldn't be called anyway.")]
+ public ExceptionContext(ControllerContext controllerContext, Exception exception)
+ : base(controllerContext) {
+ if (exception == null) {
+ throw new ArgumentNullException("exception");
+ }
+
+ Exception = exception;
+ }
+
+ public virtual Exception Exception {
+ get;
+ set;
+ }
+
+ public bool ExceptionHandled {
+ get;
+ set;
+ }
+
+ public ActionResult Result {
+ get {
+ return _result ?? EmptyResult.Instance;
+ }
+ set {
+ _result = value;
+ }
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ExpressionHelper.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ExpressionHelper.cs
new file mode 100644
index 00000000000..e72d94d8eeb
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ExpressionHelper.cs
@@ -0,0 +1,123 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Collections.Generic;
+ using System.Globalization;
+ using System.Linq;
+ using System.Linq.Expressions;
+ using System.Reflection;
+ using System.Web.Mvc.Resources;
+
+ public static class ExpressionHelper {
+ public static string GetExpressionText(string expression) {
+ return
+ String.Equals(expression, "model", StringComparison.OrdinalIgnoreCase)
+ ? String.Empty // If it's exactly "model", then give them an empty string, to replicate the lambda behavior
+ : expression;
+ }
+
+ public static string GetExpressionText(LambdaExpression expression) {
+ // Crack the expression string for property/field accessors to create its name
+ Stack<string> nameParts = new Stack<string>();
+ Expression part = expression.Body;
+
+ while (part != null) {
+ if (part.NodeType == ExpressionType.Call) {
+ MethodCallExpression methodExpression = (MethodCallExpression)part;
+
+ if (!IsSingleArgumentIndexer(methodExpression)) {
+ break;
+ }
+
+ nameParts.Push(
+ GetIndexerInvocation(
+ methodExpression.Arguments.Single(),
+ expression.Parameters.ToArray()
+ )
+ );
+
+ part = methodExpression.Object;
+ }
+ else if (part.NodeType == ExpressionType.ArrayIndex) {
+ BinaryExpression binaryExpression = (BinaryExpression)part;
+
+ nameParts.Push(
+ GetIndexerInvocation(
+ binaryExpression.Right,
+ expression.Parameters.ToArray()
+ )
+ );
+
+ part = binaryExpression.Left;
+ }
+ else if (part.NodeType == ExpressionType.MemberAccess) {
+ MemberExpression memberExpressionPart = (MemberExpression)part;
+ nameParts.Push("." + memberExpressionPart.Member.Name);
+ part = memberExpressionPart.Expression;
+ }
+ else {
+ break;
+ }
+ }
+
+ // If it starts with "model", then strip that away
+ if (nameParts.Count > 0 && String.Equals(nameParts.Peek(), ".model", StringComparison.OrdinalIgnoreCase)) {
+ nameParts.Pop();
+ }
+
+ if (nameParts.Count > 0) {
+ return nameParts.Aggregate((left, right) => left + right).TrimStart('.');
+ }
+
+ return String.Empty;
+ }
+
+ private static string GetIndexerInvocation(Expression expression, ParameterExpression[] parameters) {
+ Expression converted = Expression.Convert(expression, typeof(object));
+ ParameterExpression fakeParameter = Expression.Parameter(typeof(object), null);
+ Expression<Func<object, object>> lambda = Expression.Lambda<Func<object, object>>(converted, fakeParameter);
+ Func<object, object> func;
+
+ try {
+ func = ExpressionUtil.CachedExpressionCompiler.Process(lambda);
+ }
+ catch (InvalidOperationException ex) {
+ throw new InvalidOperationException(
+ String.Format(
+ CultureInfo.CurrentUICulture,
+ MvcResources.ExpressionHelper_InvalidIndexerExpression,
+ expression,
+ parameters[0].Name
+ ),
+ ex
+ );
+ }
+
+ return "[" + Convert.ToString(func(null), CultureInfo.InvariantCulture) + "]";
+ }
+
+ internal static bool IsSingleArgumentIndexer(Expression expression) {
+ MethodCallExpression methodExpression = expression as MethodCallExpression;
+ if (methodExpression == null || methodExpression.Arguments.Count != 1) {
+ return false;
+ }
+
+ return methodExpression.Method
+ .DeclaringType
+ .GetDefaultMembers()
+ .OfType<PropertyInfo>()
+ .Any(p => p.GetGetMethod() == methodExpression.Method);
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ExpressionUtil/BinaryExpressionFingerprint.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ExpressionUtil/BinaryExpressionFingerprint.cs
new file mode 100644
index 00000000000..90e1fd4fccd
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ExpressionUtil/BinaryExpressionFingerprint.cs
@@ -0,0 +1,111 @@
+#pragma warning disable 659 // overrides AddToHashCodeCombiner instead
+
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc.ExpressionUtil {
+ using System;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Linq.Expressions;
+ using System.Reflection;
+
+ // BinaryExpression fingerprint class
+ // Useful for things like array[index]
+ //
+ // This particular fingerprint doesn't support the BinaryExpression.Conversion property,
+ // which is used in some coalescing operations.
+ [SuppressMessage("Microsoft.Usage", "CA2218:OverrideGetHashCodeOnOverridingEquals",
+ Justification = "Overrides AddToHashCodeCombiner() instead.")]
+ internal sealed class BinaryExpressionFingerprint : ExpressionFingerprint {
+
+ private BinaryExpressionFingerprint(BinaryExpression expression)
+ : base(expression) {
+ // don't care about UnaryExpression.IsLifted since it's not necessary to uniquely describe the expression,
+ // but IsLiftedToNull *is* required
+
+ IsLiftedToNull = expression.IsLiftedToNull;
+ Method = expression.Method;
+ }
+
+ public bool IsLiftedToNull {
+ get;
+ private set;
+ }
+
+ public ExpressionFingerprint Left {
+ get;
+ private set;
+ }
+
+ public MethodInfo Method {
+ get;
+ private set;
+ }
+
+ public ExpressionFingerprint Right {
+ get;
+ private set;
+ }
+
+ internal override void AddToHashCodeCombiner(HashCodeCombiner combiner) {
+ base.AddToHashCodeCombiner(combiner);
+
+ combiner.AddInt32(IsLiftedToNull.GetHashCode());
+ combiner.AddFingerprint(Left);
+ combiner.AddObject(Method);
+ combiner.AddFingerprint(Right);
+ }
+
+ public static BinaryExpressionFingerprint Create(BinaryExpression expression, ParserContext parserContext) {
+ if (expression.Conversion != null) {
+ // we don't support the Conversion property
+ return null;
+ }
+
+ // if any fingerprinting fails, bail out
+ ExpressionFingerprint left = Create(expression.Left, parserContext);
+ if (left == null && expression.Left != null) {
+ return null;
+ }
+
+ ExpressionFingerprint right = Create(expression.Right, parserContext);
+ if (right == null && expression.Right != null) {
+ return null;
+ }
+
+ return new BinaryExpressionFingerprint(expression) {
+ Left = left,
+ Right = right
+ };
+ }
+
+ public override bool Equals(object obj) {
+ BinaryExpressionFingerprint other = obj as BinaryExpressionFingerprint;
+ if (other == null) {
+ return false;
+ }
+
+ return (this.IsLiftedToNull == other.IsLiftedToNull
+ && Object.Equals(this.Left, other.Left)
+ && this.Method == other.Method
+ && Object.Equals(this.Right, other.Right)
+ && base.Equals(other));
+ }
+
+ public override Expression ToExpression(ParserContext parserContext) {
+ Expression leftExpr = ToExpression(Left, parserContext);
+ Expression rightExpr = ToExpression(Right, parserContext);
+ return Expression.MakeBinary(NodeType, leftExpr, rightExpr, IsLiftedToNull, Method);
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ExpressionUtil/CachedExpressionCompiler.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ExpressionUtil/CachedExpressionCompiler.cs
new file mode 100644
index 00000000000..29078b69a6f
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ExpressionUtil/CachedExpressionCompiler.cs
@@ -0,0 +1,86 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc.ExpressionUtil {
+ using System;
+ using System.Linq.Expressions;
+
+ internal static class CachedExpressionCompiler {
+
+ // This is the entry point to the cached expression tree compiler. The processor will perform a series of checks
+ // and optimizations in order to return a fully-compiled func as quickly as possible to the caller. If the
+ // input expression is particularly obscure, the system will fall back to a slow but correct compilation step.
+ public static Func<TModel, TValue> Process<TModel, TValue>(Expression<Func<TModel, TValue>> lambdaExpression) {
+ return Processor<TModel, TValue>.GetFunc(lambdaExpression);
+ }
+
+ private static class Processor<TModel, TValue> {
+
+ private static readonly Cache _cache = new Cache();
+
+ public static Func<TModel, TValue> GetFunc(Expression<Func<TModel, TValue>> lambdaExpression) {
+ // look for common patterns that don't need to be fingerprinted
+ Func<TModel, TValue> func = GetFuncFastTrack(lambdaExpression);
+ if (func != null) {
+ return func;
+ }
+
+ // not a common pattern, so try fingerprinting (slower, but cached)
+ func = GetFuncFingerprinted(lambdaExpression);
+ if (func != null) {
+ return func;
+ }
+
+ // pattern not recognized by fingerprinting routine, so compile directly (slowest)
+ return GetFuncSlow(lambdaExpression);
+ }
+
+ private static Func<TModel, TValue> GetFuncFastTrack(Expression<Func<TModel, TValue>> lambdaExpression) {
+ ParameterExpression modelParameter = lambdaExpression.Parameters[0];
+ Expression body = lambdaExpression.Body;
+
+ return FastTrack<TModel, TValue>.GetFunc(modelParameter, body);
+ }
+
+ private static Func<TModel, TValue> GetFuncFingerprinted(Expression<Func<TModel, TValue>> lambdaExpression) {
+ ParserContext context = ExpressionParser.Parse(lambdaExpression);
+ if (context.Fingerprint == null) {
+ // fingerprinting failed
+ return null;
+ }
+
+ object[] hoistedValues = context.HoistedValues.ToArray();
+ var del = _cache.GetDelegate(context);
+ return model => del(model, hoistedValues);
+ }
+
+ private static Func<TModel, TValue> GetFuncSlow(Expression<Func<TModel, TValue>> lambdaExpression) {
+ Func<TModel, TValue> del = lambdaExpression.Compile();
+ return del;
+ }
+
+ private sealed class Cache : ReaderWriterCache<ExpressionFingerprint, CompiledExpressionDelegate<TModel, TValue>> {
+ private static CompiledExpressionDelegate<TModel, TValue> CreateDelegate(ParserContext context) {
+ var bodyExpr = context.Fingerprint.ToExpression(context);
+ var lambdaExpr = Expression.Lambda<CompiledExpressionDelegate<TModel, TValue>>(bodyExpr, context.ModelParameter, ParserContext.HoistedValuesParameter);
+ var del = lambdaExpr.Compile();
+ return del;
+ }
+ public CompiledExpressionDelegate<TModel, TValue> GetDelegate(ParserContext context) {
+ return FetchOrCreateItem(context.Fingerprint, () => CreateDelegate(context));
+ }
+ }
+
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ExpressionUtil/CompiledExpressionDelegate`2.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ExpressionUtil/CompiledExpressionDelegate`2.cs
new file mode 100644
index 00000000000..dc95a908da7
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ExpressionUtil/CompiledExpressionDelegate`2.cs
@@ -0,0 +1,18 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc.ExpressionUtil {
+ using System;
+
+ internal delegate TValue CompiledExpressionDelegate<TModel, TValue>(TModel model, object[] hoistedValues);
+
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ExpressionUtil/ConditionalExpressionFingerprint.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ExpressionUtil/ConditionalExpressionFingerprint.cs
new file mode 100644
index 00000000000..80e29f0a564
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ExpressionUtil/ConditionalExpressionFingerprint.cs
@@ -0,0 +1,97 @@
+#pragma warning disable 659 // overrides AddToHashCodeCombiner instead
+
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc.ExpressionUtil {
+ using System;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Linq.Expressions;
+
+ // ConditionalExpression fingerprint class
+ // Expression of form (test) ? ifTrue : ifFalse
+ [SuppressMessage("Microsoft.Usage", "CA2218:OverrideGetHashCodeOnOverridingEquals",
+ Justification = "Overrides AddToHashCodeCombiner() instead.")]
+ internal sealed class ConditionalExpressionFingerprint : ExpressionFingerprint {
+
+ private ConditionalExpressionFingerprint(ConditionalExpression expression)
+ : base(expression) {
+ }
+
+ public ExpressionFingerprint Test {
+ get;
+ private set;
+ }
+
+ public ExpressionFingerprint IfTrue {
+ get;
+ private set;
+ }
+
+ public ExpressionFingerprint IfFalse {
+ get;
+ private set;
+ }
+
+ internal override void AddToHashCodeCombiner(HashCodeCombiner combiner) {
+ base.AddToHashCodeCombiner(combiner);
+
+ combiner.AddFingerprint(Test);
+ combiner.AddFingerprint(IfTrue);
+ combiner.AddFingerprint(IfFalse);
+ }
+
+ public static ConditionalExpressionFingerprint Create(ConditionalExpression expression, ParserContext parserContext) {
+ // if any fingerprinting fails, bail out
+ ExpressionFingerprint test = Create(expression.Test, parserContext);
+ if (test == null && expression.Test != null) {
+ return null;
+ }
+
+ ExpressionFingerprint ifTrue = Create(expression.IfTrue, parserContext);
+ if (ifTrue == null && expression.IfTrue != null) {
+ return null;
+ }
+
+ ExpressionFingerprint ifFalse = Create(expression.IfFalse, parserContext);
+ if (ifFalse == null && expression.IfFalse != null) {
+ return null;
+ }
+
+ return new ConditionalExpressionFingerprint(expression) {
+ Test = test,
+ IfTrue = ifTrue,
+ IfFalse = ifFalse
+ };
+ }
+
+ public override bool Equals(object obj) {
+ ConditionalExpressionFingerprint other = obj as ConditionalExpressionFingerprint;
+ if (other == null) {
+ return false;
+ }
+
+ return (Object.Equals(this.Test, other.Test)
+ && Object.Equals(this.IfTrue, other.IfTrue)
+ && Object.Equals(this.IfFalse, other.IfFalse)
+ && base.Equals(other));
+ }
+
+ public override Expression ToExpression(ParserContext parserContext) {
+ Expression testExpr = ToExpression(Test, parserContext);
+ Expression ifTrueExpr = ToExpression(IfTrue, parserContext);
+ Expression ifFalseExpr = ToExpression(IfFalse, parserContext);
+ return Expression.Condition(testExpr, ifTrueExpr, ifFalseExpr);
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ExpressionUtil/ConstantExpressionFingerprint.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ExpressionUtil/ConstantExpressionFingerprint.cs
new file mode 100644
index 00000000000..96e08bc8ee3
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ExpressionUtil/ConstantExpressionFingerprint.cs
@@ -0,0 +1,72 @@
+#pragma warning disable 659 // overrides AddToHashCodeCombiner instead
+
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc.ExpressionUtil {
+ using System;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Linq.Expressions;
+
+ // ConstantExpression fingerprint class
+ //
+ // A ConstantExpression might represent a captured local variable, so we can't compile
+ // the value directly into the cached function. Instead, a placeholder is generated
+ // and the value is hoisted into a local variables array. This placeholder can then
+ // be compiled and cached, and the array lookup happens at runtime.
+ [SuppressMessage("Microsoft.Usage", "CA2218:OverrideGetHashCodeOnOverridingEquals",
+ Justification = "Overrides AddToHashCodeCombiner() instead.")]
+ internal sealed class ConstantExpressionFingerprint : ExpressionFingerprint {
+
+ private ConstantExpressionFingerprint(ConstantExpression expression)
+ : base(expression) {
+ }
+
+ public int HoistedLocalsIndex {
+ get;
+ private set;
+ }
+
+ internal override void AddToHashCodeCombiner(HashCodeCombiner combiner) {
+ base.AddToHashCodeCombiner(combiner);
+
+ combiner.AddInt32(HoistedLocalsIndex);
+ }
+
+ public static ConstantExpressionFingerprint Create(ConstantExpression expression, ParserContext parserContext) {
+ ConstantExpressionFingerprint fingerprint = new ConstantExpressionFingerprint(expression) {
+ HoistedLocalsIndex = parserContext.HoistedValues.Count
+ };
+
+ parserContext.HoistedValues.Add(expression.Value);
+ return fingerprint;
+ }
+
+ public override bool Equals(object obj) {
+ ConstantExpressionFingerprint other = obj as ConstantExpressionFingerprint;
+ if (other == null) {
+ return false;
+ }
+
+ return (this.HoistedLocalsIndex == other.HoistedLocalsIndex
+ && base.Equals(other));
+ }
+
+ public override Expression ToExpression(ParserContext parserContext) {
+ // (Type) HoistedValues[HoistedLocalsIndex]
+ BinaryExpression arrayIndex = Expression.ArrayIndex(ParserContext.HoistedValuesParameter, Expression.Constant(HoistedLocalsIndex));
+ UnaryExpression castExpr = Expression.Convert(arrayIndex, Type);
+ return castExpr;
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ExpressionUtil/ExpressionFingerprint.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ExpressionUtil/ExpressionFingerprint.cs
new file mode 100644
index 00000000000..c942d9bb1b5
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ExpressionUtil/ExpressionFingerprint.cs
@@ -0,0 +1,174 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc.ExpressionUtil {
+ using System;
+ using System.Collections.Generic;
+ using System.Collections.ObjectModel;
+ using System.Linq;
+ using System.Linq.Expressions;
+
+ // Expression fingerprint class
+ // Contains information used for generalizing, comparing, and recreating Expression instances
+ //
+ // Since Expression objects are immutable and are recreated for every invocation of an expression
+ // helper method, they can't be compared directly. Fingerprinting Expression objects allows
+ // information about them to be abstracted away, and the fingerprints can be directly compared.
+ // Consider the process of fingerprinting that all values (parameters, constants, etc.) are hoisted
+ // and replaced with dummies. What remains can be decomposed into a sequence of operations on specific
+ // types and specific inputs.
+ //
+ // Some sample fingerprints:
+ //
+ // 2 + 4 -> OP_ADD(CONST:int, CONST:int):int
+ // 2 + 8 -> OP_ADD(CONST:int, CONST:int):int
+ // 2.0 + 4.0 -> OP_ADD(CONST:double, CONST:double):double
+ //
+ // 2 + 4 and 2 + 8 have the same fingerprint, but 2.0 + 4.0 has a different fingerprint since its
+ // underlying types differ.
+ //
+ // "Hello " + "world" -> OP_ADD(CONST:string, CONST:string):string
+ // "Hello " + {model} -> OP_ADD(CONST:string, PARAM:string):string
+ //
+ // These string concatenations have different fingerprints since the inputs are provided differently:
+ // one is a hoisted local, the other is a parameter.
+ //
+ // ({model} ?? "sample").Length -> MEMBER_ACCESS(String.Length, OP_COALESCE(PARAM:string, CONST:string):string):int
+ // ({model} ?? "other sample").Length -> MEMBER_ACCESS(String.Length, OP_COALESCE(PARAM:string, CONST:string):string):int
+ //
+ // These expressions have the same fingerprint.
+ internal abstract class ExpressionFingerprint {
+
+ protected ExpressionFingerprint(Expression expression) {
+ // since the fingerprints are cached potentially forever, don't keep a reference
+ // to the original expression
+
+ NodeType = expression.NodeType;
+ Type = expression.Type;
+ }
+
+ // the type of expression node, e.g. OP_ADD, MEMBER_ACCESS, etc.
+ public ExpressionType NodeType {
+ get;
+ private set;
+ }
+
+ // the CLR type resulting from this expression, e.g. int, string, etc.
+ public Type Type {
+ get;
+ private set;
+ }
+
+ internal virtual void AddToHashCodeCombiner(HashCodeCombiner combiner) {
+ combiner.AddObject(NodeType);
+ combiner.AddObject(Type);
+ }
+
+ public static ExpressionFingerprint Create(Expression expression, ParserContext parserContext) {
+ {
+ BinaryExpression binaryExpression = expression as BinaryExpression;
+ if (binaryExpression != null) {
+ return BinaryExpressionFingerprint.Create(binaryExpression, parserContext);
+ }
+ }
+
+ {
+ ConditionalExpression conditionalExpression = expression as ConditionalExpression;
+ if (conditionalExpression != null) {
+ return ConditionalExpressionFingerprint.Create(conditionalExpression, parserContext);
+ }
+ }
+
+ {
+ ConstantExpression constantExpression = expression as ConstantExpression;
+ if (constantExpression != null) {
+ return ConstantExpressionFingerprint.Create(constantExpression, parserContext);
+ }
+ }
+
+ {
+ MemberExpression memberExpression = expression as MemberExpression;
+ if (memberExpression != null) {
+ return MemberExpressionFingerprint.Create(memberExpression, parserContext);
+ }
+ }
+
+ {
+ MethodCallExpression methodCallExpression = expression as MethodCallExpression;
+ if (methodCallExpression != null) {
+ return MethodCallExpressionFingerprint.Create(methodCallExpression, parserContext);
+ }
+ }
+
+ {
+ ParameterExpression parameterExpression = expression as ParameterExpression;
+ if (parameterExpression != null) {
+ return ParameterExpressionFingerprint.Create(parameterExpression, parserContext);
+ }
+ }
+
+ {
+ UnaryExpression unaryExpression = expression as UnaryExpression;
+ if (unaryExpression != null) {
+ return UnaryExpressionFingerprint.Create(unaryExpression, parserContext);
+ }
+ }
+
+ // unknown expression
+ return null;
+ }
+
+ public static ReadOnlyCollection<ExpressionFingerprint> Create(IEnumerable<Expression> expressions, ParserContext parserContext) {
+ List<ExpressionFingerprint> fingerprints = new List<ExpressionFingerprint>();
+ foreach (Expression expression in expressions) {
+ ExpressionFingerprint fingerprint = Create(expression, parserContext);
+ if (fingerprint == null && expression != null) {
+ // something couldn't be parsed properly
+ return null;
+ }
+ else {
+ fingerprints.Add(fingerprint);
+ }
+ }
+ return new ReadOnlyCollection<ExpressionFingerprint>(fingerprints);
+ }
+
+ public override int GetHashCode() {
+ HashCodeCombiner combiner = new HashCodeCombiner();
+ combiner.AddObject(GetType());
+ AddToHashCodeCombiner(combiner);
+ return combiner.CombinedHash;
+ }
+
+ public override bool Equals(object obj) {
+ ExpressionFingerprint other = obj as ExpressionFingerprint;
+ if (other == null) {
+ return false;
+ }
+
+ return (this.NodeType == other.NodeType
+ && this.Type == other.Type
+ && this.GetType() == other.GetType());
+ }
+
+ protected static Expression ToExpression(ExpressionFingerprint fingerprint, ParserContext parserContext) {
+ return (fingerprint != null) ? fingerprint.ToExpression(parserContext) : null;
+ }
+
+ protected static IEnumerable<Expression> ToExpression(IEnumerable<ExpressionFingerprint> fingerprints, ParserContext parserContext) {
+ return from fingerprint in fingerprints select ToExpression(fingerprint, parserContext);
+ }
+
+ public abstract Expression ToExpression(ParserContext parserContext);
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ExpressionUtil/ExpressionParser.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ExpressionUtil/ExpressionParser.cs
new file mode 100644
index 00000000000..f277a9f56bb
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ExpressionUtil/ExpressionParser.cs
@@ -0,0 +1,30 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc.ExpressionUtil {
+ using System;
+ using System.Linq.Expressions;
+
+ internal static class ExpressionParser {
+
+ public static ParserContext Parse<TModel, TValue>(Expression<Func<TModel, TValue>> expression) {
+ ParserContext context = new ParserContext() {
+ ModelParameter = expression.Parameters[0]
+ };
+
+ Expression body = expression.Body;
+ context.Fingerprint = ExpressionFingerprint.Create(body, context);
+ return context;
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ExpressionUtil/FastTrack`2.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ExpressionUtil/FastTrack`2.cs
new file mode 100644
index 00000000000..ec20c8c0780
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ExpressionUtil/FastTrack`2.cs
@@ -0,0 +1,112 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc.ExpressionUtil {
+ using System;
+ using System.Linq.Expressions;
+ using System.Reflection;
+
+ internal static class FastTrack<TModel, TValue> {
+
+ private static Func<TModel, TValue> _identityFunc;
+
+ private static readonly ConstMemberLookupCache _constMemberLookupCache = new ConstMemberLookupCache();
+ private static readonly ModelMemberLookupCache _modelMemberLookupCache = new ModelMemberLookupCache();
+
+ public static Func<TModel, TValue> GetFunc(ParameterExpression modelParameter, Expression body) {
+ { // model => model
+ if (modelParameter == body) {
+ return GetIdentityFunc();
+ }
+ }
+
+ { // model => {const}
+ ConstantExpression constantExpression = body as ConstantExpression;
+ if (constantExpression != null) {
+ TValue value = (TValue)constantExpression.Value;
+ return _ => value;
+ }
+ }
+
+ {
+ MemberExpression memberExpression = body as MemberExpression;
+ if (memberExpression != null) {
+ if (memberExpression.Expression == null) {
+ // model => {static member}
+ return GetModelMemberLookupFunc(memberExpression.Member, true /* isStatic */);
+ }
+ else if (memberExpression.Expression == modelParameter) {
+ // model => model.Member
+ return GetModelMemberLookupFunc(memberExpression.Member, false /* isStatic */);
+ }
+ else {
+ ConstantExpression constantExpression = memberExpression.Expression as ConstantExpression;
+ if (constantExpression != null) {
+ // model => {const}.Member, e.g. captured local variable in a foreach
+ return GetConstMemberLookupFunc(memberExpression.Member, constantExpression.Value);
+ }
+ }
+ }
+ }
+
+ // don't know how to fast-track
+ return null;
+ }
+
+ private static Func<TModel, TValue> GetIdentityFunc() {
+ // don't need to worry about locking since all identity funcs are the same
+ if (_identityFunc == null) {
+ ParameterExpression modelParameter = Expression.Parameter(typeof(TModel), "model");
+ Expression<Func<TModel, TValue>> identityLambda = Expression.Lambda<Func<TModel, TValue>>(modelParameter, modelParameter);
+ _identityFunc = identityLambda.Compile();
+ }
+
+ return _identityFunc;
+ }
+
+ private static Func<TModel, TValue> GetModelMemberLookupFunc(MemberInfo member, bool isStatic) {
+ return _modelMemberLookupCache.GetFunc(member, isStatic);
+ }
+
+ private static Func<TModel, TValue> GetConstMemberLookupFunc(MemberInfo member, object constValue) {
+ Func<object, TValue> innerFunc = _constMemberLookupCache.GetFunc(member);
+ return _ => innerFunc(constValue);
+ }
+
+ private sealed class ConstMemberLookupCache : ReaderWriterCache<MemberInfo, Func<object, TValue>> {
+ private static Func<object, TValue> CreateFunc(MemberInfo member) {
+ ParameterExpression constParam = Expression.Parameter(typeof(object), "constValue");
+ // cast is necessary since the constant value comes in as a standard 'object'
+ UnaryExpression castExpr = Expression.Convert(constParam, member.DeclaringType);
+ MemberExpression memberExpr = Expression.MakeMemberAccess(castExpr, member);
+ Expression<Func<object, TValue>> lambda = Expression.Lambda<Func<object, TValue>>(memberExpr, constParam);
+ return lambda.Compile();
+ }
+ public Func<object, TValue> GetFunc(MemberInfo member) {
+ return FetchOrCreateItem(member, () => CreateFunc(member));
+ }
+ }
+
+ private sealed class ModelMemberLookupCache : ReaderWriterCache<MemberInfo, Func<TModel, TValue>> {
+ private static Func<TModel, TValue> CreateFunc(MemberInfo member, bool isStatic) {
+ ParameterExpression modelParam = Expression.Parameter(typeof(TModel), "model");
+ MemberExpression memberExpr = Expression.MakeMemberAccess((!isStatic) ? modelParam : null, member);
+ Expression<Func<TModel, TValue>> lambda = Expression.Lambda<Func<TModel, TValue>>(memberExpr, modelParam);
+ return lambda.Compile();
+ }
+ public Func<TModel, TValue> GetFunc(MemberInfo member, bool isStatic) {
+ return FetchOrCreateItem(member, () => CreateFunc(member, isStatic));
+ }
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ExpressionUtil/HashCodeCombiner.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ExpressionUtil/HashCodeCombiner.cs
new file mode 100644
index 00000000000..5abdded6872
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ExpressionUtil/HashCodeCombiner.cs
@@ -0,0 +1,61 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc.ExpressionUtil {
+ using System;
+ using System.Collections;
+
+ // based on System.Web.Util.HashCodeCombiner
+ internal class HashCodeCombiner {
+
+ private long _combinedHash64 = 0x1505L;
+
+ public void AddFingerprint(ExpressionFingerprint fingerprint) {
+ if (fingerprint != null) {
+ fingerprint.AddToHashCodeCombiner(this);
+ }
+ else {
+ AddInt32(0);
+ }
+ }
+
+ public void AddEnumerable(IEnumerable e) {
+ if (e == null) {
+ AddInt32(0);
+ }
+ else {
+ int count = 0;
+ foreach (object o in e) {
+ AddObject(o);
+ count++;
+ }
+ AddInt32(count);
+ }
+ }
+
+ public void AddInt32(int i) {
+ _combinedHash64 = ((_combinedHash64 << 5) + _combinedHash64) ^ i;
+ }
+
+ public void AddObject(object o) {
+ int oHashCode = (o != null) ? o.GetHashCode() : 0;
+ AddInt32(oHashCode);
+ }
+
+ public int CombinedHash {
+ get {
+ return _combinedHash64.GetHashCode();
+ }
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ExpressionUtil/MemberExpressionFingerprint.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ExpressionUtil/MemberExpressionFingerprint.cs
new file mode 100644
index 00000000000..6a55769576c
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ExpressionUtil/MemberExpressionFingerprint.cs
@@ -0,0 +1,78 @@
+#pragma warning disable 659 // overrides AddToHashCodeCombiner instead
+
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc.ExpressionUtil {
+ using System;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Linq.Expressions;
+ using System.Reflection;
+
+ // MemberExpression fingerprint class
+ // Expression of form xxx.FieldOrProperty
+ [SuppressMessage("Microsoft.Usage", "CA2218:OverrideGetHashCodeOnOverridingEquals",
+ Justification = "Overrides AddToHashCodeCombiner() instead.")]
+ internal sealed class MemberExpressionFingerprint : ExpressionFingerprint {
+
+ private MemberExpressionFingerprint(MemberExpression expression)
+ : base(expression) {
+
+ Member = expression.Member;
+ }
+
+ public MemberInfo Member {
+ get;
+ private set;
+ }
+
+ public ExpressionFingerprint Target {
+ get;
+ private set;
+ }
+
+ internal override void AddToHashCodeCombiner(HashCodeCombiner combiner) {
+ base.AddToHashCodeCombiner(combiner);
+
+ combiner.AddObject(Member);
+ combiner.AddFingerprint(Target);
+ }
+
+ public static MemberExpressionFingerprint Create(MemberExpression expression, ParserContext parserContext) {
+ ExpressionFingerprint target = Create(expression.Expression, parserContext);
+ if (target == null && expression.Expression != null) {
+ return null;
+ }
+
+ return new MemberExpressionFingerprint(expression) {
+ Target = target
+ };
+ }
+
+ public override bool Equals(object obj) {
+ MemberExpressionFingerprint other = obj as MemberExpressionFingerprint;
+ if (other == null) {
+ return false;
+ }
+
+ return (this.Member == other.Member
+ && Object.Equals(this.Target, other.Target)
+ && base.Equals(other));
+ }
+
+ public override Expression ToExpression(ParserContext parserContext) {
+ Expression targetExpr = ToExpression(Target, parserContext);
+ return Expression.MakeMemberAccess(targetExpr, Member);
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ExpressionUtil/MethodCallExpressionFingerprint.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ExpressionUtil/MethodCallExpressionFingerprint.cs
new file mode 100644
index 00000000000..7b8455cdb51
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ExpressionUtil/MethodCallExpressionFingerprint.cs
@@ -0,0 +1,95 @@
+#pragma warning disable 659 // overrides AddToHashCodeCombiner instead
+
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc.ExpressionUtil {
+ using System;
+ using System.Collections.Generic;
+ using System.Collections.ObjectModel;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Linq;
+ using System.Linq.Expressions;
+ using System.Reflection;
+
+ // MethodCallExpression fingerprint class
+ // Expression of form xxx.Foo(...), xxx[...] (get_Item()), etc.
+ [SuppressMessage("Microsoft.Usage", "CA2218:OverrideGetHashCodeOnOverridingEquals",
+ Justification = "Overrides AddToHashCodeCombiner() instead.")]
+ internal sealed class MethodCallExpressionFingerprint : ExpressionFingerprint {
+
+ private MethodCallExpressionFingerprint(MethodCallExpression expression)
+ : base(expression) {
+
+ Method = expression.Method;
+ }
+
+ public ReadOnlyCollection<ExpressionFingerprint> Arguments {
+ get;
+ private set;
+ }
+
+ public MethodInfo Method {
+ get;
+ private set;
+ }
+
+ public ExpressionFingerprint Target {
+ get;
+ private set;
+ }
+
+ internal override void AddToHashCodeCombiner(HashCodeCombiner combiner) {
+ base.AddToHashCodeCombiner(combiner);
+
+ combiner.AddEnumerable(Arguments);
+ combiner.AddObject(Method);
+ combiner.AddFingerprint(Target);
+ }
+
+ public static MethodCallExpressionFingerprint Create(MethodCallExpression expression, ParserContext parserContext) {
+ ReadOnlyCollection<ExpressionFingerprint> arguments = Create(expression.Arguments, parserContext);
+ if (arguments == null) {
+ return null;
+ }
+
+ ExpressionFingerprint target = Create(expression.Object, parserContext);
+ if (target == null && expression.Object != null) {
+ return null;
+ }
+
+ return new MethodCallExpressionFingerprint(expression) {
+ Arguments = arguments,
+ Target = target
+ };
+ }
+
+ public override bool Equals(object obj) {
+ MethodCallExpressionFingerprint other = obj as MethodCallExpressionFingerprint;
+ if (other == null) {
+ return false;
+ }
+
+ return (this.Arguments.SequenceEqual(other.Arguments)
+ && this.Method == other.Method
+ && Object.Equals(this.Target, other.Target)
+ && base.Equals(other));
+ }
+
+ public override Expression ToExpression(ParserContext parserContext) {
+ Expression targetExpr = ToExpression(Target, parserContext);
+ IEnumerable<Expression> argumentsExpr = ToExpression(Arguments, parserContext);
+ return Expression.Call(targetExpr, Method, argumentsExpr);
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ExpressionUtil/ParameterExpressionFingerprint.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ExpressionUtil/ParameterExpressionFingerprint.cs
new file mode 100644
index 00000000000..399f686e1a8
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ExpressionUtil/ParameterExpressionFingerprint.cs
@@ -0,0 +1,42 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc.ExpressionUtil {
+ using System;
+ using System.Linq.Expressions;
+
+ // ParameterExpression fingerprint class
+ // Specifically, instances of this type represent the model parameter
+ internal sealed class ParameterExpressionFingerprint : ExpressionFingerprint {
+
+ private ParameterExpressionFingerprint(ParameterExpression expression)
+ : base(expression) {
+ }
+
+ public static ParameterExpressionFingerprint Create(ParameterExpression expression, ParserContext parserContext) {
+ if (expression == parserContext.ModelParameter) {
+ return new ParameterExpressionFingerprint(expression);
+ }
+ else {
+ // degenerate case - uncaptured parameter expression passed into the system
+ return null;
+ }
+ }
+
+ public override Expression ToExpression(ParserContext parserContext) {
+ // The only time an instance of this class exists is if it represents the actual model parameter,
+ // so just return it directly.
+ return parserContext.ModelParameter;
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ExpressionUtil/ParserContext.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ExpressionUtil/ParserContext.cs
new file mode 100644
index 00000000000..38281c34389
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ExpressionUtil/ParserContext.cs
@@ -0,0 +1,27 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc.ExpressionUtil {
+ using System;
+ using System.Linq.Expressions;
+ using System.Collections.Generic;
+
+ internal class ParserContext {
+
+ public static readonly ParameterExpression HoistedValuesParameter = Expression.Parameter(typeof(object[]), "hoistedValues");
+
+ public ExpressionFingerprint Fingerprint;
+ public readonly List<object> HoistedValues = new List<object>();
+ public ParameterExpression ModelParameter;
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ExpressionUtil/UnaryExpressionFingerprint.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ExpressionUtil/UnaryExpressionFingerprint.cs
new file mode 100644
index 00000000000..8ba8cfa21cf
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ExpressionUtil/UnaryExpressionFingerprint.cs
@@ -0,0 +1,87 @@
+#pragma warning disable 659 // overrides AddToHashCodeCombiner instead
+
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc.ExpressionUtil {
+ using System;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Linq.Expressions;
+ using System.Reflection;
+
+ // UnaryExpression fingerprint class
+ // The most common appearance of a UnaryExpression is a cast or other conversion operator
+ [SuppressMessage("Microsoft.Usage", "CA2218:OverrideGetHashCodeOnOverridingEquals",
+ Justification = "Overrides AddToHashCodeCombiner() instead.")]
+ internal sealed class UnaryExpressionFingerprint : ExpressionFingerprint {
+
+ private UnaryExpressionFingerprint(UnaryExpression expression)
+ : base(expression) {
+ // don't care about UnaryExpression.IsLifted / IsLiftedToNull since they're not necessary to uniquely describe the expression
+
+ Method = expression.Method;
+ }
+
+ public MethodInfo Method {
+ get;
+ private set;
+ }
+
+ public ExpressionFingerprint Operand {
+ get;
+ private set;
+ }
+
+ internal override void AddToHashCodeCombiner(HashCodeCombiner combiner) {
+ base.AddToHashCodeCombiner(combiner);
+
+ combiner.AddObject(Method);
+ combiner.AddFingerprint(Operand);
+ }
+
+ public static UnaryExpressionFingerprint Create(UnaryExpression expression, ParserContext parserContext) {
+ ExpressionFingerprint operand = Create(expression.Operand, parserContext);
+ if (operand == null && expression.Operand != null) {
+ // couldn't convert the operand, so bail
+ return null;
+ }
+
+ return new UnaryExpressionFingerprint(expression) {
+ Operand = operand
+ };
+ }
+
+ public override bool Equals(object obj) {
+ UnaryExpressionFingerprint other = obj as UnaryExpressionFingerprint;
+ if (other == null) {
+ return false;
+ }
+
+ return (this.Method == other.Method
+ && Object.Equals(this.Operand, other.Operand)
+ && base.Equals(other));
+ }
+
+ public override Expression ToExpression(ParserContext parserContext) {
+ Expression operandExpr = ToExpression(Operand, parserContext);
+
+ // in .NET 3.5 SP1, Expression.MakeUnary() throws if NodeType is UnaryPlus, so special-case
+ if (NodeType == ExpressionType.UnaryPlus) {
+ return Expression.UnaryPlus(operandExpr, Method);
+ }
+ else {
+ return Expression.MakeUnary(NodeType, operandExpr, Type, Method);
+ }
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/FieldValidationMetadata.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/FieldValidationMetadata.cs
new file mode 100644
index 00000000000..5dda0446d28
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/FieldValidationMetadata.cs
@@ -0,0 +1,49 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Collections.Generic;
+ using System.Collections.ObjectModel;
+
+ public class FieldValidationMetadata {
+
+ private string _fieldName;
+ private readonly Collection<ModelClientValidationRule> _validationRules = new Collection<ModelClientValidationRule>();
+
+ public string FieldName {
+ get {
+ return _fieldName ?? String.Empty;
+ }
+ set {
+ _fieldName = value;
+ }
+ }
+
+ public bool ReplaceValidationMessageContents {
+ get;
+ set;
+ }
+
+ public string ValidationMessageId {
+ get;
+ set;
+ }
+
+ public ICollection<ModelClientValidationRule> ValidationRules {
+ get {
+ return _validationRules;
+ }
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/FileContentResult.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/FileContentResult.cs
new file mode 100644
index 00000000000..8a2fdc0f6f5
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/FileContentResult.cs
@@ -0,0 +1,41 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Web;
+
+ public class FileContentResult : FileResult {
+
+ public FileContentResult(byte[] fileContents, string contentType)
+ : base(contentType) {
+ if (fileContents == null) {
+ throw new ArgumentNullException("fileContents");
+ }
+
+ FileContents = fileContents;
+ }
+
+ [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays",
+ Justification = "There's no reason to tamper-proof this array since it's supplied to the type's constructor.")]
+ public byte[] FileContents {
+ get;
+ private set;
+ }
+
+ protected override void WriteFile(HttpResponseBase response) {
+ response.OutputStream.Write(FileContents, 0, FileContents.Length);
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/FilePathResult.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/FilePathResult.cs
new file mode 100644
index 00000000000..550113f9aba
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/FilePathResult.cs
@@ -0,0 +1,39 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Web;
+ using System.Web.Mvc.Resources;
+
+ public class FilePathResult : FileResult {
+
+ public FilePathResult(string fileName, string contentType)
+ : base(contentType) {
+ if (String.IsNullOrEmpty(fileName)) {
+ throw new ArgumentException(MvcResources.Common_NullOrEmpty, "fileName");
+ }
+
+ FileName = fileName;
+ }
+
+ public string FileName {
+ get;
+ private set;
+ }
+
+ protected override void WriteFile(HttpResponseBase response) {
+ response.TransmitFile(FileName);
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/FileResult.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/FileResult.cs
new file mode 100644
index 00000000000..8127e46216b
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/FileResult.cs
@@ -0,0 +1,143 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Net.Mime;
+ using System.Text;
+ using System.Web;
+ using System.Web.Mvc.Resources;
+
+ public abstract class FileResult : ActionResult {
+
+ protected FileResult(string contentType) {
+ if (String.IsNullOrEmpty(contentType)) {
+ throw new ArgumentException(MvcResources.Common_NullOrEmpty, "contentType");
+ }
+
+ ContentType = contentType;
+ }
+
+ private string _fileDownloadName;
+
+ public string ContentType {
+ get;
+ private set;
+ }
+
+ public string FileDownloadName {
+ get {
+ return _fileDownloadName ?? String.Empty;
+ }
+ set {
+ _fileDownloadName = value;
+ }
+ }
+
+ public override void ExecuteResult(ControllerContext context) {
+ if (context == null) {
+ throw new ArgumentNullException("context");
+ }
+
+ HttpResponseBase response = context.HttpContext.Response;
+ response.ContentType = ContentType;
+
+ if (!String.IsNullOrEmpty(FileDownloadName)) {
+ // From RFC 2183, Sec. 2.3:
+ // The sender may want to suggest a filename to be used if the entity is
+ // detached and stored in a separate file. If the receiving MUA writes
+ // the entity to a file, the suggested filename should be used as a
+ // basis for the actual filename, where possible.
+ string headerValue = ContentDispositionUtil.GetHeaderValue(FileDownloadName);
+ context.HttpContext.Response.AddHeader("Content-Disposition", headerValue);
+ }
+
+ WriteFile(response);
+ }
+
+ protected abstract void WriteFile(HttpResponseBase response);
+
+ private static class ContentDispositionUtil {
+ private const string _hexDigits = "0123456789ABCDEF";
+
+ private static void AddByteToStringBuilder(byte b, StringBuilder builder) {
+ builder.Append('%');
+
+ int i = b;
+ AddHexDigitToStringBuilder(i >> 4, builder);
+ AddHexDigitToStringBuilder(i % 16, builder);
+ }
+
+ private static void AddHexDigitToStringBuilder(int digit, StringBuilder builder) {
+ builder.Append(_hexDigits[digit]);
+ }
+
+ private static string CreateRfc2231HeaderValue(string filename) {
+ StringBuilder builder = new StringBuilder("attachment; filename*=UTF-8''");
+
+ byte[] filenameBytes = Encoding.UTF8.GetBytes(filename);
+ foreach (byte b in filenameBytes) {
+ if (IsByteValidHeaderValueCharacter(b)) {
+ builder.Append((char)b);
+ }
+ else {
+ AddByteToStringBuilder(b, builder);
+ }
+ }
+
+ return builder.ToString();
+ }
+
+ public static string GetHeaderValue(string fileName) {
+ try {
+ // first, try using the .NET built-in generator
+ ContentDisposition disposition = new ContentDisposition() { FileName = fileName };
+ return disposition.ToString();
+ }
+ catch (FormatException) {
+ // otherwise, fall back to RFC 2231 extensions generator
+ return CreateRfc2231HeaderValue(fileName);
+ }
+ }
+
+ // Application of RFC 2231 Encoding to Hypertext Transfer Protocol (HTTP) Header Fields, sec. 3.2
+ // http://greenbytes.de/tech/webdav/draft-reschke-rfc2231-in-http-latest.html
+ private static bool IsByteValidHeaderValueCharacter(byte b) {
+ if ((byte)'0' <= b && b <= (byte)'9') {
+ return true; // is digit
+ }
+ if ((byte)'a' <= b && b <= (byte)'z') {
+ return true; // lowercase letter
+ }
+ if ((byte)'A' <= b && b <= (byte)'Z') {
+ return true; // uppercase letter
+ }
+
+ switch (b) {
+ case (byte)'-':
+ case (byte)'.':
+ case (byte)'_':
+ case (byte)'~':
+ case (byte)':':
+ case (byte)'!':
+ case (byte)'$':
+ case (byte)'&':
+ case (byte)'+':
+ return true;
+ }
+
+ return false;
+ }
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/FileStreamResult.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/FileStreamResult.cs
new file mode 100644
index 00000000000..a6e1a6fb89e
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/FileStreamResult.cs
@@ -0,0 +1,56 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.IO;
+ using System.Web;
+
+ public class FileStreamResult : FileResult {
+
+ // default buffer size as defined in BufferedStream type
+ private const int _bufferSize = 0x1000;
+
+ public FileStreamResult(Stream fileStream, string contentType)
+ : base(contentType) {
+ if (fileStream == null) {
+ throw new ArgumentNullException("fileStream");
+ }
+
+ FileStream = fileStream;
+ }
+
+ public Stream FileStream {
+ get;
+ private set;
+ }
+
+ protected override void WriteFile(HttpResponseBase response) {
+ // grab chunks of data and write to the output stream
+ Stream outputStream = response.OutputStream;
+ using (FileStream) {
+ byte[] buffer = new byte[_bufferSize];
+
+ while (true) {
+ int bytesRead = FileStream.Read(buffer, 0, _bufferSize);
+ if (bytesRead == 0) {
+ // no more data
+ break;
+ }
+
+ outputStream.Write(buffer, 0, bytesRead);
+ }
+ }
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/FilterAttribute.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/FilterAttribute.cs
new file mode 100644
index 00000000000..1cda086a21c
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/FilterAttribute.cs
@@ -0,0 +1,35 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Web.Mvc.Resources;
+
+ [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
+ public abstract class FilterAttribute : Attribute {
+
+ private int _order = -1;
+
+ public int Order {
+ get {
+ return _order;
+ }
+ set {
+ if (value < -1) {
+ throw new ArgumentOutOfRangeException("value",
+ MvcResources.FilterAttribute_OrderOutOfRange);
+ }
+ _order = value;
+ }
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/FilterInfo.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/FilterInfo.cs
new file mode 100644
index 00000000000..031ffe34d97
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/FilterInfo.cs
@@ -0,0 +1,48 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System.Collections.Generic;
+
+ public class FilterInfo {
+
+ private List<IActionFilter> _actionFilters = new List<IActionFilter>();
+ private List<IAuthorizationFilter> _authorizationFilters = new List<IAuthorizationFilter>();
+ private List<IExceptionFilter> _exceptionFilters = new List<IExceptionFilter>();
+ private List<IResultFilter> _resultFilters = new List<IResultFilter>();
+
+ public IList<IActionFilter> ActionFilters {
+ get {
+ return _actionFilters;
+ }
+ }
+
+ public IList<IAuthorizationFilter> AuthorizationFilters {
+ get {
+ return _authorizationFilters;
+ }
+ }
+
+ public IList<IExceptionFilter> ExceptionFilters {
+ get {
+ return _exceptionFilters;
+ }
+ }
+
+ public IList<IResultFilter> ResultFilters {
+ get {
+ return _resultFilters;
+ }
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/FormCollection.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/FormCollection.cs
new file mode 100644
index 00000000000..3736bae1d4b
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/FormCollection.cs
@@ -0,0 +1,89 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Collections.Specialized;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Globalization;
+ using System.Web.Mvc.Resources;
+
+ [SuppressMessage("Microsoft.Usage", "CA2237:MarkISerializableTypesWithSerializable",
+ Justification = "It is not anticipated that users will need to serialize this type.")]
+ [SuppressMessage("Microsoft.Design", "CA1035:ICollectionImplementationsHaveStronglyTypedMembers",
+ Justification = "It is not anticipated that users will call FormCollection.CopyTo().")]
+ [FormCollectionBinder]
+ public sealed class FormCollection : NameValueCollection, IValueProvider {
+
+ public FormCollection() {
+ }
+
+ public FormCollection(NameValueCollection collection) {
+ if (collection == null) {
+ throw new ArgumentNullException("collection");
+ }
+
+ Add(collection);
+ }
+
+ public ValueProviderResult GetValue(string name) {
+ if (name == null) {
+ throw new ArgumentNullException("name");
+ }
+
+ string[] rawValue = GetValues(name);
+ if (rawValue == null) {
+ return null;
+ }
+
+ string attemptedValue = this[name];
+ return new ValueProviderResult(rawValue, attemptedValue, CultureInfo.CurrentCulture);
+ }
+
+ public IValueProvider ToValueProvider() {
+ return this;
+ }
+
+ #region IValueProvider Members
+ bool IValueProvider.ContainsPrefix(string prefix) {
+ return ValueProviderUtil.CollectionContainsPrefix(AllKeys, prefix);
+ }
+
+ ValueProviderResult IValueProvider.GetValue(string key) {
+ return GetValue(key);
+ }
+ #endregion
+
+ private sealed class FormCollectionBinderAttribute : CustomModelBinderAttribute {
+
+ // since the FormCollectionModelBinder.BindModel() method is thread-safe, we only need to keep
+ // a single instance of the binder around
+ private static readonly FormCollectionModelBinder _binder = new FormCollectionModelBinder();
+
+ public override IModelBinder GetBinder() {
+ return _binder;
+ }
+
+ // this class is used for generating a FormCollection object
+ private sealed class FormCollectionModelBinder : IModelBinder {
+ public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) {
+ if (controllerContext == null) {
+ throw new ArgumentNullException("controllerContext");
+ }
+
+ return new FormCollection(controllerContext.HttpContext.Request.Form);
+ }
+ }
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/FormContext.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/FormContext.cs
new file mode 100644
index 00000000000..a2c6ec83f6a
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/FormContext.cs
@@ -0,0 +1,84 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Collections;
+ using System.Collections.Generic;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Web.Script.Serialization;
+
+ public class FormContext {
+
+ private readonly Dictionary<string, FieldValidationMetadata> _fieldValidators = new Dictionary<string, FieldValidationMetadata>();
+
+ public IDictionary<string, FieldValidationMetadata> FieldValidators {
+ get {
+ return _fieldValidators;
+ }
+ }
+
+ public string FormId {
+ get;
+ set;
+ }
+
+ public bool ReplaceValidationSummary {
+ get;
+ set;
+ }
+
+ public string ValidationSummaryId {
+ get;
+ set;
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate",
+ Justification = "Performs a potentially time-consuming conversion.")]
+ public string GetJsonValidationMetadata() {
+ JavaScriptSerializer serializer = new JavaScriptSerializer();
+
+ SortedDictionary<string, object> dict = new SortedDictionary<string, object>() {
+ { "Fields", FieldValidators.Values },
+ { "FormId", FormId }
+ };
+ if (!String.IsNullOrEmpty(ValidationSummaryId)) {
+ dict["ValidationSummaryId"] = ValidationSummaryId;
+ }
+ dict["ReplaceValidationSummary"] = ReplaceValidationSummary;
+
+ return serializer.Serialize(dict);
+ }
+
+ public FieldValidationMetadata GetValidationMetadataForField(string fieldName) {
+ return GetValidationMetadataForField(fieldName, false /* createIfNotFound */);
+ }
+
+ public FieldValidationMetadata GetValidationMetadataForField(string fieldName, bool createIfNotFound) {
+ if (String.IsNullOrEmpty(fieldName)) {
+ throw Error.ParameterCannotBeNullOrEmpty("fieldName");
+ }
+
+ FieldValidationMetadata metadata;
+ if (!FieldValidators.TryGetValue(fieldName, out metadata)) {
+ if (createIfNotFound) {
+ metadata = new FieldValidationMetadata() {
+ FieldName = fieldName
+ };
+ FieldValidators[fieldName] = metadata;
+ }
+ }
+ return metadata;
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/FormMethod.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/FormMethod.cs
new file mode 100644
index 00000000000..0c9ebc89432
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/FormMethod.cs
@@ -0,0 +1,18 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ public enum FormMethod {
+ Get,
+ Post
+ }
+} \ No newline at end of file
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/FormValueProvider.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/FormValueProvider.cs
new file mode 100644
index 00000000000..73497341ec3
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/FormValueProvider.cs
@@ -0,0 +1,25 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Collections.Specialized;
+ using System.Globalization;
+
+ public sealed class FormValueProvider : NameValueCollectionValueProvider {
+
+ public FormValueProvider(ControllerContext controllerContext)
+ : base(controllerContext.HttpContext.Request.Form, CultureInfo.CurrentCulture) {
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/FormValueProviderFactory.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/FormValueProviderFactory.cs
new file mode 100644
index 00000000000..686f3abe40d
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/FormValueProviderFactory.cs
@@ -0,0 +1,27 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+
+ public sealed class FormValueProviderFactory : ValueProviderFactory {
+
+ public override IValueProvider GetValueProvider(ControllerContext controllerContext) {
+ if (controllerContext == null) {
+ throw new ArgumentNullException("controllerContext");
+ }
+
+ return new FormValueProvider(controllerContext);
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/HandleErrorAttribute.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/HandleErrorAttribute.cs
new file mode 100644
index 00000000000..99b40cfecf8
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/HandleErrorAttribute.cs
@@ -0,0 +1,119 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Globalization;
+ using System.Web;
+ using System.Web.Mvc.Resources;
+
+ [SuppressMessage("Microsoft.Performance", "CA1813:AvoidUnsealedAttributes",
+ Justification = "This attribute is AllowMultiple = true and users might want to override behavior.")]
+ [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
+ public class HandleErrorAttribute : FilterAttribute, IExceptionFilter {
+
+ private const string _defaultView = "Error";
+
+ private readonly object _typeId = new object();
+
+ private Type _exceptionType = typeof(Exception);
+ private string _master;
+ private string _view;
+
+ public Type ExceptionType {
+ get {
+ return _exceptionType;
+ }
+ set {
+ if (value == null) {
+ throw new ArgumentNullException("value");
+ }
+ if (!typeof(Exception).IsAssignableFrom(value)) {
+ throw new ArgumentException(String.Format(CultureInfo.CurrentUICulture,
+ MvcResources.ExceptionViewAttribute_NonExceptionType, value.FullName));
+ }
+
+ _exceptionType = value;
+ }
+ }
+
+ public string Master {
+ get {
+ return _master ?? String.Empty;
+ }
+ set {
+ _master = value;
+ }
+ }
+
+ public override object TypeId {
+ get {
+ return _typeId;
+ }
+ }
+
+ public string View {
+ get {
+ return (!String.IsNullOrEmpty(_view)) ? _view : _defaultView;
+ }
+ set {
+ _view = value;
+ }
+ }
+
+ public virtual void OnException(ExceptionContext filterContext) {
+ if (filterContext == null) {
+ throw new ArgumentNullException("filterContext");
+ }
+ if (filterContext.IsChildAction) {
+ return;
+ }
+
+ // If custom errors are disabled, we need to let the normal ASP.NET exception handler
+ // execute so that the user can see useful debugging information.
+ if (filterContext.ExceptionHandled || !filterContext.HttpContext.IsCustomErrorEnabled) {
+ return;
+ }
+
+ Exception exception = filterContext.Exception;
+
+ // If this is not an HTTP 500 (for example, if somebody throws an HTTP 404 from an action method),
+ // ignore it.
+ if (new HttpException(null, exception).GetHttpCode() != 500) {
+ return;
+ }
+
+ if (!ExceptionType.IsInstanceOfType(exception)) {
+ return;
+ }
+
+ string controllerName = (string)filterContext.RouteData.Values["controller"];
+ string actionName = (string)filterContext.RouteData.Values["action"];
+ HandleErrorInfo model = new HandleErrorInfo(filterContext.Exception, controllerName, actionName);
+ filterContext.Result = new ViewResult {
+ ViewName = View,
+ MasterName = Master,
+ ViewData = new ViewDataDictionary<HandleErrorInfo>(model),
+ TempData = filterContext.Controller.TempData
+ };
+ filterContext.ExceptionHandled = true;
+ filterContext.HttpContext.Response.Clear();
+ filterContext.HttpContext.Response.StatusCode = 500;
+
+ // Certain versions of IIS will sometimes use their own error page when
+ // they detect a server error. Setting this property indicates that we
+ // want it to try to render ASP.NET MVC's error page instead.
+ filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
+ }
+ }
+} \ No newline at end of file
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/HandleErrorInfo.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/HandleErrorInfo.cs
new file mode 100644
index 00000000000..d02f911de5e
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/HandleErrorInfo.cs
@@ -0,0 +1,51 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Web.Mvc.Resources;
+
+ public class HandleErrorInfo {
+
+ public HandleErrorInfo(Exception exception, string controllerName, string actionName) {
+ if (exception == null) {
+ throw new ArgumentNullException("exception");
+ }
+ if (String.IsNullOrEmpty(controllerName)) {
+ throw new ArgumentException(MvcResources.Common_NullOrEmpty, "controllerName");
+ }
+ if (string.IsNullOrEmpty(actionName)) {
+ throw new ArgumentException(MvcResources.Common_NullOrEmpty, "actionName");
+ }
+
+ Exception = exception;
+ ControllerName = controllerName;
+ ActionName = actionName;
+ }
+
+ public string ActionName {
+ get;
+ private set;
+ }
+
+ public string ControllerName {
+ get;
+ private set;
+ }
+
+ public Exception Exception {
+ get;
+ private set;
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/HiddenInputAttribute.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/HiddenInputAttribute.cs
new file mode 100644
index 00000000000..b1778d4cf73
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/HiddenInputAttribute.cs
@@ -0,0 +1,24 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+
+ [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
+ public sealed class HiddenInputAttribute : Attribute {
+ public HiddenInputAttribute() {
+ DisplayValue = true;
+ }
+
+ public bool DisplayValue { get; set; }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/Html/ChildActionExtensions.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Html/ChildActionExtensions.cs
new file mode 100644
index 00000000000..cd22769a6ac
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Html/ChildActionExtensions.cs
@@ -0,0 +1,150 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc.Html {
+ using System.Collections.Generic;
+ using System.Globalization;
+ using System.IO;
+ using System.Linq;
+ using System.Web.Mvc.Resources;
+ using System.Web.Routing;
+
+ public static class ChildActionExtensions {
+
+ // Action
+
+ public static MvcHtmlString Action(this HtmlHelper htmlHelper, string actionName) {
+ return Action(htmlHelper, actionName, null /* controllerName */, null /* routeValues */);
+ }
+
+ public static MvcHtmlString Action(this HtmlHelper htmlHelper, string actionName, object routeValues) {
+ return Action(htmlHelper, actionName, null /* controllerName */, new RouteValueDictionary(routeValues));
+ }
+
+ public static MvcHtmlString Action(this HtmlHelper htmlHelper, string actionName, RouteValueDictionary routeValues) {
+ return Action(htmlHelper, actionName, null /* controllerName */, routeValues);
+ }
+
+ public static MvcHtmlString Action(this HtmlHelper htmlHelper, string actionName, string controllerName) {
+ return Action(htmlHelper, actionName, controllerName, null /* routeValues */);
+ }
+
+ public static MvcHtmlString Action(this HtmlHelper htmlHelper, string actionName, string controllerName, object routeValues) {
+ return Action(htmlHelper, actionName, controllerName, new RouteValueDictionary(routeValues));
+ }
+
+ public static MvcHtmlString Action(this HtmlHelper htmlHelper, string actionName, string controllerName, RouteValueDictionary routeValues) {
+ StringWriter writer = new StringWriter(CultureInfo.CurrentCulture);
+ ActionHelper(htmlHelper, actionName, controllerName, routeValues, writer);
+ return MvcHtmlString.Create(writer.ToString());
+ }
+
+ // RenderAction
+
+ public static void RenderAction(this HtmlHelper htmlHelper, string actionName) {
+ RenderAction(htmlHelper, actionName, null /* controllerName */, null /* routeValues */);
+ }
+
+ public static void RenderAction(this HtmlHelper htmlHelper, string actionName, object routeValues) {
+ RenderAction(htmlHelper, actionName, null /* controllerName */, new RouteValueDictionary(routeValues));
+ }
+
+ public static void RenderAction(this HtmlHelper htmlHelper, string actionName, RouteValueDictionary routeValues) {
+ RenderAction(htmlHelper, actionName, null /* controllerName */, routeValues);
+ }
+
+ public static void RenderAction(this HtmlHelper htmlHelper, string actionName, string controllerName) {
+ RenderAction(htmlHelper, actionName, controllerName, null /* routeValues */);
+ }
+
+ public static void RenderAction(this HtmlHelper htmlHelper, string actionName, string controllerName, object routeValues) {
+ RenderAction(htmlHelper, actionName, controllerName, new RouteValueDictionary(routeValues));
+ }
+
+ public static void RenderAction(this HtmlHelper htmlHelper, string actionName, string controllerName, RouteValueDictionary routeValues) {
+ ActionHelper(htmlHelper, actionName, controllerName, routeValues, htmlHelper.ViewContext.Writer);
+ }
+
+ // Helpers
+
+ internal static void ActionHelper(HtmlHelper htmlHelper, string actionName, string controllerName, RouteValueDictionary routeValues, TextWriter textWriter) {
+ if (htmlHelper == null) {
+ throw new ArgumentNullException("htmlHelper");
+ }
+ if (String.IsNullOrEmpty(actionName)) {
+ throw new ArgumentException(MvcResources.Common_NullOrEmpty, "actionName");
+ }
+
+ routeValues = MergeDictionaries(routeValues, htmlHelper.ViewContext.RouteData.Values);
+ routeValues["action"] = actionName;
+ if (!String.IsNullOrEmpty(controllerName)) {
+ routeValues["controller"] = controllerName;
+ }
+
+ bool usingAreas;
+ VirtualPathData vpd = htmlHelper.RouteCollection.GetVirtualPathForArea(htmlHelper.ViewContext.RequestContext, null /* name */, routeValues, out usingAreas);
+ if (vpd == null) {
+ throw new InvalidOperationException(MvcResources.Common_NoRouteMatched);
+ }
+
+ if (usingAreas) {
+ routeValues.Remove("area");
+ }
+ RouteData routeData = CreateRouteData(vpd.Route, routeValues, vpd.DataTokens, htmlHelper.ViewContext);
+ HttpContextBase httpContext = htmlHelper.ViewContext.HttpContext;
+ RequestContext requestContext = new RequestContext(httpContext, routeData);
+ ChildActionMvcHandler handler = new ChildActionMvcHandler(requestContext);
+ httpContext.Server.Execute(HttpHandlerUtil.WrapForServerExecute(handler), textWriter, true /* preserveForm */);
+ }
+
+ private static RouteData CreateRouteData(RouteBase route, RouteValueDictionary routeValues, RouteValueDictionary dataTokens, ViewContext parentViewContext) {
+ RouteData routeData = new RouteData();
+
+ foreach (KeyValuePair<string, object> kvp in routeValues) {
+ routeData.Values.Add(kvp.Key, kvp.Value);
+ }
+
+ foreach (KeyValuePair<string, object> kvp in dataTokens) {
+ routeData.DataTokens.Add(kvp.Key, kvp.Value);
+ }
+
+ routeData.Route = route;
+ routeData.DataTokens[ControllerContext.PARENT_ACTION_VIEWCONTEXT] = parentViewContext;
+ return routeData;
+ }
+
+ private static RouteValueDictionary MergeDictionaries(params RouteValueDictionary[] dictionaries) {
+ // Merge existing route values with the user provided values
+ var result = new RouteValueDictionary();
+
+ foreach (RouteValueDictionary dictionary in dictionaries.Where(d => d != null)) {
+ foreach (KeyValuePair<string, object> kvp in dictionary) {
+ if (!result.ContainsKey(kvp.Key)) {
+ result.Add(kvp.Key, kvp.Value);
+ }
+ }
+ }
+
+ return result;
+ }
+
+ internal class ChildActionMvcHandler : MvcHandler {
+ public ChildActionMvcHandler(RequestContext context)
+ : base(context) {
+ }
+
+ protected internal override void AddVersionHeader(HttpContextBase httpContext) {
+ // No version header for child actions
+ }
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/Html/DefaultDisplayTemplates.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Html/DefaultDisplayTemplates.cs
new file mode 100644
index 00000000000..7edcb3be4a0
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Html/DefaultDisplayTemplates.cs
@@ -0,0 +1,206 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc.Html {
+ using System;
+ using System.Collections;
+ using System.Collections.Generic;
+ using System.Data;
+ using System.Globalization;
+ using System.Linq;
+ using System.Text;
+ using System.Web.Mvc.Resources;
+ using System.Web.UI.WebControls;
+
+ internal static class DefaultDisplayTemplates {
+ internal static string BooleanTemplate(HtmlHelper html) {
+ bool? value = null;
+ if (html.ViewContext.ViewData.Model != null) {
+ value = Convert.ToBoolean(html.ViewContext.ViewData.Model, CultureInfo.InvariantCulture);
+ }
+
+ return html.ViewContext.ViewData.ModelMetadata.IsNullableValueType
+ ? BooleanTemplateDropDownList(value)
+ : BooleanTemplateCheckbox(value ?? false);
+ }
+
+ private static string BooleanTemplateCheckbox(bool value) {
+ TagBuilder inputTag = new TagBuilder("input");
+ inputTag.AddCssClass("check-box");
+ inputTag.Attributes["disabled"] = "disabled";
+ inputTag.Attributes["type"] = "checkbox";
+ if (value) {
+ inputTag.Attributes["checked"] = "checked";
+ }
+
+ return inputTag.ToString(TagRenderMode.SelfClosing);
+ }
+
+ private static string BooleanTemplateDropDownList(bool? value) {
+ StringBuilder builder = new StringBuilder();
+
+ TagBuilder selectTag = new TagBuilder("select");
+ selectTag.AddCssClass("list-box");
+ selectTag.AddCssClass("tri-state");
+ selectTag.Attributes["disabled"] = "disabled";
+ builder.Append(selectTag.ToString(TagRenderMode.StartTag));
+
+ foreach (SelectListItem item in DefaultEditorTemplates.TriStateValues(value)) {
+ builder.Append(SelectExtensions.ListItemToOption(item));
+ }
+
+ builder.Append(selectTag.ToString(TagRenderMode.EndTag));
+ return builder.ToString();
+ }
+
+ internal static string CollectionTemplate(HtmlHelper html) {
+ return CollectionTemplate(html, TemplateHelpers.TemplateHelper);
+ }
+
+ internal static string CollectionTemplate(HtmlHelper html, TemplateHelpers.TemplateHelperDelegate templateHelper) {
+ object model = html.ViewContext.ViewData.ModelMetadata.Model;
+ if (model == null) {
+ return String.Empty;
+ }
+
+ IEnumerable collection = model as IEnumerable;
+ if (collection == null) {
+ throw new InvalidOperationException(
+ String.Format(
+ CultureInfo.CurrentCulture,
+ MvcResources.Templates_TypeMustImplementIEnumerable,
+ model.GetType().FullName
+ )
+ );
+ }
+
+ Type typeInCollection = typeof(string);
+ Type genericEnumerableType = TypeHelpers.ExtractGenericInterface(collection.GetType(), typeof(IEnumerable<>));
+ if (genericEnumerableType != null) {
+ typeInCollection = genericEnumerableType.GetGenericArguments()[0];
+ }
+ bool typeInCollectionIsNullableValueType = TypeHelpers.IsNullableValueType(typeInCollection);
+
+ string oldPrefix = html.ViewContext.ViewData.TemplateInfo.HtmlFieldPrefix;
+
+ try {
+ html.ViewContext.ViewData.TemplateInfo.HtmlFieldPrefix = String.Empty;
+
+ string fieldNameBase = oldPrefix;
+ StringBuilder result = new StringBuilder();
+ int index = 0;
+
+ foreach (object item in collection) {
+ Type itemType = typeInCollection;
+ if (item != null && !typeInCollectionIsNullableValueType) {
+ itemType = item.GetType();
+ }
+ ModelMetadata metadata = ModelMetadataProviders.Current.GetMetadataForType(() => item, itemType);
+ string fieldName = String.Format(CultureInfo.InvariantCulture, "{0}[{1}]", fieldNameBase, index++);
+ string output = templateHelper(html, metadata, fieldName, null /* templateName */, DataBoundControlMode.ReadOnly, null /* additionalViewData */);
+ result.Append(output);
+ }
+
+ return result.ToString();
+ }
+ finally {
+ html.ViewContext.ViewData.TemplateInfo.HtmlFieldPrefix = oldPrefix;
+ }
+ }
+
+ internal static string DecimalTemplate(HtmlHelper html) {
+ if (html.ViewContext.ViewData.TemplateInfo.FormattedModelValue == html.ViewContext.ViewData.ModelMetadata.Model) {
+ html.ViewContext.ViewData.TemplateInfo.FormattedModelValue = String.Format(CultureInfo.CurrentCulture, "{0:0.00}", html.ViewContext.ViewData.ModelMetadata.Model);
+ }
+
+ return StringTemplate(html);
+ }
+
+ internal static string EmailAddressTemplate(HtmlHelper html) {
+ return String.Format(CultureInfo.InvariantCulture,
+ "<a href=\"mailto:{0}\">{1}</a>",
+ html.AttributeEncode(html.ViewContext.ViewData.Model),
+ html.Encode(html.ViewContext.ViewData.TemplateInfo.FormattedModelValue));
+ }
+
+ internal static string HiddenInputTemplate(HtmlHelper html) {
+ if (html.ViewContext.ViewData.ModelMetadata.HideSurroundingHtml) {
+ return String.Empty;
+ }
+ return StringTemplate(html);
+ }
+
+ internal static string HtmlTemplate(HtmlHelper html) {
+ return html.ViewContext.ViewData.TemplateInfo.FormattedModelValue.ToString();
+ }
+
+ internal static string ObjectTemplate(HtmlHelper html) {
+ return ObjectTemplate(html, TemplateHelpers.TemplateHelper);
+ }
+
+ internal static string ObjectTemplate(HtmlHelper html, TemplateHelpers.TemplateHelperDelegate templateHelper) {
+ ViewDataDictionary viewData = html.ViewContext.ViewData;
+ TemplateInfo templateInfo = viewData.TemplateInfo;
+ ModelMetadata modelMetadata = viewData.ModelMetadata;
+ StringBuilder builder = new StringBuilder();
+
+ if (modelMetadata.Model == null) { // DDB #225237
+ return modelMetadata.NullDisplayText;
+ }
+
+ if (templateInfo.TemplateDepth > 1) { // DDB #224751
+ return modelMetadata.SimpleDisplayText;
+ }
+
+ foreach (ModelMetadata propertyMetadata in modelMetadata.Properties.Where(pm => ShouldShow(pm, templateInfo))) {
+ if (!propertyMetadata.HideSurroundingHtml) {
+ string label = propertyMetadata.GetDisplayName();
+ if (!String.IsNullOrEmpty(label)) {
+ builder.AppendFormat(CultureInfo.InvariantCulture, "<div class=\"display-label\">{0}</div>", label);
+ builder.AppendLine();
+ }
+
+ builder.Append("<div class=\"display-field\">");
+ }
+
+ builder.Append(templateHelper(html, propertyMetadata, propertyMetadata.PropertyName, null /* templateName */, DataBoundControlMode.ReadOnly, null /* additionalViewData */));
+
+ if (!propertyMetadata.HideSurroundingHtml) {
+ builder.AppendLine("</div>");
+ }
+ }
+
+ return builder.ToString();
+ }
+
+ private static bool ShouldShow(ModelMetadata metadata, TemplateInfo templateInfo) {
+ return
+ metadata.ShowForDisplay
+#if false
+ && metadata.ModelType != typeof(EntityState)
+#endif
+ && !metadata.IsComplexType
+ && !templateInfo.Visited(metadata);
+ }
+
+ internal static string StringTemplate(HtmlHelper html) {
+ return html.Encode(html.ViewContext.ViewData.TemplateInfo.FormattedModelValue);
+ }
+
+ internal static string UrlTemplate(HtmlHelper html) {
+ return String.Format(CultureInfo.InvariantCulture,
+ "<a href=\"{0}\">{1}</a>",
+ html.AttributeEncode(html.ViewContext.ViewData.Model),
+ html.Encode(html.ViewContext.ViewData.TemplateInfo.FormattedModelValue));
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/Html/DefaultEditorTemplates.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Html/DefaultEditorTemplates.cs
new file mode 100644
index 00000000000..a478dbc5b03
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Html/DefaultEditorTemplates.cs
@@ -0,0 +1,215 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc.Html {
+ using System;
+ using System.Collections;
+ using System.Collections.Generic;
+ using System.Data;
+ using System.Data.Linq;
+ using System.Globalization;
+ using System.Linq;
+ using System.Text;
+ using System.Web.Mvc.Resources;
+ using System.Web.UI.WebControls;
+
+ internal static class DefaultEditorTemplates {
+ internal static string BooleanTemplate(HtmlHelper html) {
+ bool? value = null;
+ if (html.ViewContext.ViewData.Model != null) {
+ value = Convert.ToBoolean(html.ViewContext.ViewData.Model, CultureInfo.InvariantCulture);
+ }
+
+ return html.ViewContext.ViewData.ModelMetadata.IsNullableValueType
+ ? BooleanTemplateDropDownList(html, value)
+ : BooleanTemplateCheckbox(html, value ?? false);
+ }
+
+ private static string BooleanTemplateCheckbox(HtmlHelper html, bool value) {
+ return html.CheckBox(String.Empty, value, CreateHtmlAttributes("check-box")).ToHtmlString();
+ }
+
+ private static string BooleanTemplateDropDownList(HtmlHelper html, bool? value) {
+ return html.DropDownList(String.Empty, TriStateValues(value), CreateHtmlAttributes("list-box tri-state")).ToHtmlString();
+
+ }
+
+ internal static string CollectionTemplate(HtmlHelper html) {
+ return CollectionTemplate(html, TemplateHelpers.TemplateHelper);
+ }
+
+ internal static string CollectionTemplate(HtmlHelper html, TemplateHelpers.TemplateHelperDelegate templateHelper) {
+ object model = html.ViewContext.ViewData.ModelMetadata.Model;
+ if (model == null) {
+ return String.Empty;
+ }
+
+ IEnumerable collection = model as IEnumerable;
+ if (collection == null) {
+ throw new InvalidOperationException(
+ String.Format(
+ CultureInfo.CurrentCulture,
+ MvcResources.Templates_TypeMustImplementIEnumerable,
+ model.GetType().FullName
+ )
+ );
+ }
+
+ Type typeInCollection = typeof(string);
+ Type genericEnumerableType = TypeHelpers.ExtractGenericInterface(collection.GetType(), typeof(IEnumerable<>));
+ if (genericEnumerableType != null) {
+ typeInCollection = genericEnumerableType.GetGenericArguments()[0];
+ }
+ bool typeInCollectionIsNullableValueType = TypeHelpers.IsNullableValueType(typeInCollection);
+
+ string oldPrefix = html.ViewContext.ViewData.TemplateInfo.HtmlFieldPrefix;
+
+ try {
+ html.ViewContext.ViewData.TemplateInfo.HtmlFieldPrefix = String.Empty;
+
+ string fieldNameBase = oldPrefix;
+ StringBuilder result = new StringBuilder();
+ int index = 0;
+
+ foreach (object item in collection) {
+ Type itemType = typeInCollection;
+ if (item != null && !typeInCollectionIsNullableValueType) {
+ itemType = item.GetType();
+ }
+ ModelMetadata metadata = ModelMetadataProviders.Current.GetMetadataForType(() => item, itemType);
+ string fieldName = String.Format(CultureInfo.InvariantCulture, "{0}[{1}]", fieldNameBase, index++);
+ string output = templateHelper(html, metadata, fieldName, null /* templateName */, DataBoundControlMode.Edit, null /* additionalViewData */);
+ result.Append(output);
+ }
+
+ return result.ToString();
+ }
+ finally {
+ html.ViewContext.ViewData.TemplateInfo.HtmlFieldPrefix = oldPrefix;
+ }
+ }
+
+ internal static string DecimalTemplate(HtmlHelper html) {
+ if (html.ViewContext.ViewData.TemplateInfo.FormattedModelValue == html.ViewContext.ViewData.ModelMetadata.Model) {
+ html.ViewContext.ViewData.TemplateInfo.FormattedModelValue = String.Format(CultureInfo.CurrentCulture, "{0:0.00}", html.ViewContext.ViewData.ModelMetadata.Model);
+ }
+
+ return StringTemplate(html);
+ }
+
+ internal static string HiddenInputTemplate(HtmlHelper html) {
+ string result;
+
+ if (html.ViewContext.ViewData.ModelMetadata.HideSurroundingHtml) {
+ result = String.Empty;
+ }
+ else {
+ result = DefaultDisplayTemplates.StringTemplate(html);
+ }
+
+ object model = html.ViewContext.ViewData.Model;
+
+ Binary modelAsBinary = model as Binary;
+ if (modelAsBinary != null) {
+ model = Convert.ToBase64String(modelAsBinary.ToArray());
+ }
+ else {
+ byte[] modelAsByteArray = model as byte[];
+ if (modelAsByteArray != null) {
+ model = Convert.ToBase64String(modelAsByteArray);
+ }
+ }
+
+ result += html.Hidden(String.Empty, model).ToHtmlString();
+ return result;
+ }
+
+ internal static string MultilineTextTemplate(HtmlHelper html) {
+ return html.TextArea(String.Empty,
+ html.ViewContext.ViewData.TemplateInfo.FormattedModelValue.ToString(),
+ 0 /* rows */, 0 /* columns */,
+ CreateHtmlAttributes("text-box multi-line")).ToHtmlString();
+ }
+
+ private static IDictionary<string, object> CreateHtmlAttributes(string className) {
+ return new Dictionary<string, object>() {
+ { "class", className }
+ };
+ }
+
+ internal static string ObjectTemplate(HtmlHelper html) {
+ return ObjectTemplate(html, TemplateHelpers.TemplateHelper);
+ }
+
+ internal static string ObjectTemplate(HtmlHelper html, TemplateHelpers.TemplateHelperDelegate templateHelper) {
+ ViewDataDictionary viewData = html.ViewContext.ViewData;
+ TemplateInfo templateInfo = viewData.TemplateInfo;
+ ModelMetadata modelMetadata = viewData.ModelMetadata;
+ StringBuilder builder = new StringBuilder();
+
+ if (templateInfo.TemplateDepth > 1) { // DDB #224751
+ return modelMetadata.Model == null ? modelMetadata.NullDisplayText : modelMetadata.SimpleDisplayText;
+ }
+
+ foreach (ModelMetadata propertyMetadata in modelMetadata.Properties.Where(pm => ShouldShow(pm, templateInfo))) {
+ if (!propertyMetadata.HideSurroundingHtml) {
+ string label = LabelExtensions.LabelHelper(html, propertyMetadata, propertyMetadata.PropertyName).ToHtmlString();
+ if (!String.IsNullOrEmpty(label)) {
+ builder.AppendFormat(CultureInfo.InvariantCulture, "<div class=\"editor-label\">{0}</div>\r\n", label);
+ }
+
+ builder.Append("<div class=\"editor-field\">");
+ }
+
+ builder.Append(templateHelper(html, propertyMetadata, propertyMetadata.PropertyName, null /* templateName */, DataBoundControlMode.Edit, null /* additionalViewData */));
+
+ if (!propertyMetadata.HideSurroundingHtml) {
+ builder.Append(" ");
+ builder.Append(html.ValidationMessage(propertyMetadata.PropertyName));
+ builder.Append("</div>\r\n");
+ }
+ }
+
+ return builder.ToString();
+ }
+
+ internal static string PasswordTemplate(HtmlHelper html) {
+ return html.Password(String.Empty,
+ html.ViewContext.ViewData.TemplateInfo.FormattedModelValue,
+ CreateHtmlAttributes("text-box single-line password")).ToHtmlString();
+ }
+
+ private static bool ShouldShow(ModelMetadata metadata, TemplateInfo templateInfo) {
+ return
+ metadata.ShowForEdit
+#if false
+ && metadata.ModelType != typeof(EntityState)
+#endif
+ && !metadata.IsComplexType
+ && !templateInfo.Visited(metadata);
+ }
+
+ internal static string StringTemplate(HtmlHelper html) {
+ return html.TextBox(String.Empty,
+ html.ViewContext.ViewData.TemplateInfo.FormattedModelValue,
+ CreateHtmlAttributes("text-box single-line")).ToHtmlString();
+ }
+
+ internal static List<SelectListItem> TriStateValues(bool? value) {
+ return new List<SelectListItem> {
+ new SelectListItem { Text = MvcResources.Common_TriState_NotSet, Value = String.Empty, Selected = !value.HasValue },
+ new SelectListItem { Text = MvcResources.Common_TriState_True, Value = "true", Selected = value.HasValue && value.Value },
+ new SelectListItem { Text = MvcResources.Common_TriState_False, Value = "false", Selected = value.HasValue && !value.Value },
+ };
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/Html/DisplayExtensions.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Html/DisplayExtensions.cs
new file mode 100644
index 00000000000..372caf02529
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Html/DisplayExtensions.cs
@@ -0,0 +1,102 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc.Html {
+ using System.Linq.Expressions;
+ using System.Web.UI.WebControls;
+
+ public static class DisplayExtensions {
+ public static MvcHtmlString Display(this HtmlHelper html, string expression) {
+ return TemplateHelpers.Template(html, expression, null /* templateName */, null /* htmlFieldName */, DataBoundControlMode.ReadOnly, null /* additionalViewData */);
+ }
+
+ public static MvcHtmlString Display(this HtmlHelper html, string expression, object additionalViewData) {
+ return TemplateHelpers.Template(html, expression, null /* templateName */, null /* htmlFieldName */, DataBoundControlMode.ReadOnly, additionalViewData);
+ }
+
+ public static MvcHtmlString Display(this HtmlHelper html, string expression, string templateName) {
+ return TemplateHelpers.Template(html, expression, templateName, null /* htmlFieldName */, DataBoundControlMode.ReadOnly, null /* additionalViewData */);
+ }
+
+ public static MvcHtmlString Display(this HtmlHelper html, string expression, string templateName, object additionalViewData) {
+ return TemplateHelpers.Template(html, expression, templateName, null /* htmlFieldName */, DataBoundControlMode.ReadOnly, additionalViewData);
+ }
+
+ public static MvcHtmlString Display(this HtmlHelper html, string expression, string templateName, string htmlFieldName) {
+ return TemplateHelpers.Template(html, expression, templateName, htmlFieldName, DataBoundControlMode.ReadOnly, null /* additionalViewData */);
+ }
+
+ public static MvcHtmlString Display(this HtmlHelper html, string expression, string templateName, string htmlFieldName, object additionalViewData) {
+ return TemplateHelpers.Template(html, expression, templateName, htmlFieldName, DataBoundControlMode.ReadOnly, additionalViewData);
+ }
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures",
+ Justification = "This is an appropriate nesting of generic types")]
+ public static MvcHtmlString DisplayFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression) {
+ return TemplateHelpers.TemplateFor(html, expression, null /* templateName */, null /* htmlFieldName */, DataBoundControlMode.ReadOnly, null /* additionalViewData */);
+ }
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures",
+ Justification = "This is an appropriate nesting of generic types")]
+ public static MvcHtmlString DisplayFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, object additionalViewData) {
+ return TemplateHelpers.TemplateFor(html, expression, null /* templateName */, null /* htmlFieldName */, DataBoundControlMode.ReadOnly, additionalViewData);
+ }
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures",
+ Justification = "This is an appropriate nesting of generic types")]
+ public static MvcHtmlString DisplayFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, string templateName) {
+ return TemplateHelpers.TemplateFor(html, expression, templateName, null /* htmlFieldName */, DataBoundControlMode.ReadOnly, null /* additionalViewData */);
+ }
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures",
+ Justification = "This is an appropriate nesting of generic types")]
+ public static MvcHtmlString DisplayFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, string templateName, object additionalViewData) {
+ return TemplateHelpers.TemplateFor(html, expression, templateName, null /* htmlFieldName */, DataBoundControlMode.ReadOnly, additionalViewData);
+ }
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures",
+ Justification = "This is an appropriate nesting of generic types")]
+ public static MvcHtmlString DisplayFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, string templateName, string htmlFieldName) {
+ return TemplateHelpers.TemplateFor(html, expression, templateName, htmlFieldName, DataBoundControlMode.ReadOnly, null /* additionalViewData */);
+ }
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures",
+ Justification = "This is an appropriate nesting of generic types")]
+ public static MvcHtmlString DisplayFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, string templateName, string htmlFieldName, object additionalViewData) {
+ return TemplateHelpers.TemplateFor(html, expression, templateName, htmlFieldName, DataBoundControlMode.ReadOnly, additionalViewData);
+ }
+
+ public static MvcHtmlString DisplayForModel(this HtmlHelper html) {
+ return MvcHtmlString.Create(TemplateHelpers.TemplateHelper(html, html.ViewData.ModelMetadata, String.Empty, null /* templateName */, DataBoundControlMode.ReadOnly, null /* additionalViewData */));
+ }
+
+ public static MvcHtmlString DisplayForModel(this HtmlHelper html, object additionalViewData) {
+ return MvcHtmlString.Create(TemplateHelpers.TemplateHelper(html, html.ViewData.ModelMetadata, String.Empty, null /* templateName */, DataBoundControlMode.ReadOnly, additionalViewData));
+ }
+
+ public static MvcHtmlString DisplayForModel(this HtmlHelper html, string templateName) {
+ return MvcHtmlString.Create(TemplateHelpers.TemplateHelper(html, html.ViewData.ModelMetadata, String.Empty, templateName, DataBoundControlMode.ReadOnly, null /* additionalViewData */));
+ }
+
+ public static MvcHtmlString DisplayForModel(this HtmlHelper html, string templateName, object additionalViewData) {
+ return MvcHtmlString.Create(TemplateHelpers.TemplateHelper(html, html.ViewData.ModelMetadata, String.Empty, templateName, DataBoundControlMode.ReadOnly, additionalViewData ));
+ }
+
+ public static MvcHtmlString DisplayForModel(this HtmlHelper html, string templateName, string htmlFieldName) {
+ return MvcHtmlString.Create(TemplateHelpers.TemplateHelper(html, html.ViewData.ModelMetadata, htmlFieldName, templateName, DataBoundControlMode.ReadOnly, null /* additionalViewData */));
+ }
+
+ public static MvcHtmlString DisplayForModel(this HtmlHelper html, string templateName, string htmlFieldName, object additionalViewData) {
+ return MvcHtmlString.Create(TemplateHelpers.TemplateHelper(html, html.ViewData.ModelMetadata, htmlFieldName, templateName, DataBoundControlMode.ReadOnly, additionalViewData));
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/Html/DisplayTextExtensions.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Html/DisplayTextExtensions.cs
new file mode 100644
index 00000000000..272ab0e74e6
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Html/DisplayTextExtensions.cs
@@ -0,0 +1,32 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc.Html {
+ using System;
+ using System.Linq.Expressions;
+
+ public static class DisplayTextExtensions {
+ public static MvcHtmlString DisplayText(this HtmlHelper html, string name) {
+ return DisplayTextHelper(ModelMetadata.FromStringExpression(name, html.ViewContext.ViewData));
+ }
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures",
+ Justification = "This is an appropriate nesting of generic types")]
+ public static MvcHtmlString DisplayTextFor<TModel, TResult>(this HtmlHelper<TModel> html, Expression<Func<TModel, TResult>> expression) {
+ return DisplayTextHelper(ModelMetadata.FromLambdaExpression(expression, html.ViewData));
+ }
+
+ private static MvcHtmlString DisplayTextHelper(ModelMetadata metadata) {
+ return MvcHtmlString.Create(metadata.SimpleDisplayText);
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/Html/EditorExtensions.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Html/EditorExtensions.cs
new file mode 100644
index 00000000000..b022f5c3665
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Html/EditorExtensions.cs
@@ -0,0 +1,103 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc.Html {
+ using System;
+ using System.Linq.Expressions;
+ using System.Web.UI.WebControls;
+
+ public static class EditorExtensions {
+ public static MvcHtmlString Editor(this HtmlHelper html, string expression) {
+ return TemplateHelpers.Template(html, expression, null /* templateName */, null /* htmlFieldName */, DataBoundControlMode.Edit, null /* additionalViewData */);
+ }
+
+ public static MvcHtmlString Editor(this HtmlHelper html, string expression, object additionalViewData) {
+ return TemplateHelpers.Template(html, expression, null /* templateName */, null /* htmlFieldName */, DataBoundControlMode.Edit, additionalViewData);
+ }
+
+ public static MvcHtmlString Editor(this HtmlHelper html, string expression, string templateName) {
+ return TemplateHelpers.Template(html, expression, templateName, null /* htmlFieldName */, DataBoundControlMode.Edit, null /* additionalViewData */);
+ }
+
+ public static MvcHtmlString Editor(this HtmlHelper html, string expression, string templateName, object additionalViewData) {
+ return TemplateHelpers.Template(html, expression, templateName, null /* htmlFieldName */, DataBoundControlMode.Edit, additionalViewData);
+ }
+
+ public static MvcHtmlString Editor(this HtmlHelper html, string expression, string templateName, string htmlFieldName) {
+ return TemplateHelpers.Template(html, expression, templateName, htmlFieldName, DataBoundControlMode.Edit, null /* additionalViewData */);
+ }
+
+ public static MvcHtmlString Editor(this HtmlHelper html, string expression, string templateName, string htmlFieldName, object additionalViewData) {
+ return TemplateHelpers.Template(html, expression, templateName, htmlFieldName, DataBoundControlMode.Edit, additionalViewData);
+ }
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures",
+ Justification = "This is an appropriate nesting of generic types")]
+ public static MvcHtmlString EditorFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression) {
+ return TemplateHelpers.TemplateFor(html, expression, null /* templateName */, null /* htmlFieldName */, DataBoundControlMode.Edit, null /* additionalViewData */);
+ }
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures",
+ Justification = "This is an appropriate nesting of generic types")]
+ public static MvcHtmlString EditorFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, object additionalViewData) {
+ return TemplateHelpers.TemplateFor(html, expression, null /* templateName */, null /* htmlFieldName */, DataBoundControlMode.Edit, additionalViewData);
+ }
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures",
+ Justification = "This is an appropriate nesting of generic types")]
+ public static MvcHtmlString EditorFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, string templateName) {
+ return TemplateHelpers.TemplateFor(html, expression, templateName, null /* htmlFieldName */, DataBoundControlMode.Edit, null /* additionalViewData */);
+ }
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures",
+ Justification = "This is an appropriate nesting of generic types")]
+ public static MvcHtmlString EditorFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, string templateName, object additionalViewData) {
+ return TemplateHelpers.TemplateFor(html, expression, templateName, null /* htmlFieldName */, DataBoundControlMode.Edit, additionalViewData);
+ }
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures",
+ Justification = "This is an appropriate nesting of generic types")]
+ public static MvcHtmlString EditorFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, string templateName, string htmlFieldName) {
+ return TemplateHelpers.TemplateFor(html, expression, templateName, htmlFieldName, DataBoundControlMode.Edit, null /* additionalViewData */);
+ }
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures",
+ Justification = "This is an appropriate nesting of generic types")]
+ public static MvcHtmlString EditorFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, string templateName, string htmlFieldName, object additionalViewData) {
+ return TemplateHelpers.TemplateFor(html, expression, templateName, htmlFieldName, DataBoundControlMode.Edit, additionalViewData);
+ }
+
+ public static MvcHtmlString EditorForModel(this HtmlHelper html) {
+ return MvcHtmlString.Create(TemplateHelpers.TemplateHelper(html, html.ViewData.ModelMetadata, String.Empty, null /* templateName */, DataBoundControlMode.Edit, null /* additionalViewData */));
+ }
+
+ public static MvcHtmlString EditorForModel(this HtmlHelper html, object additionalViewData) {
+ return MvcHtmlString.Create(TemplateHelpers.TemplateHelper(html, html.ViewData.ModelMetadata, String.Empty, null /* templateName */, DataBoundControlMode.Edit, additionalViewData));
+ }
+
+ public static MvcHtmlString EditorForModel(this HtmlHelper html, string templateName) {
+ return MvcHtmlString.Create(TemplateHelpers.TemplateHelper(html, html.ViewData.ModelMetadata, String.Empty, templateName, DataBoundControlMode.Edit, null /* additionalViewData */));
+ }
+
+ public static MvcHtmlString EditorForModel(this HtmlHelper html, string templateName, object additionalViewData) {
+ return MvcHtmlString.Create(TemplateHelpers.TemplateHelper(html, html.ViewData.ModelMetadata, String.Empty, templateName, DataBoundControlMode.Edit, additionalViewData));
+ }
+
+ public static MvcHtmlString EditorForModel(this HtmlHelper html, string templateName, string htmlFieldName) {
+ return MvcHtmlString.Create(TemplateHelpers.TemplateHelper(html, html.ViewData.ModelMetadata, htmlFieldName, templateName, DataBoundControlMode.Edit, null /* additionalViewData */));
+ }
+
+ public static MvcHtmlString EditorForModel(this HtmlHelper html, string templateName, string htmlFieldName, object additionalViewData) {
+ return MvcHtmlString.Create(TemplateHelpers.TemplateHelper(html, html.ViewData.ModelMetadata, htmlFieldName, templateName, DataBoundControlMode.Edit, additionalViewData));
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/Html/FormExtensions.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Html/FormExtensions.cs
new file mode 100644
index 00000000000..b5df159c6ac
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Html/FormExtensions.cs
@@ -0,0 +1,152 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc.Html {
+ using System.Collections.Generic;
+ using System.Web.Routing;
+ using System.Globalization;
+
+ public static class FormExtensions {
+ public static MvcForm BeginForm(this HtmlHelper htmlHelper) {
+ // generates <form action="{current url}" method="post">...</form>
+ string formAction = htmlHelper.ViewContext.HttpContext.Request.RawUrl;
+ return FormHelper(htmlHelper, formAction, FormMethod.Post, new RouteValueDictionary());
+ }
+
+ public static MvcForm BeginForm(this HtmlHelper htmlHelper, object routeValues) {
+ return BeginForm(htmlHelper, null, null, new RouteValueDictionary(routeValues), FormMethod.Post, new RouteValueDictionary());
+ }
+
+ public static MvcForm BeginForm(this HtmlHelper htmlHelper, RouteValueDictionary routeValues) {
+ return BeginForm(htmlHelper, null, null, routeValues, FormMethod.Post, new RouteValueDictionary());
+ }
+
+ public static MvcForm BeginForm(this HtmlHelper htmlHelper, string actionName, string controllerName) {
+ return BeginForm(htmlHelper, actionName, controllerName, new RouteValueDictionary(), FormMethod.Post, new RouteValueDictionary());
+ }
+
+ public static MvcForm BeginForm(this HtmlHelper htmlHelper, string actionName, string controllerName, object routeValues) {
+ return BeginForm(htmlHelper, actionName, controllerName, new RouteValueDictionary(routeValues), FormMethod.Post, new RouteValueDictionary());
+ }
+
+ public static MvcForm BeginForm(this HtmlHelper htmlHelper, string actionName, string controllerName, RouteValueDictionary routeValues) {
+ return BeginForm(htmlHelper, actionName, controllerName, routeValues, FormMethod.Post, new RouteValueDictionary());
+ }
+
+ public static MvcForm BeginForm(this HtmlHelper htmlHelper, string actionName, string controllerName, FormMethod method) {
+ return BeginForm(htmlHelper, actionName, controllerName, new RouteValueDictionary(), method, new RouteValueDictionary());
+ }
+
+ public static MvcForm BeginForm(this HtmlHelper htmlHelper, string actionName, string controllerName, object routeValues, FormMethod method) {
+ return BeginForm(htmlHelper, actionName, controllerName, new RouteValueDictionary(routeValues), method, new RouteValueDictionary());
+ }
+
+ public static MvcForm BeginForm(this HtmlHelper htmlHelper, string actionName, string controllerName, RouteValueDictionary routeValues, FormMethod method) {
+ return BeginForm(htmlHelper, actionName, controllerName, routeValues, method, new RouteValueDictionary());
+ }
+
+ public static MvcForm BeginForm(this HtmlHelper htmlHelper, string actionName, string controllerName, FormMethod method, object htmlAttributes) {
+ return BeginForm(htmlHelper, actionName, controllerName, new RouteValueDictionary(), method, new RouteValueDictionary(htmlAttributes));
+ }
+
+ public static MvcForm BeginForm(this HtmlHelper htmlHelper, string actionName, string controllerName, FormMethod method, IDictionary<string, object> htmlAttributes) {
+ return BeginForm(htmlHelper, actionName, controllerName, new RouteValueDictionary(), method, htmlAttributes);
+ }
+
+ public static MvcForm BeginForm(this HtmlHelper htmlHelper, string actionName, string controllerName, object routeValues, FormMethod method, object htmlAttributes) {
+ return BeginForm(htmlHelper, actionName, controllerName, new RouteValueDictionary(routeValues), method, new RouteValueDictionary(htmlAttributes));
+ }
+
+ public static MvcForm BeginForm(this HtmlHelper htmlHelper, string actionName, string controllerName, RouteValueDictionary routeValues, FormMethod method, IDictionary<string, object> htmlAttributes) {
+ string formAction = UrlHelper.GenerateUrl(null /* routeName */, actionName, controllerName, routeValues, htmlHelper.RouteCollection, htmlHelper.ViewContext.RequestContext, true /* includeImplicitMvcValues */);
+ return FormHelper(htmlHelper, formAction, method, htmlAttributes);
+ }
+
+ public static MvcForm BeginRouteForm(this HtmlHelper htmlHelper, object routeValues) {
+ return BeginRouteForm(htmlHelper, null /* routeName */, new RouteValueDictionary(routeValues), FormMethod.Post, new RouteValueDictionary());
+ }
+
+ public static MvcForm BeginRouteForm(this HtmlHelper htmlHelper, RouteValueDictionary routeValues) {
+ return BeginRouteForm(htmlHelper, null /* routeName */, routeValues, FormMethod.Post, new RouteValueDictionary());
+ }
+
+ public static MvcForm BeginRouteForm(this HtmlHelper htmlHelper, string routeName) {
+ return BeginRouteForm(htmlHelper, routeName, new RouteValueDictionary(), FormMethod.Post, new RouteValueDictionary());
+ }
+
+ public static MvcForm BeginRouteForm(this HtmlHelper htmlHelper, string routeName, object routeValues) {
+ return BeginRouteForm(htmlHelper, routeName, new RouteValueDictionary(routeValues), FormMethod.Post, new RouteValueDictionary());
+ }
+
+ public static MvcForm BeginRouteForm(this HtmlHelper htmlHelper, string routeName, RouteValueDictionary routeValues) {
+ return BeginRouteForm(htmlHelper, routeName, routeValues, FormMethod.Post, new RouteValueDictionary());
+ }
+
+ public static MvcForm BeginRouteForm(this HtmlHelper htmlHelper, string routeName, FormMethod method) {
+ return BeginRouteForm(htmlHelper, routeName, new RouteValueDictionary(), method, new RouteValueDictionary());
+ }
+
+ public static MvcForm BeginRouteForm(this HtmlHelper htmlHelper, string routeName, object routeValues, FormMethod method) {
+ return BeginRouteForm(htmlHelper, routeName, new RouteValueDictionary(routeValues), method, new RouteValueDictionary());
+ }
+
+ public static MvcForm BeginRouteForm(this HtmlHelper htmlHelper, string routeName, RouteValueDictionary routeValues, FormMethod method) {
+ return BeginRouteForm(htmlHelper, routeName, routeValues, method, new RouteValueDictionary());
+ }
+
+ public static MvcForm BeginRouteForm(this HtmlHelper htmlHelper, string routeName, FormMethod method, object htmlAttributes) {
+ return BeginRouteForm(htmlHelper, routeName, new RouteValueDictionary(), method, new RouteValueDictionary(htmlAttributes));
+ }
+
+ public static MvcForm BeginRouteForm(this HtmlHelper htmlHelper, string routeName, FormMethod method, IDictionary<string, object> htmlAttributes) {
+ return BeginRouteForm(htmlHelper, routeName, new RouteValueDictionary(), method, htmlAttributes);
+ }
+
+ public static MvcForm BeginRouteForm(this HtmlHelper htmlHelper, string routeName, object routeValues, FormMethod method, object htmlAttributes) {
+ return BeginRouteForm(htmlHelper, routeName, new RouteValueDictionary(routeValues), method, new RouteValueDictionary(htmlAttributes));
+ }
+
+ public static MvcForm BeginRouteForm(this HtmlHelper htmlHelper, string routeName, RouteValueDictionary routeValues, FormMethod method, IDictionary<string, object> htmlAttributes) {
+ string formAction = UrlHelper.GenerateUrl(routeName, null, null, routeValues, htmlHelper.RouteCollection, htmlHelper.ViewContext.RequestContext, false /* includeImplicitMvcValues */);
+ return FormHelper(htmlHelper, formAction, method, htmlAttributes);
+ }
+
+ public static void EndForm(this HtmlHelper htmlHelper) {
+ htmlHelper.ViewContext.Writer.Write("</form>");
+ htmlHelper.ViewContext.OutputClientValidation();
+ }
+
+ private static MvcForm FormHelper(this HtmlHelper htmlHelper, string formAction, FormMethod method, IDictionary<string, object> htmlAttributes) {
+ TagBuilder tagBuilder = new TagBuilder("form");
+ tagBuilder.MergeAttributes(htmlAttributes);
+ // action is implicitly generated, so htmlAttributes take precedence.
+ tagBuilder.MergeAttribute("action", formAction);
+ // method is an explicit parameter, so it takes precedence over the htmlAttributes.
+ tagBuilder.MergeAttribute("method", HtmlHelper.GetFormMethodString(method), true);
+
+ if (htmlHelper.ViewContext.ClientValidationEnabled) {
+ // forms must have an ID for client validation
+ tagBuilder.GenerateId(htmlHelper.ViewContext.FormIdGenerator());
+ }
+
+ htmlHelper.ViewContext.Writer.Write(tagBuilder.ToString(TagRenderMode.StartTag));
+ MvcForm theForm = new MvcForm(htmlHelper.ViewContext);
+
+ if (htmlHelper.ViewContext.ClientValidationEnabled) {
+ htmlHelper.ViewContext.FormContext.FormId = tagBuilder.Attributes["id"];
+ }
+
+ return theForm;
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/Html/InputExtensions.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Html/InputExtensions.cs
new file mode 100644
index 00000000000..5d56f83bb65
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Html/InputExtensions.cs
@@ -0,0 +1,398 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc.Html {
+ using System;
+ using System.Collections.Generic;
+ using System.Data.Linq;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Globalization;
+ using System.Linq.Expressions;
+ using System.Text;
+ using System.Web.Mvc.Resources;
+ using System.Web.Routing;
+
+ public static class InputExtensions {
+ // CheckBox
+
+ public static MvcHtmlString CheckBox(this HtmlHelper htmlHelper, string name) {
+ return CheckBox(htmlHelper, name, (object)null /* htmlAttributes */);
+ }
+
+ public static MvcHtmlString CheckBox(this HtmlHelper htmlHelper, string name, bool isChecked) {
+ return CheckBox(htmlHelper, name, isChecked, (object)null /* htmlAttributes */);
+ }
+
+ public static MvcHtmlString CheckBox(this HtmlHelper htmlHelper, string name, bool isChecked, object htmlAttributes) {
+ return CheckBox(htmlHelper, name, isChecked, new RouteValueDictionary(htmlAttributes));
+ }
+
+ public static MvcHtmlString CheckBox(this HtmlHelper htmlHelper, string name, object htmlAttributes) {
+ return CheckBox(htmlHelper, name, new RouteValueDictionary(htmlAttributes));
+ }
+
+ public static MvcHtmlString CheckBox(this HtmlHelper htmlHelper, string name, IDictionary<string, object> htmlAttributes) {
+ return CheckBoxHelper(htmlHelper, name, null /* isChecked */, htmlAttributes);
+ }
+
+ public static MvcHtmlString CheckBox(this HtmlHelper htmlHelper, string name, bool isChecked, IDictionary<string, object> htmlAttributes) {
+ return CheckBoxHelper(htmlHelper, name, isChecked, htmlAttributes);
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
+ public static MvcHtmlString CheckBoxFor<TModel>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, bool>> expression) {
+ return CheckBoxFor(htmlHelper, expression, null /* htmlAttributes */);
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
+ public static MvcHtmlString CheckBoxFor<TModel>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, bool>> expression, object htmlAttributes) {
+ return CheckBoxFor(htmlHelper, expression, new RouteValueDictionary(htmlAttributes));
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
+ public static MvcHtmlString CheckBoxFor<TModel>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, bool>> expression, IDictionary<string, object> htmlAttributes) {
+ if (expression == null) {
+ throw new ArgumentNullException("expression");
+ }
+
+ ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
+ bool? isChecked = null;
+ if (metadata.Model != null) {
+ bool modelChecked;
+ if (Boolean.TryParse(metadata.Model.ToString(), out modelChecked)) {
+ isChecked = modelChecked;
+ }
+ }
+
+ return CheckBoxHelper(htmlHelper, ExpressionHelper.GetExpressionText(expression), isChecked, htmlAttributes);
+ }
+
+ private static MvcHtmlString CheckBoxHelper(HtmlHelper htmlHelper, string name, bool? isChecked, IDictionary<string, object> htmlAttributes) {
+ RouteValueDictionary attributes =
+ htmlAttributes == null ? new RouteValueDictionary()
+ : new RouteValueDictionary(htmlAttributes);
+
+ bool explicitValue = isChecked.HasValue;
+ if (explicitValue) {
+ attributes.Remove("checked"); // Explicit value must override dictionary
+ }
+
+ return InputHelper(htmlHelper, InputType.CheckBox, name, "true", !explicitValue /* useViewData */, isChecked ?? false, true /* setId */, false /* isExplicitValue */, attributes);
+ }
+
+ // Hidden
+
+ public static MvcHtmlString Hidden(this HtmlHelper htmlHelper, string name) {
+ return Hidden(htmlHelper, name, null /* value */, null /* htmlAttributes */);
+ }
+
+ public static MvcHtmlString Hidden(this HtmlHelper htmlHelper, string name, object value) {
+ return Hidden(htmlHelper, name, value, null /* hmtlAttributes */);
+ }
+
+ public static MvcHtmlString Hidden(this HtmlHelper htmlHelper, string name, object value, object htmlAttributes) {
+ return Hidden(htmlHelper, name, value, new RouteValueDictionary(htmlAttributes));
+ }
+
+ public static MvcHtmlString Hidden(this HtmlHelper htmlHelper, string name, object value, IDictionary<string, object> htmlAttributes) {
+ return HiddenHelper(htmlHelper,
+ value,
+ value == null /* useViewData */,
+ name,
+ htmlAttributes);
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
+ public static MvcHtmlString HiddenFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression) {
+ return HiddenFor(htmlHelper, expression, (IDictionary<string, object>)null);
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
+ public static MvcHtmlString HiddenFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, object htmlAttributes) {
+ return HiddenFor(htmlHelper, expression, new RouteValueDictionary(htmlAttributes));
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
+ public static MvcHtmlString HiddenFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IDictionary<string, object> htmlAttributes) {
+ return HiddenHelper(htmlHelper,
+ ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData).Model,
+ false,
+ ExpressionHelper.GetExpressionText(expression),
+ htmlAttributes);
+ }
+
+ private static MvcHtmlString HiddenHelper(HtmlHelper htmlHelper, object value, bool useViewData, string expression, IDictionary<string, object> htmlAttributes) {
+ Binary binaryValue = value as Binary;
+ if (binaryValue != null) {
+ value = binaryValue.ToArray();
+ }
+
+ byte[] byteArrayValue = value as byte[];
+ if (byteArrayValue != null) {
+ value = Convert.ToBase64String(byteArrayValue);
+ }
+
+ return InputHelper(htmlHelper, InputType.Hidden, expression, value, useViewData, false /* isChecked */, true /* setId */, true /* isExplicitValue */, htmlAttributes);
+ }
+
+ // Password
+
+ public static MvcHtmlString Password(this HtmlHelper htmlHelper, string name) {
+ return Password(htmlHelper, name, null /* value */);
+ }
+
+ public static MvcHtmlString Password(this HtmlHelper htmlHelper, string name, object value) {
+ return Password(htmlHelper, name, value, null /* htmlAttributes */);
+ }
+
+ public static MvcHtmlString Password(this HtmlHelper htmlHelper, string name, object value, object htmlAttributes) {
+ return Password(htmlHelper, name, value, new RouteValueDictionary(htmlAttributes));
+ }
+
+ public static MvcHtmlString Password(this HtmlHelper htmlHelper, string name, object value, IDictionary<string, object> htmlAttributes) {
+ return PasswordHelper(htmlHelper, name, value, htmlAttributes);
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
+ public static MvcHtmlString PasswordFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression) {
+ return PasswordFor(htmlHelper, expression, null /* htmlAttributes */);
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
+ public static MvcHtmlString PasswordFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, object htmlAttributes) {
+ return PasswordFor(htmlHelper, expression, new RouteValueDictionary(htmlAttributes));
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "Users cannot use anonymous methods with the LambdaExpression type")]
+ [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
+ public static MvcHtmlString PasswordFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IDictionary<string, object> htmlAttributes) {
+ if (expression == null) {
+ throw new ArgumentNullException("expression");
+ }
+
+ return PasswordHelper(htmlHelper,
+ ExpressionHelper.GetExpressionText(expression),
+ null /* value */,
+ htmlAttributes);
+ }
+
+ private static MvcHtmlString PasswordHelper(HtmlHelper htmlHelper, string name, object value, IDictionary<string, object> htmlAttributes) {
+ return InputHelper(htmlHelper, InputType.Password, name, value, false /* useViewData */, false /* isChecked */, true /* setId */, true /* isExplicitValue */, htmlAttributes);
+ }
+
+ // RadioButton
+
+ public static MvcHtmlString RadioButton(this HtmlHelper htmlHelper, string name, object value) {
+ return RadioButton(htmlHelper, name, value, (object)null /* htmlAttributes */);
+ }
+
+ public static MvcHtmlString RadioButton(this HtmlHelper htmlHelper, string name, object value, object htmlAttributes) {
+ return RadioButton(htmlHelper, name, value, new RouteValueDictionary(htmlAttributes));
+ }
+
+ public static MvcHtmlString RadioButton(this HtmlHelper htmlHelper, string name, object value, IDictionary<string, object> htmlAttributes) {
+ // Determine whether or not to render the checked attribute based on the contents of ViewData.
+ string valueString = Convert.ToString(value, CultureInfo.CurrentCulture);
+ bool isChecked = (!String.IsNullOrEmpty(name)) && (String.Equals(htmlHelper.EvalString(name), valueString, StringComparison.OrdinalIgnoreCase));
+ // checked attributes is implicit, so we need to ensure that the dictionary takes precedence.
+ RouteValueDictionary attributes = htmlAttributes == null ? new RouteValueDictionary() : new RouteValueDictionary(htmlAttributes);
+ if (attributes.ContainsKey("checked")) {
+ return InputHelper(htmlHelper, InputType.Radio, name, value, false, false, true, true /* isExplicitValue */, attributes);
+ }
+
+ return RadioButton(htmlHelper, name, value, isChecked, htmlAttributes);
+ }
+
+ public static MvcHtmlString RadioButton(this HtmlHelper htmlHelper, string name, object value, bool isChecked) {
+ return RadioButton(htmlHelper, name, value, isChecked, (object)null /* htmlAttributes */);
+ }
+
+ public static MvcHtmlString RadioButton(this HtmlHelper htmlHelper, string name, object value, bool isChecked, object htmlAttributes) {
+ return RadioButton(htmlHelper, name, value, isChecked, new RouteValueDictionary(htmlAttributes));
+ }
+
+ public static MvcHtmlString RadioButton(this HtmlHelper htmlHelper, string name, object value, bool isChecked, IDictionary<string, object> htmlAttributes) {
+ if (value == null) {
+ throw new ArgumentNullException("value");
+ }
+ // checked attribute is an explicit parameter so it takes precedence.
+ RouteValueDictionary attributes = htmlAttributes == null ? new RouteValueDictionary() : new RouteValueDictionary(htmlAttributes);
+ attributes.Remove("checked");
+ return InputHelper(htmlHelper, InputType.Radio, name, value, false, isChecked, true, true /* isExplicitValue */, attributes);
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
+ public static MvcHtmlString RadioButtonFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, object value) {
+ return RadioButtonFor(htmlHelper, expression, value, null /* htmlAttributes */);
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
+ public static MvcHtmlString RadioButtonFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, object value, object htmlAttributes) {
+ return RadioButtonFor(htmlHelper, expression, value, new RouteValueDictionary(htmlAttributes));
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
+ public static MvcHtmlString RadioButtonFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, object value, IDictionary<string, object> htmlAttributes) {
+ return RadioButtonHelper(htmlHelper,
+ ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData).Model,
+ ExpressionHelper.GetExpressionText(expression),
+ value,
+ null /* isChecked */,
+ htmlAttributes);
+ }
+
+ private static MvcHtmlString RadioButtonHelper(HtmlHelper htmlHelper, object model, string name, object value, bool? isChecked, IDictionary<string, object> htmlAttributes) {
+ if (value == null) {
+ throw new ArgumentNullException("value");
+ }
+
+ RouteValueDictionary attributes =
+ htmlAttributes == null ? new RouteValueDictionary()
+ : new RouteValueDictionary(htmlAttributes);
+
+ bool explicitValue = isChecked.HasValue;
+ if (explicitValue) {
+ attributes.Remove("checked"); // Explicit value must override dictionary
+ }
+ else {
+ string valueString = Convert.ToString(value, CultureInfo.CurrentCulture);
+ isChecked = model != null &&
+ !String.IsNullOrEmpty(name) &&
+ String.Equals(model.ToString(), valueString, StringComparison.OrdinalIgnoreCase);
+ }
+
+ return InputHelper(htmlHelper, InputType.Radio, name, value, false /* useViewData */, isChecked ?? false, true /* setId */, true /* isExplicitValue */, attributes);
+ }
+
+ // TextBox
+
+ public static MvcHtmlString TextBox(this HtmlHelper htmlHelper, string name) {
+ return TextBox(htmlHelper, name, null /* value */);
+ }
+
+ public static MvcHtmlString TextBox(this HtmlHelper htmlHelper, string name, object value) {
+ return TextBox(htmlHelper, name, value, (object)null /* htmlAttributes */);
+ }
+
+ public static MvcHtmlString TextBox(this HtmlHelper htmlHelper, string name, object value, object htmlAttributes) {
+ return TextBox(htmlHelper, name, value, new RouteValueDictionary(htmlAttributes));
+ }
+
+ public static MvcHtmlString TextBox(this HtmlHelper htmlHelper, string name, object value, IDictionary<string, object> htmlAttributes) {
+ return InputHelper(htmlHelper, InputType.Text, name, value, (value == null) /* useViewData */, false /* isChecked */, true /* setId */, true /* isExplicitValue */, htmlAttributes);
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
+ public static MvcHtmlString TextBoxFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression) {
+ return htmlHelper.TextBoxFor(expression, (IDictionary<string, object>)null);
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
+ public static MvcHtmlString TextBoxFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, object htmlAttributes) {
+ return htmlHelper.TextBoxFor(expression, new RouteValueDictionary(htmlAttributes));
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
+ public static MvcHtmlString TextBoxFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IDictionary<string, object> htmlAttributes) {
+ return TextBoxHelper(htmlHelper,
+ ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData).Model,
+ ExpressionHelper.GetExpressionText(expression),
+ htmlAttributes);
+ }
+
+ private static MvcHtmlString TextBoxHelper(this HtmlHelper htmlHelper, object model, string expression, IDictionary<string, object> htmlAttributes) {
+ return InputHelper(htmlHelper, InputType.Text, expression, model, false /* useViewData */, false /* isChecked */, true /* setId */, true /* isExplicitValue */, htmlAttributes);
+ }
+
+ // Helper methods
+
+ private static MvcHtmlString InputHelper(HtmlHelper htmlHelper, InputType inputType, string name, object value, bool useViewData, bool isChecked, bool setId, bool isExplicitValue, IDictionary<string, object> htmlAttributes) {
+ name = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(name);
+ if (String.IsNullOrEmpty(name)) {
+ throw new ArgumentException(MvcResources.Common_NullOrEmpty, "name");
+ }
+
+ TagBuilder tagBuilder = new TagBuilder("input");
+ tagBuilder.MergeAttributes(htmlAttributes);
+ tagBuilder.MergeAttribute("type", HtmlHelper.GetInputTypeString(inputType));
+ tagBuilder.MergeAttribute("name", name, true);
+
+ string valueParameter = Convert.ToString(value, CultureInfo.CurrentCulture);
+ bool usedModelState = false;
+
+ switch (inputType) {
+ case InputType.CheckBox:
+ bool? modelStateWasChecked = htmlHelper.GetModelStateValue(name, typeof(bool)) as bool?;
+ if (modelStateWasChecked.HasValue) {
+ isChecked = modelStateWasChecked.Value;
+ usedModelState = true;
+ }
+ goto case InputType.Radio;
+ case InputType.Radio:
+ if (!usedModelState) {
+ string modelStateValue = htmlHelper.GetModelStateValue(name, typeof(string)) as string;
+ if (modelStateValue != null) {
+ isChecked = String.Equals(modelStateValue, valueParameter, StringComparison.Ordinal);
+ usedModelState = true;
+ }
+ }
+ if (!usedModelState && useViewData) {
+ isChecked = htmlHelper.EvalBoolean(name);
+ }
+ if (isChecked) {
+ tagBuilder.MergeAttribute("checked", "checked");
+ }
+ tagBuilder.MergeAttribute("value", valueParameter, isExplicitValue);
+ break;
+ case InputType.Password:
+ if (value != null) {
+ tagBuilder.MergeAttribute("value", valueParameter, isExplicitValue);
+ }
+ break;
+ default:
+ string attemptedValue = (string)htmlHelper.GetModelStateValue(name, typeof(string));
+ tagBuilder.MergeAttribute("value", attemptedValue ?? ((useViewData) ? htmlHelper.EvalString(name) : valueParameter), isExplicitValue);
+ break;
+ }
+
+ if (setId) {
+ tagBuilder.GenerateId(name);
+ }
+
+ // If there are any errors for a named field, we add the css attribute.
+ ModelState modelState;
+ if (htmlHelper.ViewData.ModelState.TryGetValue(name, out modelState)) {
+ if (modelState.Errors.Count > 0) {
+ tagBuilder.AddCssClass(HtmlHelper.ValidationInputCssClassName);
+ }
+ }
+
+ if (inputType == InputType.CheckBox) {
+ // Render an additional <input type="hidden".../> for checkboxes. This
+ // addresses scenarios where unchecked checkboxes are not sent in the request.
+ // Sending a hidden input makes it possible to know that the checkbox was present
+ // on the page when the request was submitted.
+ StringBuilder inputItemBuilder = new StringBuilder();
+ inputItemBuilder.Append(tagBuilder.ToString(TagRenderMode.SelfClosing));
+
+ TagBuilder hiddenInput = new TagBuilder("input");
+ hiddenInput.MergeAttribute("type", HtmlHelper.GetInputTypeString(InputType.Hidden));
+ hiddenInput.MergeAttribute("name", name);
+ hiddenInput.MergeAttribute("value", "false");
+ inputItemBuilder.Append(hiddenInput.ToString(TagRenderMode.SelfClosing));
+ return MvcHtmlString.Create(inputItemBuilder.ToString());
+ }
+
+ return tagBuilder.ToMvcHtmlString(TagRenderMode.SelfClosing);
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/Html/LabelExtensions.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Html/LabelExtensions.cs
new file mode 100644
index 00000000000..4af88eaafd3
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Html/LabelExtensions.cs
@@ -0,0 +1,49 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc.Html {
+ using System;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Linq;
+ using System.Linq.Expressions;
+
+ public static class LabelExtensions {
+ public static MvcHtmlString Label(this HtmlHelper html, string expression) {
+ return LabelHelper(html,
+ ModelMetadata.FromStringExpression(expression, html.ViewData),
+ expression);
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
+ public static MvcHtmlString LabelFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression) {
+ return LabelHelper(html,
+ ModelMetadata.FromLambdaExpression(expression, html.ViewData),
+ ExpressionHelper.GetExpressionText(expression));
+ }
+
+ public static MvcHtmlString LabelForModel(this HtmlHelper html) {
+ return LabelHelper(html, html.ViewData.ModelMetadata, String.Empty);
+ }
+
+ internal static MvcHtmlString LabelHelper(HtmlHelper html, ModelMetadata metadata, string htmlFieldName) {
+ string labelText = metadata.DisplayName ?? metadata.PropertyName ?? htmlFieldName.Split('.').Last();
+ if (String.IsNullOrEmpty(labelText)) {
+ return MvcHtmlString.Empty;
+ }
+
+ TagBuilder tag = new TagBuilder("label");
+ tag.Attributes.Add("for", html.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldId(htmlFieldName));
+ tag.SetInnerText(labelText);
+ return tag.ToMvcHtmlString(TagRenderMode.Normal);
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/Html/LinkExtensions.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Html/LinkExtensions.cs
new file mode 100644
index 00000000000..8250ff341c4
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Html/LinkExtensions.cs
@@ -0,0 +1,116 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc.Html {
+ using System;
+ using System.Collections.Generic;
+ using System.Web.Mvc.Resources;
+ using System.Web.Routing;
+
+ public static class LinkExtensions {
+ public static MvcHtmlString ActionLink(this HtmlHelper htmlHelper, string linkText, string actionName) {
+ return ActionLink(htmlHelper, linkText, actionName, null /* controllerName */, new RouteValueDictionary(), new RouteValueDictionary());
+ }
+
+ public static MvcHtmlString ActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, object routeValues) {
+ return ActionLink(htmlHelper, linkText, actionName, null /* controllerName */, new RouteValueDictionary(routeValues), new RouteValueDictionary());
+ }
+
+ public static MvcHtmlString ActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, object routeValues, object htmlAttributes) {
+ return ActionLink(htmlHelper, linkText, actionName, null /* controllerName */, new RouteValueDictionary(routeValues), new RouteValueDictionary(htmlAttributes));
+ }
+
+ public static MvcHtmlString ActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, RouteValueDictionary routeValues) {
+ return ActionLink(htmlHelper, linkText, actionName, null /* controllerName */, routeValues, new RouteValueDictionary());
+ }
+
+ public static MvcHtmlString ActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, RouteValueDictionary routeValues, IDictionary<string, object> htmlAttributes) {
+ return ActionLink(htmlHelper, linkText, actionName, null /* controllerName */, routeValues, htmlAttributes);
+ }
+
+ public static MvcHtmlString ActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, string controllerName) {
+ return ActionLink(htmlHelper, linkText, actionName, controllerName, new RouteValueDictionary(), new RouteValueDictionary());
+ }
+
+ public static MvcHtmlString ActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, string controllerName, object routeValues, object htmlAttributes) {
+ return ActionLink(htmlHelper, linkText, actionName, controllerName, new RouteValueDictionary(routeValues), new RouteValueDictionary(htmlAttributes));
+ }
+
+ public static MvcHtmlString ActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, string controllerName, RouteValueDictionary routeValues, IDictionary<string, object> htmlAttributes) {
+ if (String.IsNullOrEmpty(linkText)) {
+ throw new ArgumentException(MvcResources.Common_NullOrEmpty, "linkText");
+ }
+ return MvcHtmlString.Create(HtmlHelper.GenerateLink(htmlHelper.ViewContext.RequestContext, htmlHelper.RouteCollection, linkText, null/* routeName */, actionName, controllerName, routeValues, htmlAttributes));
+ }
+
+ public static MvcHtmlString ActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, string controllerName, string protocol, string hostName, string fragment, object routeValues, object htmlAttributes) {
+ return ActionLink(htmlHelper, linkText, actionName, controllerName, protocol, hostName, fragment, new RouteValueDictionary(routeValues), new RouteValueDictionary(htmlAttributes));
+ }
+
+ public static MvcHtmlString ActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, string controllerName, string protocol, string hostName, string fragment, RouteValueDictionary routeValues, IDictionary<string, object> htmlAttributes) {
+ if (String.IsNullOrEmpty(linkText)) {
+ throw new ArgumentException(MvcResources.Common_NullOrEmpty, "linkText");
+ }
+ return MvcHtmlString.Create(HtmlHelper.GenerateLink(htmlHelper.ViewContext.RequestContext, htmlHelper.RouteCollection, linkText, null /* routeName */, actionName, controllerName, protocol, hostName, fragment, routeValues, htmlAttributes));
+ }
+
+ public static MvcHtmlString RouteLink(this HtmlHelper htmlHelper, string linkText, object routeValues) {
+ return RouteLink(htmlHelper, linkText, new RouteValueDictionary(routeValues));
+ }
+
+ public static MvcHtmlString RouteLink(this HtmlHelper htmlHelper, string linkText, RouteValueDictionary routeValues) {
+ return RouteLink(htmlHelper, linkText, routeValues, new RouteValueDictionary());
+ }
+
+ public static MvcHtmlString RouteLink(this HtmlHelper htmlHelper, string linkText, string routeName) {
+ return RouteLink(htmlHelper, linkText, routeName, (object)null /* routeValues */ );
+ }
+
+ public static MvcHtmlString RouteLink(this HtmlHelper htmlHelper, string linkText, string routeName, object routeValues) {
+ return RouteLink(htmlHelper, linkText, routeName, new RouteValueDictionary(routeValues));
+ }
+
+ public static MvcHtmlString RouteLink(this HtmlHelper htmlHelper, string linkText, string routeName, RouteValueDictionary routeValues) {
+ return RouteLink(htmlHelper, linkText, routeName, routeValues, new RouteValueDictionary());
+ }
+
+ public static MvcHtmlString RouteLink(this HtmlHelper htmlHelper, string linkText, object routeValues, object htmlAttributes) {
+ return RouteLink(htmlHelper, linkText, new RouteValueDictionary(routeValues), new RouteValueDictionary(htmlAttributes));
+ }
+
+ public static MvcHtmlString RouteLink(this HtmlHelper htmlHelper, string linkText, RouteValueDictionary routeValues, IDictionary<string, object> htmlAttributes) {
+ return RouteLink(htmlHelper, linkText, null /* routeName */, routeValues, htmlAttributes);
+ }
+
+ public static MvcHtmlString RouteLink(this HtmlHelper htmlHelper, string linkText, string routeName, object routeValues, object htmlAttributes) {
+ return RouteLink(htmlHelper, linkText, routeName, new RouteValueDictionary(routeValues), new RouteValueDictionary(htmlAttributes));
+ }
+
+ public static MvcHtmlString RouteLink(this HtmlHelper htmlHelper, string linkText, string routeName, RouteValueDictionary routeValues, IDictionary<string, object> htmlAttributes) {
+ if (String.IsNullOrEmpty(linkText)) {
+ throw new ArgumentException(MvcResources.Common_NullOrEmpty, "linkText");
+ }
+ return MvcHtmlString.Create(HtmlHelper.GenerateRouteLink(htmlHelper.ViewContext.RequestContext, htmlHelper.RouteCollection, linkText, routeName, routeValues, htmlAttributes));
+ }
+
+ public static MvcHtmlString RouteLink(this HtmlHelper htmlHelper, string linkText, string routeName, string protocol, string hostName, string fragment, object routeValues, object htmlAttributes) {
+ return RouteLink(htmlHelper, linkText, routeName, protocol, hostName, fragment, new RouteValueDictionary(routeValues), new RouteValueDictionary(htmlAttributes));
+ }
+
+ public static MvcHtmlString RouteLink(this HtmlHelper htmlHelper, string linkText, string routeName, string protocol, string hostName, string fragment, RouteValueDictionary routeValues, IDictionary<string, object> htmlAttributes) {
+ if (String.IsNullOrEmpty(linkText)) {
+ throw new ArgumentException(MvcResources.Common_NullOrEmpty, "linkText");
+ }
+ return MvcHtmlString.Create(HtmlHelper.GenerateRouteLink(htmlHelper.ViewContext.RequestContext, htmlHelper.RouteCollection, linkText, routeName, protocol, hostName, fragment, routeValues, htmlAttributes));
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/Html/MvcForm.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Html/MvcForm.cs
new file mode 100644
index 00000000000..6aa1d992b3d
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Html/MvcForm.cs
@@ -0,0 +1,69 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc.Html {
+ using System;
+ using System.IO;
+
+ public class MvcForm : IDisposable {
+
+ private bool _disposed;
+ private readonly FormContext _originalFormContext;
+ private readonly ViewContext _viewContext;
+ private readonly TextWriter _writer;
+
+ [Obsolete("The recommended alternative is the constructor MvcForm(ViewContext viewContext).", true /* error */)]
+ public MvcForm(HttpResponseBase httpResponse) {
+ if (httpResponse == null) {
+ throw new ArgumentNullException("httpResponse");
+ }
+
+ _writer = httpResponse.Output;
+ }
+
+ public MvcForm(ViewContext viewContext) {
+ if (viewContext == null) {
+ throw new ArgumentNullException("viewContext");
+ }
+
+ _viewContext = viewContext;
+ _writer = viewContext.Writer;
+
+ // push the new FormContext
+ _originalFormContext = viewContext.FormContext;
+ viewContext.FormContext = new FormContext();
+ }
+
+ public void Dispose() {
+ Dispose(true /* disposing */);
+ GC.SuppressFinalize(this);
+ }
+
+ protected virtual void Dispose(bool disposing) {
+ if (!_disposed) {
+ _disposed = true;
+ _writer.Write("</form>");
+
+ // output client validation and restore the original form context
+ if (_viewContext != null) {
+ _viewContext.OutputClientValidation();
+ _viewContext.FormContext = _originalFormContext;
+ }
+ }
+ }
+
+ public void EndForm() {
+ Dispose(true);
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/Html/PartialExtensions.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Html/PartialExtensions.cs
new file mode 100644
index 00000000000..8f488c6331b
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Html/PartialExtensions.cs
@@ -0,0 +1,36 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc.Html {
+ using System.Globalization;
+ using System.IO;
+
+ public static class PartialExtensions {
+ public static MvcHtmlString Partial(this HtmlHelper htmlHelper, string partialViewName) {
+ return Partial(htmlHelper, partialViewName, null /* model */, htmlHelper.ViewData);
+ }
+
+ public static MvcHtmlString Partial(this HtmlHelper htmlHelper, string partialViewName, ViewDataDictionary viewData) {
+ return Partial(htmlHelper, partialViewName, null /* model */, viewData);
+ }
+
+ public static MvcHtmlString Partial(this HtmlHelper htmlHelper, string partialViewName, object model) {
+ return Partial(htmlHelper, partialViewName, model, htmlHelper.ViewData);
+ }
+
+ public static MvcHtmlString Partial(this HtmlHelper htmlHelper, string partialViewName, object model, ViewDataDictionary viewData) {
+ StringWriter writer = new StringWriter(CultureInfo.CurrentCulture);
+ htmlHelper.RenderPartialInternal(partialViewName, viewData, model, writer, ViewEngines.Engines);
+ return MvcHtmlString.Create(writer.ToString());
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/Html/RenderPartialExtensions.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Html/RenderPartialExtensions.cs
new file mode 100644
index 00000000000..3d25070aae9
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Html/RenderPartialExtensions.cs
@@ -0,0 +1,35 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc.Html {
+ public static class RenderPartialExtensions {
+ // Renders the partial view with the parent's view data and model
+ public static void RenderPartial(this HtmlHelper htmlHelper, string partialViewName) {
+ htmlHelper.RenderPartialInternal(partialViewName, htmlHelper.ViewData, null /* model */, htmlHelper.ViewContext.Writer, ViewEngines.Engines);
+ }
+
+ // Renders the partial view with the given view data and, implicitly, the given view data's model
+ public static void RenderPartial(this HtmlHelper htmlHelper, string partialViewName, ViewDataDictionary viewData) {
+ htmlHelper.RenderPartialInternal(partialViewName, viewData, null /* model */, htmlHelper.ViewContext.Writer, ViewEngines.Engines);
+ }
+
+ // Renders the partial view with an empty view data and the given model
+ public static void RenderPartial(this HtmlHelper htmlHelper, string partialViewName, object model) {
+ htmlHelper.RenderPartialInternal(partialViewName, htmlHelper.ViewData, model, htmlHelper.ViewContext.Writer, ViewEngines.Engines);
+ }
+
+ // Renders the partial view with a copy of the given view data plus the given model
+ public static void RenderPartial(this HtmlHelper htmlHelper, string partialViewName, object model, ViewDataDictionary viewData) {
+ htmlHelper.RenderPartialInternal(partialViewName, viewData, model, htmlHelper.ViewContext.Writer, ViewEngines.Engines);
+ }
+ }
+} \ No newline at end of file
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/Html/SelectExtensions.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Html/SelectExtensions.cs
new file mode 100644
index 00000000000..e3cf48b7e27
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Html/SelectExtensions.cs
@@ -0,0 +1,256 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc.Html {
+ using System.Collections;
+ using System.Collections.Generic;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Globalization;
+ using System.Linq;
+ using System.Linq.Expressions;
+ using System.Text;
+ using System.Web;
+ using System.Web.Mvc.Resources;
+ using System.Web.Routing;
+
+ public static class SelectExtensions {
+
+ // DropDownList
+
+ public static MvcHtmlString DropDownList(this HtmlHelper htmlHelper, string name) {
+ return DropDownList(htmlHelper, name, null /* selectList */, null /* optionLabel */, null /* htmlAttributes */);
+ }
+
+ public static MvcHtmlString DropDownList(this HtmlHelper htmlHelper, string name, string optionLabel) {
+ return DropDownList(htmlHelper, name, null /* selectList */, optionLabel, null /* htmlAttributes */);
+ }
+
+ public static MvcHtmlString DropDownList(this HtmlHelper htmlHelper, string name, IEnumerable<SelectListItem> selectList) {
+ return DropDownList(htmlHelper, name, selectList, null /* optionLabel */, null /* htmlAttributes */);
+ }
+
+ public static MvcHtmlString DropDownList(this HtmlHelper htmlHelper, string name, IEnumerable<SelectListItem> selectList, object htmlAttributes) {
+ return DropDownList(htmlHelper, name, selectList, null /* optionLabel */, new RouteValueDictionary(htmlAttributes));
+ }
+
+ public static MvcHtmlString DropDownList(this HtmlHelper htmlHelper, string name, IEnumerable<SelectListItem> selectList, IDictionary<string, object> htmlAttributes) {
+ return DropDownList(htmlHelper, name, selectList, null /* optionLabel */, htmlAttributes);
+ }
+
+ public static MvcHtmlString DropDownList(this HtmlHelper htmlHelper, string name, IEnumerable<SelectListItem> selectList, string optionLabel) {
+ return DropDownList(htmlHelper, name, selectList, optionLabel, null /* htmlAttributes */);
+ }
+
+ public static MvcHtmlString DropDownList(this HtmlHelper htmlHelper, string name, IEnumerable<SelectListItem> selectList, string optionLabel, object htmlAttributes) {
+ return DropDownList(htmlHelper, name, selectList, optionLabel, new RouteValueDictionary(htmlAttributes));
+ }
+
+ public static MvcHtmlString DropDownList(this HtmlHelper htmlHelper, string name, IEnumerable<SelectListItem> selectList, string optionLabel, IDictionary<string, object> htmlAttributes) {
+ return DropDownListHelper(htmlHelper, name, selectList, optionLabel, htmlAttributes);
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
+ public static MvcHtmlString DropDownListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IEnumerable<SelectListItem> selectList) {
+ return DropDownListFor(htmlHelper, expression, selectList, null /* optionLabel */, null /* htmlAttributes */);
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
+ public static MvcHtmlString DropDownListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IEnumerable<SelectListItem> selectList, object htmlAttributes) {
+ return DropDownListFor(htmlHelper, expression, selectList, null /* optionLabel */, new RouteValueDictionary(htmlAttributes));
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
+ public static MvcHtmlString DropDownListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IEnumerable<SelectListItem> selectList, IDictionary<string, object> htmlAttributes) {
+ return DropDownListFor(htmlHelper, expression, selectList, null /* optionLabel */, htmlAttributes);
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
+ public static MvcHtmlString DropDownListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IEnumerable<SelectListItem> selectList, string optionLabel) {
+ return DropDownListFor(htmlHelper, expression, selectList, optionLabel, null /* htmlAttributes */);
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
+ public static MvcHtmlString DropDownListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IEnumerable<SelectListItem> selectList, string optionLabel, object htmlAttributes) {
+ return DropDownListFor(htmlHelper, expression, selectList, optionLabel, new RouteValueDictionary(htmlAttributes));
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "Users cannot use anonymous methods with the LambdaExpression type")]
+ [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
+ public static MvcHtmlString DropDownListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IEnumerable<SelectListItem> selectList, string optionLabel, IDictionary<string, object> htmlAttributes) {
+ if (expression == null) {
+ throw new ArgumentNullException("expression");
+ }
+
+ return DropDownListHelper(htmlHelper, ExpressionHelper.GetExpressionText(expression), selectList, optionLabel, htmlAttributes);
+ }
+
+ private static MvcHtmlString DropDownListHelper(HtmlHelper htmlHelper, string expression, IEnumerable<SelectListItem> selectList, string optionLabel, IDictionary<string, object> htmlAttributes) {
+ return SelectInternal(htmlHelper, optionLabel, expression, selectList, false /* allowMultiple */, htmlAttributes);
+ }
+
+ // ListBox
+
+ public static MvcHtmlString ListBox(this HtmlHelper htmlHelper, string name) {
+ return ListBox(htmlHelper, name, null /* selectList */, null /* htmlAttributes */);
+ }
+
+ public static MvcHtmlString ListBox(this HtmlHelper htmlHelper, string name, IEnumerable<SelectListItem> selectList) {
+ return ListBox(htmlHelper, name, selectList, (IDictionary<string, object>)null);
+ }
+
+ public static MvcHtmlString ListBox(this HtmlHelper htmlHelper, string name, IEnumerable<SelectListItem> selectList, object htmlAttributes) {
+ return ListBox(htmlHelper, name, selectList, new RouteValueDictionary(htmlAttributes));
+ }
+
+ public static MvcHtmlString ListBox(this HtmlHelper htmlHelper, string name, IEnumerable<SelectListItem> selectList, IDictionary<string, object> htmlAttributes) {
+ return ListBoxHelper(htmlHelper, name, selectList, htmlAttributes);
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
+ public static MvcHtmlString ListBoxFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IEnumerable<SelectListItem> selectList) {
+ return ListBoxFor(htmlHelper, expression, selectList, null /* htmlAttributes */);
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
+ public static MvcHtmlString ListBoxFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IEnumerable<SelectListItem> selectList, object htmlAttributes) {
+ return ListBoxFor(htmlHelper, expression, selectList, new RouteValueDictionary(htmlAttributes));
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "Users cannot use anonymous methods with the LambdaExpression type")]
+ [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
+ public static MvcHtmlString ListBoxFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IEnumerable<SelectListItem> selectList, IDictionary<string, object> htmlAttributes) {
+ if (expression == null) {
+ throw new ArgumentNullException("expression");
+ }
+
+ return ListBoxHelper(htmlHelper,
+ ExpressionHelper.GetExpressionText(expression),
+ selectList,
+ htmlAttributes);
+ }
+
+ private static MvcHtmlString ListBoxHelper(HtmlHelper htmlHelper, string name, IEnumerable<SelectListItem> selectList, IDictionary<string, object> htmlAttributes) {
+ return SelectInternal(htmlHelper, null /* optionLabel */, name, selectList, true /* allowMultiple */, htmlAttributes);
+ }
+
+ // Helper methods
+
+ private static IEnumerable<SelectListItem> GetSelectData(this HtmlHelper htmlHelper, string name) {
+ object o = null;
+ if (htmlHelper.ViewData != null) {
+ o = htmlHelper.ViewData.Eval(name);
+ }
+ if (o == null) {
+ throw new InvalidOperationException(
+ String.Format(
+ CultureInfo.CurrentUICulture,
+ MvcResources.HtmlHelper_MissingSelectData,
+ name,
+ "IEnumerable<SelectListItem>"));
+ }
+ IEnumerable<SelectListItem> selectList = o as IEnumerable<SelectListItem>;
+ if (selectList == null) {
+ throw new InvalidOperationException(
+ String.Format(
+ CultureInfo.CurrentUICulture,
+ MvcResources.HtmlHelper_WrongSelectDataType,
+ name,
+ o.GetType().FullName,
+ "IEnumerable<SelectListItem>"));
+ }
+ return selectList;
+ }
+
+ internal static string ListItemToOption(SelectListItem item) {
+ TagBuilder builder = new TagBuilder("option") {
+ InnerHtml = HttpUtility.HtmlEncode(item.Text)
+ };
+ if (item.Value != null) {
+ builder.Attributes["value"] = item.Value;
+ }
+ if (item.Selected) {
+ builder.Attributes["selected"] = "selected";
+ }
+ return builder.ToString(TagRenderMode.Normal);
+ }
+
+ private static MvcHtmlString SelectInternal(this HtmlHelper htmlHelper, string optionLabel, string name, IEnumerable<SelectListItem> selectList, bool allowMultiple, IDictionary<string, object> htmlAttributes) {
+ name = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(name);
+ if (String.IsNullOrEmpty(name)) {
+ throw new ArgumentException(MvcResources.Common_NullOrEmpty, "name");
+ }
+
+ bool usedViewData = false;
+
+ // If we got a null selectList, try to use ViewData to get the list of items.
+ if (selectList == null) {
+ selectList = htmlHelper.GetSelectData(name);
+ usedViewData = true;
+ }
+
+ object defaultValue = (allowMultiple) ? htmlHelper.GetModelStateValue(name, typeof(string[])) : htmlHelper.GetModelStateValue(name, typeof(string));
+
+ // If we haven't already used ViewData to get the entire list of items then we need to
+ // use the ViewData-supplied value before using the parameter-supplied value.
+ if (!usedViewData) {
+ if (defaultValue == null) {
+ defaultValue = htmlHelper.ViewData.Eval(name);
+ }
+ }
+
+ if (defaultValue != null) {
+ IEnumerable defaultValues = (allowMultiple) ? defaultValue as IEnumerable : new[] { defaultValue };
+ IEnumerable<string> values = from object value in defaultValues select Convert.ToString(value, CultureInfo.CurrentCulture);
+ HashSet<string> selectedValues = new HashSet<string>(values, StringComparer.OrdinalIgnoreCase);
+ List<SelectListItem> newSelectList = new List<SelectListItem>();
+
+ foreach (SelectListItem item in selectList) {
+ item.Selected = (item.Value != null) ? selectedValues.Contains(item.Value) : selectedValues.Contains(item.Text);
+ newSelectList.Add(item);
+ }
+ selectList = newSelectList;
+ }
+
+ // Convert each ListItem to an <option> tag
+ StringBuilder listItemBuilder = new StringBuilder();
+
+ // Make optionLabel the first item that gets rendered.
+ if (optionLabel != null) {
+ listItemBuilder.AppendLine(ListItemToOption(new SelectListItem() { Text = optionLabel, Value = String.Empty, Selected = false }));
+ }
+
+ foreach (SelectListItem item in selectList) {
+ listItemBuilder.AppendLine(ListItemToOption(item));
+ }
+
+ TagBuilder tagBuilder = new TagBuilder("select") {
+ InnerHtml = listItemBuilder.ToString()
+ };
+ tagBuilder.MergeAttributes(htmlAttributes);
+ tagBuilder.MergeAttribute("name", name, true /* replaceExisting */);
+ tagBuilder.GenerateId(name);
+ if (allowMultiple) {
+ tagBuilder.MergeAttribute("multiple", "multiple");
+ }
+
+ // If there are any errors for a named field, we add the css attribute.
+ ModelState modelState;
+ if (htmlHelper.ViewData.ModelState.TryGetValue(name, out modelState)) {
+ if (modelState.Errors.Count > 0) {
+ tagBuilder.AddCssClass(HtmlHelper.ValidationInputCssClassName);
+ }
+ }
+
+ return tagBuilder.ToMvcHtmlString(TagRenderMode.Normal);
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/Html/TemplateHelpers.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Html/TemplateHelpers.cs
new file mode 100644
index 00000000000..b220233fa23
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Html/TemplateHelpers.cs
@@ -0,0 +1,277 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc.Html {
+ using System;
+ using System.Collections;
+ using System.Collections.Generic;
+ using System.Globalization;
+ using System.IO;
+ using System.Linq;
+ using System.Linq.Expressions;
+ using System.Web.Mvc.Resources;
+ using System.Web.Routing;
+ using System.Web.UI.WebControls;
+
+ internal static class TemplateHelpers {
+ static readonly Dictionary<DataBoundControlMode, string> modeViewPaths =
+ new Dictionary<DataBoundControlMode, string> {
+ { DataBoundControlMode.ReadOnly, "DisplayTemplates" },
+ { DataBoundControlMode.Edit, "EditorTemplates" }
+ };
+
+ static readonly Dictionary<string, Func<HtmlHelper, string>> defaultDisplayActions =
+ new Dictionary<string, Func<HtmlHelper, string>>(StringComparer.OrdinalIgnoreCase) {
+ { "EmailAddress", DefaultDisplayTemplates.EmailAddressTemplate },
+ { "HiddenInput", DefaultDisplayTemplates.HiddenInputTemplate },
+ { "Html", DefaultDisplayTemplates.HtmlTemplate },
+ { "Text", DefaultDisplayTemplates.StringTemplate },
+ { "Url", DefaultDisplayTemplates.UrlTemplate },
+ { "Collection", DefaultDisplayTemplates.CollectionTemplate },
+ { typeof(bool).Name, DefaultDisplayTemplates.BooleanTemplate },
+ { typeof(decimal).Name, DefaultDisplayTemplates.DecimalTemplate },
+ { typeof(string).Name, DefaultDisplayTemplates.StringTemplate },
+ { typeof(object).Name, DefaultDisplayTemplates.ObjectTemplate },
+ };
+
+ static readonly Dictionary<string, Func<HtmlHelper, string>> defaultEditorActions =
+ new Dictionary<string, Func<HtmlHelper, string>>(StringComparer.OrdinalIgnoreCase) {
+ { "HiddenInput", DefaultEditorTemplates.HiddenInputTemplate },
+ { "MultilineText", DefaultEditorTemplates.MultilineTextTemplate },
+ { "Password", DefaultEditorTemplates.PasswordTemplate },
+ { "Text", DefaultEditorTemplates.StringTemplate },
+ { "Collection", DefaultEditorTemplates.CollectionTemplate },
+ { typeof(bool).Name, DefaultEditorTemplates.BooleanTemplate },
+ { typeof(decimal).Name, DefaultEditorTemplates.DecimalTemplate },
+ { typeof(string).Name, DefaultEditorTemplates.StringTemplate },
+ { typeof(object).Name, DefaultEditorTemplates.ObjectTemplate },
+ };
+
+ internal static string cacheItemId = Guid.NewGuid().ToString();
+
+ internal delegate string ExecuteTemplateDelegate(HtmlHelper html, ViewDataDictionary viewData, string templateName, DataBoundControlMode mode, GetViewNamesDelegate getViewNames);
+
+ internal static string ExecuteTemplate(HtmlHelper html, ViewDataDictionary viewData, string templateName, DataBoundControlMode mode, GetViewNamesDelegate getViewNames) {
+ Dictionary<string, ActionCacheItem> actionCache = GetActionCache(html);
+ Dictionary<string, Func<HtmlHelper, string>> defaultActions = mode == DataBoundControlMode.ReadOnly ? defaultDisplayActions : defaultEditorActions;
+ string modeViewPath = modeViewPaths[mode];
+
+ foreach (string viewName in getViewNames(viewData.ModelMetadata, templateName, viewData.ModelMetadata.TemplateHint, viewData.ModelMetadata.DataTypeName)) {
+ string fullViewName = modeViewPath + "/" + viewName;
+ ActionCacheItem cacheItem;
+
+ if (actionCache.TryGetValue(fullViewName, out cacheItem)) {
+ if (cacheItem != null) {
+ return cacheItem.Execute(html, viewData);
+ }
+ }
+ else {
+ ViewEngineResult viewEngineResult = ViewEngines.Engines.FindPartialView(html.ViewContext, fullViewName);
+ if (viewEngineResult.View != null) {
+ actionCache[fullViewName] = new ActionCacheViewItem { ViewName = fullViewName };
+
+ StringWriter writer = new StringWriter(CultureInfo.InvariantCulture);
+ viewEngineResult.View.Render(new ViewContext(html.ViewContext, viewEngineResult.View, viewData, html.ViewContext.TempData, writer), writer);
+ return writer.ToString();
+ }
+
+ Func<HtmlHelper, string> defaultAction;
+ if (defaultActions.TryGetValue(viewName, out defaultAction)) {
+ actionCache[fullViewName] = new ActionCacheCodeItem { Action = defaultAction };
+ return defaultAction(
+ new HtmlHelper(
+ new ViewContext(html.ViewContext, html.ViewContext.View, viewData, html.ViewContext.TempData, html.ViewContext.Writer),
+ html.ViewDataContainer
+ )
+ );
+ }
+
+ actionCache[fullViewName] = null;
+ }
+ }
+
+ throw new InvalidOperationException(
+ String.Format(
+ CultureInfo.CurrentCulture,
+ MvcResources.TemplateHelpers_NoTemplate,
+ viewData.ModelMetadata.RealModelType.FullName
+ )
+ );
+ }
+
+ internal static Dictionary<string, ActionCacheItem> GetActionCache(HtmlHelper html) {
+ HttpContextBase context = html.ViewContext.HttpContext;
+ Dictionary<string, ActionCacheItem> result;
+
+ if (!context.Items.Contains(cacheItemId)) {
+ result = new Dictionary<string, ActionCacheItem>();
+ context.Items[cacheItemId] = result;
+ }
+ else {
+ result = (Dictionary<string, ActionCacheItem>)context.Items[cacheItemId];
+ }
+
+ return result;
+ }
+
+ internal delegate IEnumerable<string> GetViewNamesDelegate(ModelMetadata metadata, params string[] templateHints);
+
+ internal static IEnumerable<string> GetViewNames(ModelMetadata metadata, params string[] templateHints) {
+ foreach (string templateHint in templateHints.Where(s => !String.IsNullOrEmpty(s))) {
+ yield return templateHint;
+ }
+
+ // We don't want to search for Nullable<T>, we want to search for T (which should handle both T and Nullable<T>)
+ Type fieldType = Nullable.GetUnderlyingType(metadata.RealModelType) ?? metadata.RealModelType;
+
+ // TODO: Make better string names for generic types
+ yield return fieldType.Name;
+
+ if (!metadata.IsComplexType) {
+ yield return "String";
+ }
+ else if (fieldType.IsInterface) {
+ if (typeof(IEnumerable).IsAssignableFrom(fieldType)) {
+ yield return "Collection";
+ }
+
+ yield return "Object";
+ }
+ else {
+ bool isEnumerable = typeof(IEnumerable).IsAssignableFrom(fieldType);
+
+ while (true) {
+ fieldType = fieldType.BaseType;
+ if (fieldType == null)
+ break;
+
+ if (isEnumerable && fieldType == typeof(Object)) {
+ yield return "Collection";
+ }
+
+ yield return fieldType.Name;
+ }
+ }
+ }
+
+ internal static MvcHtmlString Template(HtmlHelper html, string expression, string templateName, string htmlFieldName, DataBoundControlMode mode, object additionalViewData) {
+ return MvcHtmlString.Create(Template(html, expression, templateName, htmlFieldName, mode, additionalViewData, TemplateHelper));
+ }
+
+ // Unit testing version
+ internal static string Template(HtmlHelper html, string expression, string templateName, string htmlFieldName,
+ DataBoundControlMode mode, object additionalViewData, TemplateHelperDelegate templateHelper) {
+ return templateHelper(html,
+ ModelMetadata.FromStringExpression(expression, html.ViewData),
+ htmlFieldName ?? ExpressionHelper.GetExpressionText(expression),
+ templateName,
+ mode,
+ additionalViewData);
+ }
+
+ internal static MvcHtmlString TemplateFor<TContainer, TValue>(this HtmlHelper<TContainer> html, Expression<Func<TContainer, TValue>> expression,
+ string templateName, string htmlFieldName, DataBoundControlMode mode,
+ object additionalViewData) {
+ return MvcHtmlString.Create(TemplateFor(html, expression, templateName, htmlFieldName, mode, additionalViewData, TemplateHelper));
+ }
+
+ // Unit testing version
+ internal static string TemplateFor<TContainer, TValue>(this HtmlHelper<TContainer> html, Expression<Func<TContainer, TValue>> expression,
+ string templateName, string htmlFieldName, DataBoundControlMode mode,
+ object additionalViewData, TemplateHelperDelegate templateHelper) {
+ return templateHelper(html,
+ ModelMetadata.FromLambdaExpression(expression, html.ViewData),
+ htmlFieldName ?? ExpressionHelper.GetExpressionText(expression),
+ templateName,
+ mode,
+ additionalViewData);
+ }
+
+ internal delegate string TemplateHelperDelegate(HtmlHelper html, ModelMetadata metadata, string htmlFieldName, string templateName, DataBoundControlMode mode, object additionalViewData);
+
+ internal static string TemplateHelper(HtmlHelper html, ModelMetadata metadata, string htmlFieldName, string templateName, DataBoundControlMode mode, object additionalViewData) {
+ return TemplateHelper(html, metadata, htmlFieldName, templateName, mode, additionalViewData, ExecuteTemplate);
+ }
+
+ internal static string TemplateHelper(HtmlHelper html, ModelMetadata metadata, string htmlFieldName, string templateName, DataBoundControlMode mode, object additionalViewData, ExecuteTemplateDelegate executeTemplate) {
+ // TODO: Convert Editor into Display if model.IsReadOnly is true? Need to be careful about this because
+ // the Model property on the ViewPage/ViewUserControl is get-only, so the type descriptor automatically
+ // decorates it with a [ReadOnly] attribute...
+
+ if (metadata.ConvertEmptyStringToNull && String.Empty.Equals(metadata.Model)) {
+ metadata.Model = null;
+ }
+
+ object formattedModelValue = metadata.Model;
+ if (metadata.Model == null && mode == DataBoundControlMode.ReadOnly) {
+ formattedModelValue = metadata.NullDisplayText;
+ }
+
+ string formatString = mode == DataBoundControlMode.ReadOnly ? metadata.DisplayFormatString : metadata.EditFormatString;
+ if (metadata.Model != null && !String.IsNullOrEmpty(formatString)) {
+ formattedModelValue = String.Format(CultureInfo.CurrentCulture, formatString, metadata.Model);
+ }
+
+ // Normally this shouldn't happen, unless someone writes their own custom Object templates which
+ // don't check to make sure that the object hasn't already been displayed
+ object visitedObjectsKey = metadata.Model ?? metadata.RealModelType;
+ if (html.ViewDataContainer.ViewData.TemplateInfo.VisitedObjects.Contains(visitedObjectsKey)) { // DDB #224750
+ return String.Empty;
+ }
+
+ ViewDataDictionary viewData = new ViewDataDictionary(html.ViewDataContainer.ViewData) {
+ Model = metadata.Model,
+ ModelMetadata = metadata,
+ TemplateInfo = new TemplateInfo {
+ FormattedModelValue = formattedModelValue,
+ HtmlFieldPrefix = html.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(htmlFieldName),
+ VisitedObjects = new HashSet<object>(html.ViewContext.ViewData.TemplateInfo.VisitedObjects), // DDB #224750
+ }
+ };
+
+ if (additionalViewData != null) {
+ foreach (KeyValuePair<string, object> kvp in new RouteValueDictionary(additionalViewData)) {
+ viewData[kvp.Key] = kvp.Value;
+ }
+ }
+
+ viewData.TemplateInfo.VisitedObjects.Add(visitedObjectsKey); // DDB #224750
+
+ return executeTemplate(html, viewData, templateName, mode, GetViewNames);
+ }
+
+ internal abstract class ActionCacheItem {
+ public abstract string Execute(HtmlHelper html, ViewDataDictionary viewData);
+ }
+
+ internal class ActionCacheCodeItem : ActionCacheItem {
+ public Func<HtmlHelper, string> Action { get; set; }
+
+ public override string Execute(HtmlHelper html, ViewDataDictionary viewData) {
+ ViewContext newViewContext = new ViewContext(html.ViewContext, html.ViewContext.View, viewData, html.ViewContext.TempData, html.ViewContext.Writer);
+ HtmlHelper newHtmlHelper = new HtmlHelper(newViewContext, html.ViewDataContainer);
+ return Action(newHtmlHelper);
+ }
+ }
+
+ internal class ActionCacheViewItem : ActionCacheItem {
+ public string ViewName { get; set; }
+
+ public override string Execute(HtmlHelper html, ViewDataDictionary viewData) {
+ ViewEngineResult viewEngineResult = ViewEngines.Engines.FindPartialView(html.ViewContext, ViewName);
+ StringWriter writer = new StringWriter(CultureInfo.InvariantCulture);
+ viewEngineResult.View.Render(new ViewContext(html.ViewContext, viewEngineResult.View, viewData, html.ViewContext.TempData, writer), writer);
+ return writer.ToString();
+ }
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/Html/TextAreaExtensions.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Html/TextAreaExtensions.cs
new file mode 100644
index 00000000000..4443c43082a
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Html/TextAreaExtensions.cs
@@ -0,0 +1,172 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc.Html {
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Globalization;
+ using System.Linq.Expressions;
+ using System.Web.Mvc.Resources;
+ using System.Web.Routing;
+
+ public static class TextAreaExtensions {
+ // These values are similar to the defaults used by WebForms
+ // when using <asp:TextBox TextMode="MultiLine"> without specifying
+ // the Rows and Columns attributes.
+ private const int TextAreaRows = 2;
+ private const int TextAreaColumns = 20;
+ private static Dictionary<string, object> implicitRowsAndColumns = new Dictionary<string, object> {
+ { "rows", TextAreaRows.ToString(CultureInfo.InvariantCulture) },
+ { "cols", TextAreaColumns.ToString(CultureInfo.InvariantCulture) },
+ };
+
+ private static Dictionary<string, object> GetRowsAndColumnsDictionary(int rows, int columns) {
+ if (rows < 0) {
+ throw new ArgumentOutOfRangeException("rows", MvcResources.HtmlHelper_TextAreaParameterOutOfRange);
+ }
+ if (columns < 0) {
+ throw new ArgumentOutOfRangeException("columns", MvcResources.HtmlHelper_TextAreaParameterOutOfRange);
+ }
+
+ Dictionary<string, object> result = new Dictionary<string, object>();
+ if (rows > 0) {
+ result.Add("rows", rows.ToString(CultureInfo.InvariantCulture));
+ }
+ if (columns > 0) {
+ result.Add("cols", columns.ToString(CultureInfo.InvariantCulture));
+ }
+
+ return result;
+ }
+
+ public static MvcHtmlString TextArea(this HtmlHelper htmlHelper, string name) {
+ return TextArea(htmlHelper, name, null /* value */, null /* htmlAttributes */);
+ }
+
+ public static MvcHtmlString TextArea(this HtmlHelper htmlHelper, string name, object htmlAttributes) {
+ return TextArea(htmlHelper, name, null /* value */, new RouteValueDictionary(htmlAttributes));
+ }
+
+ public static MvcHtmlString TextArea(this HtmlHelper htmlHelper, string name, IDictionary<string, object> htmlAttributes) {
+ return TextArea(htmlHelper, name, null /* value */, htmlAttributes);
+ }
+
+ public static MvcHtmlString TextArea(this HtmlHelper htmlHelper, string name, string value) {
+ return TextArea(htmlHelper, name, value, null /* htmlAttributes */);
+ }
+
+ public static MvcHtmlString TextArea(this HtmlHelper htmlHelper, string name, string value, object htmlAttributes) {
+ return TextArea(htmlHelper, name, value, new RouteValueDictionary(htmlAttributes));
+ }
+
+ public static MvcHtmlString TextArea(this HtmlHelper htmlHelper, string name, string value, IDictionary<string, object> htmlAttributes) {
+ ModelMetadata metadata = ModelMetadata.FromStringExpression(name, htmlHelper.ViewContext.ViewData);
+ if (value != null) {
+ metadata.Model = value;
+ }
+
+ return TextAreaHelper(htmlHelper, metadata, name, implicitRowsAndColumns, htmlAttributes);
+ }
+
+ public static MvcHtmlString TextArea(this HtmlHelper htmlHelper, string name, string value, int rows, int columns, object htmlAttributes) {
+ return TextArea(htmlHelper, name, value, rows, columns, new RouteValueDictionary(htmlAttributes));
+ }
+
+ public static MvcHtmlString TextArea(this HtmlHelper htmlHelper, string name, string value, int rows, int columns, IDictionary<string, object> htmlAttributes) {
+ ModelMetadata metadata = ModelMetadata.FromStringExpression(name, htmlHelper.ViewContext.ViewData);
+ if (value != null) {
+ metadata.Model = value;
+ }
+
+ return TextAreaHelper(htmlHelper, metadata, name, GetRowsAndColumnsDictionary(rows, columns), htmlAttributes);
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
+ public static MvcHtmlString TextAreaFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression) {
+ return TextAreaFor(htmlHelper, expression, (IDictionary<string, object>)null);
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
+ public static MvcHtmlString TextAreaFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, object htmlAttributes) {
+ return TextAreaFor(htmlHelper, expression, new RouteValueDictionary(htmlAttributes));
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
+ public static MvcHtmlString TextAreaFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IDictionary<string, object> htmlAttributes) {
+ if (expression == null) {
+ throw new ArgumentNullException("expression");
+ }
+
+ return TextAreaHelper(htmlHelper,
+ ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData),
+ ExpressionHelper.GetExpressionText(expression),
+ implicitRowsAndColumns,
+ htmlAttributes);
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
+ public static MvcHtmlString TextAreaFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, int rows, int columns, object htmlAttributes) {
+ return TextAreaFor(htmlHelper, expression, rows, columns, new RouteValueDictionary(htmlAttributes));
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
+ public static MvcHtmlString TextAreaFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, int rows, int columns, IDictionary<string, object> htmlAttributes) {
+ if (expression == null) {
+ throw new ArgumentNullException("expression");
+ }
+
+ return TextAreaHelper(htmlHelper,
+ ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData),
+ ExpressionHelper.GetExpressionText(expression),
+ GetRowsAndColumnsDictionary(rows, columns),
+ htmlAttributes);
+ }
+
+ [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly", Justification = "If this fails, it is because the string-based version had an empty 'name' parameter")]
+ private static MvcHtmlString TextAreaHelper(HtmlHelper htmlHelper, ModelMetadata modelMetadata, string expression, IDictionary<string, object> rowsAndColumns, IDictionary<string, object> htmlAttributes) {
+ string name = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(expression);
+ if (String.IsNullOrEmpty(name)) {
+ throw new ArgumentException(MvcResources.Common_NullOrEmpty, "name");
+ }
+
+ TagBuilder tagBuilder = new TagBuilder("textarea");
+ tagBuilder.GenerateId(name);
+ tagBuilder.MergeAttributes(htmlAttributes, true);
+ tagBuilder.MergeAttributes(rowsAndColumns, rowsAndColumns != implicitRowsAndColumns); // Only force explicit rows/cols
+ tagBuilder.MergeAttribute("name", name, true);
+
+ // If there are any errors for a named field, we add the CSS attribute.
+ ModelState modelState;
+ if (htmlHelper.ViewData.ModelState.TryGetValue(name, out modelState) && modelState.Errors.Count > 0) {
+ tagBuilder.AddCssClass(HtmlHelper.ValidationInputCssClassName);
+ }
+
+ string value;
+ if (modelState != null && modelState.Value != null) {
+ value = modelState.Value.AttemptedValue;
+ }
+ else if (modelMetadata.Model != null) {
+ value = modelMetadata.Model.ToString();
+ }
+ else {
+ value = String.Empty;
+ }
+
+ // The first newline is always trimmed when a TextArea is rendered, so we add an extra one
+ // in case the value being rendered is something like "\r\nHello".
+ tagBuilder.SetInnerText(Environment.NewLine + value);
+
+ return tagBuilder.ToMvcHtmlString(TagRenderMode.Normal);
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/Html/ValidationExtensions.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Html/ValidationExtensions.cs
new file mode 100644
index 00000000000..1284a498929
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Html/ValidationExtensions.cs
@@ -0,0 +1,304 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc.Html {
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Globalization;
+ using System.Linq;
+ using System.Linq.Expressions;
+ using System.Text;
+ using System.Web.Mvc.Resources;
+ using System.Web.Routing;
+
+ public static class ValidationExtensions {
+
+ private const string _hiddenListItem = @"<li style=""display:none""></li>";
+ private static string _resourceClassKey;
+
+ public static string ResourceClassKey {
+ get {
+ return _resourceClassKey ?? String.Empty;
+ }
+ set {
+ _resourceClassKey = value;
+ }
+ }
+
+ private static FieldValidationMetadata ApplyFieldValidationMetadata(HtmlHelper htmlHelper, ModelMetadata modelMetadata, string modelName) {
+ FormContext formContext = htmlHelper.ViewContext.FormContext;
+ FieldValidationMetadata fieldMetadata = formContext.GetValidationMetadataForField(modelName, true /* createIfNotFound */);
+
+ // write rules to context object
+ IEnumerable<ModelValidator> validators = ModelValidatorProviders.Providers.GetValidators(modelMetadata, htmlHelper.ViewContext);
+ foreach (ModelClientValidationRule rule in validators.SelectMany(v => v.GetClientValidationRules())) {
+ fieldMetadata.ValidationRules.Add(rule);
+ }
+
+ return fieldMetadata;
+ }
+
+ private static string GetInvalidPropertyValueResource(HttpContextBase httpContext) {
+ string resourceValue = null;
+ if (!String.IsNullOrEmpty(ResourceClassKey) && (httpContext != null)) {
+ // If the user specified a ResourceClassKey try to load the resource they specified.
+ // If the class key is invalid, an exception will be thrown.
+ // If the class key is valid but the resource is not found, it returns null, in which
+ // case it will fall back to the MVC default error message.
+ resourceValue = httpContext.GetGlobalResourceObject(ResourceClassKey, "InvalidPropertyValue", CultureInfo.CurrentUICulture) as string;
+ }
+ return resourceValue ?? MvcResources.Common_ValueNotValidForProperty;
+ }
+
+ private static string GetUserErrorMessageOrDefault(HttpContextBase httpContext, ModelError error, ModelState modelState) {
+ if (!String.IsNullOrEmpty(error.ErrorMessage)) {
+ return error.ErrorMessage;
+ }
+ if (modelState == null) {
+ return null;
+ }
+
+ string attemptedValue = (modelState.Value != null) ? modelState.Value.AttemptedValue : null;
+ return String.Format(CultureInfo.CurrentCulture, GetInvalidPropertyValueResource(httpContext), attemptedValue);
+ }
+
+ // Validate
+
+ public static void Validate(this HtmlHelper htmlHelper, string modelName) {
+ if (modelName == null) {
+ throw new ArgumentNullException("modelName");
+ }
+
+ ValidateHelper(htmlHelper,
+ ModelMetadata.FromStringExpression(modelName, htmlHelper.ViewContext.ViewData),
+ modelName);
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
+ public static void ValidateFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression) {
+ ValidateHelper(htmlHelper,
+ ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData),
+ ExpressionHelper.GetExpressionText(expression));
+ }
+
+ private static void ValidateHelper(HtmlHelper htmlHelper, ModelMetadata modelMetadata, string expression) {
+ FormContext formContext = htmlHelper.ViewContext.GetFormContextForClientValidation();
+ if (formContext == null) {
+ return; // nothing to do
+ }
+
+ string modelName = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(expression);
+ ApplyFieldValidationMetadata(htmlHelper, modelMetadata, modelName);
+ }
+
+ // ValidationMessage
+
+ public static MvcHtmlString ValidationMessage(this HtmlHelper htmlHelper, string modelName) {
+ return ValidationMessage(htmlHelper, modelName, null /* validationMessage */, new RouteValueDictionary());
+ }
+
+ public static MvcHtmlString ValidationMessage(this HtmlHelper htmlHelper, string modelName, object htmlAttributes) {
+ return ValidationMessage(htmlHelper, modelName, null /* validationMessage */, new RouteValueDictionary(htmlAttributes));
+ }
+
+ [SuppressMessage("Microsoft.Naming", "CA1719:ParameterNamesShouldNotMatchMemberNames", Justification = "'validationMessage' refers to the message that will be rendered by the ValidationMessage helper.")]
+ public static MvcHtmlString ValidationMessage(this HtmlHelper htmlHelper, string modelName, string validationMessage) {
+ return ValidationMessage(htmlHelper, modelName, validationMessage, new RouteValueDictionary());
+ }
+
+ [SuppressMessage("Microsoft.Naming", "CA1719:ParameterNamesShouldNotMatchMemberNames", Justification = "'validationMessage' refers to the message that will be rendered by the ValidationMessage helper.")]
+ public static MvcHtmlString ValidationMessage(this HtmlHelper htmlHelper, string modelName, string validationMessage, object htmlAttributes) {
+ return ValidationMessage(htmlHelper, modelName, validationMessage, new RouteValueDictionary(htmlAttributes));
+ }
+
+ public static MvcHtmlString ValidationMessage(this HtmlHelper htmlHelper, string modelName, IDictionary<string, object> htmlAttributes) {
+ return ValidationMessage(htmlHelper, modelName, null /* validationMessage */, htmlAttributes);
+ }
+
+ [SuppressMessage("Microsoft.Naming", "CA1719:ParameterNamesShouldNotMatchMemberNames", Justification = "'validationMessage' refers to the message that will be rendered by the ValidationMessage helper.")]
+ public static MvcHtmlString ValidationMessage(this HtmlHelper htmlHelper, string modelName, string validationMessage, IDictionary<string, object> htmlAttributes) {
+ if (modelName == null) {
+ throw new ArgumentNullException("modelName");
+ }
+
+ return ValidationMessageHelper(htmlHelper,
+ ModelMetadata.FromStringExpression(modelName, htmlHelper.ViewContext.ViewData),
+ modelName,
+ validationMessage,
+ htmlAttributes);
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
+ public static MvcHtmlString ValidationMessageFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression) {
+ return ValidationMessageFor(htmlHelper, expression, null /* validationMessage */, new RouteValueDictionary());
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
+ public static MvcHtmlString ValidationMessageFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, string validationMessage) {
+ return ValidationMessageFor(htmlHelper, expression, validationMessage, new RouteValueDictionary());
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
+ public static MvcHtmlString ValidationMessageFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, string validationMessage, object htmlAttributes) {
+ return ValidationMessageFor(htmlHelper, expression, validationMessage, new RouteValueDictionary(htmlAttributes));
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
+ public static MvcHtmlString ValidationMessageFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, string validationMessage, IDictionary<string, object> htmlAttributes) {
+ return ValidationMessageHelper(htmlHelper,
+ ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData),
+ ExpressionHelper.GetExpressionText(expression),
+ validationMessage,
+ htmlAttributes);
+ }
+
+ private static MvcHtmlString ValidationMessageHelper(this HtmlHelper htmlHelper, ModelMetadata modelMetadata, string expression, string validationMessage, IDictionary<string, object> htmlAttributes) {
+ string modelName = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(expression);
+ FormContext formContext = htmlHelper.ViewContext.GetFormContextForClientValidation();
+
+ if (!htmlHelper.ViewData.ModelState.ContainsKey(modelName) && formContext == null) {
+ return null;
+ }
+
+ ModelState modelState = htmlHelper.ViewData.ModelState[modelName];
+ ModelErrorCollection modelErrors = (modelState == null) ? null : modelState.Errors;
+ ModelError modelError = ((modelErrors == null) || (modelErrors.Count == 0)) ? null : modelErrors[0];
+
+ if (modelError == null && formContext == null) {
+ return null;
+ }
+
+ TagBuilder builder = new TagBuilder("span");
+ builder.MergeAttributes(htmlAttributes);
+ builder.AddCssClass((modelError != null) ? HtmlHelper.ValidationMessageCssClassName : HtmlHelper.ValidationMessageValidCssClassName);
+
+ if (!String.IsNullOrEmpty(validationMessage)) {
+ builder.SetInnerText(validationMessage);
+ }
+ else if (modelError != null) {
+ builder.SetInnerText(GetUserErrorMessageOrDefault(htmlHelper.ViewContext.HttpContext, modelError, modelState));
+ }
+
+ if (formContext != null) {
+ // client validation always requires an ID
+ builder.GenerateId(modelName + ".validationMessage");
+
+ FieldValidationMetadata fieldMetadata = ApplyFieldValidationMetadata(htmlHelper, modelMetadata, modelName);
+ // rules will already have been written to the metadata object
+ fieldMetadata.ReplaceValidationMessageContents = (String.IsNullOrEmpty(validationMessage)); // only replace contents if no explicit message was specified
+ fieldMetadata.ValidationMessageId = builder.Attributes["id"];
+ }
+
+ return builder.ToMvcHtmlString(TagRenderMode.Normal);
+ }
+
+ // ValidationSummary
+
+ public static MvcHtmlString ValidationSummary(this HtmlHelper htmlHelper) {
+ return ValidationSummary(htmlHelper, false /* excludePropertyErrors */ );
+ }
+
+ public static MvcHtmlString ValidationSummary(this HtmlHelper htmlHelper, bool excludePropertyErrors) {
+ return ValidationSummary(htmlHelper, excludePropertyErrors, null /* message */);
+ }
+
+ public static MvcHtmlString ValidationSummary(this HtmlHelper htmlHelper, string message) {
+ return ValidationSummary(htmlHelper, false /* excludePropertyErrors */, message, (object)null /* htmlAttributes */);
+ }
+
+ public static MvcHtmlString ValidationSummary(this HtmlHelper htmlHelper, bool excludePropertyErrors, string message) {
+ return ValidationSummary(htmlHelper, excludePropertyErrors, message, (object)null /* htmlAttributes */);
+ }
+
+ public static MvcHtmlString ValidationSummary(this HtmlHelper htmlHelper, string message, object htmlAttributes) {
+ return ValidationSummary(htmlHelper, false /* excludePropertyErrors */, message, new RouteValueDictionary(htmlAttributes));
+ }
+
+ public static MvcHtmlString ValidationSummary(this HtmlHelper htmlHelper, bool excludePropertyErrors, string message, object htmlAttributes) {
+ return ValidationSummary(htmlHelper, excludePropertyErrors, message, new RouteValueDictionary(htmlAttributes));
+ }
+
+ public static MvcHtmlString ValidationSummary(this HtmlHelper htmlHelper, string message, IDictionary<string, object> htmlAttributes) {
+ return ValidationSummary(htmlHelper, false /* excludePropertyErrors */, message, htmlAttributes);
+ }
+
+ public static MvcHtmlString ValidationSummary(this HtmlHelper htmlHelper, bool excludePropertyErrors, string message, IDictionary<string, object> htmlAttributes) {
+ if (htmlHelper == null) {
+ throw new ArgumentNullException("htmlHelper");
+ }
+
+ FormContext formContext = htmlHelper.ViewContext.GetFormContextForClientValidation();
+ if (formContext == null && htmlHelper.ViewData.ModelState.IsValid) {
+ return null;
+ }
+
+ string messageSpan;
+ if (!String.IsNullOrEmpty(message)) {
+ TagBuilder spanTag = new TagBuilder("span");
+ spanTag.SetInnerText(message);
+ messageSpan = spanTag.ToString(TagRenderMode.Normal) + Environment.NewLine;
+ }
+ else {
+ messageSpan = null;
+ }
+
+ StringBuilder htmlSummary = new StringBuilder();
+ TagBuilder unorderedList = new TagBuilder("ul");
+
+ IEnumerable<ModelState> modelStates = null;
+ if (excludePropertyErrors) {
+ ModelState ms;
+ htmlHelper.ViewData.ModelState.TryGetValue(htmlHelper.ViewData.TemplateInfo.HtmlFieldPrefix, out ms);
+ if (ms != null) {
+ modelStates = new ModelState[] { ms };
+ }
+ }
+ else {
+ modelStates = htmlHelper.ViewData.ModelState.Values;
+ }
+
+ if (modelStates != null) {
+ foreach (ModelState modelState in modelStates) {
+ foreach (ModelError modelError in modelState.Errors) {
+ string errorText = GetUserErrorMessageOrDefault(htmlHelper.ViewContext.HttpContext, modelError, null /* modelState */);
+ if (!String.IsNullOrEmpty(errorText)) {
+ TagBuilder listItem = new TagBuilder("li");
+ listItem.SetInnerText(errorText);
+ htmlSummary.AppendLine(listItem.ToString(TagRenderMode.Normal));
+ }
+ }
+ }
+ }
+
+ if (htmlSummary.Length == 0) {
+ htmlSummary.AppendLine(_hiddenListItem);
+ }
+
+ unorderedList.InnerHtml = htmlSummary.ToString();
+
+ TagBuilder divBuilder = new TagBuilder("div");
+ divBuilder.MergeAttributes(htmlAttributes);
+ divBuilder.AddCssClass((htmlHelper.ViewData.ModelState.IsValid) ? HtmlHelper.ValidationSummaryValidCssClassName : HtmlHelper.ValidationSummaryCssClassName);
+ divBuilder.InnerHtml = messageSpan + unorderedList.ToString(TagRenderMode.Normal);
+
+ if (formContext != null) {
+ // client val summaries need an ID
+ divBuilder.GenerateId("validationSummary");
+ formContext.ValidationSummaryId = divBuilder.Attributes["id"];
+ formContext.ReplaceValidationSummary = !excludePropertyErrors;
+ }
+ return divBuilder.ToMvcHtmlString(TagRenderMode.Normal);
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/HtmlHelper.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/HtmlHelper.cs
new file mode 100644
index 00000000000..ae08fcf3c1f
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/HtmlHelper.cs
@@ -0,0 +1,361 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Globalization;
+ using System.IO;
+ using System.Text;
+ using System.Web;
+ using System.Web.Mvc.Resources;
+ using System.Web.Routing;
+
+ [SuppressMessage("Microsoft.Security", "CA2112:SecuredTypesShouldNotExposeFields",
+ Justification = "Public fields for CSS names do not contain secure information.")]
+ public class HtmlHelper {
+
+ private delegate string HtmlEncoder(object value);
+ private static readonly HtmlEncoder _htmlEncoder = GetHtmlEncoder();
+
+ private static string _idAttributeDotReplacement;
+
+ public static readonly string ValidationInputCssClassName = "input-validation-error";
+ public static readonly string ValidationInputValidCssClassName = "input-validation-valid";
+ public static readonly string ValidationMessageCssClassName = "field-validation-error";
+ public static readonly string ValidationMessageValidCssClassName = "field-validation-valid";
+ public static readonly string ValidationSummaryCssClassName = "validation-summary-errors";
+ public static readonly string ValidationSummaryValidCssClassName = "validation-summary-valid";
+
+ private AntiForgeryDataSerializer _serializer;
+
+ public HtmlHelper(ViewContext viewContext, IViewDataContainer viewDataContainer)
+ : this(viewContext, viewDataContainer, RouteTable.Routes) {
+ }
+
+ public HtmlHelper(ViewContext viewContext, IViewDataContainer viewDataContainer, RouteCollection routeCollection) {
+ if (viewContext == null) {
+ throw new ArgumentNullException("viewContext");
+ }
+ if (viewDataContainer == null) {
+ throw new ArgumentNullException("viewDataContainer");
+ }
+ if (routeCollection == null) {
+ throw new ArgumentNullException("routeCollection");
+ }
+ ViewContext = viewContext;
+ ViewDataContainer = viewDataContainer;
+ RouteCollection = routeCollection;
+ }
+
+ public static string IdAttributeDotReplacement {
+ get {
+ if (String.IsNullOrEmpty(_idAttributeDotReplacement)) {
+ _idAttributeDotReplacement = "_";
+ }
+ return _idAttributeDotReplacement;
+ }
+ set {
+ _idAttributeDotReplacement = value;
+ }
+ }
+
+ public RouteCollection RouteCollection {
+ get;
+ private set;
+ }
+
+ internal AntiForgeryDataSerializer Serializer {
+ get {
+ if (_serializer == null) {
+ _serializer = new AntiForgeryDataSerializer();
+ }
+ return _serializer;
+ }
+ set {
+ _serializer = value;
+ }
+ }
+
+ public ViewContext ViewContext {
+ get;
+ private set;
+ }
+
+ public ViewDataDictionary ViewData {
+ get {
+ return ViewDataContainer.ViewData;
+ }
+ }
+
+ public IViewDataContainer ViewDataContainer {
+ get;
+ private set;
+ }
+
+ public MvcHtmlString AntiForgeryToken() {
+ return AntiForgeryToken(null /* salt */);
+ }
+
+ public MvcHtmlString AntiForgeryToken(string salt) {
+ return AntiForgeryToken(salt, null /* domain */, null /* path */);
+ }
+
+ public MvcHtmlString AntiForgeryToken(string salt, string domain, string path) {
+ string formValue = GetAntiForgeryTokenAndSetCookie(salt, domain, path);
+ string fieldName = AntiForgeryData.GetAntiForgeryTokenName(null);
+
+ TagBuilder builder = new TagBuilder("input");
+ builder.Attributes["type"] = "hidden";
+ builder.Attributes["name"] = fieldName;
+ builder.Attributes["value"] = formValue;
+ return builder.ToMvcHtmlString(TagRenderMode.SelfClosing);
+ }
+
+ [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic",
+ Justification = "For consistency, all helpers are instance methods.")]
+ public string AttributeEncode(string value) {
+ return (!String.IsNullOrEmpty(value)) ? HttpUtility.HtmlAttributeEncode(value) : String.Empty;
+ }
+
+ public string AttributeEncode(object value) {
+ return AttributeEncode(Convert.ToString(value, CultureInfo.InvariantCulture));
+ }
+
+ public void EnableClientValidation() {
+ ViewContext.ClientValidationEnabled = true;
+ }
+
+ [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic",
+ Justification = "For consistency, all helpers are instance methods.")]
+ public string Encode(string value) {
+ return (!String.IsNullOrEmpty(value)) ? HttpUtility.HtmlEncode(value) : String.Empty;
+ }
+
+ [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic",
+ Justification = "For consistency, all helpers are instance methods.")]
+ public string Encode(object value) {
+ return _htmlEncoder(value);
+ }
+
+ // method used if HttpUtility.HtmlEncode(object) method does not exist
+ private static string EncodeLegacy(object value) {
+ string stringVal = Convert.ToString(value, CultureInfo.CurrentCulture);
+ return (!String.IsNullOrEmpty(stringVal)) ? HttpUtility.HtmlEncode(stringVal) : String.Empty;
+ }
+
+ internal string EvalString(string key) {
+ return Convert.ToString(ViewData.Eval(key), CultureInfo.CurrentCulture);
+ }
+
+ internal bool EvalBoolean(string key) {
+ return Convert.ToBoolean(ViewData.Eval(key), CultureInfo.InvariantCulture);
+ }
+
+ internal static IView FindPartialView(ViewContext viewContext, string partialViewName, ViewEngineCollection viewEngineCollection) {
+ ViewEngineResult result = viewEngineCollection.FindPartialView(viewContext, partialViewName);
+ if (result.View != null) {
+ return result.View;
+ }
+
+ StringBuilder locationsText = new StringBuilder();
+ foreach (string location in result.SearchedLocations) {
+ locationsText.AppendLine();
+ locationsText.Append(location);
+ }
+
+ throw new InvalidOperationException(String.Format(CultureInfo.CurrentUICulture,
+ MvcResources.Common_PartialViewNotFound, partialViewName, locationsText));
+ }
+
+ public static string GenerateIdFromName(string name) {
+ return GenerateIdFromName(name, IdAttributeDotReplacement);
+ }
+
+ public static string GenerateIdFromName(string name, string idAttributeDotReplacement) {
+ if (name == null) {
+ throw new ArgumentNullException("name");
+ }
+ if (idAttributeDotReplacement == null) {
+ throw new ArgumentNullException("idAttributeDotReplacement");
+ }
+
+ return name.Replace(".", idAttributeDotReplacement);
+ }
+
+ public static string GenerateLink(RequestContext requestContext, RouteCollection routeCollection, string linkText, string routeName, string actionName, string controllerName, RouteValueDictionary routeValues, IDictionary<string, object> htmlAttributes) {
+ return GenerateLink(requestContext, routeCollection, linkText, routeName, actionName, controllerName, null/* protocol */, null/* hostName */, null/* fragment */, routeValues, htmlAttributes);
+ }
+
+ public static string GenerateLink(RequestContext requestContext, RouteCollection routeCollection, string linkText, string routeName, string actionName, string controllerName, string protocol, string hostName, string fragment, RouteValueDictionary routeValues, IDictionary<string, object> htmlAttributes) {
+ return GenerateLinkInternal(requestContext, routeCollection, linkText, routeName, actionName, controllerName, protocol, hostName, fragment, routeValues, htmlAttributes, true /* includeImplicitMvcValues */);
+ }
+
+ private static string GenerateLinkInternal(RequestContext requestContext, RouteCollection routeCollection, string linkText, string routeName, string actionName, string controllerName, string protocol, string hostName, string fragment, RouteValueDictionary routeValues, IDictionary<string, object> htmlAttributes, bool includeImplicitMvcValues) {
+ string url = UrlHelper.GenerateUrl(routeName, actionName, controllerName, protocol, hostName, fragment, routeValues, routeCollection, requestContext, includeImplicitMvcValues);
+ TagBuilder tagBuilder = new TagBuilder("a") {
+ InnerHtml = (!String.IsNullOrEmpty(linkText)) ? HttpUtility.HtmlEncode(linkText) : String.Empty
+ };
+ tagBuilder.MergeAttributes(htmlAttributes);
+ tagBuilder.MergeAttribute("href", url);
+ return tagBuilder.ToString(TagRenderMode.Normal);
+ }
+
+ public static string GenerateRouteLink(RequestContext requestContext, RouteCollection routeCollection, string linkText, string routeName, RouteValueDictionary routeValues, IDictionary<string, object> htmlAttributes) {
+ return GenerateRouteLink(requestContext, routeCollection, linkText, routeName, null/* protocol */, null/* hostName */, null/* fragment */, routeValues, htmlAttributes);
+ }
+
+ public static string GenerateRouteLink(RequestContext requestContext, RouteCollection routeCollection, string linkText, string routeName, string protocol, string hostName, string fragment, RouteValueDictionary routeValues, IDictionary<string, object> htmlAttributes) {
+ return GenerateLinkInternal(requestContext, routeCollection, linkText, routeName, null /* actionName */, null /* controllerName */, protocol, hostName, fragment, routeValues, htmlAttributes, false /* includeImplicitMvcValues */);
+ }
+
+ private string GetAntiForgeryTokenAndSetCookie(string salt, string domain, string path) {
+ string cookieName = AntiForgeryData.GetAntiForgeryTokenName(ViewContext.HttpContext.Request.ApplicationPath);
+
+ AntiForgeryData cookieToken;
+ HttpCookie cookie = ViewContext.HttpContext.Request.Cookies[cookieName];
+ if (cookie != null) {
+ cookieToken = Serializer.Deserialize(cookie.Value);
+ }
+ else {
+ cookieToken = AntiForgeryData.NewToken();
+ string cookieValue = Serializer.Serialize(cookieToken);
+
+ HttpCookie newCookie = new HttpCookie(cookieName, cookieValue) { HttpOnly = true, Domain = domain };
+ if (!String.IsNullOrEmpty(path)) {
+ newCookie.Path = path;
+ }
+ ViewContext.HttpContext.Response.Cookies.Set(newCookie);
+ }
+
+ AntiForgeryData formToken = new AntiForgeryData(cookieToken) {
+ Salt = salt,
+ Username = AntiForgeryData.GetUsername(ViewContext.HttpContext.User)
+ };
+ string formValue = Serializer.Serialize(formToken);
+ return formValue;
+ }
+
+ public static string GetFormMethodString(FormMethod method) {
+ switch (method) {
+ case FormMethod.Get:
+ return "get";
+ case FormMethod.Post:
+ return "post";
+ default:
+ return "post";
+ }
+ }
+
+ // selects the v3.5 (legacy) or v4 HTML encoder
+ private static HtmlEncoder GetHtmlEncoder() {
+ return TypeHelpers.CreateDelegate<HtmlEncoder>(TypeHelpers.SystemWebAssembly, "System.Web.HttpUtility", "HtmlEncode", null)
+ ?? EncodeLegacy;
+ }
+
+ public static string GetInputTypeString(InputType inputType) {
+ switch (inputType) {
+ case InputType.CheckBox:
+ return "checkbox";
+ case InputType.Hidden:
+ return "hidden";
+ case InputType.Password:
+ return "password";
+ case InputType.Radio:
+ return "radio";
+ case InputType.Text:
+ return "text";
+ default:
+ return "text";
+ }
+ }
+
+ internal object GetModelStateValue(string key, Type destinationType) {
+ ModelState modelState;
+ if (ViewData.ModelState.TryGetValue(key, out modelState)) {
+ if (modelState.Value != null) {
+ return modelState.Value.ConvertTo(destinationType, null /* culture */);
+ }
+ }
+ return null;
+ }
+
+ public MvcHtmlString HttpMethodOverride(HttpVerbs httpVerb) {
+ string httpMethod;
+ switch (httpVerb) {
+ case HttpVerbs.Delete:
+ httpMethod = "DELETE";
+ break;
+ case HttpVerbs.Head:
+ httpMethod = "HEAD";
+ break;
+ case HttpVerbs.Put:
+ httpMethod = "PUT";
+ break;
+ default:
+ throw new ArgumentException(MvcResources.HtmlHelper_InvalidHttpVerb, "httpVerb");
+ }
+
+ return HttpMethodOverride(httpMethod);
+ }
+
+ [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic",
+ Justification = "For consistency, all helpers are instance methods.")]
+ public MvcHtmlString HttpMethodOverride(string httpMethod) {
+ if (String.IsNullOrEmpty(httpMethod)) {
+ throw new ArgumentException(MvcResources.Common_NullOrEmpty, "httpMethod");
+ }
+ if (String.Equals(httpMethod, "GET", StringComparison.OrdinalIgnoreCase) ||
+ String.Equals(httpMethod, "POST", StringComparison.OrdinalIgnoreCase)) {
+ throw new ArgumentException(MvcResources.HtmlHelper_InvalidHttpMethod, "httpMethod");
+ }
+
+ TagBuilder tagBuilder = new TagBuilder("input");
+ tagBuilder.Attributes["type"] = "hidden";
+ tagBuilder.Attributes["name"] = HttpRequestExtensions.XHttpMethodOverrideKey;
+ tagBuilder.Attributes["value"] = httpMethod;
+
+ return tagBuilder.ToMvcHtmlString(TagRenderMode.SelfClosing);
+ }
+
+ internal virtual void RenderPartialInternal(string partialViewName, ViewDataDictionary viewData, object model, TextWriter writer, ViewEngineCollection viewEngineCollection) {
+ if (String.IsNullOrEmpty(partialViewName)) {
+ throw new ArgumentException(MvcResources.Common_NullOrEmpty, "partialViewName");
+ }
+
+ ViewDataDictionary newViewData = null;
+
+ if (model == null) {
+ if (viewData == null) {
+ newViewData = new ViewDataDictionary(ViewData);
+ }
+ else {
+ newViewData = new ViewDataDictionary(viewData);
+ }
+ }
+ else {
+ if (viewData == null) {
+ newViewData = new ViewDataDictionary(model);
+ }
+ else {
+ newViewData = new ViewDataDictionary(viewData) { Model = model };
+ }
+ }
+
+ ViewContext newViewContext = new ViewContext(ViewContext, ViewContext.View, newViewData, ViewContext.TempData, writer);
+ IView view = FindPartialView(newViewContext, partialViewName, viewEngineCollection);
+ view.Render(newViewContext, writer);
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/HtmlHelper`1.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/HtmlHelper`1.cs
new file mode 100644
index 00000000000..243f7f65fb8
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/HtmlHelper`1.cs
@@ -0,0 +1,35 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System.Web.Routing;
+
+ public class HtmlHelper<TModel> : HtmlHelper {
+ private ViewDataDictionary<TModel> _viewData;
+
+ public HtmlHelper(ViewContext viewContext, IViewDataContainer viewDataContainer)
+ : this(viewContext, viewDataContainer, RouteTable.Routes) {
+ }
+
+ public HtmlHelper(ViewContext viewContext, IViewDataContainer viewDataContainer, RouteCollection routeCollection)
+ : base(viewContext, viewDataContainer, routeCollection) {
+
+ _viewData = new ViewDataDictionary<TModel>(viewDataContainer.ViewData);
+ }
+
+ public new ViewDataDictionary<TModel> ViewData {
+ get {
+ return _viewData;
+ }
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/HttpAntiForgeryException.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/HttpAntiForgeryException.cs
new file mode 100644
index 00000000000..8a8cf343df8
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/HttpAntiForgeryException.cs
@@ -0,0 +1,37 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Runtime.Serialization;
+ using System.Web;
+
+ [Serializable]
+ public sealed class HttpAntiForgeryException : HttpException {
+
+ public HttpAntiForgeryException() {
+ }
+
+ private HttpAntiForgeryException(SerializationInfo info, StreamingContext context)
+ : base(info, context) {
+ }
+
+ public HttpAntiForgeryException(string message)
+ : base(message) {
+ }
+
+ public HttpAntiForgeryException(string message, Exception innerException)
+ : base(message, innerException) {
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/HttpDeleteAttribute.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/HttpDeleteAttribute.cs
new file mode 100644
index 00000000000..1314f051169
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/HttpDeleteAttribute.cs
@@ -0,0 +1,27 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Reflection;
+ using System.Web;
+
+ [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
+ public sealed class HttpDeleteAttribute : ActionMethodSelectorAttribute {
+
+ private static readonly AcceptVerbsAttribute _innerAttribute = new AcceptVerbsAttribute(HttpVerbs.Delete);
+
+ public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo) {
+ return _innerAttribute.IsValidForRequest(controllerContext, methodInfo);
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/HttpFileCollectionValueProvider.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/HttpFileCollectionValueProvider.cs
new file mode 100644
index 00000000000..d67ec5e8291
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/HttpFileCollectionValueProvider.cs
@@ -0,0 +1,52 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Collections.Generic;
+ using System.Globalization;
+ using System.Linq;
+
+ public sealed class HttpFileCollectionValueProvider : DictionaryValueProvider<HttpPostedFileBase[]> {
+
+ private static readonly Dictionary<string, HttpPostedFileBase[]> _emptyDictionary = new Dictionary<string, HttpPostedFileBase[]>();
+
+ public HttpFileCollectionValueProvider(ControllerContext controllerContext)
+ : base(GetHttpPostedFileDictionary(controllerContext), CultureInfo.InvariantCulture) {
+ }
+
+ private static Dictionary<string, HttpPostedFileBase[]> GetHttpPostedFileDictionary(ControllerContext controllerContext) {
+ HttpFileCollectionBase files = controllerContext.HttpContext.Request.Files;
+
+ // fast-track common case of no files
+ if (files.Count == 0) {
+ return _emptyDictionary;
+ }
+
+ // build up the 1:many file mapping
+ List<KeyValuePair<string, HttpPostedFileBase>> mapping = new List<KeyValuePair<string, HttpPostedFileBase>>();
+ string[] allKeys = files.AllKeys;
+ for (int i = 0; i < files.Count; i++) {
+ string key = allKeys[i];
+ if (key != null) {
+ HttpPostedFileBase file = HttpPostedFileBaseModelBinder.ChooseFileOrNull(files[i]);
+ mapping.Add(new KeyValuePair<string, HttpPostedFileBase>(key, file));
+ }
+ }
+
+ // turn the mapping into a 1:many dictionary
+ var grouped = mapping.GroupBy(el => el.Key, el => el.Value, StringComparer.OrdinalIgnoreCase);
+ return grouped.ToDictionary(g => g.Key, g => g.ToArray(), StringComparer.OrdinalIgnoreCase);
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/HttpFileCollectionValueProviderFactory.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/HttpFileCollectionValueProviderFactory.cs
new file mode 100644
index 00000000000..6eec3cec8c7
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/HttpFileCollectionValueProviderFactory.cs
@@ -0,0 +1,27 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+
+ public sealed class HttpFileCollectionValueProviderFactory : ValueProviderFactory {
+
+ public override IValueProvider GetValueProvider(ControllerContext controllerContext) {
+ if (controllerContext == null) {
+ throw new ArgumentNullException("controllerContext");
+ }
+
+ return new HttpFileCollectionValueProvider(controllerContext);
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/HttpGetAttribute.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/HttpGetAttribute.cs
new file mode 100644
index 00000000000..d2f2e9ef3fd
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/HttpGetAttribute.cs
@@ -0,0 +1,27 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Reflection;
+ using System.Web;
+
+ [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
+ public sealed class HttpGetAttribute : ActionMethodSelectorAttribute {
+
+ private static readonly AcceptVerbsAttribute _innerAttribute = new AcceptVerbsAttribute(HttpVerbs.Get);
+
+ public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo) {
+ return _innerAttribute.IsValidForRequest(controllerContext, methodInfo);
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/HttpHandlerUtil.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/HttpHandlerUtil.cs
new file mode 100644
index 00000000000..a9b1f8de7ea
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/HttpHandlerUtil.cs
@@ -0,0 +1,87 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Web;
+ using System.Web.Mvc.Resources;
+ using System.Web.UI;
+
+ internal static class HttpHandlerUtil {
+
+ // Since Server.Execute() doesn't propagate HttpExceptions where the status code is
+ // anything other than 500, we need to wrap these exceptions ourselves.
+ public static IHttpHandler WrapForServerExecute(IHttpHandler httpHandler) {
+ IHttpAsyncHandler asyncHandler = httpHandler as IHttpAsyncHandler;
+ return (asyncHandler != null) ? new ServerExecuteHttpHandlerAsyncWrapper(asyncHandler) : new ServerExecuteHttpHandlerWrapper(httpHandler);
+ }
+
+ // Server.Execute() requires that the provided IHttpHandler subclass Page.
+ internal class ServerExecuteHttpHandlerWrapper : Page {
+ private readonly IHttpHandler _httpHandler;
+
+ public ServerExecuteHttpHandlerWrapper(IHttpHandler httpHandler) {
+ _httpHandler = httpHandler;
+ }
+
+ internal IHttpHandler InnerHandler {
+ get {
+ return _httpHandler;
+ }
+ }
+
+ public override void ProcessRequest(HttpContext context) {
+ Wrap(() => _httpHandler.ProcessRequest(context));
+ }
+
+ protected static void Wrap(Action action) {
+ Wrap(delegate {
+ action();
+ return (object)null;
+ });
+ }
+
+ protected static TResult Wrap<TResult>(Func<TResult> func) {
+ try {
+ return func();
+ }
+ catch (HttpException he) {
+ if (he.GetHttpCode() == 500) {
+ throw; // doesn't need to be wrapped
+ }
+ else {
+ HttpException newHe = new HttpException(500, MvcResources.ViewPageHttpHandlerWrapper_ExceptionOccurred, he);
+ throw newHe;
+ }
+ }
+ }
+ }
+
+ private sealed class ServerExecuteHttpHandlerAsyncWrapper : ServerExecuteHttpHandlerWrapper, IHttpAsyncHandler {
+ private readonly IHttpAsyncHandler _httpHandler;
+
+ public ServerExecuteHttpHandlerAsyncWrapper(IHttpAsyncHandler httpHandler)
+ : base(httpHandler) {
+ _httpHandler = httpHandler;
+ }
+
+ public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData) {
+ return Wrap(() => _httpHandler.BeginProcessRequest(context, cb, extraData));
+ }
+
+ public void EndProcessRequest(IAsyncResult result) {
+ Wrap(() => _httpHandler.EndProcessRequest(result));
+ }
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/HttpPostAttribute.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/HttpPostAttribute.cs
new file mode 100644
index 00000000000..8a07d950797
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/HttpPostAttribute.cs
@@ -0,0 +1,27 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Reflection;
+ using System.Web;
+
+ [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
+ public sealed class HttpPostAttribute : ActionMethodSelectorAttribute {
+
+ private static readonly AcceptVerbsAttribute _innerAttribute = new AcceptVerbsAttribute(HttpVerbs.Post);
+
+ public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo) {
+ return _innerAttribute.IsValidForRequest(controllerContext, methodInfo);
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/HttpPostedFileBaseModelBinder.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/HttpPostedFileBaseModelBinder.cs
new file mode 100644
index 00000000000..4adb3695edb
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/HttpPostedFileBaseModelBinder.cs
@@ -0,0 +1,48 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Web;
+
+ public class HttpPostedFileBaseModelBinder : IModelBinder {
+
+ public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) {
+ if (controllerContext == null) {
+ throw new ArgumentNullException("controllerContext");
+ }
+ if (bindingContext == null) {
+ throw new ArgumentNullException("bindingContext");
+ }
+
+ HttpPostedFileBase theFile = controllerContext.HttpContext.Request.Files[bindingContext.ModelName];
+ return ChooseFileOrNull(theFile);
+ }
+
+ // helper that returns the original file if there was content uploaded, null if empty
+ internal static HttpPostedFileBase ChooseFileOrNull(HttpPostedFileBase rawFile) {
+ // case 1: there was no <input type="file" ... /> element in the post
+ if (rawFile == null) {
+ return null;
+ }
+
+ // case 2: there was an <input type="file" ... /> element in the post, but it was left blank
+ if (rawFile.ContentLength == 0 && String.IsNullOrEmpty(rawFile.FileName)) {
+ return null;
+ }
+
+ // case 3: the file was posted
+ return rawFile;
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/HttpPutAttribute.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/HttpPutAttribute.cs
new file mode 100644
index 00000000000..d438f3d7358
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/HttpPutAttribute.cs
@@ -0,0 +1,27 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Reflection;
+ using System.Web;
+
+ [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
+ public sealed class HttpPutAttribute : ActionMethodSelectorAttribute {
+
+ private static readonly AcceptVerbsAttribute _innerAttribute = new AcceptVerbsAttribute(HttpVerbs.Put);
+
+ public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo) {
+ return _innerAttribute.IsValidForRequest(controllerContext, methodInfo);
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/HttpRequestExtensions.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/HttpRequestExtensions.cs
new file mode 100644
index 00000000000..07a76e1338e
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/HttpRequestExtensions.cs
@@ -0,0 +1,56 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+
+ public static class HttpRequestExtensions {
+ internal const string XHttpMethodOverrideKey = "X-HTTP-Method-Override";
+
+ public static string GetHttpMethodOverride(this HttpRequestBase request) {
+ if (request == null) {
+ throw new ArgumentNullException("request");
+ }
+
+ string incomingVerb = request.HttpMethod;
+
+ if (!String.Equals(incomingVerb, "POST", StringComparison.OrdinalIgnoreCase)) {
+ return incomingVerb;
+ }
+
+ string verbOverride = null;
+ string headerOverrideValue = request.Headers[XHttpMethodOverrideKey];
+ if (!String.IsNullOrEmpty(headerOverrideValue)) {
+ verbOverride = headerOverrideValue;
+ }
+ else {
+ string formOverrideValue = request.Form[XHttpMethodOverrideKey];
+ if (!String.IsNullOrEmpty(formOverrideValue)) {
+ verbOverride = formOverrideValue;
+ }
+ else {
+ string queryStringOverrideValue = request.QueryString[XHttpMethodOverrideKey];
+ if (!String.IsNullOrEmpty(queryStringOverrideValue)) {
+ verbOverride = queryStringOverrideValue;
+ }
+ }
+ }
+ if (verbOverride != null) {
+ if (!String.Equals(verbOverride, "GET", StringComparison.OrdinalIgnoreCase) &&
+ !String.Equals(verbOverride, "POST", StringComparison.OrdinalIgnoreCase)) {
+ incomingVerb = verbOverride;
+ }
+ }
+ return incomingVerb;
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/HttpUnauthorizedResult.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/HttpUnauthorizedResult.cs
new file mode 100644
index 00000000000..cae9d1dd79d
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/HttpUnauthorizedResult.cs
@@ -0,0 +1,30 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+
+ public class HttpUnauthorizedResult : ActionResult {
+
+ public override void ExecuteResult(ControllerContext context) {
+ if (context == null) {
+ throw new ArgumentNullException("context");
+ }
+
+ // HTTP 401 is the status code for unauthorized access. Other code might
+ // intercept this and perform some special logic. For example, the
+ // FormsAuthenticationModule looks for 401 responses and instead redirects
+ // the user to the login page.
+ context.HttpContext.Response.StatusCode = 401;
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/HttpVerbs.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/HttpVerbs.cs
new file mode 100644
index 00000000000..b9506c155e9
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/HttpVerbs.cs
@@ -0,0 +1,24 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+
+ [Flags]
+ public enum HttpVerbs {
+ Get = 1 << 0,
+ Post = 1 << 1,
+ Put = 1 << 2,
+ Delete = 1 << 3,
+ Head = 1 << 4
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/IActionFilter.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/IActionFilter.cs
new file mode 100644
index 00000000000..2cc7f3e5344
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/IActionFilter.cs
@@ -0,0 +1,19 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+
+ public interface IActionFilter {
+ void OnActionExecuting(ActionExecutingContext filterContext);
+ void OnActionExecuted(ActionExecutedContext filterContext);
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/IActionInvoker.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/IActionInvoker.cs
new file mode 100644
index 00000000000..9ab878dd971
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/IActionInvoker.cs
@@ -0,0 +1,18 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+
+ public interface IActionInvoker {
+ bool InvokeAction(ControllerContext controllerContext, string actionName);
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/IAuthorizationFilter.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/IAuthorizationFilter.cs
new file mode 100644
index 00000000000..93ab7b3a767
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/IAuthorizationFilter.cs
@@ -0,0 +1,18 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+
+ public interface IAuthorizationFilter {
+ void OnAuthorization(AuthorizationContext filterContext);
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/IBuildManager.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/IBuildManager.cs
new file mode 100644
index 00000000000..34eea42c4bd
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/IBuildManager.cs
@@ -0,0 +1,26 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System.Collections;
+ using System.IO;
+
+ // REVIEW: Should we make this public?
+ internal interface IBuildManager {
+ object CreateInstanceFromVirtualPath(string virtualPath, Type requiredBaseType);
+ ICollection GetReferencedAssemblies();
+
+ // ASP.NET 4 methods
+ Stream ReadCachedFile(string fileName);
+ Stream CreateCachedFile(string fileName);
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/IController.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/IController.cs
new file mode 100644
index 00000000000..d5bb4ca370c
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/IController.cs
@@ -0,0 +1,19 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System.Web.Routing;
+
+ public interface IController {
+ void Execute(RequestContext requestContext);
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/IControllerFactory.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/IControllerFactory.cs
new file mode 100644
index 00000000000..316774ff44b
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/IControllerFactory.cs
@@ -0,0 +1,20 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System.Web.Routing;
+
+ public interface IControllerFactory {
+ IController CreateController(RequestContext requestContext, string controllerName);
+ void ReleaseController(IController controller);
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/IExceptionFilter.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/IExceptionFilter.cs
new file mode 100644
index 00000000000..ede6fd3b7d1
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/IExceptionFilter.cs
@@ -0,0 +1,18 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+
+ public interface IExceptionFilter {
+ void OnException(ExceptionContext filterContext);
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/IModelBinder.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/IModelBinder.cs
new file mode 100644
index 00000000000..d6ce03988c8
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/IModelBinder.cs
@@ -0,0 +1,18 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+
+ public interface IModelBinder {
+ object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext);
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/IResultFilter.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/IResultFilter.cs
new file mode 100644
index 00000000000..c00395f5504
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/IResultFilter.cs
@@ -0,0 +1,19 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+
+ public interface IResultFilter {
+ void OnResultExecuting(ResultExecutingContext filterContext);
+ void OnResultExecuted(ResultExecutedContext filterContext);
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/IRouteWithArea.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/IRouteWithArea.cs
new file mode 100644
index 00000000000..23eab10c3de
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/IRouteWithArea.cs
@@ -0,0 +1,21 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+
+ public interface IRouteWithArea {
+
+ string Area { get; }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ITempDataProvider.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ITempDataProvider.cs
new file mode 100644
index 00000000000..3361130ffc7
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ITempDataProvider.cs
@@ -0,0 +1,20 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System.Collections.Generic;
+
+ public interface ITempDataProvider {
+ IDictionary<string, object> LoadTempData(ControllerContext controllerContext);
+ void SaveTempData(ControllerContext controllerContext, IDictionary<string, object> values);
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/IValueProvider.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/IValueProvider.cs
new file mode 100644
index 00000000000..95c7e1613fe
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/IValueProvider.cs
@@ -0,0 +1,20 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+
+ public interface IValueProvider {
+ bool ContainsPrefix(string prefix);
+ ValueProviderResult GetValue(string key);
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/IView.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/IView.cs
new file mode 100644
index 00000000000..97d044c6dc6
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/IView.cs
@@ -0,0 +1,19 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System.IO;
+
+ public interface IView {
+ void Render(ViewContext viewContext, TextWriter writer);
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/IViewDataContainer.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/IViewDataContainer.cs
new file mode 100644
index 00000000000..c1bfacb92a1
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/IViewDataContainer.cs
@@ -0,0 +1,21 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System.Diagnostics.CodeAnalysis;
+
+ public interface IViewDataContainer {
+ [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly",
+ Justification = "This is the mechanism by which the ViewPage / ViewUserControl get their ViewDataDictionary objects.")]
+ ViewDataDictionary ViewData { get; set; }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/IViewEngine.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/IViewEngine.cs
new file mode 100644
index 00000000000..9950ce777f2
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/IViewEngine.cs
@@ -0,0 +1,20 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+
+ public interface IViewEngine {
+ ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache);
+ ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache);
+ void ReleaseView(ControllerContext controllerContext, IView view);
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/IViewLocationCache.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/IViewLocationCache.cs
new file mode 100644
index 00000000000..824950b9c3d
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/IViewLocationCache.cs
@@ -0,0 +1,20 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System.Web;
+
+ public interface IViewLocationCache {
+ string GetViewLocation(HttpContextBase httpContext, string key);
+ void InsertViewLocation(HttpContextBase httpContext, string key, string virtualPath);
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/InputType.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/InputType.cs
new file mode 100644
index 00000000000..a4a39d82f93
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/InputType.cs
@@ -0,0 +1,21 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ public enum InputType {
+ CheckBox,
+ Hidden,
+ Password,
+ Radio,
+ Text
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/JavaScriptResult.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/JavaScriptResult.cs
new file mode 100644
index 00000000000..4ee833eb265
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/JavaScriptResult.cs
@@ -0,0 +1,36 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+
+ public class JavaScriptResult : ActionResult {
+
+ public string Script {
+ get;
+ set;
+ }
+
+ public override void ExecuteResult(ControllerContext context) {
+ if (context == null) {
+ throw new ArgumentNullException("context");
+ }
+
+ HttpResponseBase response = context.HttpContext.Response;
+ response.ContentType = "application/x-javascript";
+
+ if (Script != null) {
+ response.Write(Script);
+ }
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/JsonRequestBehavior.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/JsonRequestBehavior.cs
new file mode 100644
index 00000000000..da9ee8faa04
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/JsonRequestBehavior.cs
@@ -0,0 +1,18 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ public enum JsonRequestBehavior {
+ AllowGet,
+ DenyGet,
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/JsonResult.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/JsonResult.cs
new file mode 100644
index 00000000000..3950b09c059
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/JsonResult.cs
@@ -0,0 +1,72 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Text;
+ using System.Web;
+ using System.Web.Mvc.Resources;
+ using System.Web.Script.Serialization;
+
+ public class JsonResult : ActionResult {
+
+ public JsonResult() {
+ JsonRequestBehavior = JsonRequestBehavior.DenyGet;
+ }
+
+ public Encoding ContentEncoding {
+ get;
+ set;
+ }
+
+ public string ContentType {
+ get;
+ set;
+ }
+
+ public object Data {
+ get;
+ set;
+ }
+
+ public JsonRequestBehavior JsonRequestBehavior {
+ get;
+ set;
+ }
+
+ public override void ExecuteResult(ControllerContext context) {
+ if (context == null) {
+ throw new ArgumentNullException("context");
+ }
+ if (JsonRequestBehavior == JsonRequestBehavior.DenyGet &&
+ String.Equals(context.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase)) {
+ throw new InvalidOperationException(MvcResources.JsonRequest_GetNotAllowed);
+ }
+
+ HttpResponseBase response = context.HttpContext.Response;
+
+ if (!String.IsNullOrEmpty(ContentType)) {
+ response.ContentType = ContentType;
+ }
+ else {
+ response.ContentType = "application/json";
+ }
+ if (ContentEncoding != null) {
+ response.ContentEncoding = ContentEncoding;
+ }
+ if (Data != null) {
+ JavaScriptSerializer serializer = new JavaScriptSerializer();
+ response.Write(serializer.Serialize(Data));
+ }
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/LinqBinaryModelBinder.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/LinqBinaryModelBinder.cs
new file mode 100644
index 00000000000..4d5edccde08
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/LinqBinaryModelBinder.cs
@@ -0,0 +1,26 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System.Data.Linq;
+
+ public class LinqBinaryModelBinder : ByteArrayModelBinder {
+ public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) {
+ byte[] byteValue = (byte[])base.BindModel(controllerContext, bindingContext);
+ if (byteValue == null) {
+ return null;
+ }
+
+ return new Binary(byteValue);
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelBinderAttribute.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelBinderAttribute.cs
new file mode 100644
index 00000000000..b057c737d0c
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelBinderAttribute.cs
@@ -0,0 +1,54 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Globalization;
+ using System.Web.Mvc.Resources;
+
+ [AttributeUsage(ValidTargets, AllowMultiple = false, Inherited = false)]
+ public sealed class ModelBinderAttribute : CustomModelBinderAttribute {
+
+ public ModelBinderAttribute(Type binderType) {
+ if (binderType == null) {
+ throw new ArgumentNullException("binderType");
+ }
+ if (!typeof(IModelBinder).IsAssignableFrom(binderType)) {
+ string message = String.Format(CultureInfo.CurrentUICulture,
+ MvcResources.ModelBinderAttribute_TypeNotIModelBinder, binderType.FullName);
+ throw new ArgumentException(message, "binderType");
+ }
+
+ BinderType = binderType;
+ }
+
+ public Type BinderType {
+ get;
+ private set;
+ }
+
+ public override IModelBinder GetBinder() {
+ try {
+ return (IModelBinder)Activator.CreateInstance(BinderType);
+ }
+ catch (Exception ex) {
+ throw new InvalidOperationException(
+ String.Format(
+ CultureInfo.CurrentUICulture,
+ MvcResources.ModelBinderAttribute_ErrorCreatingModelBinder,
+ BinderType.FullName),
+ ex);
+ }
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelBinderDictionary.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelBinderDictionary.cs
new file mode 100644
index 00000000000..964db46cfac
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelBinderDictionary.cs
@@ -0,0 +1,148 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Collections;
+ using System.Collections.Generic;
+ using System.Globalization;
+ using System.Web.Mvc.Resources;
+
+ public class ModelBinderDictionary : IDictionary<Type, IModelBinder> {
+
+ private IModelBinder _defaultBinder;
+ private readonly Dictionary<Type, IModelBinder> _innerDictionary = new Dictionary<Type, IModelBinder>();
+
+ public int Count {
+ get {
+ return _innerDictionary.Count;
+ }
+ }
+
+ public IModelBinder DefaultBinder {
+ get {
+ if (_defaultBinder == null) {
+ _defaultBinder = new DefaultModelBinder();
+ }
+ return _defaultBinder;
+ }
+ set {
+ _defaultBinder = value;
+ }
+ }
+
+ public bool IsReadOnly {
+ get {
+ return ((IDictionary<Type, IModelBinder>)_innerDictionary).IsReadOnly;
+ }
+ }
+
+ public ICollection<Type> Keys {
+ get {
+ return _innerDictionary.Keys;
+ }
+ }
+
+ public IModelBinder this[Type key] {
+ get {
+ IModelBinder binder;
+ _innerDictionary.TryGetValue(key, out binder);
+ return binder;
+ }
+ set {
+ _innerDictionary[key] = value;
+ }
+ }
+
+ public ICollection<IModelBinder> Values {
+ get {
+ return _innerDictionary.Values;
+ }
+ }
+
+ public void Add(KeyValuePair<Type, IModelBinder> item) {
+ ((IDictionary<Type, IModelBinder>)_innerDictionary).Add(item);
+ }
+
+ public void Add(Type key, IModelBinder value) {
+ _innerDictionary.Add(key, value);
+ }
+
+ public void Clear() {
+ _innerDictionary.Clear();
+ }
+
+ public bool Contains(KeyValuePair<Type, IModelBinder> item) {
+ return ((IDictionary<Type, IModelBinder>)_innerDictionary).Contains(item);
+ }
+
+ public bool ContainsKey(Type key) {
+ return _innerDictionary.ContainsKey(key);
+ }
+
+ public void CopyTo(KeyValuePair<Type, IModelBinder>[] array, int arrayIndex) {
+ ((IDictionary<Type, IModelBinder>)_innerDictionary).CopyTo(array, arrayIndex);
+ }
+
+ public IModelBinder GetBinder(Type modelType) {
+ return GetBinder(modelType, true /* fallbackToDefault */);
+ }
+
+ public virtual IModelBinder GetBinder(Type modelType, bool fallbackToDefault) {
+ if (modelType == null) {
+ throw new ArgumentNullException("modelType");
+ }
+
+ return GetBinder(modelType, (fallbackToDefault) ? DefaultBinder : null);
+ }
+
+ private IModelBinder GetBinder(Type modelType, IModelBinder fallbackBinder) {
+ // Try to look up a binder for this type. We use this order of precedence:
+ // 1. Binder registered in the global table
+ // 2. Binder attribute defined on the type
+ // 3. Supplied fallback binder
+
+ IModelBinder binder;
+ if (_innerDictionary.TryGetValue(modelType, out binder)) {
+ return binder;
+ }
+
+ binder = ModelBinders.GetBinderFromAttributes(modelType,
+ () => String.Format(CultureInfo.CurrentUICulture, MvcResources.ModelBinderDictionary_MultipleAttributes, modelType.FullName));
+
+ return binder ?? fallbackBinder;
+ }
+
+ public IEnumerator<KeyValuePair<Type, IModelBinder>> GetEnumerator() {
+ return _innerDictionary.GetEnumerator();
+ }
+
+ public bool Remove(KeyValuePair<Type, IModelBinder> item) {
+ return ((IDictionary<Type, IModelBinder>)_innerDictionary).Remove(item);
+ }
+
+ public bool Remove(Type key) {
+ return _innerDictionary.Remove(key);
+ }
+
+ public bool TryGetValue(Type key, out IModelBinder value) {
+ return _innerDictionary.TryGetValue(key, out value);
+ }
+
+ #region IEnumerable Members
+ IEnumerator IEnumerable.GetEnumerator() {
+ return ((IEnumerable)_innerDictionary).GetEnumerator();
+ }
+ #endregion
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelBinders.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelBinders.cs
new file mode 100644
index 00000000000..f354b9d5510
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelBinders.cs
@@ -0,0 +1,77 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.ComponentModel;
+ using System.Data.Linq;
+ using System.Linq;
+ using System.Reflection;
+ using System.Web;
+
+ public static class ModelBinders {
+
+ private static readonly ModelBinderDictionary _binders = CreateDefaultBinderDictionary();
+
+ public static ModelBinderDictionary Binders {
+ get {
+ return _binders;
+ }
+ }
+
+ internal static IModelBinder GetBinderFromAttributes(Type type, Func<string> errorMessageAccessor) {
+ AttributeCollection allAttrs = TypeDescriptorHelper.Get(type).GetAttributes();
+ CustomModelBinderAttribute[] filteredAttrs = allAttrs.OfType<CustomModelBinderAttribute>().ToArray();
+ return GetBinderFromAttributesImpl(filteredAttrs, errorMessageAccessor);
+ }
+
+ internal static IModelBinder GetBinderFromAttributes(ICustomAttributeProvider element, Func<string> errorMessageAccessor) {
+ CustomModelBinderAttribute[] attrs = (CustomModelBinderAttribute[])element.GetCustomAttributes(typeof(CustomModelBinderAttribute), true /* inherit */);
+ return GetBinderFromAttributesImpl(attrs, errorMessageAccessor);
+ }
+
+ private static IModelBinder GetBinderFromAttributesImpl(CustomModelBinderAttribute[] attrs, Func<string> errorMessageAccessor) {
+ // this method is used to get a custom binder based on the attributes of the element passed to it.
+ // it will return null if a binder cannot be detected based on the attributes alone.
+
+ if (attrs == null) {
+ return null;
+ }
+
+ switch (attrs.Length) {
+ case 0:
+ return null;
+
+ case 1:
+ IModelBinder binder = attrs[0].GetBinder();
+ return binder;
+
+ default:
+ string errorMessage = errorMessageAccessor();
+ throw new InvalidOperationException(errorMessage);
+ }
+ }
+
+ private static ModelBinderDictionary CreateDefaultBinderDictionary() {
+ // We can't add a binder to the HttpPostedFileBase type as an attribute, so we'll just
+ // prepopulate the dictionary as a convenience to users.
+
+ ModelBinderDictionary binders = new ModelBinderDictionary() {
+ { typeof(HttpPostedFileBase), new HttpPostedFileBaseModelBinder() },
+ { typeof(byte[]), new ByteArrayModelBinder() },
+ { typeof(Binary), new LinqBinaryModelBinder() }
+ };
+ return binders;
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelBindingContext.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelBindingContext.cs
new file mode 100644
index 00000000000..c3f8fbd63e1
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelBindingContext.cs
@@ -0,0 +1,125 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Linq;
+ using System.Web.Mvc.Resources;
+
+ public class ModelBindingContext {
+
+ private static readonly Predicate<string> _defaultPropertyFilter = _ => true;
+
+ private string _modelName;
+ private ModelStateDictionary _modelState;
+ private Predicate<string> _propertyFilter;
+ private Dictionary<string, ModelMetadata> _propertyMetadata;
+
+ public ModelBindingContext()
+ : this(null) {
+ }
+
+ // copies certain values that won't change between parent and child objects,
+ // e.g. ValueProvider, ModelState
+ public ModelBindingContext(ModelBindingContext bindingContext) {
+ if (bindingContext != null) {
+ ModelState = bindingContext.ModelState;
+ ValueProvider = bindingContext.ValueProvider;
+ }
+ }
+
+ public bool FallbackToEmptyPrefix {
+ get;
+ set;
+ }
+
+ [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "value", Justification = "Cannot remove setter as that's a breaking change")]
+ public object Model {
+ get {
+ return ModelMetadata.Model;
+ }
+ set {
+ throw new InvalidOperationException(MvcResources.ModelMetadata_PropertyNotSettable);
+ }
+ }
+
+ public ModelMetadata ModelMetadata {
+ get;
+ set;
+ }
+
+ public string ModelName {
+ get {
+ if (_modelName == null) {
+ _modelName = String.Empty;
+ }
+ return _modelName;
+ }
+ set {
+ _modelName = value;
+ }
+ }
+
+ [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Justification = "The containing type is mutable.")]
+ public ModelStateDictionary ModelState {
+ get {
+ if (_modelState == null) {
+ _modelState = new ModelStateDictionary();
+ }
+ return _modelState;
+ }
+ set {
+ _modelState = value;
+ }
+ }
+
+ [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "value", Justification = "Cannot remove setter as that's a breaking change")]
+ public Type ModelType {
+ get {
+ return ModelMetadata.ModelType;
+ }
+ set {
+ throw new InvalidOperationException(MvcResources.ModelMetadata_PropertyNotSettable);
+ }
+ }
+
+ public Predicate<string> PropertyFilter {
+ get {
+ if (_propertyFilter == null) {
+ _propertyFilter = _defaultPropertyFilter;
+ }
+ return _propertyFilter;
+ }
+ set {
+ _propertyFilter = value;
+ }
+ }
+
+ public IDictionary<string, ModelMetadata> PropertyMetadata {
+ get {
+ if (_propertyMetadata == null) {
+ _propertyMetadata = ModelMetadata.Properties.ToDictionary(m => m.PropertyName, StringComparer.OrdinalIgnoreCase);
+ }
+
+ return _propertyMetadata;
+ }
+ }
+
+ public IValueProvider ValueProvider {
+ get;
+ set;
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelClientValidationRangeRule.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelClientValidationRangeRule.cs
new file mode 100644
index 00000000000..0d128584f3f
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelClientValidationRangeRule.cs
@@ -0,0 +1,22 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ public class ModelClientValidationRangeRule : ModelClientValidationRule {
+ public ModelClientValidationRangeRule(string errorMessage, object minValue, object maxValue) {
+ ErrorMessage = errorMessage;
+ ValidationType = "range";
+ ValidationParameters["minimum"] = minValue;
+ ValidationParameters["maximum"] = maxValue;
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelClientValidationRegexRule.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelClientValidationRegexRule.cs
new file mode 100644
index 00000000000..a012da6d596
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelClientValidationRegexRule.cs
@@ -0,0 +1,21 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ public class ModelClientValidationRegexRule : ModelClientValidationRule {
+ public ModelClientValidationRegexRule(string errorMessage, string pattern) {
+ ErrorMessage = errorMessage;
+ ValidationType = "regularExpression";
+ ValidationParameters.Add("pattern", pattern);
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelClientValidationRequiredRule.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelClientValidationRequiredRule.cs
new file mode 100644
index 00000000000..6ae6dc9a068
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelClientValidationRequiredRule.cs
@@ -0,0 +1,20 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ public class ModelClientValidationRequiredRule : ModelClientValidationRule {
+ public ModelClientValidationRequiredRule(string errorMessage) {
+ ErrorMessage = errorMessage;
+ ValidationType = "required";
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelClientValidationRule.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelClientValidationRule.cs
new file mode 100644
index 00000000000..99ed5788f14
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelClientValidationRule.cs
@@ -0,0 +1,43 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Collections.Generic;
+
+ public class ModelClientValidationRule {
+
+ private readonly Dictionary<string, object> _validationParameters = new Dictionary<string, object>();
+ private string _validationType;
+
+ public string ErrorMessage {
+ get;
+ set;
+ }
+
+ public IDictionary<string, object> ValidationParameters {
+ get {
+ return _validationParameters;
+ }
+ }
+
+ public string ValidationType {
+ get {
+ return _validationType ?? String.Empty;
+ }
+ set {
+ _validationType = value;
+ }
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelClientValidationStringLengthRule.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelClientValidationStringLengthRule.cs
new file mode 100644
index 00000000000..92999ee9eae
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelClientValidationStringLengthRule.cs
@@ -0,0 +1,22 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ public class ModelClientValidationStringLengthRule : ModelClientValidationRule {
+ public ModelClientValidationStringLengthRule(string errorMessage, int minimumLength, int maximumLength) {
+ ErrorMessage = errorMessage;
+ ValidationType = "stringLength";
+ ValidationParameters["minimumLength"] = minimumLength;
+ ValidationParameters["maximumLength"] = maximumLength;
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelError.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelError.cs
new file mode 100644
index 00000000000..a6fb1778f80
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelError.cs
@@ -0,0 +1,46 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+
+ [Serializable]
+ public class ModelError {
+
+ public ModelError(Exception exception)
+ : this(exception, null /* errorMessage */) {
+ }
+
+ public ModelError(Exception exception, string errorMessage)
+ : this(errorMessage) {
+ if (exception == null) {
+ throw new ArgumentNullException("exception");
+ }
+
+ Exception = exception;
+ }
+
+ public ModelError(string errorMessage) {
+ ErrorMessage = errorMessage ?? String.Empty;
+ }
+
+ public Exception Exception {
+ get;
+ private set;
+ }
+
+ public string ErrorMessage {
+ get;
+ private set;
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelErrorCollection.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelErrorCollection.cs
new file mode 100644
index 00000000000..61898ddf0c1
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelErrorCollection.cs
@@ -0,0 +1,28 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Collections.ObjectModel;
+
+ [Serializable]
+ public class ModelErrorCollection : Collection<ModelError> {
+
+ public void Add(Exception exception) {
+ Add(new ModelError(exception));
+ }
+
+ public void Add(string errorMessage) {
+ Add(new ModelError(errorMessage));
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelMetadata.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelMetadata.cs
new file mode 100644
index 00000000000..54a56d6c0d3
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelMetadata.cs
@@ -0,0 +1,388 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Collections.Generic;
+ using System.ComponentModel;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Globalization;
+ using System.Linq;
+ using System.Linq.Expressions;
+ using System.Reflection;
+ using System.Web.Mvc.ExpressionUtil;
+ using System.Web.Mvc.Resources;
+
+ public class ModelMetadata {
+ // Explicit backing store for the things we want initialized by default, so don't have to call
+ // the protected virtual setters of an auto-generated property
+ private Dictionary<string, object> _additionalValues = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
+ private readonly Type _containerType;
+ private bool _convertEmptyStringToNull = true;
+ private bool _isRequired;
+ private object _model;
+ private Func<object> _modelAccessor;
+ private readonly Type _modelType;
+ private IEnumerable<ModelMetadata> _properties;
+ private readonly string _propertyName;
+ private Type _realModelType;
+ private bool _showForDisplay = true;
+ private bool _showForEdit = true;
+ private string _simpleDisplayText;
+
+ public ModelMetadata(ModelMetadataProvider provider, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName) {
+ if (provider == null) {
+ throw new ArgumentNullException("provider");
+ }
+ if (modelType == null) {
+ throw new ArgumentNullException("modelType");
+ }
+
+ Provider = provider;
+
+ _containerType = containerType;
+ _isRequired = !TypeHelpers.TypeAllowsNullValue(modelType);
+ _modelAccessor = modelAccessor;
+ _modelType = modelType;
+ _propertyName = propertyName;
+ }
+
+ public virtual Dictionary<string, object> AdditionalValues {
+ get {
+ return _additionalValues;
+ }
+ }
+
+ public Type ContainerType {
+ get {
+ return _containerType;
+ }
+ }
+
+ public virtual bool ConvertEmptyStringToNull {
+ get {
+ return _convertEmptyStringToNull;
+ }
+ set {
+ _convertEmptyStringToNull = value;
+ }
+ }
+
+ public virtual string DataTypeName {
+ get;
+ set;
+ }
+
+ public virtual string Description {
+ get;
+ set;
+ }
+
+ public virtual string DisplayFormatString {
+ get;
+ set;
+ }
+
+ [SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods", Justification = "The method is a delegating helper to choose among multiple property values")]
+ public virtual string DisplayName {
+ get;
+ set;
+ }
+
+ public virtual string EditFormatString {
+ get;
+ set;
+ }
+
+ public virtual bool HideSurroundingHtml {
+ get;
+ set;
+ }
+
+ public virtual bool IsComplexType {
+ get {
+ return !(TypeDescriptor.GetConverter(ModelType).CanConvertFrom(typeof(string)));
+ }
+ }
+
+ public bool IsNullableValueType {
+ get {
+ return TypeHelpers.IsNullableValueType(ModelType);
+ }
+ }
+
+ public virtual bool IsReadOnly {
+ get;
+ set;
+ }
+
+ public virtual bool IsRequired {
+ get {
+ return _isRequired;
+ }
+ set {
+ _isRequired = value;
+ }
+ }
+
+ public object Model {
+ get {
+ if (_modelAccessor != null) {
+ _model = _modelAccessor();
+ _modelAccessor = null;
+ }
+ return _model;
+ }
+ set {
+ _model = value;
+ _modelAccessor = null;
+ }
+ }
+
+ public Type ModelType {
+ get {
+ return _modelType;
+ }
+ }
+
+ public virtual string NullDisplayText { get; set; }
+
+ public virtual IEnumerable<ModelMetadata> Properties {
+ get {
+ if (_properties == null) {
+ _properties = Provider.GetMetadataForProperties(Model, RealModelType);
+ }
+ return _properties;
+ }
+ }
+
+ public string PropertyName {
+ get {
+ return _propertyName;
+ }
+ }
+
+ protected ModelMetadataProvider Provider {
+ get;
+ set;
+ }
+
+ internal Type RealModelType {
+ get {
+ if (_realModelType == null) {
+ _realModelType = ModelType;
+
+ // Don't call GetType() if the model is Nullable<T>, because it will
+ // turn Nullable<T> into T for non-null values
+ if (Model != null && !TypeHelpers.IsNullableValueType(ModelType)) {
+ _realModelType = Model.GetType();
+ }
+ }
+
+ return _realModelType;
+ }
+ }
+
+ public virtual string ShortDisplayName {
+ get;
+ set;
+ }
+
+ public virtual bool ShowForDisplay {
+ get {
+ return _showForDisplay;
+ }
+ set {
+ _showForDisplay = value;
+ }
+ }
+
+ public virtual bool ShowForEdit {
+ get {
+ return _showForEdit;
+ }
+ set {
+ _showForEdit = value;
+ }
+ }
+
+ [SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods", Justification = "This property delegates to the method when the user has not yet set a simple display text value.")]
+ public virtual string SimpleDisplayText {
+ get {
+ if (_simpleDisplayText == null) {
+ _simpleDisplayText = GetSimpleDisplayText();
+ }
+ return _simpleDisplayText;
+ }
+ set {
+ _simpleDisplayText = value;
+ }
+ }
+
+ public virtual string TemplateHint {
+ get;
+ set;
+ }
+
+ public virtual string Watermark {
+ get;
+ set;
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
+ public static ModelMetadata FromLambdaExpression<TParameter, TValue>(Expression<Func<TParameter, TValue>> expression,
+ ViewDataDictionary<TParameter> viewData) {
+ if (expression == null) {
+ throw new ArgumentNullException("expression");
+ }
+ if (viewData == null) {
+ throw new ArgumentNullException("viewData");
+ }
+
+ string propertyName = null;
+ Type containerType = null;
+ bool legalExpression = false;
+
+ // Need to verify the expression is valid; it needs to at least end in something
+ // that we can convert to a meaningful string for model binding purposes
+
+ switch (expression.Body.NodeType) {
+ // ArrayIndex always means a single-dimensional indexer; multi-dimensional indexer is a method call to Get()
+ case ExpressionType.ArrayIndex:
+ legalExpression = true;
+ break;
+
+ // Only legal method call is a single argument indexer/DefaultMember call
+ case ExpressionType.Call:
+ legalExpression = ExpressionHelper.IsSingleArgumentIndexer(expression.Body);
+ break;
+
+ // Property/field access is always legal
+ case ExpressionType.MemberAccess:
+ MemberExpression memberExpression = (MemberExpression)expression.Body;
+ propertyName = memberExpression.Member is PropertyInfo ? memberExpression.Member.Name : null;
+ containerType = memberExpression.Member.DeclaringType;
+ legalExpression = true;
+ break;
+
+ // Parameter expression means "model => model", so we delegate to FromModel
+ case ExpressionType.Parameter:
+ return FromModel(viewData);
+ }
+
+ if (!legalExpression) {
+ throw new InvalidOperationException(MvcResources.TemplateHelpers_TemplateLimitations);
+ }
+
+ TParameter container = viewData.Model;
+ Func<object> modelAccessor = () =>
+ {
+ try {
+ return CachedExpressionCompiler.Process(expression)(container);
+ }
+ catch (NullReferenceException) {
+ return null;
+ }
+ };
+
+ return GetMetadataFromProvider(modelAccessor, typeof(TValue), propertyName, containerType);
+ }
+
+ private static ModelMetadata FromModel(ViewDataDictionary viewData) {
+ return viewData.ModelMetadata ?? GetMetadataFromProvider(null, typeof(string), null, null);
+ }
+
+ public static ModelMetadata FromStringExpression(string expression, ViewDataDictionary viewData) {
+ if (expression == null) {
+ throw new ArgumentNullException("expression");
+ }
+ if (viewData == null) {
+ throw new ArgumentNullException("viewData");
+ }
+ if (expression.Length == 0) { // Empty string really means "model metadata for the current model"
+ return FromModel(viewData);
+ }
+
+ ViewDataInfo vdi = viewData.GetViewDataInfo(expression);
+ Type containerType = null;
+ Type modelType = null;
+ Func<object> modelAccessor = null;
+ string propertyName = null;
+
+ if (vdi != null) {
+ if (vdi.Container != null) {
+ containerType = vdi.Container.GetType();
+ }
+
+ modelAccessor = () => vdi.Value;
+
+ if (vdi.PropertyDescriptor != null) {
+ propertyName = vdi.PropertyDescriptor.Name;
+ modelType = vdi.PropertyDescriptor.PropertyType;
+ }
+ else if (vdi.Value != null) { // We only need to delay accessing properties (for LINQ to SQL)
+ modelType = vdi.Value.GetType();
+ }
+ }
+ // Try getting a property from ModelMetadata if we couldn't find an answer in ViewData
+ else if (viewData.ModelMetadata != null) {
+ ModelMetadata propertyMetadata = viewData.ModelMetadata.Properties.Where(p => p.PropertyName == expression).FirstOrDefault();
+ if (propertyMetadata != null) {
+ return propertyMetadata;
+ }
+ }
+
+
+ return GetMetadataFromProvider(modelAccessor, modelType ?? typeof(string), propertyName, containerType);
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "The method is a delegating helper to choose among multiple property values")]
+ public string GetDisplayName() {
+ return DisplayName ?? PropertyName ?? ModelType.Name;
+ }
+
+ private static ModelMetadata GetMetadataFromProvider(Func<object> modelAccessor, Type modelType, string propertyName, Type containerType) {
+ if (containerType != null && !String.IsNullOrEmpty(propertyName)) {
+ return ModelMetadataProviders.Current.GetMetadataForProperty(modelAccessor, containerType, propertyName);
+ }
+ return ModelMetadataProviders.Current.GetMetadataForType(modelAccessor, modelType);
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "This method is used to resolve the simple display text when it was not explicitly set through other means.")]
+ protected virtual string GetSimpleDisplayText() {
+ if (Model == null) {
+ return NullDisplayText;
+ }
+
+ string toStringResult = Convert.ToString(Model, CultureInfo.CurrentCulture);
+ if (!toStringResult.Equals(Model.GetType().FullName, StringComparison.Ordinal)) {
+ return toStringResult;
+ }
+
+ ModelMetadata firstProperty = Properties.FirstOrDefault();
+ if (firstProperty == null) {
+ return String.Empty;
+ }
+
+ if (firstProperty.Model == null) {
+ return firstProperty.NullDisplayText;
+ }
+
+ return Convert.ToString(firstProperty.Model, CultureInfo.CurrentCulture);
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "This method may perform non-trivial work.")]
+ public virtual IEnumerable<ModelValidator> GetValidators(ControllerContext context) {
+ return ModelValidatorProviders.Providers.GetValidators(this, context);
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelMetadataProvider.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelMetadataProvider.cs
new file mode 100644
index 00000000000..6e92367b8c6
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelMetadataProvider.cs
@@ -0,0 +1,23 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System.Collections.Generic;
+
+ public abstract class ModelMetadataProvider {
+ public abstract IEnumerable<ModelMetadata> GetMetadataForProperties(object container, Type containerType);
+
+ public abstract ModelMetadata GetMetadataForProperty(Func<object> modelAccessor, Type containerType, string propertyName);
+
+ public abstract ModelMetadata GetMetadataForType(Func<object> modelAccessor, Type modelType);
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelMetadataProviders.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelMetadataProviders.cs
new file mode 100644
index 00000000000..de948d1a501
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelMetadataProviders.cs
@@ -0,0 +1,26 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ public static class ModelMetadataProviders {
+ private static ModelMetadataProvider _current = new DataAnnotationsModelMetadataProvider();
+
+ public static ModelMetadataProvider Current {
+ get {
+ return _current;
+ }
+ set {
+ _current = value ?? new EmptyModelMetadataProvider();
+ }
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelState.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelState.cs
new file mode 100644
index 00000000000..38c5860e490
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelState.cs
@@ -0,0 +1,31 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+
+ [Serializable]
+ public class ModelState {
+
+ private ModelErrorCollection _errors = new ModelErrorCollection();
+
+ public ValueProviderResult Value {
+ get;
+ set;
+ }
+
+ public ModelErrorCollection Errors {
+ get {
+ return _errors;
+ }
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelStateDictionary.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelStateDictionary.cs
new file mode 100644
index 00000000000..25105050c04
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelStateDictionary.cs
@@ -0,0 +1,170 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Collections;
+ using System.Collections.Generic;
+ using System.Linq;
+
+ [Serializable]
+ public class ModelStateDictionary : IDictionary<string, ModelState> {
+
+ private readonly Dictionary<string, ModelState> _innerDictionary = new Dictionary<string, ModelState>(StringComparer.OrdinalIgnoreCase);
+
+ public ModelStateDictionary() {
+ }
+
+ public ModelStateDictionary(ModelStateDictionary dictionary) {
+ if (dictionary == null) {
+ throw new ArgumentNullException("dictionary");
+ }
+
+ foreach (var entry in dictionary) {
+ _innerDictionary.Add(entry.Key, entry.Value);
+ }
+ }
+
+ public int Count {
+ get {
+ return _innerDictionary.Count;
+ }
+ }
+
+ public bool IsReadOnly {
+ get {
+ return ((IDictionary<string, ModelState>)_innerDictionary).IsReadOnly;
+ }
+ }
+
+ public bool IsValid {
+ get {
+ return Values.All(modelState => modelState.Errors.Count == 0);
+ }
+ }
+
+ public ICollection<string> Keys {
+ get {
+ return _innerDictionary.Keys;
+ }
+ }
+
+ public ModelState this[string key] {
+ get {
+ ModelState value;
+ _innerDictionary.TryGetValue(key, out value);
+ return value;
+ }
+ set {
+ _innerDictionary[key] = value;
+ }
+ }
+
+ public ICollection<ModelState> Values {
+ get {
+ return _innerDictionary.Values;
+ }
+ }
+
+ public void Add(KeyValuePair<string, ModelState> item) {
+ ((IDictionary<string, ModelState>)_innerDictionary).Add(item);
+ }
+
+ public void Add(string key, ModelState value) {
+ _innerDictionary.Add(key, value);
+ }
+
+ public void AddModelError(string key, Exception exception) {
+ GetModelStateForKey(key).Errors.Add(exception);
+ }
+
+ public void AddModelError(string key, string errorMessage) {
+ GetModelStateForKey(key).Errors.Add(errorMessage);
+ }
+
+ public void Clear() {
+ _innerDictionary.Clear();
+ }
+
+ public bool Contains(KeyValuePair<string, ModelState> item) {
+ return ((IDictionary<string, ModelState>)_innerDictionary).Contains(item);
+ }
+
+ public bool ContainsKey(string key) {
+ return _innerDictionary.ContainsKey(key);
+ }
+
+ public void CopyTo(KeyValuePair<string, ModelState>[] array, int arrayIndex) {
+ ((IDictionary<string, ModelState>)_innerDictionary).CopyTo(array, arrayIndex);
+ }
+
+ public IEnumerator<KeyValuePair<string, ModelState>> GetEnumerator() {
+ return _innerDictionary.GetEnumerator();
+ }
+
+ private ModelState GetModelStateForKey(string key) {
+ if (key == null) {
+ throw new ArgumentNullException("key");
+ }
+
+ ModelState modelState;
+ if (!TryGetValue(key, out modelState)) {
+ modelState = new ModelState();
+ this[key] = modelState;
+ }
+
+ return modelState;
+ }
+
+ public bool IsValidField(string key) {
+ if (key == null) {
+ throw new ArgumentNullException("key");
+ }
+
+ // if the key is not found in the dictionary, we just say that it's valid (since there are no errors)
+ return DictionaryHelpers.FindKeysWithPrefix(this, key).All(entry => entry.Value.Errors.Count == 0);
+ }
+
+ public void Merge(ModelStateDictionary dictionary) {
+ if (dictionary == null) {
+ return;
+ }
+
+ foreach (var entry in dictionary) {
+ this[entry.Key] = entry.Value;
+ }
+ }
+
+ public bool Remove(KeyValuePair<string, ModelState> item) {
+ return ((IDictionary<string, ModelState>)_innerDictionary).Remove(item);
+ }
+
+ public bool Remove(string key) {
+ return _innerDictionary.Remove(key);
+ }
+
+ public void SetModelValue(string key, ValueProviderResult value) {
+ GetModelStateForKey(key).Value = value;
+ }
+
+ public bool TryGetValue(string key, out ModelState value) {
+ return _innerDictionary.TryGetValue(key, out value);
+ }
+
+ #region IEnumerable Members
+ IEnumerator IEnumerable.GetEnumerator() {
+ return ((IEnumerable)_innerDictionary).GetEnumerator();
+ }
+ #endregion
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelValidationResult.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelValidationResult.cs
new file mode 100644
index 00000000000..dcb262ecaca
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelValidationResult.cs
@@ -0,0 +1,40 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+
+ public class ModelValidationResult {
+
+ private string _memberName;
+ private string _message;
+
+ public string MemberName {
+ get {
+ return _memberName ?? String.Empty;
+ }
+ set {
+ _memberName = value;
+ }
+ }
+
+ public string Message {
+ get {
+ return _message ?? String.Empty;
+ }
+ set {
+ _message = value;
+ }
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelValidator.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelValidator.cs
new file mode 100644
index 00000000000..0570bffd6c9
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelValidator.cs
@@ -0,0 +1,83 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Linq;
+
+ public abstract class ModelValidator {
+ protected ModelValidator(ModelMetadata metadata, ControllerContext controllerContext) {
+ if (metadata == null) {
+ throw new ArgumentNullException("metadata");
+ }
+ if (controllerContext == null) {
+ throw new ArgumentNullException("controllerContext");
+ }
+
+ Metadata = metadata;
+ ControllerContext = controllerContext;
+ }
+
+ protected internal ControllerContext ControllerContext { get; private set; }
+
+ public virtual bool IsRequired {
+ get {
+ return false;
+ }
+ }
+
+ protected internal ModelMetadata Metadata { get; private set; }
+
+ [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "This method may perform non-trivial work.")]
+ public virtual IEnumerable<ModelClientValidationRule> GetClientValidationRules() {
+ return Enumerable.Empty<ModelClientValidationRule>();
+ }
+
+ public static ModelValidator GetModelValidator(ModelMetadata metadata, ControllerContext context) {
+ return new CompositeModelValidator(metadata, context);
+ }
+
+ public abstract IEnumerable<ModelValidationResult> Validate(object container);
+
+ private class CompositeModelValidator : ModelValidator {
+ public CompositeModelValidator(ModelMetadata metadata, ControllerContext controllerContext)
+ : base(metadata, controllerContext) {
+ }
+
+ public override IEnumerable<ModelValidationResult> Validate(object container) {
+ bool propertiesValid = true;
+
+ foreach (ModelMetadata propertyMetadata in Metadata.Properties) {
+ foreach (ModelValidator propertyValidator in propertyMetadata.GetValidators(ControllerContext)) {
+ foreach (ModelValidationResult propertyResult in propertyValidator.Validate(Metadata.Model)) {
+ propertiesValid = false;
+ yield return new ModelValidationResult {
+ MemberName = DefaultModelBinder.CreateSubPropertyName(propertyMetadata.PropertyName, propertyResult.MemberName),
+ Message = propertyResult.Message
+ };
+ }
+ }
+ }
+
+ if (propertiesValid) {
+ foreach (ModelValidator typeValidator in Metadata.GetValidators(ControllerContext)) {
+ foreach (ModelValidationResult typeResult in typeValidator.Validate(container)) {
+ yield return typeResult;
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelValidatorProvider.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelValidatorProvider.cs
new file mode 100644
index 00000000000..88dc2239c14
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelValidatorProvider.cs
@@ -0,0 +1,19 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System.Collections.Generic;
+
+ public abstract class ModelValidatorProvider {
+ public abstract IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, ControllerContext context);
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelValidatorProviderCollection.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelValidatorProviderCollection.cs
new file mode 100644
index 00000000000..ffc845bedb5
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelValidatorProviderCollection.cs
@@ -0,0 +1,47 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Collections.Generic;
+ using System.Collections.ObjectModel;
+ using System.Linq;
+
+ public class ModelValidatorProviderCollection : Collection<ModelValidatorProvider> {
+
+ public ModelValidatorProviderCollection() {
+ }
+
+ public ModelValidatorProviderCollection(IList<ModelValidatorProvider> list)
+ : base(list) {
+ }
+
+ protected override void InsertItem(int index, ModelValidatorProvider item) {
+ if (item == null) {
+ throw new ArgumentNullException("item");
+ }
+ base.InsertItem(index, item);
+ }
+
+ protected override void SetItem(int index, ModelValidatorProvider item) {
+ if (item == null) {
+ throw new ArgumentNullException("item");
+ }
+ base.SetItem(index, item);
+ }
+
+ public IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, ControllerContext context) {
+ return this.SelectMany(provider => provider.GetValidators(metadata, context));
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelValidatorProviders.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelValidatorProviders.cs
new file mode 100644
index 00000000000..563d495a03b
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ModelValidatorProviders.cs
@@ -0,0 +1,29 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ public static class ModelValidatorProviders {
+
+ private static readonly ModelValidatorProviderCollection _providers = new ModelValidatorProviderCollection() {
+ new DataAnnotationsModelValidatorProvider(),
+ new DataErrorInfoModelValidatorProvider(),
+ new ClientDataTypeModelValidatorProvider()
+ };
+
+ public static ModelValidatorProviderCollection Providers {
+ get {
+ return _providers;
+ }
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/MultiSelectList.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/MultiSelectList.cs
new file mode 100644
index 00000000000..2f1776f61ca
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/MultiSelectList.cs
@@ -0,0 +1,126 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Collections;
+ using System.Collections.Generic;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Globalization;
+ using System.Linq;
+ using System.Web.UI;
+
+ [SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")]
+ [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Multi",
+ Justification = "Common shorthand for 'multiple'.")]
+ public class MultiSelectList : IEnumerable<SelectListItem> {
+
+ public MultiSelectList(IEnumerable items)
+ : this(items, null /* selectedValues */) {
+ }
+
+ public MultiSelectList(IEnumerable items, IEnumerable selectedValues)
+ : this(items, null /* dataValuefield */, null /* dataTextField */, selectedValues) {
+ }
+
+ public MultiSelectList(IEnumerable items, string dataValueField, string dataTextField)
+ : this(items, dataValueField, dataTextField, null /* selectedValues */) {
+ }
+
+ public MultiSelectList(IEnumerable items, string dataValueField, string dataTextField, IEnumerable selectedValues) {
+ if (items == null) {
+ throw new ArgumentNullException("items");
+ }
+
+ Items = items;
+ DataValueField = dataValueField;
+ DataTextField = dataTextField;
+ SelectedValues = selectedValues;
+ }
+
+ public string DataTextField {
+ get;
+ private set;
+ }
+
+ public string DataValueField {
+ get;
+ private set;
+ }
+
+ public IEnumerable Items {
+ get;
+ private set;
+ }
+
+ public IEnumerable SelectedValues {
+ get;
+ private set;
+ }
+
+ public virtual IEnumerator<SelectListItem> GetEnumerator() {
+ return GetListItems().GetEnumerator();
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate",
+ Justification = "Operation performs conversions and returns a unique instance on each call.")]
+ internal IList<SelectListItem> GetListItems() {
+ return (!String.IsNullOrEmpty(DataValueField)) ?
+ GetListItemsWithValueField() :
+ GetListItemsWithoutValueField();
+ }
+
+ private IList<SelectListItem> GetListItemsWithValueField() {
+ HashSet<string> selectedValues = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
+ if (SelectedValues != null) {
+ selectedValues.UnionWith(from object value in SelectedValues select Convert.ToString(value, CultureInfo.CurrentCulture));
+ }
+
+ var listItems = from object item in Items
+ let value = Eval(item, DataValueField)
+ select new SelectListItem {
+ Value = value,
+ Text = Eval(item, DataTextField),
+ Selected = selectedValues.Contains(value)
+ };
+ return listItems.ToList();
+ }
+
+ private IList<SelectListItem> GetListItemsWithoutValueField() {
+ HashSet<object> selectedValues = new HashSet<object>();
+ if (SelectedValues != null) {
+ selectedValues.UnionWith(SelectedValues.Cast<object>());
+ }
+
+ var listItems = from object item in Items
+ select new SelectListItem {
+ Text = Eval(item, DataTextField),
+ Selected = selectedValues.Contains(item)
+ };
+ return listItems.ToList();
+ }
+
+ private static string Eval(object container, string expression) {
+ object value = container;
+ if (!String.IsNullOrEmpty(expression)) {
+ value = DataBinder.Eval(container, expression);
+ }
+ return Convert.ToString(value, CultureInfo.CurrentCulture);
+ }
+
+ #region IEnumerable Members
+ IEnumerator IEnumerable.GetEnumerator() {
+ return GetEnumerator();
+ }
+ #endregion
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/MvcHandler.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/MvcHandler.cs
new file mode 100644
index 00000000000..1f15ae79c55
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/MvcHandler.cs
@@ -0,0 +1,212 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Globalization;
+ using System.Linq;
+ using System.Reflection;
+ using System.Threading;
+ using System.Web;
+ using System.Web.Mvc.Async;
+ using System.Web.Mvc.Resources;
+ using System.Web.Routing;
+ using System.Web.SessionState;
+
+ [SuppressMessage("Microsoft.Security", "CA2112:SecuredTypesShouldNotExposeFields", Justification = "There's nothing secret about the value of this field.")]
+ public class MvcHandler : IHttpAsyncHandler, IHttpHandler, IRequiresSessionState {
+ private static readonly object _processRequestTag = new object();
+ private ControllerBuilder _controllerBuilder;
+
+ internal static readonly string MvcVersion = GetMvcVersionString();
+ public static readonly string MvcVersionHeaderName = "X-AspNetMvc-Version";
+
+ public MvcHandler(RequestContext requestContext) {
+ if (requestContext == null) {
+ throw new ArgumentNullException("requestContext");
+ }
+
+ RequestContext = requestContext;
+ }
+
+ internal ControllerBuilder ControllerBuilder {
+ get {
+ if (_controllerBuilder == null) {
+ _controllerBuilder = ControllerBuilder.Current;
+ }
+ return _controllerBuilder;
+ }
+ set {
+ _controllerBuilder = value;
+ }
+ }
+
+ public static bool DisableMvcResponseHeader {
+ get;
+ set;
+ }
+
+ protected virtual bool IsReusable {
+ get {
+ return false;
+ }
+ }
+
+ public RequestContext RequestContext {
+ get;
+ private set;
+ }
+
+ protected internal virtual void AddVersionHeader(HttpContextBase httpContext) {
+ if (!DisableMvcResponseHeader) {
+ httpContext.Response.AppendHeader(MvcVersionHeaderName, MvcVersion);
+ }
+ }
+
+ protected virtual IAsyncResult BeginProcessRequest(HttpContext httpContext, AsyncCallback callback, object state) {
+ HttpContextBase iHttpContext = new HttpContextWrapper(httpContext);
+ return BeginProcessRequest(iHttpContext, callback, state);
+ }
+
+ protected internal virtual IAsyncResult BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, object state) {
+ IController controller;
+ IControllerFactory factory;
+ ProcessRequestInit(httpContext, out controller, out factory);
+
+ IAsyncController asyncController = controller as IAsyncController;
+ if (asyncController != null) {
+ // asynchronous controller
+ BeginInvokeDelegate beginDelegate = delegate(AsyncCallback asyncCallback, object asyncState) {
+ try {
+ return asyncController.BeginExecute(RequestContext, asyncCallback, asyncState);
+ }
+ catch {
+ factory.ReleaseController(asyncController);
+ throw;
+ }
+ };
+
+ EndInvokeDelegate endDelegate = delegate(IAsyncResult asyncResult) {
+ try {
+ asyncController.EndExecute(asyncResult);
+ }
+ finally {
+ factory.ReleaseController(asyncController);
+ }
+ };
+
+ SynchronizationContext syncContext = SynchronizationContextUtil.GetSynchronizationContext();
+ AsyncCallback newCallback = AsyncUtil.WrapCallbackForSynchronizedExecution(callback, syncContext);
+ return AsyncResultWrapper.Begin(newCallback, state, beginDelegate, endDelegate, _processRequestTag);
+ }
+ else {
+ // synchronous controller
+ Action action = delegate {
+ try {
+ controller.Execute(RequestContext);
+ }
+ finally {
+ factory.ReleaseController(controller);
+ }
+ };
+
+ return AsyncResultWrapper.BeginSynchronous(callback, state, action, _processRequestTag);
+ }
+ }
+
+ protected internal virtual void EndProcessRequest(IAsyncResult asyncResult) {
+ AsyncResultWrapper.End(asyncResult, _processRequestTag);
+ }
+
+ private static string GetMvcVersionString() {
+ // DevDiv 216459:
+ // This code originally used Assembly.GetName(), but that requires FileIOPermission, which isn't granted in
+ // medium trust. However, Assembly.FullName *is* accessible in medium trust.
+ return new AssemblyName(typeof(MvcHandler).Assembly.FullName).Version.ToString(2);
+ }
+
+ protected virtual void ProcessRequest(HttpContext httpContext) {
+ HttpContextBase iHttpContext = new HttpContextWrapper(httpContext);
+ ProcessRequest(iHttpContext);
+ }
+
+ protected internal virtual void ProcessRequest(HttpContextBase httpContext) {
+ IController controller;
+ IControllerFactory factory;
+ ProcessRequestInit(httpContext, out controller, out factory);
+
+ try {
+ controller.Execute(RequestContext);
+ }
+ finally {
+ factory.ReleaseController(controller);
+ }
+ }
+
+ private void ProcessRequestInit(HttpContextBase httpContext, out IController controller, out IControllerFactory factory) {
+ AddVersionHeader(httpContext);
+ RemoveOptionalRoutingParameters();
+
+ // Get the controller type
+ string controllerName = RequestContext.RouteData.GetRequiredString("controller");
+
+ // Instantiate the controller and call Execute
+ factory = ControllerBuilder.GetControllerFactory();
+ controller = factory.CreateController(RequestContext, controllerName);
+ if (controller == null) {
+ throw new InvalidOperationException(
+ String.Format(
+ CultureInfo.CurrentUICulture,
+ MvcResources.ControllerBuilder_FactoryReturnedNull,
+ factory.GetType(),
+ controllerName));
+ }
+ }
+
+ private void RemoveOptionalRoutingParameters() {
+ RouteValueDictionary rvd = RequestContext.RouteData.Values;
+
+ // Get all keys for which the corresponding value is 'Optional'.
+ // ToArray() necessary so that we don't manipulate the dictionary while enumerating.
+ string[] matchingKeys = (from entry in rvd
+ where entry.Value == UrlParameter.Optional
+ select entry.Key).ToArray();
+
+ foreach (string key in matchingKeys) {
+ rvd.Remove(key);
+ }
+ }
+
+ #region IHttpHandler Members
+ bool IHttpHandler.IsReusable {
+ get {
+ return IsReusable;
+ }
+ }
+
+ void IHttpHandler.ProcessRequest(HttpContext httpContext) {
+ ProcessRequest(httpContext);
+ }
+ #endregion
+
+ #region IHttpAsyncHandler Members
+ IAsyncResult IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData) {
+ return BeginProcessRequest(context, cb, extraData);
+ }
+
+ void IHttpAsyncHandler.EndProcessRequest(IAsyncResult result) {
+ EndProcessRequest(result);
+ }
+ #endregion
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/MvcHtmlString.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/MvcHtmlString.cs
new file mode 100644
index 00000000000..a6674bf458c
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/MvcHtmlString.cs
@@ -0,0 +1,101 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.ComponentModel;
+ using System.Linq.Expressions;
+ using System.Web;
+
+ // In ASP.NET 4, a new syntax <%: %> is being introduced in WebForms pages, where <%: expression %> is equivalent to
+ // <%= HttpUtility.HtmlEncode(expression) %>. The intent of this is to reduce common causes of XSS vulnerabilities
+ // in WebForms pages (WebForms views in the case of MVC). This involves the addition of an interface
+ // System.Web.IHtmlString and a static method overload System.Web.HttpUtility::HtmlEncode(object). The interface
+ // definition is roughly:
+ // public interface IHtmlString {
+ // string ToHtmlString();
+ // }
+ // And the HtmlEncode(object) logic is roughly:
+ // - If the input argument is an IHtmlString, return argument.ToHtmlString(),
+ // - Otherwise, return HtmlEncode(Convert.ToString(argument)).
+ //
+ // Unfortunately this has the effect that calling <%: Html.SomeHelper() %> in an MVC application running on .NET 4
+ // will end up encoding output that is already HTML-safe. As a result, we're changing out HTML helpers to return
+ // MvcHtmlString where appropriate. <%= Html.SomeHelper() %> will continue to work in both .NET 3.5 and .NET 4, but
+ // changing the return types to MvcHtmlString has the added benefit that <%: Html.SomeHelper() %> will also work
+ // properly in .NET 4 rather than resulting in a double-encoded output. MVC developers in .NET 4 will then be able
+ // to use the <%: %> syntax almost everywhere instead of having to remember where to use <%= %> and where to use
+ // <%: %>. This should help developers craft more secure web applications by default.
+ //
+ // To create an MvcHtmlString, use the static Create() method instead of calling the protected constructor.
+
+ public class MvcHtmlString {
+
+ private delegate MvcHtmlString MvcHtmlStringCreator(string value);
+ private static readonly MvcHtmlStringCreator _creator = GetCreator();
+
+ // imporant: this declaration must occur after the _creator declaration
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes",
+ Justification = "MvcHtmlString is immutable")]
+ public static readonly MvcHtmlString Empty = Create(String.Empty);
+
+ private readonly string _value;
+
+ // This constructor is only protected so that we can subclass it in a dynamic module. In practice,
+ // nobody should ever call this constructor, and it is likely to be removed in a future version
+ // of the framework. Use the static Create() method instead.
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ [Obsolete("The recommended alternative is the static MvcHtmlString.Create(String value) method.")]
+ protected MvcHtmlString(string value) {
+ _value = value ?? String.Empty;
+ }
+
+ public static MvcHtmlString Create(string value) {
+ return _creator(value);
+ }
+
+ // in .NET 4, we dynamically create a type that subclasses MvcHtmlString and implements IHtmlString
+ private static MvcHtmlStringCreator GetCreator() {
+ Type iHtmlStringType = typeof(HttpContext).Assembly.GetType("System.Web.IHtmlString");
+ if (iHtmlStringType != null) {
+ // first, create the dynamic type
+ Type dynamicType = DynamicTypeGenerator.GenerateType("DynamicMvcHtmlString", typeof(MvcHtmlString), new Type[] { iHtmlStringType });
+
+ // then, create the delegate to instantiate the dynamic type
+ ParameterExpression valueParamExpr = Expression.Parameter(typeof(string), "value");
+ NewExpression newObjExpr = Expression.New(dynamicType.GetConstructor(new Type[] { typeof(string) }), valueParamExpr);
+ Expression<MvcHtmlStringCreator> lambdaExpr = Expression.Lambda<MvcHtmlStringCreator>(newObjExpr, valueParamExpr);
+ return lambdaExpr.Compile();
+ }
+ else {
+ // disabling 0618 allows us to call the MvcHtmlString() constructor
+#pragma warning disable 0618
+ return value => new MvcHtmlString(value);
+#pragma warning restore 0618
+ }
+ }
+
+ public static bool IsNullOrEmpty(MvcHtmlString value) {
+ return (value == null || value._value.Length == 0);
+ }
+
+ // IHtmlString.ToHtmlString()
+ public string ToHtmlString() {
+ return _value;
+ }
+
+ public override string ToString() {
+ return _value;
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/MvcHttpHandler.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/MvcHttpHandler.cs
new file mode 100644
index 00000000000..c9773d77c96
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/MvcHttpHandler.cs
@@ -0,0 +1,101 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Web;
+ using System.Web.Mvc.Async;
+ using System.Web.Routing;
+ using System.Web.SessionState;
+
+ public class MvcHttpHandler : UrlRoutingHandler, IHttpAsyncHandler, IRequiresSessionState {
+
+ private static readonly object _processRequestTag = new object();
+
+ protected virtual IAsyncResult BeginProcessRequest(HttpContext httpContext, AsyncCallback callback, object state) {
+ HttpContextBase iHttpContext = new HttpContextWrapper(httpContext);
+ return BeginProcessRequest(iHttpContext, callback, state);
+ }
+
+ protected internal virtual IAsyncResult BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, object state) {
+ IHttpHandler httpHandler = GetHttpHandler(httpContext);
+ IHttpAsyncHandler httpAsyncHandler = httpHandler as IHttpAsyncHandler;
+
+ if (httpAsyncHandler != null) {
+ // asynchronous handler
+ BeginInvokeDelegate beginDelegate = delegate(AsyncCallback asyncCallback, object asyncState) {
+ return httpAsyncHandler.BeginProcessRequest(HttpContext.Current, asyncCallback, asyncState);
+ };
+ EndInvokeDelegate endDelegate = delegate(IAsyncResult asyncResult) {
+ httpAsyncHandler.EndProcessRequest(asyncResult);
+ };
+ return AsyncResultWrapper.Begin(callback, state, beginDelegate, endDelegate, _processRequestTag);
+ }
+ else {
+ // synchronous handler
+ Action action = delegate {
+ httpHandler.ProcessRequest(HttpContext.Current);
+ };
+ return AsyncResultWrapper.BeginSynchronous(callback, state, action, _processRequestTag);
+ }
+ }
+
+ protected internal virtual void EndProcessRequest(IAsyncResult asyncResult) {
+ AsyncResultWrapper.End(asyncResult, _processRequestTag);
+ }
+
+ private static IHttpHandler GetHttpHandler(HttpContextBase httpContext) {
+ DummyHttpHandler dummyHandler = new DummyHttpHandler();
+ dummyHandler.PublicProcessRequest(httpContext);
+ return dummyHandler.HttpHandler;
+ }
+
+ // synchronous code
+ protected override void VerifyAndProcessRequest(IHttpHandler httpHandler, HttpContextBase httpContext) {
+ if (httpHandler == null) {
+ throw new ArgumentNullException("httpHandler");
+ }
+
+ httpHandler.ProcessRequest(HttpContext.Current);
+ }
+
+ #region IHttpAsyncHandler Members
+ IAsyncResult IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData) {
+ return BeginProcessRequest(context, cb, extraData);
+ }
+
+ void IHttpAsyncHandler.EndProcessRequest(IAsyncResult result) {
+ EndProcessRequest(result);
+ }
+ #endregion
+
+ // Since UrlRoutingHandler.ProcessRequest() does the heavy lifting of looking at the RouteCollection for
+ // a matching route, we need to call into it. However, that method is also responsible for kicking off
+ // the synchronous request, and we can't allow it to do that. The purpose of this dummy class is to run
+ // only the lookup portion of UrlRoutingHandler.ProcessRequest(), then intercept the handler it returns
+ // and execute it asynchronously.
+
+ private sealed class DummyHttpHandler : UrlRoutingHandler {
+ public IHttpHandler HttpHandler;
+
+ public void PublicProcessRequest(HttpContextBase httpContext) {
+ ProcessRequest(httpContext);
+ }
+
+ protected override void VerifyAndProcessRequest(IHttpHandler httpHandler, HttpContextBase httpContext) {
+ // don't process the request, just store a reference to it
+ HttpHandler = httpHandler;
+ }
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/MvcRouteHandler.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/MvcRouteHandler.cs
new file mode 100644
index 00000000000..5d2bbaa4512
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/MvcRouteHandler.cs
@@ -0,0 +1,27 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System.Web.Routing;
+
+ public class MvcRouteHandler : IRouteHandler {
+ protected virtual IHttpHandler GetHttpHandler(RequestContext requestContext) {
+ return new MvcHandler(requestContext);
+ }
+
+ #region IRouteHandler Members
+ IHttpHandler IRouteHandler.GetHttpHandler(RequestContext requestContext) {
+ return GetHttpHandler(requestContext);
+ }
+ #endregion
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/NameValueCollectionExtensions.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/NameValueCollectionExtensions.cs
new file mode 100644
index 00000000000..7faf9f09877
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/NameValueCollectionExtensions.cs
@@ -0,0 +1,39 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Collections.Generic;
+ using System.Collections.Specialized;
+
+ public static class NameValueCollectionExtensions {
+
+ public static void CopyTo(this NameValueCollection collection, IDictionary<string, object> destination) {
+ CopyTo(collection, destination, false /* replaceEntries */);
+ }
+
+ public static void CopyTo(this NameValueCollection collection, IDictionary<string, object> destination, bool replaceEntries) {
+ if (collection == null) {
+ throw new ArgumentNullException("collection");
+ }
+ if (destination == null) {
+ throw new ArgumentNullException("destination");
+ }
+
+ foreach (string key in collection.Keys) {
+ if (replaceEntries || !destination.ContainsKey(key)) {
+ destination[key] = collection[key];
+ }
+ }
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/NameValueCollectionValueProvider.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/NameValueCollectionValueProvider.cs
new file mode 100644
index 00000000000..9bc990bbbd1
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/NameValueCollectionValueProvider.cs
@@ -0,0 +1,68 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Collections.Generic;
+ using System.Collections.Specialized;
+ using System.Globalization;
+ using System.Linq;
+
+ public class NameValueCollectionValueProvider : IValueProvider {
+
+ private readonly HashSet<string> _prefixes = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
+ private readonly Dictionary<string, ValueProviderResult> _values = new Dictionary<string, ValueProviderResult>(StringComparer.OrdinalIgnoreCase);
+
+ public NameValueCollectionValueProvider(NameValueCollection collection, CultureInfo culture) {
+ if (collection == null) {
+ throw new ArgumentNullException("collection");
+ }
+
+ AddValues(collection, culture);
+ }
+
+ private void AddValues(NameValueCollection collection, CultureInfo culture) {
+ if (collection.Count > 0) {
+ _prefixes.Add("");
+ }
+
+ foreach (string key in collection) {
+ if (key != null) {
+ _prefixes.UnionWith(ValueProviderUtil.GetPrefixes(key));
+
+ string[] rawValue = collection.GetValues(key);
+ string attemptedValue = collection[key];
+ _values[key] = new ValueProviderResult(rawValue, attemptedValue, culture);
+ }
+ }
+ }
+
+ public virtual bool ContainsPrefix(string prefix) {
+ if (prefix == null) {
+ throw new ArgumentNullException("prefix");
+ }
+
+ return _prefixes.Contains(prefix);
+ }
+
+ public virtual ValueProviderResult GetValue(string key) {
+ if (key == null) {
+ throw new ArgumentNullException("key");
+ }
+
+ ValueProviderResult vpResult;
+ _values.TryGetValue(key, out vpResult);
+ return vpResult;
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/NoAsyncTimeoutAttribute.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/NoAsyncTimeoutAttribute.cs
new file mode 100644
index 00000000000..b482dc86a87
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/NoAsyncTimeoutAttribute.cs
@@ -0,0 +1,24 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System.Threading;
+
+ [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
+ public sealed class NoAsyncTimeoutAttribute : AsyncTimeoutAttribute {
+
+ public NoAsyncTimeoutAttribute()
+ : base(Timeout.Infinite) {
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/NonActionAttribute.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/NonActionAttribute.cs
new file mode 100644
index 00000000000..c50ef63118c
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/NonActionAttribute.cs
@@ -0,0 +1,22 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System.Reflection;
+
+ [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
+ public sealed class NonActionAttribute : ActionMethodSelectorAttribute {
+ public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo) {
+ return false;
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/NullViewLocationCache.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/NullViewLocationCache.cs
new file mode 100644
index 00000000000..dbfc9ae5805
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/NullViewLocationCache.cs
@@ -0,0 +1,30 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+
+ internal sealed class NullViewLocationCache : IViewLocationCache {
+
+ #region IViewLocationCache Members
+ public string GetViewLocation(HttpContextBase httpContext, string key) {
+ return null;
+ }
+
+ public void InsertViewLocation(HttpContextBase httpContext, string key, string virtualPath) {
+ }
+ #endregion
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/OutputCacheAttribute.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/OutputCacheAttribute.cs
new file mode 100644
index 00000000000..a83a82b2d2c
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/OutputCacheAttribute.cs
@@ -0,0 +1,146 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Web;
+ using System.Web.UI;
+
+ [SuppressMessage("Microsoft.Performance", "CA1813:AvoidUnsealedAttributes",
+ Justification = "Unsealed so that subclassed types can set properties in the default constructor.")]
+ [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
+ public class OutputCacheAttribute : ActionFilterAttribute {
+
+ private OutputCacheParameters _cacheSettings = new OutputCacheParameters();
+
+ public string CacheProfile {
+ get {
+ return _cacheSettings.CacheProfile ?? String.Empty;
+ }
+ set {
+ _cacheSettings.CacheProfile = value;
+ }
+ }
+
+ internal OutputCacheParameters CacheSettings {
+ get {
+ return _cacheSettings;
+ }
+ }
+
+ public int Duration {
+ get {
+ return _cacheSettings.Duration;
+ }
+ set {
+ _cacheSettings.Duration = value;
+ }
+ }
+
+ public OutputCacheLocation Location {
+ get {
+ return _cacheSettings.Location;
+ }
+ set {
+ _cacheSettings.Location = value;
+ }
+ }
+
+ public bool NoStore {
+ get {
+ return _cacheSettings.NoStore;
+ }
+ set {
+ _cacheSettings.NoStore = value;
+ }
+ }
+
+ public string SqlDependency {
+ get {
+ return _cacheSettings.SqlDependency ?? String.Empty;
+ }
+ set {
+ _cacheSettings.SqlDependency = value;
+ }
+ }
+
+ public string VaryByContentEncoding {
+ get {
+ return _cacheSettings.VaryByContentEncoding ?? String.Empty;
+ }
+ set {
+ _cacheSettings.VaryByContentEncoding = value;
+ }
+ }
+
+ public string VaryByCustom {
+ get {
+ return _cacheSettings.VaryByCustom ?? String.Empty;
+ }
+ set {
+ _cacheSettings.VaryByCustom = value;
+ }
+ }
+
+ public string VaryByHeader {
+ get {
+ return _cacheSettings.VaryByHeader ?? String.Empty;
+ }
+ set {
+ _cacheSettings.VaryByHeader = value;
+ }
+ }
+
+ [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Param",
+ Justification = "Matches the @ OutputCache page directive.")]
+ public string VaryByParam {
+ get {
+ return _cacheSettings.VaryByParam ?? String.Empty;
+ }
+ set {
+ _cacheSettings.VaryByParam = value;
+ }
+ }
+
+ public override void OnResultExecuting(ResultExecutingContext filterContext) {
+ if (filterContext == null) {
+ throw new ArgumentNullException("filterContext");
+ }
+
+ if (filterContext.IsChildAction) {
+ return;
+ }
+
+ // we need to call ProcessRequest() since there's no other way to set the Page.Response intrinsic
+ OutputCachedPage page = new OutputCachedPage(_cacheSettings);
+ page.ProcessRequest(HttpContext.Current);
+ }
+
+ private sealed class OutputCachedPage : Page {
+ private OutputCacheParameters _cacheSettings;
+
+ public OutputCachedPage(OutputCacheParameters cacheSettings) {
+ // Tracing requires Page IDs to be unique.
+ ID = Guid.NewGuid().ToString();
+ _cacheSettings = cacheSettings;
+ }
+
+ protected override void FrameworkInitialize() {
+ // when you put the <%@ OutputCache %> directive on a page, the generated code calls InitOutputCache() from here
+ base.FrameworkInitialize();
+ InitOutputCache(_cacheSettings);
+ }
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ParameterBindingInfo.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ParameterBindingInfo.cs
new file mode 100644
index 00000000000..09334b8dbe3
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ParameterBindingInfo.cs
@@ -0,0 +1,43 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System.Collections.Generic;
+
+ public abstract class ParameterBindingInfo {
+
+ public virtual IModelBinder Binder {
+ get {
+ return null;
+ }
+ }
+
+ public virtual ICollection<string> Exclude {
+ get {
+ return new string[0];
+ }
+ }
+
+ public virtual ICollection<string> Include {
+ get {
+ return new string[0];
+ }
+ }
+
+ public virtual string Prefix {
+ get {
+ return null;
+ }
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ParameterDescriptor.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ParameterDescriptor.cs
new file mode 100644
index 00000000000..92ddce4d8a8
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ParameterDescriptor.cs
@@ -0,0 +1,69 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Reflection;
+
+ public abstract class ParameterDescriptor : ICustomAttributeProvider {
+
+ private static readonly EmptyParameterBindingInfo _emptyBindingInfo = new EmptyParameterBindingInfo();
+
+ public abstract ActionDescriptor ActionDescriptor {
+ get;
+ }
+
+ public virtual ParameterBindingInfo BindingInfo {
+ get {
+ return _emptyBindingInfo;
+ }
+ }
+
+ public virtual object DefaultValue {
+ get {
+ return null;
+ }
+ }
+
+ public abstract string ParameterName {
+ get;
+ }
+
+ public abstract Type ParameterType {
+ get;
+ }
+
+ public virtual object[] GetCustomAttributes(bool inherit) {
+ return GetCustomAttributes(typeof(object), inherit);
+ }
+
+ public virtual object[] GetCustomAttributes(Type attributeType, bool inherit) {
+ if (attributeType == null) {
+ throw new ArgumentNullException("attributeType");
+ }
+
+ return (object[])Array.CreateInstance(attributeType, 0);
+ }
+
+ public virtual bool IsDefined(Type attributeType, bool inherit) {
+ if (attributeType == null) {
+ throw new ArgumentNullException("attributeType");
+ }
+
+ return false;
+ }
+
+ private sealed class EmptyParameterBindingInfo : ParameterBindingInfo {
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ParameterInfoUtil.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ParameterInfoUtil.cs
new file mode 100644
index 00000000000..c538e145afa
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ParameterInfoUtil.cs
@@ -0,0 +1,42 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.ComponentModel;
+ using System.Reflection;
+
+ internal static class ParameterInfoUtil {
+
+ public static bool TryGetDefaultValue(ParameterInfo parameterInfo, out object value) {
+ // this will get the default value as seen by the VB / C# compilers
+ // if no value was baked in, RawDefaultValue returns DBNull.Value
+ object rawDefaultValue = parameterInfo.RawDefaultValue;
+ if (rawDefaultValue != DBNull.Value) {
+ value = rawDefaultValue;
+ return true;
+ }
+
+ // if the compiler did not bake in a default value, check the [DefaultValue] attribute
+ DefaultValueAttribute[] attrs = (DefaultValueAttribute[])parameterInfo.GetCustomAttributes(typeof(DefaultValueAttribute), false);
+ if (attrs == null || attrs.Length == 0) {
+ value = default(object);
+ return false;
+ }
+ else {
+ value = attrs[0].Value;
+ return true;
+ }
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/PartialViewResult.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/PartialViewResult.cs
new file mode 100644
index 00000000000..86f9fb1560e
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/PartialViewResult.cs
@@ -0,0 +1,37 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Globalization;
+ using System.Text;
+ using System.Web.Mvc.Resources;
+
+ public class PartialViewResult : ViewResultBase {
+
+ protected override ViewEngineResult FindView(ControllerContext context) {
+ ViewEngineResult result = ViewEngineCollection.FindPartialView(context, ViewName);
+ if (result.View != null) {
+ return result;
+ }
+
+ // we need to generate an exception containing all the locations we searched
+ StringBuilder locationsText = new StringBuilder();
+ foreach (string location in result.SearchedLocations) {
+ locationsText.AppendLine();
+ locationsText.Append(location);
+ }
+ throw new InvalidOperationException(String.Format(CultureInfo.CurrentUICulture,
+ MvcResources.Common_PartialViewNotFound, ViewName, locationsText));
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/PathHelpers.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/PathHelpers.cs
new file mode 100644
index 00000000000..cb5be743895
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/PathHelpers.cs
@@ -0,0 +1,97 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Collections.Specialized;
+ using System.Web;
+
+ internal static class PathHelpers {
+
+ private const string _urlRewriterServerVar = "HTTP_X_ORIGINAL_URL";
+
+ // this method can accept an app-relative path or an absolute path for contentPath
+ public static string GenerateClientUrl(HttpContextBase httpContext, string contentPath) {
+ if (String.IsNullOrEmpty(contentPath)) {
+ return contentPath;
+ }
+
+ // many of the methods we call internally can't handle query strings properly, so just strip it out for
+ // the time being
+ string query;
+ contentPath = StripQuery(contentPath, out query);
+
+ return GenerateClientUrlInternal(httpContext, contentPath) + query;
+ }
+
+ private static string GenerateClientUrlInternal(HttpContextBase httpContext, string contentPath) {
+ if (String.IsNullOrEmpty(contentPath)) {
+ return contentPath;
+ }
+
+ // can't call VirtualPathUtility.IsAppRelative since it throws on some inputs
+ bool isAppRelative = contentPath[0] == '~';
+ if (isAppRelative) {
+ string absoluteContentPath = VirtualPathUtility.ToAbsolute(contentPath, httpContext.Request.ApplicationPath);
+ string modifiedAbsoluteContentPath = httpContext.Response.ApplyAppPathModifier(absoluteContentPath);
+ return GenerateClientUrlInternal(httpContext, modifiedAbsoluteContentPath);
+ }
+
+ // we only want to manipulate the path if URL rewriting is active, else we risk breaking the generated URL
+ NameValueCollection serverVars = httpContext.Request.ServerVariables;
+ bool urlRewriterIsEnabled = (serverVars != null && serverVars[_urlRewriterServerVar] != null);
+ if (!urlRewriterIsEnabled) {
+ return contentPath;
+ }
+
+ // Since the rawUrl represents what the user sees in his browser, it is what we want to use as the base
+ // of our absolute paths. For example, consider mysite.example.com/foo, which is internally
+ // rewritten to content.example.com/mysite/foo. When we want to generate a link to ~/bar, we want to
+ // base it from / instead of /foo, otherwise the user ends up seeing mysite.example.com/foo/bar,
+ // which is incorrect.
+ string relativeUrlToDestination = MakeRelative(httpContext.Request.Path, contentPath);
+ string absoluteUrlToDestination = MakeAbsolute(httpContext.Request.RawUrl, relativeUrlToDestination);
+ return absoluteUrlToDestination;
+ }
+
+ public static string MakeAbsolute(string basePath, string relativePath) {
+ // The Combine() method can't handle query strings on the base path, so we trim it off.
+ string query;
+ basePath = StripQuery(basePath, out query);
+ return VirtualPathUtility.Combine(basePath, relativePath);
+ }
+
+ public static string MakeRelative(string fromPath, string toPath) {
+ string relativeUrl = VirtualPathUtility.MakeRelative(fromPath, toPath);
+ if (String.IsNullOrEmpty(relativeUrl) || relativeUrl[0] == '?') {
+ // Sometimes VirtualPathUtility.MakeRelative() will return an empty string when it meant to return '.',
+ // but links to {empty string} are browser dependent. We replace it with an explicit path to force
+ // consistency across browsers.
+ relativeUrl = "./" + relativeUrl;
+ }
+ return relativeUrl;
+ }
+
+ private static string StripQuery(string path, out string query) {
+ int queryIndex = path.IndexOf('?');
+ if (queryIndex >= 0) {
+ query = path.Substring(queryIndex);
+ return path.Substring(0, queryIndex);
+ }
+ else {
+ query = null;
+ return path;
+ }
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/QueryStringValueProvider.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/QueryStringValueProvider.cs
new file mode 100644
index 00000000000..19f4c6eb7cc
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/QueryStringValueProvider.cs
@@ -0,0 +1,27 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Collections.Specialized;
+ using System.Globalization;
+
+ public sealed class QueryStringValueProvider : NameValueCollectionValueProvider {
+
+ // QueryString should use the invariant culture since it's part of the URL, and the URL should be
+ // interpreted in a uniform fashion regardless of the origin of a particular request.
+ public QueryStringValueProvider(ControllerContext controllerContext)
+ : base(controllerContext.HttpContext.Request.QueryString, CultureInfo.InvariantCulture) {
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/QueryStringValueProviderFactory.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/QueryStringValueProviderFactory.cs
new file mode 100644
index 00000000000..c12bb3b1967
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/QueryStringValueProviderFactory.cs
@@ -0,0 +1,27 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+
+ public sealed class QueryStringValueProviderFactory : ValueProviderFactory {
+
+ public override IValueProvider GetValueProvider(ControllerContext controllerContext) {
+ if (controllerContext == null) {
+ throw new ArgumentNullException("controllerContext");
+ }
+
+ return new QueryStringValueProvider(controllerContext);
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/RangeAttributeAdapter.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/RangeAttributeAdapter.cs
new file mode 100644
index 00000000000..d789d988aaf
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/RangeAttributeAdapter.cs
@@ -0,0 +1,26 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System.Collections.Generic;
+ using System.ComponentModel.DataAnnotations;
+
+ public class RangeAttributeAdapter : DataAnnotationsModelValidator<RangeAttribute> {
+ public RangeAttributeAdapter(ModelMetadata metadata, ControllerContext context, RangeAttribute attribute)
+ : base(metadata, context, attribute) {
+ }
+
+ public override IEnumerable<ModelClientValidationRule> GetClientValidationRules() {
+ return new[] { new ModelClientValidationRangeRule(ErrorMessage, Attribute.Minimum, Attribute.Maximum) };
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ReaderWriterCache`2.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ReaderWriterCache`2.cs
new file mode 100644
index 00000000000..63e2fd42b49
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ReaderWriterCache`2.cs
@@ -0,0 +1,72 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Threading;
+
+ [SuppressMessage("Microsoft.Design", "CA1001:TypesThatOwnDisposableFieldsShouldBeDisposable",
+ Justification = "Instances of this type are meant to be singletons.")]
+ internal abstract class ReaderWriterCache<TKey, TValue> {
+
+ private readonly Dictionary<TKey, TValue> _cache;
+ private readonly ReaderWriterLockSlim _rwLock = new ReaderWriterLockSlim();
+
+ protected ReaderWriterCache()
+ : this(null) {
+ }
+
+ protected ReaderWriterCache(IEqualityComparer<TKey> comparer) {
+ _cache = new Dictionary<TKey, TValue>(comparer);
+ }
+
+ protected Dictionary<TKey, TValue> Cache {
+ get {
+ return _cache;
+ }
+ }
+
+ protected TValue FetchOrCreateItem(TKey key, Func<TValue> creator) {
+ // first, see if the item already exists in the cache
+ _rwLock.EnterReadLock();
+ try {
+ TValue existingEntry;
+ if (_cache.TryGetValue(key, out existingEntry)) {
+ return existingEntry;
+ }
+ }
+ finally {
+ _rwLock.ExitReadLock();
+ }
+
+ // insert the new item into the cache
+ TValue newEntry = creator();
+ _rwLock.EnterWriteLock();
+ try {
+ TValue existingEntry;
+ if (_cache.TryGetValue(key, out existingEntry)) {
+ // another thread already inserted an item, so use that one
+ return existingEntry;
+ }
+
+ _cache[key] = newEntry;
+ return newEntry;
+ }
+ finally {
+ _rwLock.ExitWriteLock();
+ }
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/RedirectResult.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/RedirectResult.cs
new file mode 100644
index 00000000000..10abf152fdf
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/RedirectResult.cs
@@ -0,0 +1,52 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Web.Mvc.Resources;
+
+ // represents a result that performs a redirection given some URI
+ public class RedirectResult : ActionResult {
+
+ [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "0#",
+ Justification = "Response.Redirect() takes its URI as a string parameter.")]
+ public RedirectResult(string url) {
+ if (String.IsNullOrEmpty(url)) {
+ throw new ArgumentException(MvcResources.Common_NullOrEmpty, "url");
+ }
+
+ Url = url;
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1056:UriPropertiesShouldNotBeStrings",
+ Justification = "Response.Redirect() takes its URI as a string parameter.")]
+ public string Url {
+ get;
+ private set;
+ }
+
+ public override void ExecuteResult(ControllerContext context) {
+ if (context == null) {
+ throw new ArgumentNullException("context");
+ }
+ if (context.IsChildAction) {
+ throw new InvalidOperationException(MvcResources.RedirectAction_CannotRedirectInChildAction);
+ }
+
+ string destinationUrl = UrlHelper.GenerateContentUrl(Url, context.HttpContext);
+ context.Controller.TempData.Keep();
+ context.HttpContext.Response.Redirect(destinationUrl, false /* endResponse */);
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/RedirectToRouteResult.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/RedirectToRouteResult.cs
new file mode 100644
index 00000000000..74b5ce50aa6
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/RedirectToRouteResult.cs
@@ -0,0 +1,71 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Web.Mvc.Resources;
+ using System.Web.Routing;
+
+ // represents a result that performs a redirection given some values dictionary
+ public class RedirectToRouteResult : ActionResult {
+
+ private RouteCollection _routes;
+
+ public RedirectToRouteResult(RouteValueDictionary routeValues) :
+ this(null, routeValues) {
+ }
+
+ public RedirectToRouteResult(string routeName, RouteValueDictionary routeValues) {
+ RouteName = routeName ?? String.Empty;
+ RouteValues = routeValues ?? new RouteValueDictionary();
+ }
+
+ public string RouteName {
+ get;
+ private set;
+ }
+
+ public RouteValueDictionary RouteValues {
+ get;
+ private set;
+ }
+
+ internal RouteCollection Routes {
+ get {
+ if (_routes == null) {
+ _routes = RouteTable.Routes;
+ }
+ return _routes;
+ }
+ set {
+ _routes = value;
+ }
+ }
+
+ public override void ExecuteResult(ControllerContext context) {
+ if (context == null) {
+ throw new ArgumentNullException("context");
+ }
+ if (context.IsChildAction) {
+ throw new InvalidOperationException(MvcResources.RedirectAction_CannotRedirectInChildAction);
+ }
+
+ string destinationUrl = UrlHelper.GenerateUrl(RouteName, null /* actionName */, null /* controllerName */, RouteValues, Routes, context.RequestContext, false /* includeImplicitMvcValues */);
+ if (String.IsNullOrEmpty(destinationUrl)) {
+ throw new InvalidOperationException(MvcResources.Common_NoRouteMatched);
+ }
+
+ context.Controller.TempData.Keep();
+ context.HttpContext.Response.Redirect(destinationUrl, false /* endResponse */);
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ReflectedActionDescriptor.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ReflectedActionDescriptor.cs
new file mode 100644
index 00000000000..c8ff6ba2e38
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ReflectedActionDescriptor.cs
@@ -0,0 +1,131 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Reflection;
+ using System.Web.Mvc.Resources;
+
+ public class ReflectedActionDescriptor : ActionDescriptor {
+
+ private readonly string _actionName;
+ private readonly ControllerDescriptor _controllerDescriptor;
+ private ParameterDescriptor[] _parametersCache;
+
+ public ReflectedActionDescriptor(MethodInfo methodInfo, string actionName, ControllerDescriptor controllerDescriptor)
+ : this(methodInfo, actionName, controllerDescriptor, true /* validateMethod */) {
+ }
+
+ internal ReflectedActionDescriptor(MethodInfo methodInfo, string actionName, ControllerDescriptor controllerDescriptor, bool validateMethod) {
+ if (methodInfo == null) {
+ throw new ArgumentNullException("methodInfo");
+ }
+ if (String.IsNullOrEmpty(actionName)) {
+ throw new ArgumentException(MvcResources.Common_NullOrEmpty, "actionName");
+ }
+ if (controllerDescriptor == null) {
+ throw new ArgumentNullException("controllerDescriptor");
+ }
+
+ if (validateMethod) {
+ string failedMessage = VerifyActionMethodIsCallable(methodInfo);
+ if (failedMessage != null) {
+ throw new ArgumentException(failedMessage, "methodInfo");
+ }
+ }
+
+ MethodInfo = methodInfo;
+ _actionName = actionName;
+ _controllerDescriptor = controllerDescriptor;
+ }
+
+ public override string ActionName {
+ get {
+ return _actionName;
+ }
+ }
+
+ public override ControllerDescriptor ControllerDescriptor {
+ get {
+ return _controllerDescriptor;
+ }
+ }
+
+ public MethodInfo MethodInfo {
+ get;
+ private set;
+ }
+
+ public override object Execute(ControllerContext controllerContext, IDictionary<string, object> parameters) {
+ if (controllerContext == null) {
+ throw new ArgumentNullException("controllerContext");
+ }
+ if (parameters == null) {
+ throw new ArgumentNullException("parameters");
+ }
+
+ ParameterInfo[] parameterInfos = MethodInfo.GetParameters();
+ var rawParameterValues = from parameterInfo in parameterInfos
+ select ExtractParameterFromDictionary(parameterInfo, parameters, MethodInfo);
+ object[] parametersArray = rawParameterValues.ToArray();
+
+ ActionMethodDispatcher dispatcher = DispatcherCache.GetDispatcher(MethodInfo);
+ object actionReturnValue = dispatcher.Execute(controllerContext.Controller, parametersArray);
+ return actionReturnValue;
+ }
+
+ public override object[] GetCustomAttributes(bool inherit) {
+ return MethodInfo.GetCustomAttributes(inherit);
+ }
+
+ public override object[] GetCustomAttributes(Type attributeType, bool inherit) {
+ return MethodInfo.GetCustomAttributes(attributeType, inherit);
+ }
+
+ public override FilterInfo GetFilters() {
+ return GetFilters(MethodInfo);
+ }
+
+ public override ParameterDescriptor[] GetParameters() {
+ ParameterDescriptor[] parameters = LazilyFetchParametersCollection();
+
+ // need to clone array so that user modifications aren't accidentally stored
+ return (ParameterDescriptor[])parameters.Clone();
+ }
+
+ public override ICollection<ActionSelector> GetSelectors() {
+ ActionMethodSelectorAttribute[] attrs = (ActionMethodSelectorAttribute[])MethodInfo.GetCustomAttributes(typeof(ActionMethodSelectorAttribute), true /* inherit */);
+ ActionSelector[] selectors = Array.ConvertAll(attrs, attr => (ActionSelector)(controllerContext => attr.IsValidForRequest(controllerContext, MethodInfo)));
+ return selectors;
+ }
+
+ public override bool IsDefined(Type attributeType, bool inherit) {
+ return MethodInfo.IsDefined(attributeType, inherit);
+ }
+
+ private ParameterDescriptor[] LazilyFetchParametersCollection() {
+ return DescriptorUtil.LazilyFetchOrCreateDescriptors<ParameterInfo, ParameterDescriptor>(
+ ref _parametersCache /* cacheLocation */,
+ MethodInfo.GetParameters /* initializer */,
+ parameterInfo => new ReflectedParameterDescriptor(parameterInfo, this) /* converter */);
+ }
+
+ internal static ReflectedActionDescriptor TryCreateDescriptor(MethodInfo methodInfo, string name, ControllerDescriptor controllerDescriptor) {
+ ReflectedActionDescriptor descriptor = new ReflectedActionDescriptor(methodInfo, name, controllerDescriptor, false /* validateMethod */);
+ string failedMessage = VerifyActionMethodIsCallable(methodInfo);
+ return (failedMessage == null) ? descriptor : null;
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ReflectedControllerDescriptor.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ReflectedControllerDescriptor.cs
new file mode 100644
index 00000000000..990f1369e92
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ReflectedControllerDescriptor.cs
@@ -0,0 +1,91 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Reflection;
+ using System.Web.Mvc.Resources;
+
+ public class ReflectedControllerDescriptor : ControllerDescriptor {
+
+ private ActionDescriptor[] _canonicalActionsCache;
+ private readonly Type _controllerType;
+ private readonly ActionMethodSelector _selector;
+
+ public ReflectedControllerDescriptor(Type controllerType) {
+ if (controllerType == null) {
+ throw new ArgumentNullException("controllerType");
+ }
+
+ _controllerType = controllerType;
+ _selector = new ActionMethodSelector(_controllerType);
+ }
+
+ public sealed override Type ControllerType {
+ get {
+ return _controllerType;
+ }
+ }
+
+ public override ActionDescriptor FindAction(ControllerContext controllerContext, string actionName) {
+ if (controllerContext == null) {
+ throw new ArgumentNullException("controllerContext");
+ }
+ if (String.IsNullOrEmpty(actionName)) {
+ throw new ArgumentException(MvcResources.Common_NullOrEmpty, "actionName");
+ }
+
+ MethodInfo matched = _selector.FindActionMethod(controllerContext, actionName);
+ if (matched == null) {
+ return null;
+ }
+
+ return new ReflectedActionDescriptor(matched, actionName, this);
+ }
+
+ private MethodInfo[] GetAllActionMethodsFromSelector() {
+ List<MethodInfo> allValidMethods = new List<MethodInfo>();
+ allValidMethods.AddRange(_selector.AliasedMethods);
+ allValidMethods.AddRange(_selector.NonAliasedMethods.SelectMany(g => g));
+ return allValidMethods.ToArray();
+ }
+
+ public override ActionDescriptor[] GetCanonicalActions() {
+ ActionDescriptor[] actions = LazilyFetchCanonicalActionsCollection();
+
+ // need to clone array so that user modifications aren't accidentally stored
+ return (ActionDescriptor[])actions.Clone();
+ }
+
+ public override object[] GetCustomAttributes(bool inherit) {
+ return ControllerType.GetCustomAttributes(inherit);
+ }
+
+ public override object[] GetCustomAttributes(Type attributeType, bool inherit) {
+ return ControllerType.GetCustomAttributes(attributeType, inherit);
+ }
+
+ public override bool IsDefined(Type attributeType, bool inherit) {
+ return ControllerType.IsDefined(attributeType, inherit);
+ }
+
+ private ActionDescriptor[] LazilyFetchCanonicalActionsCollection() {
+ return DescriptorUtil.LazilyFetchOrCreateDescriptors<MethodInfo, ActionDescriptor>(
+ ref _canonicalActionsCache /* cacheLocation */,
+ GetAllActionMethodsFromSelector /* initializer */,
+ methodInfo => ReflectedActionDescriptor.TryCreateDescriptor(methodInfo, methodInfo.Name, this) /* converter */);
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ReflectedParameterBindingInfo.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ReflectedParameterBindingInfo.cs
new file mode 100644
index 00000000000..02817655aff
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ReflectedParameterBindingInfo.cs
@@ -0,0 +1,73 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Collections.Generic;
+ using System.Collections.ObjectModel;
+ using System.Globalization;
+ using System.Reflection;
+ using System.Web.Mvc.Resources;
+
+ internal class ReflectedParameterBindingInfo : ParameterBindingInfo {
+
+ private ICollection<string> _exclude = new string[0];
+ private ICollection<string> _include = new string[0];
+ private readonly ParameterInfo _parameterInfo;
+ private string _prefix;
+
+ public ReflectedParameterBindingInfo(ParameterInfo parameterInfo) {
+ _parameterInfo = parameterInfo;
+ ReadSettingsFromBindAttribute();
+ }
+
+ public override IModelBinder Binder {
+ get {
+ IModelBinder binder = ModelBinders.GetBinderFromAttributes(_parameterInfo,
+ () => String.Format(CultureInfo.CurrentUICulture, MvcResources.ReflectedParameterBindingInfo_MultipleConverterAttributes,
+ _parameterInfo.Name, _parameterInfo.Member));
+
+ return binder;
+ }
+ }
+
+ public override ICollection<string> Exclude {
+ get {
+ return _exclude;
+ }
+ }
+
+ public override ICollection<string> Include {
+ get {
+ return _include;
+ }
+ }
+
+ public override string Prefix {
+ get {
+ return _prefix;
+ }
+ }
+
+ private void ReadSettingsFromBindAttribute() {
+ BindAttribute attr = (BindAttribute)Attribute.GetCustomAttribute(_parameterInfo, typeof(BindAttribute));
+ if (attr == null) {
+ return;
+ }
+
+ _exclude = new ReadOnlyCollection<string>(AuthorizeAttribute.SplitString(attr.Exclude));
+ _include = new ReadOnlyCollection<string>(AuthorizeAttribute.SplitString(attr.Include));
+ _prefix = attr.Prefix;
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ReflectedParameterDescriptor.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ReflectedParameterDescriptor.cs
new file mode 100644
index 00000000000..357f45e52e9
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ReflectedParameterDescriptor.cs
@@ -0,0 +1,90 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.ComponentModel;
+ using System.Reflection;
+
+ public class ReflectedParameterDescriptor : ParameterDescriptor {
+
+ private readonly ActionDescriptor _actionDescriptor;
+ private readonly ReflectedParameterBindingInfo _bindingInfo;
+
+ public ReflectedParameterDescriptor(ParameterInfo parameterInfo, ActionDescriptor actionDescriptor) {
+ if (parameterInfo == null) {
+ throw new ArgumentNullException("parameterInfo");
+ }
+ if (actionDescriptor == null) {
+ throw new ArgumentNullException("actionDescriptor");
+ }
+
+ ParameterInfo = parameterInfo;
+ _actionDescriptor = actionDescriptor;
+ _bindingInfo = new ReflectedParameterBindingInfo(parameterInfo);
+ }
+
+ public override ActionDescriptor ActionDescriptor {
+ get {
+ return _actionDescriptor;
+ }
+ }
+
+ public override ParameterBindingInfo BindingInfo {
+ get {
+ return _bindingInfo;
+ }
+ }
+
+ public override object DefaultValue {
+ get {
+ object value;
+ if (ParameterInfoUtil.TryGetDefaultValue(ParameterInfo, out value)) {
+ return value;
+ }
+ else {
+ return base.DefaultValue;
+ }
+ }
+ }
+
+ public ParameterInfo ParameterInfo {
+ get;
+ private set;
+ }
+
+ public override string ParameterName {
+ get {
+ return ParameterInfo.Name;
+ }
+ }
+
+ public override Type ParameterType {
+ get {
+ return ParameterInfo.ParameterType;
+ }
+ }
+
+ public override object[] GetCustomAttributes(bool inherit) {
+ return ParameterInfo.GetCustomAttributes(inherit);
+ }
+
+ public override object[] GetCustomAttributes(Type attributeType, bool inherit) {
+ return ParameterInfo.GetCustomAttributes(attributeType, inherit);
+ }
+
+ public override bool IsDefined(Type attributeType, bool inherit) {
+ return ParameterInfo.IsDefined(attributeType, inherit);
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/RegularExpressionAttributeAdapter.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/RegularExpressionAttributeAdapter.cs
new file mode 100644
index 00000000000..f17550256ad
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/RegularExpressionAttributeAdapter.cs
@@ -0,0 +1,26 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System.Collections.Generic;
+ using System.ComponentModel.DataAnnotations;
+
+ public class RegularExpressionAttributeAdapter : DataAnnotationsModelValidator<RegularExpressionAttribute> {
+ public RegularExpressionAttributeAdapter(ModelMetadata metadata, ControllerContext context, RegularExpressionAttribute attribute)
+ : base(metadata, context, attribute) {
+ }
+
+ public override IEnumerable<ModelClientValidationRule> GetClientValidationRules() {
+ return new[] { new ModelClientValidationRegexRule(ErrorMessage, Attribute.Pattern) };
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/RequireHttpsAttribute.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/RequireHttpsAttribute.cs
new file mode 100644
index 00000000000..dff4c921dba
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/RequireHttpsAttribute.cs
@@ -0,0 +1,47 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Web.Mvc.Resources;
+
+ [SuppressMessage("Microsoft.Performance", "CA1813:AvoidUnsealedAttributes",
+ Justification = "Unsealed because type contains virtual extensibility points.")]
+ [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
+ public class RequireHttpsAttribute : FilterAttribute, IAuthorizationFilter {
+
+ public virtual void OnAuthorization(AuthorizationContext filterContext) {
+ if (filterContext == null) {
+ throw new ArgumentNullException("filterContext");
+ }
+
+ if (!filterContext.HttpContext.Request.IsSecureConnection) {
+ HandleNonHttpsRequest(filterContext);
+ }
+ }
+
+ protected virtual void HandleNonHttpsRequest(AuthorizationContext filterContext) {
+ // only redirect for GET requests, otherwise the browser might not propagate the verb and request
+ // body correctly.
+
+ if (!String.Equals(filterContext.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase)) {
+ throw new InvalidOperationException(MvcResources.RequireHttpsAttribute_MustUseSsl);
+ }
+
+ // redirect to HTTPS version of page
+ string url = "https://" + filterContext.HttpContext.Request.Url.Host + filterContext.HttpContext.Request.RawUrl;
+ filterContext.Result = new RedirectResult(url);
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/RequiredAttributeAdapter.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/RequiredAttributeAdapter.cs
new file mode 100644
index 00000000000..edb7dc37f95
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/RequiredAttributeAdapter.cs
@@ -0,0 +1,26 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System.Collections.Generic;
+ using System.ComponentModel.DataAnnotations;
+
+ public class RequiredAttributeAdapter : DataAnnotationsModelValidator<RequiredAttribute> {
+ public RequiredAttributeAdapter(ModelMetadata metadata, ControllerContext context, RequiredAttribute attribute)
+ : base(metadata, context, attribute) {
+ }
+
+ public override IEnumerable<ModelClientValidationRule> GetClientValidationRules() {
+ return new[] { new ModelClientValidationRequiredRule(ErrorMessage) };
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/Resources/MvcResources.Designer.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Resources/MvcResources.Designer.cs
new file mode 100644
index 00000000000..4be3e6bf755
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Resources/MvcResources.Designer.cs
@@ -0,0 +1,797 @@
+//------------------------------------------------------------------------------
+// <auto-generated>
+// This code was generated by a tool.
+// Runtime Version:2.0.50727.4200
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace System.Web.Mvc.Resources {
+ using System;
+
+
+ /// <summary>
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ /// </summary>
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "2.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class MvcResources {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal MvcResources() {
+ }
+
+ /// <summary>
+ /// Returns the cached ResourceManager instance used by this class.
+ /// </summary>
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("System.Web.Mvc.Resources.MvcResources", typeof(MvcResources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ /// <summary>
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ /// </summary>
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The current request for action &apos;{0}&apos; on controller type &apos;{1}&apos; is ambiguous between the following action methods:{2}.
+ /// </summary>
+ internal static string ActionMethodSelector_AmbiguousMatch {
+ get {
+ return ResourceManager.GetString("ActionMethodSelector_AmbiguousMatch", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to {0} on type {1}.
+ /// </summary>
+ internal static string ActionMethodSelector_AmbiguousMatchType {
+ get {
+ return ResourceManager.GetString("ActionMethodSelector_AmbiguousMatchType", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to A required anti-forgery token was not supplied or was invalid..
+ /// </summary>
+ internal static string AntiForgeryToken_ValidationFailed {
+ get {
+ return ResourceManager.GetString("AntiForgeryToken_ValidationFailed", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to Lookup for method &apos;{0}&apos; on controller type &apos;{1}&apos; failed because of an ambiguity between the following methods:{2}.
+ /// </summary>
+ internal static string AsyncActionMethodSelector_AmbiguousMethodMatch {
+ get {
+ return ResourceManager.GetString("AsyncActionMethodSelector_AmbiguousMethodMatch", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to Could not locate a method named &apos;{0}&apos; on controller type {1}..
+ /// </summary>
+ internal static string AsyncActionMethodSelector_CouldNotFindMethod {
+ get {
+ return ResourceManager.GetString("AsyncActionMethodSelector_CouldNotFindMethod", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The provided IAsyncResult has already been consumed..
+ /// </summary>
+ internal static string AsyncCommon_AsyncResultAlreadyConsumed {
+ get {
+ return ResourceManager.GetString("AsyncCommon_AsyncResultAlreadyConsumed", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The controller of type &apos;{0}&apos; must subclass AsyncController or implement the IAsyncManagerContainer interface..
+ /// </summary>
+ internal static string AsyncCommon_ControllerMustImplementIAsyncManagerContainer {
+ get {
+ return ResourceManager.GetString("AsyncCommon_ControllerMustImplementIAsyncManagerContainer", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The provided IAsyncResult is not valid for this method..
+ /// </summary>
+ internal static string AsyncCommon_InvalidAsyncResult {
+ get {
+ return ResourceManager.GetString("AsyncCommon_InvalidAsyncResult", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The timeout value must be non-negative or Timeout.Infinite..
+ /// </summary>
+ internal static string AsyncCommon_InvalidTimeout {
+ get {
+ return ResourceManager.GetString("AsyncCommon_InvalidTimeout", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The action &apos;{0}&apos; is accessible only by a child request..
+ /// </summary>
+ internal static string ChildActionOnlyAttribute_MustBeInChildRequest {
+ get {
+ return ResourceManager.GetString("ChildActionOnlyAttribute_MustBeInChildRequest", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The field {0} must be a number..
+ /// </summary>
+ internal static string ClientDataTypeModelValidatorProvider_FieldMustBeNumeric {
+ get {
+ return ResourceManager.GetString("ClientDataTypeModelValidatorProvider_FieldMustBeNumeric", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to No route in the route table matches the supplied values..
+ /// </summary>
+ internal static string Common_NoRouteMatched {
+ get {
+ return ResourceManager.GetString("Common_NoRouteMatched", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to Value cannot be null or empty..
+ /// </summary>
+ internal static string Common_NullOrEmpty {
+ get {
+ return ResourceManager.GetString("Common_NullOrEmpty", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The partial view &apos;{0}&apos; was not found. The following locations were searched:{1}.
+ /// </summary>
+ internal static string Common_PartialViewNotFound {
+ get {
+ return ResourceManager.GetString("Common_PartialViewNotFound", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The property &apos;{0}&apos; cannot be null or empty..
+ /// </summary>
+ internal static string Common_PropertyCannotBeNullOrEmpty {
+ get {
+ return ResourceManager.GetString("Common_PropertyCannotBeNullOrEmpty", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The property {0}.{1} could not be found..
+ /// </summary>
+ internal static string Common_PropertyNotFound {
+ get {
+ return ResourceManager.GetString("Common_PropertyNotFound", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to False.
+ /// </summary>
+ internal static string Common_TriState_False {
+ get {
+ return ResourceManager.GetString("Common_TriState_False", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to Not Set.
+ /// </summary>
+ internal static string Common_TriState_NotSet {
+ get {
+ return ResourceManager.GetString("Common_TriState_NotSet", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to True.
+ /// </summary>
+ internal static string Common_TriState_True {
+ get {
+ return ResourceManager.GetString("Common_TriState_True", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The type {0} must derive from {1}.
+ /// </summary>
+ internal static string Common_TypeMustDriveFromType {
+ get {
+ return ResourceManager.GetString("Common_TypeMustDriveFromType", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The value &apos;{0}&apos; is invalid..
+ /// </summary>
+ internal static string Common_ValueNotValidForProperty {
+ get {
+ return ResourceManager.GetString("Common_ValueNotValidForProperty", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The view &apos;{0}&apos; or its master was not found. The following locations were searched:{1}.
+ /// </summary>
+ internal static string Common_ViewNotFound {
+ get {
+ return ResourceManager.GetString("Common_ViewNotFound", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to A public action method &apos;{0}&apos; was not found on controller &apos;{1}&apos;..
+ /// </summary>
+ internal static string Controller_UnknownAction {
+ get {
+ return ResourceManager.GetString("Controller_UnknownAction", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The model of type &apos;{0}&apos; could not be updated..
+ /// </summary>
+ internal static string Controller_UpdateModel_UpdateUnsuccessful {
+ get {
+ return ResourceManager.GetString("Controller_UpdateModel_UpdateUnsuccessful", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The model of type &apos;{0}&apos; is not valid..
+ /// </summary>
+ internal static string Controller_Validate_ValidationFailed {
+ get {
+ return ResourceManager.GetString("Controller_Validate_ValidationFailed", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to A single instance of controller &apos;{0}&apos; cannot be used to handle multiple requests. If a custom controller factory is in use, make sure that it creates a new instance of the controller for each request..
+ /// </summary>
+ internal static string ControllerBase_CannotHandleMultipleRequests {
+ get {
+ return ResourceManager.GetString("ControllerBase_CannotHandleMultipleRequests", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to An error occurred when trying to create the IControllerFactory &apos;{0}&apos;. Make sure that the controller factory has a public parameterless constructor..
+ /// </summary>
+ internal static string ControllerBuilder_ErrorCreatingControllerFactory {
+ get {
+ return ResourceManager.GetString("ControllerBuilder_ErrorCreatingControllerFactory", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The IControllerFactory &apos;{0}&apos; did not return a controller for the name &apos;{1}&apos;..
+ /// </summary>
+ internal static string ControllerBuilder_FactoryReturnedNull {
+ get {
+ return ResourceManager.GetString("ControllerBuilder_FactoryReturnedNull", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The controller factory type &apos;{0}&apos; must implement the IControllerFactory interface..
+ /// </summary>
+ internal static string ControllerBuilder_MissingIControllerFactory {
+ get {
+ return ResourceManager.GetString("ControllerBuilder_MissingIControllerFactory", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to {0} has a DisplayColumn attribute for {1}, but property {1} does not exist..
+ /// </summary>
+ internal static string DataAnnotationsModelMetadataProvider_UnknownProperty {
+ get {
+ return ResourceManager.GetString("DataAnnotationsModelMetadataProvider_UnknownProperty", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to {0} has a DisplayColumn attribute for {1}, but property {1} does not have a public getter..
+ /// </summary>
+ internal static string DataAnnotationsModelMetadataProvider_UnreadableProperty {
+ get {
+ return ResourceManager.GetString("DataAnnotationsModelMetadataProvider_UnreadableProperty", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The type {0} must have a public constructor which accepts three parameters of types {1}, {2}, and {3}.
+ /// </summary>
+ internal static string DataAnnotationsModelValidatorProvider_ConstructorRequirements {
+ get {
+ return ResourceManager.GetString("DataAnnotationsModelValidatorProvider_ConstructorRequirements", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to Multiple types were found that match the controller named &apos;{0}&apos;. This can happen if the route that services this request does not specify namespaces to search for a controller that matches the request. If this is the case, register this route by calling an overload of the &apos;MapRoute&apos; method that takes a &apos;namespaces&apos; parameter.
+ ///
+ ///The request for &apos;{0}&apos; has found the following matching controllers:{1}.
+ /// </summary>
+ internal static string DefaultControllerFactory_ControllerNameAmbiguous_WithoutRouteUrl {
+ get {
+ return ResourceManager.GetString("DefaultControllerFactory_ControllerNameAmbiguous_WithoutRouteUrl", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to Multiple types were found that match the controller named &apos;{0}&apos;. This can happen if the route that services this request (&apos;{1}&apos;) does not specify namespaces to search for a controller that matches the request. If this is the case, register this route by calling an overload of the &apos;MapRoute&apos; method that takes a &apos;namespaces&apos; parameter.
+ ///
+ ///The request for &apos;{0}&apos; has found the following matching controllers:{2}.
+ /// </summary>
+ internal static string DefaultControllerFactory_ControllerNameAmbiguous_WithRouteUrl {
+ get {
+ return ResourceManager.GetString("DefaultControllerFactory_ControllerNameAmbiguous_WithRouteUrl", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to An error occurred when trying to create a controller of type &apos;{0}&apos;. Make sure that the controller has a parameterless public constructor..
+ /// </summary>
+ internal static string DefaultControllerFactory_ErrorCreatingController {
+ get {
+ return ResourceManager.GetString("DefaultControllerFactory_ErrorCreatingController", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The controller for path &apos;{0}&apos; was not found or does not implement IController..
+ /// </summary>
+ internal static string DefaultControllerFactory_NoControllerFound {
+ get {
+ return ResourceManager.GetString("DefaultControllerFactory_NoControllerFound", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The controller type &apos;{0}&apos; must implement IController..
+ /// </summary>
+ internal static string DefaultControllerFactory_TypeDoesNotSubclassControllerBase {
+ get {
+ return ResourceManager.GetString("DefaultControllerFactory_TypeDoesNotSubclassControllerBase", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The value &apos;{0}&apos; is not valid for {1}..
+ /// </summary>
+ internal static string DefaultModelBinder_ValueInvalid {
+ get {
+ return ResourceManager.GetString("DefaultModelBinder_ValueInvalid", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to A value is required..
+ /// </summary>
+ internal static string DefaultModelBinder_ValueRequired {
+ get {
+ return ResourceManager.GetString("DefaultModelBinder_ValueRequired", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The number of ticks for the TimeSpan value must be greater than or equal to 0..
+ /// </summary>
+ internal static string DefaultViewLocationCache_NegativeTimeSpan {
+ get {
+ return ResourceManager.GetString("DefaultViewLocationCache_NegativeTimeSpan", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The type &apos;{0}&apos; does not inherit from Exception..
+ /// </summary>
+ internal static string ExceptionViewAttribute_NonExceptionType {
+ get {
+ return ResourceManager.GetString("ExceptionViewAttribute_NonExceptionType", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The expression compiler was unable to evaluate the indexer expression &apos;{0}&apos; because it references the model parameter &apos;{1}&apos; which is unavailable..
+ /// </summary>
+ internal static string ExpressionHelper_InvalidIndexerExpression {
+ get {
+ return ResourceManager.GetString("ExpressionHelper_InvalidIndexerExpression", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to Order must be greater than or equal to -1..
+ /// </summary>
+ internal static string FilterAttribute_OrderOutOfRange {
+ get {
+ return ResourceManager.GetString("FilterAttribute_OrderOutOfRange", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The GET and POST HTTP methods are not supported..
+ /// </summary>
+ internal static string HtmlHelper_InvalidHttpMethod {
+ get {
+ return ResourceManager.GetString("HtmlHelper_InvalidHttpMethod", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The specified HttpVerbs value is not supported. The supported values are Delete, Head, and Put..
+ /// </summary>
+ internal static string HtmlHelper_InvalidHttpVerb {
+ get {
+ return ResourceManager.GetString("HtmlHelper_InvalidHttpVerb", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to There is no ViewData item of type &apos;{1}&apos; that has the key &apos;{0}&apos;..
+ /// </summary>
+ internal static string HtmlHelper_MissingSelectData {
+ get {
+ return ResourceManager.GetString("HtmlHelper_MissingSelectData", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The value must be greater than or equal to zero..
+ /// </summary>
+ internal static string HtmlHelper_TextAreaParameterOutOfRange {
+ get {
+ return ResourceManager.GetString("HtmlHelper_TextAreaParameterOutOfRange", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The ViewData item that has the key &apos;{0}&apos; is of type &apos;{1}&apos; but must be of type &apos;{2}&apos;..
+ /// </summary>
+ internal static string HtmlHelper_WrongSelectDataType {
+ get {
+ return ResourceManager.GetString("HtmlHelper_WrongSelectDataType", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to This request has been blocked because sensitive information could be disclosed to third party web sites when this is used in a GET request. To allow GET requests, set JsonRequestBehavior to AllowGet..
+ /// </summary>
+ internal static string JsonRequest_GetNotAllowed {
+ get {
+ return ResourceManager.GetString("JsonRequest_GetNotAllowed", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to An error occurred when trying to create the IModelBinder &apos;{0}&apos;. Make sure that the binder has a public parameterless constructor..
+ /// </summary>
+ internal static string ModelBinderAttribute_ErrorCreatingModelBinder {
+ get {
+ return ResourceManager.GetString("ModelBinderAttribute_ErrorCreatingModelBinder", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The type &apos;{0}&apos; does not implement the IModelBinder interface..
+ /// </summary>
+ internal static string ModelBinderAttribute_TypeNotIModelBinder {
+ get {
+ return ResourceManager.GetString("ModelBinderAttribute_TypeNotIModelBinder", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The type &apos;{0}&apos; contains multiple attributes that inherit from CustomModelBinderAttribute..
+ /// </summary>
+ internal static string ModelBinderDictionary_MultipleAttributes {
+ get {
+ return ResourceManager.GetString("ModelBinderDictionary_MultipleAttributes", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to This property setter is obsolete, because its value is derived from ModelMetadata.Model now..
+ /// </summary>
+ internal static string ModelMetadata_PropertyNotSettable {
+ get {
+ return ResourceManager.GetString("ModelMetadata_PropertyNotSettable", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The associated metadata type for type &apos;{0}&apos; contains the following unknown properties or fields: {1}. Please make sure that the names of these members match the names of the properties on the main type..
+ /// </summary>
+ internal static string PrivateAssociatedMetadataTypeTypeDescriptor_MetadataTypeContainsUnknownProperties {
+ get {
+ return ResourceManager.GetString("PrivateAssociatedMetadataTypeTypeDescriptor_MetadataTypeContainsUnknownProperties" +
+ "", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to Child actions are not allowed to perform redirect actions..
+ /// </summary>
+ internal static string RedirectAction_CannotRedirectInChildAction {
+ get {
+ return ResourceManager.GetString("RedirectAction_CannotRedirectInChildAction", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to Cannot create a descriptor for instance method &apos;{0}&apos; on type &apos;{1}&apos; because the type does not derive from ControllerBase..
+ /// </summary>
+ internal static string ReflectedActionDescriptor_CannotCallInstanceMethodOnNonControllerType {
+ get {
+ return ResourceManager.GetString("ReflectedActionDescriptor_CannotCallInstanceMethodOnNonControllerType", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to Cannot call action method &apos;{0}&apos; on controller &apos;{1}&apos; because the parameter &apos;{2}&apos; is passed by reference..
+ /// </summary>
+ internal static string ReflectedActionDescriptor_CannotCallMethodsWithOutOrRefParameters {
+ get {
+ return ResourceManager.GetString("ReflectedActionDescriptor_CannotCallMethodsWithOutOrRefParameters", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to Cannot call action method &apos;{0}&apos; on controller &apos;{1}&apos; because the action method is a generic method..
+ /// </summary>
+ internal static string ReflectedActionDescriptor_CannotCallOpenGenericMethods {
+ get {
+ return ResourceManager.GetString("ReflectedActionDescriptor_CannotCallOpenGenericMethods", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The parameters dictionary contains a null entry for parameter &apos;{0}&apos; of non-nullable type &apos;{1}&apos; for method &apos;{2}&apos; in &apos;{3}&apos;. An optional parameter must be a reference type, a nullable type, or be declared as an optional parameter..
+ /// </summary>
+ internal static string ReflectedActionDescriptor_ParameterCannotBeNull {
+ get {
+ return ResourceManager.GetString("ReflectedActionDescriptor_ParameterCannotBeNull", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The parameters dictionary does not contain an entry for parameter &apos;{0}&apos; of type &apos;{1}&apos; for method &apos;{2}&apos; in &apos;{3}&apos;. The dictionary must contain an entry for each parameter, including parameters that have null values..
+ /// </summary>
+ internal static string ReflectedActionDescriptor_ParameterNotInDictionary {
+ get {
+ return ResourceManager.GetString("ReflectedActionDescriptor_ParameterNotInDictionary", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The parameters dictionary contains an invalid entry for parameter &apos;{0}&apos; for method &apos;{1}&apos; in &apos;{2}&apos;. The dictionary contains a value of type &apos;{3}&apos;, but the parameter requires a value of type &apos;{4}&apos;..
+ /// </summary>
+ internal static string ReflectedActionDescriptor_ParameterValueHasWrongType {
+ get {
+ return ResourceManager.GetString("ReflectedActionDescriptor_ParameterValueHasWrongType", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The asynchronous action method &apos;{0}&apos; cannot be executed synchronously..
+ /// </summary>
+ internal static string ReflectedAsyncActionDescriptor_CannotExecuteSynchronously {
+ get {
+ return ResourceManager.GetString("ReflectedAsyncActionDescriptor_CannotExecuteSynchronously", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The parameter &apos;{0}&apos; on method &apos;{1}&apos; contains multiple attributes that inherit from CustomModelBinderAttribute..
+ /// </summary>
+ internal static string ReflectedParameterBindingInfo_MultipleConverterAttributes {
+ get {
+ return ResourceManager.GetString("ReflectedParameterBindingInfo_MultipleConverterAttributes", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The requested resource can only be accessed via SSL..
+ /// </summary>
+ internal static string RequireHttpsAttribute_MustUseSsl {
+ get {
+ return ResourceManager.GetString("RequireHttpsAttribute_MustUseSsl", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The SessionStateTempDataProvider class requires session state to be enabled..
+ /// </summary>
+ internal static string SessionStateTempDataProvider_SessionStateDisabled {
+ get {
+ return ResourceManager.GetString("SessionStateTempDataProvider_SessionStateDisabled", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to An operation that crossed a synchronization context failed. See the inner exception for more information..
+ /// </summary>
+ internal static string SynchronizationContextUtil_ExceptionThrown {
+ get {
+ return ResourceManager.GetString("SynchronizationContextUtil_ExceptionThrown", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to Unable to locate an appropriate template for type {0}..
+ /// </summary>
+ internal static string TemplateHelpers_NoTemplate {
+ get {
+ return ResourceManager.GetString("TemplateHelpers_NoTemplate", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to Templates can be used only with field access, property access, single-dimension array index, or single-parameter custom indexer expressions..
+ /// </summary>
+ internal static string TemplateHelpers_TemplateLimitations {
+ get {
+ return ResourceManager.GetString("TemplateHelpers_TemplateLimitations", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The Collection template was used with an object of type &apos;{0}&apos;, which does not implement System.IEnumerable..
+ /// </summary>
+ internal static string Templates_TypeMustImplementIEnumerable {
+ get {
+ return ResourceManager.GetString("Templates_TypeMustImplementIEnumerable", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to This file is automatically generated. Please do not modify the contents of this file..
+ /// </summary>
+ internal static string TypeCache_DoNotModify {
+ get {
+ return ResourceManager.GetString("TypeCache_DoNotModify", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The parameter conversion from type &apos;{0}&apos; to type &apos;{1}&apos; failed. See the inner exception for more information..
+ /// </summary>
+ internal static string ValueProviderResult_ConversionThrew {
+ get {
+ return ResourceManager.GetString("ValueProviderResult_ConversionThrew", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The parameter conversion from type &apos;{0}&apos; to type &apos;{1}&apos; failed because no type converter can convert between these types..
+ /// </summary>
+ internal static string ValueProviderResult_NoConverterExists {
+ get {
+ return ResourceManager.GetString("ValueProviderResult_NoConverterExists", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The model item passed into the dictionary is null, but this dictionary requires a non-null model item of type &apos;{0}&apos;..
+ /// </summary>
+ internal static string ViewDataDictionary_ModelCannotBeNull {
+ get {
+ return ResourceManager.GetString("ViewDataDictionary_ModelCannotBeNull", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The model item passed into the dictionary is of type &apos;{0}&apos;, but this dictionary requires a model item of type &apos;{1}&apos;..
+ /// </summary>
+ internal static string ViewDataDictionary_WrongTModelType {
+ get {
+ return ResourceManager.GetString("ViewDataDictionary_WrongTModelType", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to A ViewMasterPage can be used only with content pages that derive from ViewPage or ViewPage&lt;TViewItem&gt;..
+ /// </summary>
+ internal static string ViewMasterPage_RequiresViewPage {
+ get {
+ return ResourceManager.GetString("ViewMasterPage_RequiresViewPage", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to Execution of the child request failed. Please examine the InnerException for more information..
+ /// </summary>
+ internal static string ViewPageHttpHandlerWrapper_ExceptionOccurred {
+ get {
+ return ResourceManager.GetString("ViewPageHttpHandlerWrapper_ExceptionOccurred", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The ViewUserControl &apos;{0}&apos; cannot find an IViewDataContainer object. The ViewUserControl must be inside a ViewPage, a ViewMasterPage, or another ViewUserControl..
+ /// </summary>
+ internal static string ViewUserControl_RequiresViewDataProvider {
+ get {
+ return ResourceManager.GetString("ViewUserControl_RequiresViewDataProvider", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to A ViewUserControl can be used only in pages that derive from ViewPage or ViewPage&lt;TViewItem&gt;..
+ /// </summary>
+ internal static string ViewUserControl_RequiresViewPage {
+ get {
+ return ResourceManager.GetString("ViewUserControl_RequiresViewPage", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to A master name cannot be specified when the view is a ViewUserControl..
+ /// </summary>
+ internal static string WebFormViewEngine_UserControlCannotHaveMaster {
+ get {
+ return ResourceManager.GetString("WebFormViewEngine_UserControlCannotHaveMaster", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The view found at &apos;{0}&apos; was not created..
+ /// </summary>
+ internal static string WebFormViewEngine_ViewCouldNotBeCreated {
+ get {
+ return ResourceManager.GetString("WebFormViewEngine_ViewCouldNotBeCreated", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The view at &apos;{0}&apos; must derive from ViewPage, ViewPage&lt;TViewData&gt;, ViewUserControl, or ViewUserControl&lt;TViewData&gt;..
+ /// </summary>
+ internal static string WebFormViewEngine_WrongViewBase {
+ get {
+ return ResourceManager.GetString("WebFormViewEngine_WrongViewBase", resourceCulture);
+ }
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/Resources/MvcResources.resx b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Resources/MvcResources.resx
new file mode 100644
index 00000000000..6e29c4dc8e1
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/Resources/MvcResources.resx
@@ -0,0 +1,367 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+ <!--
+ Microsoft ResX Schema
+
+ Version 2.0
+
+ The primary goals of this format is to allow a simple XML format
+ that is mostly human readable. The generation and parsing of the
+ various data types are done through the TypeConverter classes
+ associated with the data types.
+
+ Example:
+
+ ... ado.net/XML headers & schema ...
+ <resheader name="resmimetype">text/microsoft-resx</resheader>
+ <resheader name="version">2.0</resheader>
+ <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+ <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+ <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+ <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+ <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+ <value>[base64 mime encoded serialized .NET Framework object]</value>
+ </data>
+ <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+ <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+ <comment>This is a comment</comment>
+ </data>
+
+ There are any number of "resheader" rows that contain simple
+ name/value pairs.
+
+ Each data row contains a name, and value. The row also contains a
+ type or mimetype. Type corresponds to a .NET class that support
+ text/value conversion through the TypeConverter architecture.
+ Classes that don't support this are serialized and stored with the
+ mimetype set.
+
+ The mimetype is used for serialized objects, and tells the
+ ResXResourceReader how to depersist the object. This is currently not
+ extensible. For a given mimetype the value must be set accordingly:
+
+ Note - application/x-microsoft.net.object.binary.base64 is the format
+ that the ResXResourceWriter will generate, however the reader can
+ read any of the formats listed below.
+
+ mimetype: application/x-microsoft.net.object.binary.base64
+ value : The object must be serialized with
+ : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+ : and then encoded with base64 encoding.
+
+ mimetype: application/x-microsoft.net.object.soap.base64
+ value : The object must be serialized with
+ : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+ : and then encoded with base64 encoding.
+
+ mimetype: application/x-microsoft.net.object.bytearray.base64
+ value : The object must be serialized into a byte array
+ : using a System.ComponentModel.TypeConverter
+ : and then encoded with base64 encoding.
+ -->
+ <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+ <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+ <xsd:element name="root" msdata:IsDataSet="true">
+ <xsd:complexType>
+ <xsd:choice maxOccurs="unbounded">
+ <xsd:element name="metadata">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" />
+ </xsd:sequence>
+ <xsd:attribute name="name" use="required" type="xsd:string" />
+ <xsd:attribute name="type" type="xsd:string" />
+ <xsd:attribute name="mimetype" type="xsd:string" />
+ <xsd:attribute ref="xml:space" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="assembly">
+ <xsd:complexType>
+ <xsd:attribute name="alias" type="xsd:string" />
+ <xsd:attribute name="name" type="xsd:string" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="data">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+ <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+ <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+ <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+ <xsd:attribute ref="xml:space" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="resheader">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required" />
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:choice>
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:schema>
+ <resheader name="resmimetype">
+ <value>text/microsoft-resx</value>
+ </resheader>
+ <resheader name="version">
+ <value>2.0</value>
+ </resheader>
+ <resheader name="reader">
+ <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+ <resheader name="writer">
+ <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+ <data name="ActionMethodSelector_AmbiguousMatch" xml:space="preserve">
+ <value>The current request for action '{0}' on controller type '{1}' is ambiguous between the following action methods:{2}</value>
+ </data>
+ <data name="Common_NoRouteMatched" xml:space="preserve">
+ <value>No route in the route table matches the supplied values.</value>
+ </data>
+ <data name="Common_NullOrEmpty" xml:space="preserve">
+ <value>Value cannot be null or empty.</value>
+ </data>
+ <data name="Common_PartialViewNotFound" xml:space="preserve">
+ <value>The partial view '{0}' was not found. The following locations were searched:{1}</value>
+ </data>
+ <data name="Common_PropertyCannotBeNullOrEmpty" xml:space="preserve">
+ <value>The property '{0}' cannot be null or empty.</value>
+ </data>
+ <data name="Common_ViewNotFound" xml:space="preserve">
+ <value>The view '{0}' or its master was not found. The following locations were searched:{1}</value>
+ </data>
+ <data name="ControllerBuilder_ErrorCreatingControllerFactory" xml:space="preserve">
+ <value>An error occurred when trying to create the IControllerFactory '{0}'. Make sure that the controller factory has a public parameterless constructor.</value>
+ </data>
+ <data name="ControllerBuilder_FactoryReturnedNull" xml:space="preserve">
+ <value>The IControllerFactory '{0}' did not return a controller for the name '{1}'.</value>
+ </data>
+ <data name="ControllerBuilder_MissingIControllerFactory" xml:space="preserve">
+ <value>The controller factory type '{0}' must implement the IControllerFactory interface.</value>
+ </data>
+ <data name="Controller_UnknownAction" xml:space="preserve">
+ <value>A public action method '{0}' was not found on controller '{1}'.</value>
+ </data>
+ <data name="DefaultControllerFactory_ErrorCreatingController" xml:space="preserve">
+ <value>An error occurred when trying to create a controller of type '{0}'. Make sure that the controller has a parameterless public constructor.</value>
+ </data>
+ <data name="DefaultControllerFactory_NoControllerFound" xml:space="preserve">
+ <value>The controller for path '{0}' was not found or does not implement IController.</value>
+ </data>
+ <data name="DefaultControllerFactory_TypeDoesNotSubclassControllerBase" xml:space="preserve">
+ <value>The controller type '{0}' must implement IController.</value>
+ </data>
+ <data name="ValueProviderResult_ConversionThrew" xml:space="preserve">
+ <value>The parameter conversion from type '{0}' to type '{1}' failed. See the inner exception for more information.</value>
+ </data>
+ <data name="ValueProviderResult_NoConverterExists" xml:space="preserve">
+ <value>The parameter conversion from type '{0}' to type '{1}' failed because no type converter can convert between these types.</value>
+ </data>
+ <data name="ExceptionViewAttribute_NonExceptionType" xml:space="preserve">
+ <value>The type '{0}' does not inherit from Exception.</value>
+ </data>
+ <data name="FilterAttribute_OrderOutOfRange" xml:space="preserve">
+ <value>Order must be greater than or equal to -1.</value>
+ </data>
+ <data name="HtmlHelper_MissingSelectData" xml:space="preserve">
+ <value>There is no ViewData item of type '{1}' that has the key '{0}'.</value>
+ </data>
+ <data name="HtmlHelper_TextAreaParameterOutOfRange" xml:space="preserve">
+ <value>The value must be greater than or equal to zero.</value>
+ </data>
+ <data name="HtmlHelper_WrongSelectDataType" xml:space="preserve">
+ <value>The ViewData item that has the key '{0}' is of type '{1}' but must be of type '{2}'.</value>
+ </data>
+ <data name="ModelBinderAttribute_ErrorCreatingModelBinder" xml:space="preserve">
+ <value>An error occurred when trying to create the IModelBinder '{0}'. Make sure that the binder has a public parameterless constructor.</value>
+ </data>
+ <data name="ModelBinderAttribute_TypeNotIModelBinder" xml:space="preserve">
+ <value>The type '{0}' does not implement the IModelBinder interface.</value>
+ </data>
+ <data name="ModelBinderDictionary_MultipleAttributes" xml:space="preserve">
+ <value>The type '{0}' contains multiple attributes that inherit from CustomModelBinderAttribute.</value>
+ </data>
+ <data name="SessionStateTempDataProvider_SessionStateDisabled" xml:space="preserve">
+ <value>The SessionStateTempDataProvider class requires session state to be enabled.</value>
+ </data>
+ <data name="ViewDataDictionary_WrongTModelType" xml:space="preserve">
+ <value>The model item passed into the dictionary is of type '{0}', but this dictionary requires a model item of type '{1}'.</value>
+ </data>
+ <data name="ViewMasterPage_RequiresViewPage" xml:space="preserve">
+ <value>A ViewMasterPage can be used only with content pages that derive from ViewPage or ViewPage&lt;TViewItem&gt;.</value>
+ </data>
+ <data name="ViewUserControl_RequiresViewDataProvider" xml:space="preserve">
+ <value>The ViewUserControl '{0}' cannot find an IViewDataContainer object. The ViewUserControl must be inside a ViewPage, a ViewMasterPage, or another ViewUserControl.</value>
+ </data>
+ <data name="ViewUserControl_RequiresViewPage" xml:space="preserve">
+ <value>A ViewUserControl can be used only in pages that derive from ViewPage or ViewPage&lt;TViewItem&gt;.</value>
+ </data>
+ <data name="WebFormViewEngine_UserControlCannotHaveMaster" xml:space="preserve">
+ <value>A master name cannot be specified when the view is a ViewUserControl.</value>
+ </data>
+ <data name="WebFormViewEngine_ViewCouldNotBeCreated" xml:space="preserve">
+ <value>The view found at '{0}' was not created.</value>
+ </data>
+ <data name="WebFormViewEngine_WrongViewBase" xml:space="preserve">
+ <value>The view at '{0}' must derive from ViewPage, ViewPage&lt;TViewData&gt;, ViewUserControl, or ViewUserControl&lt;TViewData&gt;.</value>
+ </data>
+ <data name="Common_ValueNotValidForProperty" xml:space="preserve">
+ <value>The value '{0}' is invalid.</value>
+ </data>
+ <data name="ActionMethodSelector_AmbiguousMatchType" xml:space="preserve">
+ <value>{0} on type {1}</value>
+ </data>
+ <data name="Controller_UpdateModel_UpdateUnsuccessful" xml:space="preserve">
+ <value>The model of type '{0}' could not be updated.</value>
+ </data>
+ <data name="DefaultModelBinder_ValueRequired" xml:space="preserve">
+ <value>A value is required.</value>
+ </data>
+ <data name="ReflectedActionDescriptor_ParameterCannotBeNull" xml:space="preserve">
+ <value>The parameters dictionary contains a null entry for parameter '{0}' of non-nullable type '{1}' for method '{2}' in '{3}'. An optional parameter must be a reference type, a nullable type, or be declared as an optional parameter.</value>
+ </data>
+ <data name="ReflectedActionDescriptor_ParameterNotInDictionary" xml:space="preserve">
+ <value>The parameters dictionary does not contain an entry for parameter '{0}' of type '{1}' for method '{2}' in '{3}'. The dictionary must contain an entry for each parameter, including parameters that have null values.</value>
+ </data>
+ <data name="ReflectedActionDescriptor_ParameterValueHasWrongType" xml:space="preserve">
+ <value>The parameters dictionary contains an invalid entry for parameter '{0}' for method '{1}' in '{2}'. The dictionary contains a value of type '{3}', but the parameter requires a value of type '{4}'.</value>
+ </data>
+ <data name="ReflectedParameterBindingInfo_MultipleConverterAttributes" xml:space="preserve">
+ <value>The parameter '{0}' on method '{1}' contains multiple attributes that inherit from CustomModelBinderAttribute.</value>
+ </data>
+ <data name="ReflectedActionDescriptor_CannotCallInstanceMethodOnNonControllerType" xml:space="preserve">
+ <value>Cannot create a descriptor for instance method '{0}' on type '{1}' because the type does not derive from ControllerBase.</value>
+ </data>
+ <data name="ReflectedActionDescriptor_CannotCallMethodsWithOutOrRefParameters" xml:space="preserve">
+ <value>Cannot call action method '{0}' on controller '{1}' because the parameter '{2}' is passed by reference.</value>
+ </data>
+ <data name="ReflectedActionDescriptor_CannotCallOpenGenericMethods" xml:space="preserve">
+ <value>Cannot call action method '{0}' on controller '{1}' because the action method is a generic method.</value>
+ </data>
+ <data name="DefaultViewLocationCache_NegativeTimeSpan" xml:space="preserve">
+ <value>The number of ticks for the TimeSpan value must be greater than or equal to 0.</value>
+ </data>
+ <data name="AntiForgeryToken_ValidationFailed" xml:space="preserve">
+ <value>A required anti-forgery token was not supplied or was invalid.</value>
+ </data>
+ <data name="DefaultModelBinder_ValueInvalid" xml:space="preserve">
+ <value>The value '{0}' is not valid for {1}.</value>
+ </data>
+ <data name="TemplateHelpers_TemplateLimitations" xml:space="preserve">
+ <value>Templates can be used only with field access, property access, single-dimension array index, or single-parameter custom indexer expressions.</value>
+ </data>
+ <data name="Common_TriState_False" xml:space="preserve">
+ <value>False</value>
+ </data>
+ <data name="Common_TriState_NotSet" xml:space="preserve">
+ <value>Not Set</value>
+ </data>
+ <data name="Common_TriState_True" xml:space="preserve">
+ <value>True</value>
+ </data>
+ <data name="ControllerBase_CannotHandleMultipleRequests" xml:space="preserve">
+ <value>A single instance of controller '{0}' cannot be used to handle multiple requests. If a custom controller factory is in use, make sure that it creates a new instance of the controller for each request.</value>
+ </data>
+ <data name="Common_PropertyNotFound" xml:space="preserve">
+ <value>The property {0}.{1} could not be found.</value>
+ </data>
+ <data name="DataAnnotationsModelMetadataProvider_UnknownProperty" xml:space="preserve">
+ <value>{0} has a DisplayColumn attribute for {1}, but property {1} does not exist.</value>
+ </data>
+ <data name="DataAnnotationsModelMetadataProvider_UnreadableProperty" xml:space="preserve">
+ <value>{0} has a DisplayColumn attribute for {1}, but property {1} does not have a public getter.</value>
+ </data>
+ <data name="TemplateHelpers_NoTemplate" xml:space="preserve">
+ <value>Unable to locate an appropriate template for type {0}.</value>
+ </data>
+ <data name="RequireHttpsAttribute_MustUseSsl" xml:space="preserve">
+ <value>The requested resource can only be accessed via SSL.</value>
+ </data>
+ <data name="HtmlHelper_InvalidHttpVerb" xml:space="preserve">
+ <value>The specified HttpVerbs value is not supported. The supported values are Delete, Head, and Put.</value>
+ </data>
+ <data name="HtmlHelper_InvalidHttpMethod" xml:space="preserve">
+ <value>The GET and POST HTTP methods are not supported.</value>
+ </data>
+ <data name="JsonRequest_GetNotAllowed" xml:space="preserve">
+ <value>This request has been blocked because sensitive information could be disclosed to third party web sites when this is used in a GET request. To allow GET requests, set JsonRequestBehavior to AllowGet.</value>
+ </data>
+ <data name="ModelMetadata_PropertyNotSettable" xml:space="preserve">
+ <value>This property setter is obsolete, because its value is derived from ModelMetadata.Model now.</value>
+ </data>
+ <data name="ViewDataDictionary_ModelCannotBeNull" xml:space="preserve">
+ <value>The model item passed into the dictionary is null, but this dictionary requires a non-null model item of type '{0}'.</value>
+ </data>
+ <data name="Common_TypeMustDriveFromType" xml:space="preserve">
+ <value>The type {0} must derive from {1}</value>
+ </data>
+ <data name="DataAnnotationsModelValidatorProvider_ConstructorRequirements" xml:space="preserve">
+ <value>The type {0} must have a public constructor which accepts three parameters of types {1}, {2}, and {3}</value>
+ </data>
+ <data name="ViewPageHttpHandlerWrapper_ExceptionOccurred" xml:space="preserve">
+ <value>Execution of the child request failed. Please examine the InnerException for more information.</value>
+ </data>
+ <data name="RedirectAction_CannotRedirectInChildAction" xml:space="preserve">
+ <value>Child actions are not allowed to perform redirect actions.</value>
+ </data>
+ <data name="AsyncCommon_AsyncResultAlreadyConsumed" xml:space="preserve">
+ <value>The provided IAsyncResult has already been consumed.</value>
+ </data>
+ <data name="AsyncCommon_InvalidAsyncResult" xml:space="preserve">
+ <value>The provided IAsyncResult is not valid for this method.</value>
+ </data>
+ <data name="SynchronizationContextUtil_ExceptionThrown" xml:space="preserve">
+ <value>An operation that crossed a synchronization context failed. See the inner exception for more information.</value>
+ </data>
+ <data name="ReflectedAsyncActionDescriptor_CannotExecuteSynchronously" xml:space="preserve">
+ <value>The asynchronous action method '{0}' cannot be executed synchronously.</value>
+ </data>
+ <data name="AsyncCommon_ControllerMustImplementIAsyncManagerContainer" xml:space="preserve">
+ <value>The controller of type '{0}' must subclass AsyncController or implement the IAsyncManagerContainer interface.</value>
+ </data>
+ <data name="AsyncCommon_InvalidTimeout" xml:space="preserve">
+ <value>The timeout value must be non-negative or Timeout.Infinite.</value>
+ </data>
+ <data name="AsyncActionMethodSelector_AmbiguousMethodMatch" xml:space="preserve">
+ <value>Lookup for method '{0}' on controller type '{1}' failed because of an ambiguity between the following methods:{2}</value>
+ </data>
+ <data name="AsyncActionMethodSelector_CouldNotFindMethod" xml:space="preserve">
+ <value>Could not locate a method named '{0}' on controller type {1}.</value>
+ </data>
+ <data name="ChildActionOnlyAttribute_MustBeInChildRequest" xml:space="preserve">
+ <value>The action '{0}' is accessible only by a child request.</value>
+ </data>
+ <data name="Templates_TypeMustImplementIEnumerable" xml:space="preserve">
+ <value>The Collection template was used with an object of type '{0}', which does not implement System.IEnumerable.</value>
+ </data>
+ <data name="TypeCache_DoNotModify" xml:space="preserve">
+ <value>This file is automatically generated. Please do not modify the contents of this file.</value>
+ </data>
+ <data name="PrivateAssociatedMetadataTypeTypeDescriptor_MetadataTypeContainsUnknownProperties" xml:space="preserve">
+ <value>The associated metadata type for type '{0}' contains the following unknown properties or fields: {1}. Please make sure that the names of these members match the names of the properties on the main type.</value>
+ </data>
+ <data name="ClientDataTypeModelValidatorProvider_FieldMustBeNumeric" xml:space="preserve">
+ <value>The field {0} must be a number.</value>
+ </data>
+ <data name="ExpressionHelper_InvalidIndexerExpression" xml:space="preserve">
+ <value>The expression compiler was unable to evaluate the indexer expression '{0}' because it references the model parameter '{1}' which is unavailable.</value>
+ </data>
+ <data name="Controller_Validate_ValidationFailed" xml:space="preserve">
+ <value>The model of type '{0}' is not valid.</value>
+ </data>
+ <data name="DefaultControllerFactory_ControllerNameAmbiguous_WithoutRouteUrl" xml:space="preserve">
+ <value>Multiple types were found that match the controller named '{0}'. This can happen if the route that services this request does not specify namespaces to search for a controller that matches the request. If this is the case, register this route by calling an overload of the 'MapRoute' method that takes a 'namespaces' parameter.
+
+The request for '{0}' has found the following matching controllers:{1}</value>
+ </data>
+ <data name="DefaultControllerFactory_ControllerNameAmbiguous_WithRouteUrl" xml:space="preserve">
+ <value>Multiple types were found that match the controller named '{0}'. This can happen if the route that services this request ('{1}') does not specify namespaces to search for a controller that matches the request. If this is the case, register this route by calling an overload of the 'MapRoute' method that takes a 'namespaces' parameter.
+
+The request for '{0}' has found the following matching controllers:{2}</value>
+ </data>
+</root> \ No newline at end of file
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ResultExecutedContext.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ResultExecutedContext.cs
new file mode 100644
index 00000000000..aec1a1ffe92
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ResultExecutedContext.cs
@@ -0,0 +1,57 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Diagnostics.CodeAnalysis;
+
+ public class ResultExecutedContext : ControllerContext {
+
+ // parameterless constructor used for mocking
+ public ResultExecutedContext() {
+ }
+
+ [SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors",
+ Justification = "The virtual property setters are only to support mocking frameworks, in which case this constructor shouldn't be called anyway.")]
+ public ResultExecutedContext(ControllerContext controllerContext, ActionResult result, bool canceled, Exception exception)
+ : base(controllerContext) {
+ if (result == null) {
+ throw new ArgumentNullException("result");
+ }
+
+ Result = result;
+ Canceled = canceled;
+ Exception = exception;
+ }
+
+ public virtual bool Canceled {
+ get;
+ set;
+ }
+
+ public virtual Exception Exception {
+ get;
+ set;
+ }
+
+ public bool ExceptionHandled {
+ get;
+ set;
+ }
+
+ public virtual ActionResult Result {
+ get;
+ set;
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ResultExecutingContext.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ResultExecutingContext.cs
new file mode 100644
index 00000000000..893bfd071d6
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ResultExecutingContext.cs
@@ -0,0 +1,45 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Diagnostics.CodeAnalysis;
+
+ public class ResultExecutingContext : ControllerContext {
+
+ // parameterless constructor used for mocking
+ public ResultExecutingContext() {
+ }
+
+ [SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors",
+ Justification = "The virtual property setters are only to support mocking frameworks, in which case this constructor shouldn't be called anyway.")]
+ public ResultExecutingContext(ControllerContext controllerContext, ActionResult result)
+ : base(controllerContext) {
+ if (result == null) {
+ throw new ArgumentNullException("result");
+ }
+
+ Result = result;
+ }
+
+ public bool Cancel {
+ get;
+ set;
+ }
+
+ public virtual ActionResult Result {
+ get;
+ set;
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/RouteCollectionExtensions.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/RouteCollectionExtensions.cs
new file mode 100644
index 00000000000..a42b6f6768f
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/RouteCollectionExtensions.cs
@@ -0,0 +1,181 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Web.Routing;
+
+ public static class RouteCollectionExtensions {
+
+ // This method returns a new RouteCollection containing only routes that matched a particular area.
+ // The Boolean out parameter is just a flag specifying whether any registered routes were area-aware.
+ private static RouteCollection FilterRouteCollectionByArea(RouteCollection routes, string areaName, out bool usingAreas) {
+ if (areaName == null) {
+ areaName = String.Empty;
+ }
+
+ usingAreas = false;
+ RouteCollection filteredRoutes = new RouteCollection();
+
+ using (routes.GetReadLock()) {
+ foreach (RouteBase route in routes) {
+ string thisAreaName = AreaHelpers.GetAreaName(route) ?? String.Empty;
+ usingAreas |= (thisAreaName.Length > 0);
+ if (String.Equals(thisAreaName, areaName, StringComparison.OrdinalIgnoreCase)) {
+ filteredRoutes.Add(route);
+ }
+ }
+ }
+
+ // if areas are not in use, the filtered route collection might be incorrect
+ return (usingAreas) ? filteredRoutes : routes;
+ }
+
+ public static VirtualPathData GetVirtualPathForArea(this RouteCollection routes, RequestContext requestContext, RouteValueDictionary values) {
+ return GetVirtualPathForArea(routes, requestContext, null /* name */, values);
+ }
+
+ public static VirtualPathData GetVirtualPathForArea(this RouteCollection routes, RequestContext requestContext, string name, RouteValueDictionary values) {
+ bool usingAreas; // don't care about this value
+ return GetVirtualPathForArea(routes, requestContext, name, values, out usingAreas);
+ }
+
+ internal static VirtualPathData GetVirtualPathForArea(this RouteCollection routes, RequestContext requestContext, string name, RouteValueDictionary values, out bool usingAreas) {
+ if (routes == null) {
+ throw new ArgumentNullException("routes");
+ }
+
+ if (!String.IsNullOrEmpty(name)) {
+ // the route name is a stronger qualifier than the area name, so just pipe it through
+ usingAreas = false;
+ return routes.GetVirtualPath(requestContext, name, values);
+ }
+
+ string targetArea = null;
+ if (values != null) {
+ object targetAreaRawValue;
+ if (values.TryGetValue("area", out targetAreaRawValue)) {
+ targetArea = targetAreaRawValue as string;
+ }
+ else {
+ // set target area to current area
+ if (requestContext != null) {
+ targetArea = AreaHelpers.GetAreaName(requestContext.RouteData);
+ }
+ }
+ }
+
+ // need to apply a correction to the RVD if areas are in use
+ RouteValueDictionary correctedValues = values;
+ RouteCollection filteredRoutes = FilterRouteCollectionByArea(routes, targetArea, out usingAreas);
+ if (usingAreas) {
+ correctedValues = new RouteValueDictionary(values);
+ correctedValues.Remove("area");
+ }
+
+ VirtualPathData vpd = filteredRoutes.GetVirtualPath(requestContext, correctedValues);
+ return vpd;
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "1#",
+ Justification = "This is not a regular URL as it may contain special routing characters.")]
+ public static void IgnoreRoute(this RouteCollection routes, string url) {
+ IgnoreRoute(routes, url, null /* constraints */);
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "1#",
+ Justification = "This is not a regular URL as it may contain special routing characters.")]
+ public static void IgnoreRoute(this RouteCollection routes, string url, object constraints) {
+ if (routes == null) {
+ throw new ArgumentNullException("routes");
+ }
+ if (url == null) {
+ throw new ArgumentNullException("url");
+ }
+
+ IgnoreRouteInternal route = new IgnoreRouteInternal(url) {
+ Constraints = new RouteValueDictionary(constraints)
+ };
+
+ routes.Add(route);
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "2#",
+ Justification = "This is not a regular URL as it may contain special routing characters.")]
+ public static Route MapRoute(this RouteCollection routes, string name, string url) {
+ return MapRoute(routes, name, url, null /* defaults */, (object)null /* constraints */);
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "2#",
+ Justification = "This is not a regular URL as it may contain special routing characters.")]
+ public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults) {
+ return MapRoute(routes, name, url, defaults, (object)null /* constraints */);
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "2#",
+ Justification = "This is not a regular URL as it may contain special routing characters.")]
+ public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults, object constraints) {
+ return MapRoute(routes, name, url, defaults, constraints, null /* namespaces */);
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "2#",
+ Justification = "This is not a regular URL as it may contain special routing characters.")]
+ public static Route MapRoute(this RouteCollection routes, string name, string url, string[] namespaces) {
+ return MapRoute(routes, name, url, null /* defaults */, null /* constraints */, namespaces);
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "2#",
+ Justification = "This is not a regular URL as it may contain special routing characters.")]
+ public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults, string[] namespaces) {
+ return MapRoute(routes, name, url, defaults, null /* constraints */, namespaces);
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "2#",
+ Justification = "This is not a regular URL as it may contain special routing characters.")]
+ public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults, object constraints, string[] namespaces) {
+ if (routes == null) {
+ throw new ArgumentNullException("routes");
+ }
+ if (url == null) {
+ throw new ArgumentNullException("url");
+ }
+
+ Route route = new Route(url, new MvcRouteHandler()) {
+ Defaults = new RouteValueDictionary(defaults),
+ Constraints = new RouteValueDictionary(constraints),
+ DataTokens = new RouteValueDictionary()
+ };
+
+ if ((namespaces != null) && (namespaces.Length > 0)) {
+ route.DataTokens["Namespaces"] = namespaces;
+ }
+
+ routes.Add(name, route);
+
+ return route;
+ }
+
+ private sealed class IgnoreRouteInternal : Route {
+ public IgnoreRouteInternal(string url)
+ : base(url, new StopRoutingHandler()) {
+ }
+
+ public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary routeValues) {
+ // Never match during route generation. This avoids the scenario where an IgnoreRoute with
+ // fairly relaxed constraints ends up eagerly matching all generated URLs.
+ return null;
+ }
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/RouteDataValueProvider.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/RouteDataValueProvider.cs
new file mode 100644
index 00000000000..5ae1ee7c324
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/RouteDataValueProvider.cs
@@ -0,0 +1,26 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Globalization;
+
+ public sealed class RouteDataValueProvider : DictionaryValueProvider<object> {
+
+ // RouteData should use the invariant culture since it's part of the URL, and the URL should be
+ // interpreted in a uniform fashion regardless of the origin of a particular request.
+ public RouteDataValueProvider(ControllerContext controllerContext)
+ : base(controllerContext.RouteData.Values, CultureInfo.InvariantCulture) {
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/RouteDataValueProviderFactory.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/RouteDataValueProviderFactory.cs
new file mode 100644
index 00000000000..4efadbfc4d5
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/RouteDataValueProviderFactory.cs
@@ -0,0 +1,27 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+
+ public sealed class RouteDataValueProviderFactory : ValueProviderFactory {
+
+ public override IValueProvider GetValueProvider(ControllerContext controllerContext) {
+ if (controllerContext == null) {
+ throw new ArgumentNullException("controllerContext");
+ }
+
+ return new RouteDataValueProvider(controllerContext);
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/RouteValuesHelpers.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/RouteValuesHelpers.cs
new file mode 100644
index 00000000000..d227cb44b55
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/RouteValuesHelpers.cs
@@ -0,0 +1,61 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System.Collections.Generic;
+ using System.Web.Routing;
+
+ internal static class RouteValuesHelpers {
+ public static RouteValueDictionary GetRouteValues(RouteValueDictionary routeValues) {
+ return (routeValues != null) ? new RouteValueDictionary(routeValues) : new RouteValueDictionary();
+ }
+
+ public static RouteValueDictionary MergeRouteValues(string actionName, string controllerName, RouteValueDictionary implicitRouteValues, RouteValueDictionary routeValues, bool includeImplicitMvcValues) {
+ // Create a new dictionary containing implicit and auto-generated values
+ RouteValueDictionary mergedRouteValues = new RouteValueDictionary();
+
+ if (includeImplicitMvcValues) {
+ // We only include MVC-specific values like 'controller' and 'action' if we are generating an action link.
+ // If we are generating a route link [as to MapRoute("Foo", "any/url", new { controller = ... })], including
+ // the current controller name will cause the route match to fail if the current controller is not the same
+ // as the destination controller.
+
+ object implicitValue;
+ if (implicitRouteValues != null && implicitRouteValues.TryGetValue("action", out implicitValue)) {
+ mergedRouteValues["action"] = implicitValue;
+ }
+
+ if (implicitRouteValues != null && implicitRouteValues.TryGetValue("controller", out implicitValue)) {
+ mergedRouteValues["controller"] = implicitValue;
+ }
+ }
+
+ // Merge values from the user's dictionary/object
+ if (routeValues != null) {
+ foreach (KeyValuePair<string, object> routeElement in GetRouteValues(routeValues)) {
+ mergedRouteValues[routeElement.Key] = routeElement.Value;
+ }
+ }
+
+ // Merge explicit parameters when not null
+ if (actionName != null) {
+ mergedRouteValues["action"] = actionName;
+ }
+
+ if (controllerName != null) {
+ mergedRouteValues["controller"] = controllerName;
+ }
+
+ return mergedRouteValues;
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/SelectList.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/SelectList.cs
new file mode 100644
index 00000000000..9c8562431b7
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/SelectList.cs
@@ -0,0 +1,46 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System.Collections;
+ using System.Diagnostics.CodeAnalysis;
+
+ [SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")]
+ public class SelectList : MultiSelectList {
+
+ public SelectList(IEnumerable items)
+ : this(items, null /* selectedValue */) {
+ }
+
+ public SelectList(IEnumerable items, object selectedValue)
+ : this(items, null /* dataValuefield */, null /* dataTextField */, selectedValue) {
+ }
+
+ public SelectList(IEnumerable items, string dataValueField, string dataTextField)
+ : this(items, dataValueField, dataTextField, null /* selectedValue */) {
+ }
+
+ public SelectList(IEnumerable items, string dataValueField, string dataTextField, object selectedValue)
+ : base(items, dataValueField, dataTextField, ToEnumerable(selectedValue)) {
+ SelectedValue = selectedValue;
+ }
+
+ public object SelectedValue {
+ get;
+ private set;
+ }
+
+ private static IEnumerable ToEnumerable(object selectedValue) {
+ return (selectedValue != null) ? new object[] { selectedValue } : null;
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/SelectListItem.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/SelectListItem.cs
new file mode 100644
index 00000000000..962bf361dc1
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/SelectListItem.cs
@@ -0,0 +1,32 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+
+ public class SelectListItem {
+
+ public bool Selected {
+ get;
+ set;
+ }
+
+ public string Text {
+ get;
+ set;
+ }
+
+ public string Value {
+ get;
+ set;
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/SessionStateTempDataProvider.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/SessionStateTempDataProvider.cs
new file mode 100644
index 00000000000..1da67bd83ff
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/SessionStateTempDataProvider.cs
@@ -0,0 +1,65 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Collections.Generic;
+ using System.Web.Mvc.Resources;
+
+ public class SessionStateTempDataProvider : ITempDataProvider {
+ internal const string TempDataSessionStateKey = "__ControllerTempData";
+
+ public virtual IDictionary<string, object> LoadTempData(ControllerContext controllerContext) {
+ HttpSessionStateBase session = controllerContext.HttpContext.Session;
+
+ if (session != null) {
+ Dictionary<string, object> tempDataDictionary = session[TempDataSessionStateKey] as Dictionary<string, object>;
+
+ if (tempDataDictionary != null) {
+ // If we got it from Session, remove it so that no other request gets it
+ session.Remove(TempDataSessionStateKey);
+ return tempDataDictionary;
+ }
+ }
+
+ return new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
+ }
+
+ public virtual void SaveTempData(ControllerContext controllerContext, IDictionary<string, object> values) {
+ if (controllerContext == null) {
+ throw new ArgumentNullException("controllerContext");
+ }
+
+ HttpSessionStateBase session = controllerContext.HttpContext.Session;
+ bool isDirty = (values != null && values.Count > 0);
+
+ if (session == null) {
+ if (isDirty) {
+ throw new InvalidOperationException(MvcResources.SessionStateTempDataProvider_SessionStateDisabled);
+ }
+ }
+ else {
+ if (isDirty) {
+ session[TempDataSessionStateKey] = values;
+ }
+ else {
+ // Since the default implementation of Remove() (from SessionStateItemCollection) dirties the
+ // collection, we shouldn't call it unless we really do need to remove the existing key.
+ if (session[TempDataSessionStateKey] != null) {
+ session.Remove(TempDataSessionStateKey);
+ }
+ }
+ }
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/StringLengthAttributeAdapter.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/StringLengthAttributeAdapter.cs
new file mode 100644
index 00000000000..6e96be4dd99
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/StringLengthAttributeAdapter.cs
@@ -0,0 +1,26 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System.Collections.Generic;
+ using System.ComponentModel.DataAnnotations;
+
+ public class StringLengthAttributeAdapter : DataAnnotationsModelValidator<StringLengthAttribute> {
+ public StringLengthAttributeAdapter(ModelMetadata metadata, ControllerContext context, StringLengthAttribute attribute)
+ : base(metadata, context, attribute) {
+ }
+
+ public override IEnumerable<ModelClientValidationRule> GetClientValidationRules() {
+ return new[] { new ModelClientValidationStringLengthRule(ErrorMessage, 0, Attribute.MaximumLength) };
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/TagBuilder.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/TagBuilder.cs
new file mode 100644
index 00000000000..d0932043c8f
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/TagBuilder.cs
@@ -0,0 +1,214 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Collections.Generic;
+ using System.Globalization;
+ using System.Text;
+ using System.Web;
+ using System.Web.Mvc.Resources;
+
+ public class TagBuilder {
+ private string _idAttributeDotReplacement;
+
+ private const string _attributeFormat = @" {0}=""{1}""";
+ private const string _elementFormatEndTag = "</{0}>";
+ private const string _elementFormatNormal = "<{0}{1}>{2}</{0}>";
+ private const string _elementFormatSelfClosing = "<{0}{1} />";
+ private const string _elementFormatStartTag = "<{0}{1}>";
+
+ private string _innerHtml;
+
+ public TagBuilder(string tagName) {
+ if (String.IsNullOrEmpty(tagName)) {
+ throw new ArgumentException(MvcResources.Common_NullOrEmpty, "tagName");
+ }
+
+ TagName = tagName;
+ Attributes = new SortedDictionary<string, string>(StringComparer.Ordinal);
+ }
+
+ public IDictionary<string, string> Attributes {
+ get;
+ private set;
+ }
+
+ public string IdAttributeDotReplacement {
+ get {
+ if (String.IsNullOrEmpty(_idAttributeDotReplacement)) {
+ _idAttributeDotReplacement = HtmlHelper.IdAttributeDotReplacement;
+ }
+ return _idAttributeDotReplacement;
+ }
+ set {
+ _idAttributeDotReplacement = value;
+ }
+ }
+
+ public string InnerHtml {
+ get {
+ return _innerHtml ?? String.Empty;
+ }
+ set {
+ _innerHtml = value;
+ }
+ }
+
+ public string TagName {
+ get;
+ private set;
+ }
+
+ public void AddCssClass(string value) {
+ string currentValue;
+
+ if (Attributes.TryGetValue("class", out currentValue)) {
+ Attributes["class"] = value + " " + currentValue;
+ }
+ else {
+ Attributes["class"] = value;
+ }
+ }
+
+ internal static string CreateSanitizedId(string originalId, string dotReplacement) {
+ if (String.IsNullOrEmpty(originalId)) {
+ return null;
+ }
+
+ char firstChar = originalId[0];
+ if (!Html401IdUtil.IsLetter(firstChar)) {
+ // the first character must be a letter
+ return null;
+ }
+
+ StringBuilder sb = new StringBuilder(originalId.Length);
+ sb.Append(firstChar);
+
+ for (int i = 1; i < originalId.Length; i++) {
+ char thisChar = originalId[i];
+ if (Html401IdUtil.IsValidIdCharacter(thisChar)) {
+ sb.Append(thisChar);
+ }
+ else {
+ sb.Append(dotReplacement);
+ }
+ }
+
+ return sb.ToString();
+ }
+
+ public void GenerateId(string name) {
+ if (!Attributes.ContainsKey("id")) {
+ string sanitizedId = CreateSanitizedId(name, IdAttributeDotReplacement);
+ if (!String.IsNullOrEmpty(sanitizedId)) {
+ Attributes["id"] = sanitizedId;
+ }
+ }
+ }
+
+ private string GetAttributesString() {
+ StringBuilder sb = new StringBuilder();
+ foreach (var attribute in Attributes) {
+ string key = attribute.Key;
+ if (String.Equals(key, "id", StringComparison.Ordinal /* case-sensitive */) && String.IsNullOrEmpty(attribute.Value)) {
+ continue; // DevDiv Bugs #227595: don't output empty IDs
+ }
+ string value = HttpUtility.HtmlAttributeEncode(attribute.Value);
+ sb.AppendFormat(CultureInfo.InvariantCulture, _attributeFormat, key, value);
+ }
+ return sb.ToString();
+ }
+
+ public void MergeAttribute(string key, string value) {
+ MergeAttribute(key, value, false /* replaceExisting */);
+ }
+
+ public void MergeAttribute(string key, string value, bool replaceExisting) {
+ if (String.IsNullOrEmpty(key)) {
+ throw new ArgumentException(MvcResources.Common_NullOrEmpty, "key");
+ }
+
+ if (replaceExisting || !Attributes.ContainsKey(key)) {
+ Attributes[key] = value;
+ }
+ }
+
+ public void MergeAttributes<TKey, TValue>(IDictionary<TKey, TValue> attributes) {
+ MergeAttributes(attributes, false /* replaceExisting */);
+ }
+
+ public void MergeAttributes<TKey, TValue>(IDictionary<TKey, TValue> attributes, bool replaceExisting) {
+ if (attributes != null) {
+ foreach (var entry in attributes) {
+ string key = Convert.ToString(entry.Key, CultureInfo.InvariantCulture);
+ string value = Convert.ToString(entry.Value, CultureInfo.InvariantCulture);
+ MergeAttribute(key, value, replaceExisting);
+ }
+ }
+ }
+
+ public void SetInnerText(string innerText) {
+ InnerHtml = HttpUtility.HtmlEncode(innerText);
+ }
+
+ internal MvcHtmlString ToMvcHtmlString(TagRenderMode renderMode) {
+ return MvcHtmlString.Create(ToString(renderMode));
+ }
+
+ public override string ToString() {
+ return ToString(TagRenderMode.Normal);
+ }
+
+ public string ToString(TagRenderMode renderMode) {
+ switch (renderMode) {
+ case TagRenderMode.StartTag:
+ return String.Format(CultureInfo.InvariantCulture, _elementFormatStartTag, TagName, GetAttributesString());
+ case TagRenderMode.EndTag:
+ return String.Format(CultureInfo.InvariantCulture, _elementFormatEndTag, TagName);
+ case TagRenderMode.SelfClosing:
+ return String.Format(CultureInfo.InvariantCulture, _elementFormatSelfClosing, TagName, GetAttributesString());
+ default:
+ return String.Format(CultureInfo.InvariantCulture, _elementFormatNormal, TagName, GetAttributesString(), InnerHtml);
+ }
+ }
+
+ // Valid IDs are defined in http://www.w3.org/TR/html401/types.html#type-id
+ private static class Html401IdUtil {
+ private static bool IsAllowableSpecialCharacter(char c) {
+ switch (c) {
+ case '-':
+ case '_':
+ case ':':
+ // note that we're specifically excluding the '.' character
+ return true;
+
+ default:
+ return false;
+ }
+ }
+
+ private static bool IsDigit(char c) {
+ return ('0' <= c && c <= '9');
+ }
+
+ public static bool IsLetter(char c) {
+ return (('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z'));
+ }
+
+ public static bool IsValidIdCharacter(char c) {
+ return (IsLetter(c) || IsDigit(c) || IsAllowableSpecialCharacter(c));
+ }
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/TagRenderMode.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/TagRenderMode.cs
new file mode 100644
index 00000000000..8f889e7cab9
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/TagRenderMode.cs
@@ -0,0 +1,20 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ public enum TagRenderMode {
+ Normal,
+ StartTag,
+ EndTag,
+ SelfClosing
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/TempDataDictionary.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/TempDataDictionary.cs
new file mode 100644
index 00000000000..b2aa489367e
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/TempDataDictionary.cs
@@ -0,0 +1,219 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Collections;
+ using System.Collections.Generic;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Linq;
+ using System.Runtime.Serialization;
+
+ [Serializable]
+ public class TempDataDictionary : IDictionary<string, object>, ISerializable {
+ internal const string _tempDataSerializationKey = "__tempData";
+
+ private Dictionary<string, object> _data;
+ private HashSet<string> _initialKeys = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
+ private HashSet<string> _retainedKeys = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
+
+ public TempDataDictionary() {
+ _data = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
+ }
+
+ protected TempDataDictionary(SerializationInfo info, StreamingContext context) {
+ _data = info.GetValue(_tempDataSerializationKey, typeof(Dictionary<string, object>)) as Dictionary<string, object>;
+ }
+
+ public int Count {
+ get {
+ return _data.Count;
+ }
+ }
+
+ public ICollection<string> Keys {
+ get {
+ return _data.Keys;
+ }
+ }
+
+ public void Keep() {
+ _retainedKeys.Clear();
+ _retainedKeys.UnionWith(_data.Keys);
+ }
+
+ public void Keep(string key) {
+ _retainedKeys.Add(key);
+ }
+
+ public void Load(ControllerContext controllerContext, ITempDataProvider tempDataProvider) {
+ IDictionary<string, object> providerDictionary = tempDataProvider.LoadTempData(controllerContext);
+ _data = (providerDictionary != null) ? new Dictionary<string, object>(providerDictionary, StringComparer.OrdinalIgnoreCase) :
+ new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
+ _initialKeys = new HashSet<string>(_data.Keys, StringComparer.OrdinalIgnoreCase);
+ _retainedKeys.Clear();
+ }
+
+ public object Peek(string key) {
+ object value;
+ _data.TryGetValue(key, out value);
+ return value;
+ }
+
+ public void Save(ControllerContext controllerContext, ITempDataProvider tempDataProvider) {
+ string[] keysToKeep = _initialKeys.Union(_retainedKeys, StringComparer.OrdinalIgnoreCase).ToArray();
+ string[] keysToRemove = _data.Keys.Except(keysToKeep, StringComparer.OrdinalIgnoreCase).ToArray();
+ foreach (string key in keysToRemove) {
+ _data.Remove(key);
+ }
+ tempDataProvider.SaveTempData(controllerContext, _data);
+ }
+
+ public ICollection<object> Values {
+ get {
+ return _data.Values;
+ }
+ }
+
+ public object this[string key] {
+ get {
+ object value;
+ if (TryGetValue(key, out value)) {
+ _initialKeys.Remove(key);
+ return value;
+ }
+ return null;
+ }
+ set {
+ _data[key] = value;
+ _initialKeys.Add(key);
+ }
+ }
+
+ public void Add(string key, object value) {
+ _data.Add(key, value);
+ _initialKeys.Add(key);
+ }
+
+ public void Clear() {
+ _data.Clear();
+ _retainedKeys.Clear();
+ _initialKeys.Clear();
+ }
+
+ public bool ContainsKey(string key) {
+ return _data.ContainsKey(key);
+ }
+
+ public bool ContainsValue(object value) {
+ return _data.ContainsValue(value);
+ }
+
+ public IEnumerator<KeyValuePair<string, object>> GetEnumerator() {
+ return new TempDataDictionaryEnumerator(this);
+ }
+
+ protected virtual void GetObjectData(SerializationInfo info, StreamingContext context) {
+ info.AddValue(_tempDataSerializationKey, _data);
+ }
+
+ public bool Remove(string key) {
+ _retainedKeys.Remove(key);
+ _initialKeys.Remove(key);
+ return _data.Remove(key);
+ }
+
+ public bool TryGetValue(string key, out object value) {
+ _initialKeys.Remove(key);
+ return _data.TryGetValue(key, out value);
+ }
+
+ #region ICollection<KeyValuePair<string, object>> Implementation
+ bool ICollection<KeyValuePair<string, object>>.IsReadOnly {
+ get {
+ return ((ICollection<KeyValuePair<string, object>>)_data).IsReadOnly;
+ }
+ }
+
+ void ICollection<KeyValuePair<string, object>>.CopyTo(KeyValuePair<string, object>[] array, int index) {
+ ((ICollection<KeyValuePair<string, object>>)_data).CopyTo(array, index);
+ }
+
+ void ICollection<KeyValuePair<string, object>>.Add(KeyValuePair<string, object> keyValuePair) {
+ _initialKeys.Add(keyValuePair.Key);
+ ((ICollection<KeyValuePair<string, object>>)_data).Add(keyValuePair);
+ }
+
+ bool ICollection<KeyValuePair<string, object>>.Contains(KeyValuePair<string, object> keyValuePair) {
+ return ((ICollection<KeyValuePair<string, object>>)_data).Contains(keyValuePair);
+ }
+
+ bool ICollection<KeyValuePair<string, object>>.Remove(KeyValuePair<string, object> keyValuePair) {
+ _initialKeys.Remove(keyValuePair.Key);
+ return ((ICollection<KeyValuePair<string, object>>)_data).Remove(keyValuePair);
+ }
+ #endregion
+
+ #region IEnumerable Implementation
+ IEnumerator IEnumerable.GetEnumerator() {
+ return (IEnumerator)(new TempDataDictionaryEnumerator(this));
+ }
+ #endregion
+
+ #region ISerializable Members
+ [SuppressMessage("Microsoft.Security", "CA2123:OverrideLinkDemandsShouldBeIdenticalToBase")]
+ void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) {
+ GetObjectData(info, context);
+ }
+ #endregion
+
+ private sealed class TempDataDictionaryEnumerator : IEnumerator<KeyValuePair<string, object>> {
+ private IEnumerator<KeyValuePair<string, object>> _enumerator;
+ private TempDataDictionary _tempData;
+
+ public TempDataDictionaryEnumerator(TempDataDictionary tempData) {
+ _tempData = tempData;
+ _enumerator = _tempData._data.GetEnumerator();
+ }
+
+ public KeyValuePair<string, object> Current {
+ get {
+ KeyValuePair<string, object> kvp = _enumerator.Current;
+ _tempData._initialKeys.Remove(kvp.Key);
+ return kvp;
+ }
+ }
+
+ public bool MoveNext() {
+ return _enumerator.MoveNext();
+ }
+
+ public void Reset() {
+ _enumerator.Reset();
+ }
+
+ #region IEnumerator Implementation
+ object IEnumerator.Current {
+ get {
+ return Current;
+ }
+ }
+ #endregion
+
+ #region IDisposable Implementation
+ void IDisposable.Dispose() {
+ _enumerator.Dispose();
+ }
+ #endregion
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/TemplateInfo.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/TemplateInfo.cs
new file mode 100644
index 00000000000..5ca828048c0
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/TemplateInfo.cs
@@ -0,0 +1,71 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System.Collections.Generic;
+
+ public class TemplateInfo {
+ private string _htmlFieldPrefix;
+ private object _formattedModelValue;
+ private HashSet<object> _visitedObjects;
+
+ public object FormattedModelValue {
+ get {
+ return _formattedModelValue ?? String.Empty;
+ }
+ set {
+ _formattedModelValue = value;
+ }
+ }
+
+ public string HtmlFieldPrefix {
+ get {
+ return _htmlFieldPrefix ?? String.Empty;
+ }
+ set {
+ _htmlFieldPrefix = value;
+ }
+ }
+
+ public int TemplateDepth {
+ get {
+ return VisitedObjects.Count;
+ }
+ }
+
+ // DDB #224750 - Keep a collection of visited objects to prevent infinite recursion
+ internal HashSet<object> VisitedObjects {
+ get {
+ if (_visitedObjects == null) {
+ _visitedObjects = new HashSet<object>();
+ }
+ return _visitedObjects;
+ }
+ set {
+ _visitedObjects = value;
+ }
+ }
+
+ public string GetFullHtmlFieldId(string partialFieldName) {
+ return HtmlHelper.GenerateIdFromName(GetFullHtmlFieldName(partialFieldName));
+ }
+
+ public string GetFullHtmlFieldName(string partialFieldName) {
+ // This uses "combine and trim" because either or both of these values might be empty
+ return (HtmlFieldPrefix + "." + (partialFieldName ?? String.Empty)).Trim('.');
+ }
+
+ public bool Visited(ModelMetadata metadata) {
+ return VisitedObjects.Contains(metadata.Model ?? metadata.ModelType);
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/TryGetValueDelegate.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/TryGetValueDelegate.cs
new file mode 100644
index 00000000000..66a19666e97
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/TryGetValueDelegate.cs
@@ -0,0 +1,17 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+
+ internal delegate bool TryGetValueDelegate(object dictionary, string key, out object value);
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/TypeCacheSerializer.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/TypeCacheSerializer.cs
new file mode 100644
index 00000000000..bec4f0b8666
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/TypeCacheSerializer.cs
@@ -0,0 +1,128 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics.CodeAnalysis;
+ using System.IO;
+ using System.Linq;
+ using System.Reflection;
+ using System.Web.Mvc.Resources;
+ using System.Xml;
+
+ // Processes files with this format:
+ //
+ // <typeCache lastModified=... mvcVersionId=...>
+ // <assembly name=...>
+ // <module versionId=...>
+ // <type>...</type>
+ // </module>
+ // </assembly>
+ // </typeCache>
+ //
+ // This is used to store caches of files between AppDomain resets, leading to improved cold boot time
+ // and more efficient use of memory.
+
+ internal sealed class TypeCacheSerializer {
+
+ private static readonly Guid _mvcVersionId = typeof(TypeCacheSerializer).Module.ModuleVersionId;
+
+ // used for unit testing
+
+ private DateTime CurrentDate {
+ get {
+ return CurrentDateOverride ?? DateTime.Now;
+ }
+ }
+
+ internal DateTime? CurrentDateOverride {
+ get;
+ set;
+ }
+
+ [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic",
+ Justification = "This is an instance method for consistency with the SerializeTypes() method.")]
+ public List<Type> DeserializeTypes(TextReader input) {
+ XmlDocument doc = new XmlDocument();
+ doc.Load(input);
+ XmlElement rootElement = doc.DocumentElement;
+
+ Guid readMvcVersionId = new Guid(rootElement.Attributes["mvcVersionId"].Value);
+ if (readMvcVersionId != _mvcVersionId) {
+ // The cache is outdated because the cache file was produced by a different version
+ // of MVC.
+ return null;
+ }
+
+ List<Type> deserializedTypes = new List<Type>();
+ foreach (XmlNode assemblyNode in rootElement.ChildNodes) {
+ string assemblyName = assemblyNode.Attributes["name"].Value;
+ Assembly assembly = Assembly.Load(assemblyName);
+
+ foreach (XmlNode moduleNode in assemblyNode.ChildNodes) {
+ Guid moduleVersionId = new Guid(moduleNode.Attributes["versionId"].Value);
+
+ foreach (XmlNode typeNode in moduleNode.ChildNodes) {
+ string typeName = typeNode.InnerText;
+ Type type = assembly.GetType(typeName);
+ if (type == null || type.Module.ModuleVersionId != moduleVersionId) {
+ // The cache is outdated because we couldn't find a previously recorded
+ // type or the type's containing module was modified.
+ return null;
+ }
+ else {
+ deserializedTypes.Add(type);
+ }
+ }
+ }
+ }
+
+ return deserializedTypes;
+ }
+
+ public void SerializeTypes(IEnumerable<Type> types, TextWriter output) {
+ var groupedByAssembly = from type in types
+ group type by type.Module into groupedByModule
+ group groupedByModule by groupedByModule.Key.Assembly;
+
+ XmlDocument doc = new XmlDocument();
+ doc.AppendChild(doc.CreateComment(MvcResources.TypeCache_DoNotModify));
+
+ XmlElement typeCacheElement = doc.CreateElement("typeCache");
+ doc.AppendChild(typeCacheElement);
+ typeCacheElement.SetAttribute("lastModified", CurrentDate.ToString());
+ typeCacheElement.SetAttribute("mvcVersionId", _mvcVersionId.ToString());
+
+ foreach (var assemblyGroup in groupedByAssembly) {
+ XmlElement assemblyElement = doc.CreateElement("assembly");
+ typeCacheElement.AppendChild(assemblyElement);
+ assemblyElement.SetAttribute("name", assemblyGroup.Key.FullName);
+
+ foreach (var moduleGroup in assemblyGroup) {
+ XmlElement moduleElement = doc.CreateElement("module");
+ assemblyElement.AppendChild(moduleElement);
+ moduleElement.SetAttribute("versionId", moduleGroup.Key.ModuleVersionId.ToString());
+
+ foreach (Type type in moduleGroup) {
+ XmlElement typeElement = doc.CreateElement("type");
+ moduleElement.AppendChild(typeElement);
+ typeElement.AppendChild(doc.CreateTextNode(type.FullName));
+ }
+ }
+ }
+
+ doc.Save(output);
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/TypeCacheUtil.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/TypeCacheUtil.cs
new file mode 100644
index 00000000000..b3366d1b1f1
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/TypeCacheUtil.cs
@@ -0,0 +1,103 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Collections;
+ using System.Collections.Generic;
+ using System.Diagnostics.CodeAnalysis;
+ using System.IO;
+ using System.Linq;
+ using System.Reflection;
+
+ internal static class TypeCacheUtil {
+
+ private static IEnumerable<Type> FilterTypesInAssemblies(IBuildManager buildManager, Predicate<Type> predicate) {
+ // Go through all assemblies referenced by the application and search for types matching a predicate
+ IEnumerable<Type> typesSoFar = Type.EmptyTypes;
+
+ ICollection assemblies = buildManager.GetReferencedAssemblies();
+ foreach (Assembly assembly in assemblies) {
+ Type[] typesInAsm;
+ try {
+ typesInAsm = assembly.GetTypes();
+ }
+ catch (ReflectionTypeLoadException ex) {
+ typesInAsm = ex.Types;
+ }
+ typesSoFar = typesSoFar.Concat(typesInAsm);
+ }
+ return typesSoFar.Where(type => TypeIsPublicClass(type) && predicate(type));
+ }
+
+ public static List<Type> GetFilteredTypesFromAssemblies(string cacheName, Predicate<Type> predicate, IBuildManager buildManager) {
+ TypeCacheSerializer serializer = new TypeCacheSerializer();
+
+ // first, try reading from the cache on disk
+ List<Type> matchingTypes = ReadTypesFromCache(cacheName, predicate, buildManager, serializer);
+ if (matchingTypes != null) {
+ return matchingTypes;
+ }
+
+ // if reading from the cache failed, enumerate over every assembly looking for a matching type
+ matchingTypes = FilterTypesInAssemblies(buildManager, predicate).ToList();
+
+ // finally, save the cache back to disk
+ SaveTypesToCache(cacheName, matchingTypes, buildManager, serializer);
+
+ return matchingTypes;
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes",
+ Justification = "Cache failures are not fatal, and the code should continue executing normally.")]
+ internal static List<Type> ReadTypesFromCache(string cacheName, Predicate<Type> predicate, IBuildManager buildManager, TypeCacheSerializer serializer) {
+ try {
+ using (Stream stream = buildManager.ReadCachedFile(cacheName)) {
+ if (stream != null) {
+ using (StreamReader reader = new StreamReader(stream)) {
+ List<Type> deserializedTypes = serializer.DeserializeTypes(reader);
+ if (deserializedTypes != null && deserializedTypes.All(type => TypeIsPublicClass(type) && predicate(type))) {
+ // If all read types still match the predicate, success!
+ return deserializedTypes;
+ }
+ }
+ }
+ }
+ }
+ catch {
+ }
+
+ return null;
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes",
+ Justification = "Cache failures are not fatal, and the code should continue executing normally.")]
+ internal static void SaveTypesToCache(string cacheName, IList<Type> matchingTypes, IBuildManager buildManager, TypeCacheSerializer serializer) {
+ try {
+ using (Stream stream = buildManager.CreateCachedFile(cacheName)) {
+ if (stream != null) {
+ using (StreamWriter writer = new StreamWriter(stream)) {
+ serializer.SerializeTypes(matchingTypes, writer);
+ }
+ }
+ }
+ }
+ catch {
+ }
+ }
+
+ private static bool TypeIsPublicClass(Type type) {
+ return (type != null && type.IsPublic && type.IsClass && !type.IsAbstract);
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/TypeDescriptorHelper.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/TypeDescriptorHelper.cs
new file mode 100644
index 00000000000..48ccb8e85b5
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/TypeDescriptorHelper.cs
@@ -0,0 +1,282 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Collections.Generic;
+ using System.ComponentModel;
+ using System.ComponentModel.DataAnnotations;
+ using System.Globalization;
+ using System.Linq;
+ using System.Reflection;
+ using System.Web.Mvc.Resources;
+
+ // TODO: Remove this class in MVC 3
+ //
+ // We brought in a private copy of the AssociatedMetadataTypeTypeDescriptionProvider
+ // from .NET 4, because it provides several bug fixes and perf improvements. If we're
+ // running on .NET < 4, we'll use our private copy.
+
+ internal static class TypeDescriptorHelper {
+
+ private static Func<Type, ICustomTypeDescriptor> _typeDescriptorFactory = GetTypeDescriptorFactory();
+
+ private static Func<Type, ICustomTypeDescriptor> GetTypeDescriptorFactory() {
+ if (Environment.Version.Major < 4) {
+ return type => new _AssociatedMetadataTypeTypeDescriptionProvider(type).GetTypeDescriptor(type);
+ }
+
+ return type => new AssociatedMetadataTypeTypeDescriptionProvider(type).GetTypeDescriptor(type);
+ }
+
+ public static ICustomTypeDescriptor Get(Type type) {
+ return _typeDescriptorFactory(type);
+ }
+
+ // Private copies of the .NET 4 AssociatedMetadataType classes
+
+ private class _AssociatedMetadataTypeTypeDescriptionProvider : TypeDescriptionProvider {
+ public _AssociatedMetadataTypeTypeDescriptionProvider(Type type)
+ : base(TypeDescriptor.GetProvider(type)) {
+ }
+
+ public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance) {
+ ICustomTypeDescriptor baseDescriptor = base.GetTypeDescriptor(objectType, instance);
+ return new _AssociatedMetadataTypeTypeDescriptor(baseDescriptor, objectType);
+ }
+ }
+
+ private class _AssociatedMetadataTypeTypeDescriptor : CustomTypeDescriptor {
+ private Type AssociatedMetadataType {
+ get;
+ set;
+ }
+
+ public _AssociatedMetadataTypeTypeDescriptor(ICustomTypeDescriptor parent, Type type)
+ : base(parent) {
+ AssociatedMetadataType = TypeDescriptorCache.GetAssociatedMetadataType(type);
+ if (AssociatedMetadataType != null) {
+ TypeDescriptorCache.ValidateMetadataType(type, AssociatedMetadataType);
+ }
+ }
+
+ public override PropertyDescriptorCollection GetProperties(Attribute[] attributes) {
+ return GetPropertiesWithMetadata(base.GetProperties(attributes));
+ }
+
+ public override PropertyDescriptorCollection GetProperties() {
+ return GetPropertiesWithMetadata(base.GetProperties());
+ }
+
+ private PropertyDescriptorCollection GetPropertiesWithMetadata(PropertyDescriptorCollection originalCollection) {
+ if (AssociatedMetadataType == null) {
+ return originalCollection;
+ }
+
+ bool customDescriptorsCreated = false;
+ List<PropertyDescriptor> tempPropertyDescriptors = new List<PropertyDescriptor>();
+ foreach (PropertyDescriptor propDescriptor in originalCollection) {
+ Attribute[] newMetadata = TypeDescriptorCache.GetAssociatedMetadata(AssociatedMetadataType, propDescriptor.Name);
+ PropertyDescriptor descriptor = propDescriptor;
+ if (newMetadata.Length > 0) {
+ // Create a metadata descriptor that wraps the property descriptor
+ descriptor = new _MetadataPropertyDescriptorWrapper(propDescriptor, newMetadata);
+ customDescriptorsCreated = true;
+ }
+
+ tempPropertyDescriptors.Add(descriptor);
+ }
+
+ if (customDescriptorsCreated) {
+ return new PropertyDescriptorCollection(tempPropertyDescriptors.ToArray(), true);
+ }
+ return originalCollection;
+ }
+
+ public override AttributeCollection GetAttributes() {
+ // Since normal TD behavior is to return cached attribute instances on subsequent
+ // calls to GetAttributes, we must be sure below to use the TD APIs to get both
+ // the base and associated attributes
+ AttributeCollection attributes = base.GetAttributes();
+ if (AssociatedMetadataType != null) {
+ // Note that the use of TypeDescriptor.GetAttributes here opens up the possibility of
+ // infinite recursion, in the corner case of two Types referencing each other as
+ // metadata types (or a longer cycle)
+ Attribute[] newAttributes = TypeDescriptor.GetAttributes(AssociatedMetadataType).OfType<Attribute>().ToArray();
+ attributes = AttributeCollection.FromExisting(attributes, newAttributes);
+ }
+ return attributes;
+ }
+
+ private static class TypeDescriptorCache {
+ private static readonly Attribute[] emptyAttributes = new Attribute[0];
+
+ // Stores the associated metadata type for a type
+ private static readonly Dictionary<Type, Type> _metadataTypeCache = new Dictionary<Type, Type>();
+
+ // For a type and a property name stores the attributes for that property name.
+ private static readonly Dictionary<Tuple<Type, string>, Attribute[]> _typeMemberCache = new Dictionary<Tuple<Type, string>, Attribute[]>();
+
+ // Stores whether or not a type and associated metadata type has been checked for validity
+ private static readonly Dictionary<Tuple<Type, Type>, bool> _validatedMetadataTypeCache = new Dictionary<Tuple<Type, Type>, bool>();
+
+ public static void ValidateMetadataType(Type type, Type associatedType) {
+ Tuple<Type, Type> typeTuple = new Tuple<Type, Type>(type, associatedType);
+
+ lock (_validatedMetadataTypeCache) {
+ if (!_validatedMetadataTypeCache.ContainsKey(typeTuple)) {
+ CheckAssociatedMetadataType(type, associatedType);
+ _validatedMetadataTypeCache.Add(typeTuple, true);
+ }
+ }
+ }
+
+ public static Type GetAssociatedMetadataType(Type type) {
+ Type associatedMetadataType = null;
+ lock (_metadataTypeCache) {
+ if (_metadataTypeCache.TryGetValue(type, out associatedMetadataType)) {
+ return associatedMetadataType;
+ }
+ }
+
+ // Try association attribute
+ MetadataTypeAttribute attribute = (MetadataTypeAttribute)Attribute.GetCustomAttribute(type, typeof(MetadataTypeAttribute));
+ if (attribute != null) {
+ associatedMetadataType = attribute.MetadataClassType;
+ }
+
+ lock (_metadataTypeCache) {
+ _metadataTypeCache[type] = associatedMetadataType;
+ }
+
+ return associatedMetadataType;
+ }
+
+ private static void CheckAssociatedMetadataType(Type mainType, Type associatedMetadataType) {
+ // Only properties from main type
+ HashSet<string> mainTypeMemberNames = new HashSet<string>(mainType.GetProperties().Select(p => p.Name));
+
+ // Properties and fields from buddy type
+ var buddyFields = associatedMetadataType.GetFields().Select(f => f.Name);
+ var buddyProperties = associatedMetadataType.GetProperties().Select(p => p.Name);
+ HashSet<string> buddyTypeMembers = new HashSet<string>(buddyFields.Concat(buddyProperties), StringComparer.Ordinal);
+
+ // Buddy members should be a subset of the main type's members
+ if (!buddyTypeMembers.IsSubsetOf(mainTypeMemberNames)) {
+ // Reduce the buddy members to the set not contained in the main members
+ buddyTypeMembers.ExceptWith(mainTypeMemberNames);
+
+ throw new InvalidOperationException(String.Format(
+ CultureInfo.CurrentCulture,
+ MvcResources.PrivateAssociatedMetadataTypeTypeDescriptor_MetadataTypeContainsUnknownProperties,
+ mainType.FullName,
+ String.Join(", ", buddyTypeMembers.ToArray())));
+ }
+ }
+
+ public static Attribute[] GetAssociatedMetadata(Type type, string memberName) {
+ var memberTuple = new Tuple<Type, string>(type, memberName);
+ Attribute[] attributes;
+ lock (_typeMemberCache) {
+ if (_typeMemberCache.TryGetValue(memberTuple, out attributes)) {
+ return attributes;
+ }
+ }
+
+ // Allow fields and properties
+ MemberTypes allowedMemberTypes = MemberTypes.Property | MemberTypes.Field;
+ // Only public static/instance members
+ BindingFlags searchFlags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static;
+ // Try to find a matching member on type
+ MemberInfo matchingMember = type.GetMember(memberName, allowedMemberTypes, searchFlags).FirstOrDefault();
+ if (matchingMember != null) {
+ attributes = Attribute.GetCustomAttributes(matchingMember, true /* inherit */);
+ }
+ else {
+ attributes = emptyAttributes;
+ }
+
+ lock (_typeMemberCache) {
+ _typeMemberCache[memberTuple] = attributes;
+ }
+ return attributes;
+ }
+
+ private class Tuple<T1, T2> {
+ public T1 Item1 { get; set; }
+ public T2 Item2 { get; set; }
+
+ public Tuple(T1 item1, T2 item2) {
+ Item1 = item1;
+ Item2 = item2;
+ }
+
+ public override int GetHashCode() {
+ int h1 = Item1.GetHashCode();
+ int h2 = Item2.GetHashCode();
+ return ((h1 << 5) + h1) ^ h2;
+ }
+
+ public override bool Equals(object obj) {
+ var other = obj as Tuple<T1, T2>;
+ if (other != null) {
+ return other.Item1.Equals(Item1) && other.Item2.Equals(Item2);
+ }
+ return false;
+ }
+ }
+ }
+ }
+
+ private class _MetadataPropertyDescriptorWrapper : PropertyDescriptor {
+ private PropertyDescriptor _descriptor;
+ private bool _isReadOnly;
+
+ public _MetadataPropertyDescriptorWrapper(PropertyDescriptor descriptor, Attribute[] newAttributes)
+ : base(descriptor, newAttributes) {
+ _descriptor = descriptor;
+ var readOnlyAttribute = newAttributes.OfType<ReadOnlyAttribute>().FirstOrDefault();
+ _isReadOnly = (readOnlyAttribute != null ? readOnlyAttribute.IsReadOnly : false);
+ }
+
+ public override void AddValueChanged(object component, EventHandler handler) { _descriptor.AddValueChanged(component, handler); }
+
+ public override bool CanResetValue(object component) { return _descriptor.CanResetValue(component); }
+
+ public override Type ComponentType { get { return _descriptor.ComponentType; } }
+
+ public override object GetValue(object component) { return _descriptor.GetValue(component); }
+
+ public override bool IsReadOnly {
+ get {
+ // Dev10 Bug 594083
+ // It's not enough to call the wrapped _descriptor because it does not know anything about
+ // new attributes passed into the constructor of this class.
+ return _isReadOnly || _descriptor.IsReadOnly;
+ }
+ }
+
+ public override Type PropertyType { get { return _descriptor.PropertyType; } }
+
+ public override void RemoveValueChanged(object component, EventHandler handler) { _descriptor.RemoveValueChanged(component, handler); }
+
+ public override void ResetValue(object component) { _descriptor.ResetValue(component); }
+
+ public override void SetValue(object component, object value) { _descriptor.SetValue(component, value); }
+
+ public override bool ShouldSerializeValue(object component) { return _descriptor.ShouldSerializeValue(component); }
+
+ public override bool SupportsChangeEvents { get { return _descriptor.SupportsChangeEvents; } }
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/TypeHelpers.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/TypeHelpers.cs
new file mode 100644
index 00000000000..6a024bbc4e9
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/TypeHelpers.cs
@@ -0,0 +1,138 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Collections;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Reflection;
+ using System.Threading;
+
+ internal static class TypeHelpers {
+
+ private static readonly Dictionary<Type, TryGetValueDelegate> _tryGetValueDelegateCache = new Dictionary<Type, TryGetValueDelegate>();
+ private static readonly ReaderWriterLockSlim _tryGetValueDelegateCacheLock = new ReaderWriterLockSlim();
+
+ private static readonly MethodInfo _strongTryGetValueImplInfo = typeof(TypeHelpers).GetMethod("StrongTryGetValueImpl", BindingFlags.NonPublic | BindingFlags.Static);
+
+ public static readonly Assembly MsCorLibAssembly = typeof(string).Assembly;
+ public static readonly Assembly MvcAssembly = typeof(Controller).Assembly;
+ public static readonly Assembly SystemWebAssembly = typeof(HttpContext).Assembly;
+
+ // method is used primarily for lighting up new .NET Framework features even if MVC targets the previous version
+ // thisParameter is the 'this' parameter if target method is instance method, should be null for static method
+ public static TDelegate CreateDelegate<TDelegate>(Assembly assembly, string typeName, string methodName, object thisParameter) where TDelegate : class {
+ // ensure target type exists
+ Type targetType = assembly.GetType(typeName, false /* throwOnError */);
+ if (targetType == null) {
+ return null;
+ }
+
+ return CreateDelegate<TDelegate>(targetType, methodName, thisParameter);
+ }
+
+ public static TDelegate CreateDelegate<TDelegate>(Type targetType, string methodName, object thisParameter) where TDelegate : class {
+ // ensure target method exists
+ ParameterInfo[] delegateParameters = typeof(TDelegate).GetMethod("Invoke").GetParameters();
+ Type[] argumentTypes = Array.ConvertAll(delegateParameters, pInfo => pInfo.ParameterType);
+ MethodInfo targetMethod = targetType.GetMethod(methodName, argumentTypes);
+ if (targetMethod == null) {
+ return null;
+ }
+
+ TDelegate d = Delegate.CreateDelegate(typeof(TDelegate), thisParameter, targetMethod, false /* throwOnBindFailure */) as TDelegate;
+ return d;
+ }
+
+ public static TryGetValueDelegate CreateTryGetValueDelegate(Type targetType) {
+ TryGetValueDelegate result;
+
+ _tryGetValueDelegateCacheLock.EnterReadLock();
+ try {
+ if (_tryGetValueDelegateCache.TryGetValue(targetType, out result)) {
+ return result;
+ }
+ }
+ finally {
+ _tryGetValueDelegateCacheLock.ExitReadLock();
+ }
+
+ Type dictionaryType = ExtractGenericInterface(targetType, typeof(IDictionary<,>));
+
+ // just wrap a call to the underlying IDictionary<TKey, TValue>.TryGetValue() where string can be cast to TKey
+ if (dictionaryType != null) {
+ Type[] typeArguments = dictionaryType.GetGenericArguments();
+ Type keyType = typeArguments[0];
+ Type returnType = typeArguments[1];
+
+ if (keyType.IsAssignableFrom(typeof(string))) {
+ MethodInfo strongImplInfo = _strongTryGetValueImplInfo.MakeGenericMethod(keyType, returnType);
+ result = (TryGetValueDelegate)Delegate.CreateDelegate(typeof(TryGetValueDelegate), strongImplInfo);
+ }
+ }
+
+ // wrap a call to the underlying IDictionary.Item()
+ if (result == null && typeof(IDictionary).IsAssignableFrom(targetType)) {
+ result = TryGetValueFromNonGenericDictionary;
+ }
+
+ _tryGetValueDelegateCacheLock.EnterWriteLock();
+ try {
+ _tryGetValueDelegateCache[targetType] = result;
+ }
+ finally {
+ _tryGetValueDelegateCacheLock.ExitWriteLock();
+ }
+
+ return result;
+ }
+
+ public static Type ExtractGenericInterface(Type queryType, Type interfaceType) {
+ Func<Type, bool> matchesInterface = t => t.IsGenericType && t.GetGenericTypeDefinition() == interfaceType;
+ return (matchesInterface(queryType)) ? queryType : queryType.GetInterfaces().FirstOrDefault(matchesInterface);
+ }
+
+ public static object GetDefaultValue(Type type) {
+ return (TypeAllowsNullValue(type)) ? null : Activator.CreateInstance(type);
+ }
+
+ public static bool IsCompatibleObject<T>(object value) {
+ return (value is T || (value == null && TypeAllowsNullValue(typeof(T))));
+ }
+
+ public static bool IsNullableValueType(Type type) {
+ return Nullable.GetUnderlyingType(type) != null;
+ }
+
+ private static bool StrongTryGetValueImpl<TKey, TValue>(object dictionary, string key, out object value) {
+ IDictionary<TKey, TValue> strongDict = (IDictionary<TKey, TValue>)dictionary;
+
+ TValue strongValue;
+ bool retVal = strongDict.TryGetValue((TKey)(object)key, out strongValue);
+ value = strongValue;
+ return retVal;
+ }
+
+ private static bool TryGetValueFromNonGenericDictionary(object dictionary, string key, out object value) {
+ IDictionary weakDict = (IDictionary)dictionary;
+
+ bool containsKey = weakDict.Contains(key);
+ value = (containsKey) ? weakDict[key] : null;
+ return containsKey;
+ }
+
+ public static bool TypeAllowsNullValue(Type type) {
+ return (!type.IsValueType || IsNullableValueType(type));
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/UrlHelper.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/UrlHelper.cs
new file mode 100644
index 00000000000..6b310c89c24
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/UrlHelper.cs
@@ -0,0 +1,209 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Globalization;
+ using System.Web.Mvc.Resources;
+ using System.Web.Routing;
+
+ public class UrlHelper {
+ public UrlHelper(RequestContext requestContext)
+ : this(requestContext, RouteTable.Routes) {
+ }
+
+ public UrlHelper(RequestContext requestContext, RouteCollection routeCollection) {
+ if (requestContext == null) {
+ throw new ArgumentNullException("requestContext");
+ }
+ if (routeCollection == null) {
+ throw new ArgumentNullException("routeCollection");
+ }
+ RequestContext = requestContext;
+ RouteCollection = routeCollection;
+ }
+
+ public RequestContext RequestContext {
+ get;
+ private set;
+ }
+
+ public RouteCollection RouteCollection {
+ get;
+ private set;
+ }
+
+ public string Action(string actionName) {
+ return GenerateUrl(null /* routeName */, actionName, null, (RouteValueDictionary)null /* routeValues */);
+ }
+
+ public string Action(string actionName, object routeValues) {
+ return GenerateUrl(null /* routeName */, actionName, null /* controllerName */, new RouteValueDictionary(routeValues));
+ }
+
+ public string Action(string actionName, RouteValueDictionary routeValues) {
+ return GenerateUrl(null /* routeName */, actionName, null /* controllerName */, routeValues);
+ }
+
+ public string Action(string actionName, string controllerName) {
+ return GenerateUrl(null /* routeName */, actionName, controllerName, (RouteValueDictionary)null /* routeValues */);
+ }
+
+ public string Action(string actionName, string controllerName, object routeValues) {
+ return GenerateUrl(null /* routeName */, actionName, controllerName, new RouteValueDictionary(routeValues));
+ }
+
+ public string Action(string actionName, string controllerName, RouteValueDictionary routeValues) {
+ return GenerateUrl(null /* routeName */, actionName, controllerName, routeValues);
+ }
+
+ public string Action(string actionName, string controllerName, object routeValues, string protocol) {
+ return GenerateUrl(null /* routeName */, actionName, controllerName, protocol, null /* hostName */, null /* fragment */, new RouteValueDictionary(routeValues), RouteCollection, RequestContext, true /* includeImplicitMvcValues */);
+ }
+
+ public string Action(string actionName, string controllerName, RouteValueDictionary routeValues, string protocol, string hostName) {
+ return GenerateUrl(null /* routeName */, actionName, controllerName, protocol, hostName, null /* fragment */, routeValues, RouteCollection, RequestContext, true /* includeImplicitMvcValues */);
+ }
+
+ public string Content(string contentPath) {
+ return GenerateContentUrl(contentPath, RequestContext.HttpContext);
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1055:UriReturnValuesShouldNotBeStrings",
+ Justification = "As the return value will used only for rendering, string return value is more appropriate.")]
+ public static string GenerateContentUrl(string contentPath, HttpContextBase httpContext) {
+ if (String.IsNullOrEmpty(contentPath)) {
+ throw new ArgumentException(MvcResources.Common_NullOrEmpty, "contentPath");
+ }
+
+ if (httpContext == null) {
+ throw new ArgumentNullException("httpContext");
+ }
+
+ if (contentPath[0] == '~') {
+ return PathHelpers.GenerateClientUrl(httpContext, contentPath);
+ }
+ else {
+ return contentPath;
+ }
+ }
+
+ //REVIEW: Should we have an overload that takes Uri?
+ [SuppressMessage("Microsoft.Design", "CA1055:UriReturnValuesShouldNotBeStrings",
+ Justification = "As the return value will used only for rendering, string return value is more appropriate.")]
+ [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings",
+ Justification = "Needs to take same parameters as HttpUtility.UrlEncode()")]
+ [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic",
+ Justification = "For consistency, all helpers are instance methods.")]
+ public string Encode(string url) {
+ return HttpUtility.UrlEncode(url);
+ }
+
+ private string GenerateUrl(string routeName, string actionName, string controllerName, RouteValueDictionary routeValues) {
+ return GenerateUrl(routeName, actionName, controllerName, routeValues, RouteCollection, RequestContext, true /* includeImplicitMvcValues */);
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1055:UriReturnValuesShouldNotBeStrings",
+ Justification = "As the return value will used only for rendering, string return value is more appropriate.")]
+ public static string GenerateUrl(string routeName, string actionName, string controllerName, string protocol, string hostName, string fragment, RouteValueDictionary routeValues, RouteCollection routeCollection, RequestContext requestContext, bool includeImplicitMvcValues) {
+ string url = GenerateUrl(routeName, actionName, controllerName, routeValues, routeCollection, requestContext, includeImplicitMvcValues);
+
+ if (url != null) {
+ if (!String.IsNullOrEmpty(fragment)) {
+ url = url + "#" + fragment;
+ }
+
+ if (!String.IsNullOrEmpty(protocol) || !String.IsNullOrEmpty(hostName)) {
+ Uri requestUrl = requestContext.HttpContext.Request.Url;
+ protocol = (!String.IsNullOrEmpty(protocol)) ? protocol : Uri.UriSchemeHttp;
+ hostName = (!String.IsNullOrEmpty(hostName)) ? hostName : requestUrl.Host;
+
+ string port = String.Empty;
+ string requestProtocol = requestUrl.Scheme;
+
+ if (String.Equals(protocol, requestProtocol, StringComparison.OrdinalIgnoreCase)) {
+ port = requestUrl.IsDefaultPort ? String.Empty : (":" + Convert.ToString(requestUrl.Port, CultureInfo.InvariantCulture));
+ }
+
+ url = protocol + Uri.SchemeDelimiter + hostName + port + url;
+ }
+ }
+
+ return url;
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1055:UriReturnValuesShouldNotBeStrings",
+ Justification = "As the return value will used only for rendering, string return value is more appropriate.")]
+ public static string GenerateUrl(string routeName, string actionName, string controllerName, RouteValueDictionary routeValues, RouteCollection routeCollection, RequestContext requestContext, bool includeImplicitMvcValues) {
+ if (routeCollection == null) {
+ throw new ArgumentNullException("routeCollection");
+ }
+
+ if (requestContext == null) {
+ throw new ArgumentNullException("requestContext");
+ }
+
+ RouteValueDictionary mergedRouteValues = RouteValuesHelpers.MergeRouteValues(actionName, controllerName, requestContext.RouteData.Values, routeValues, includeImplicitMvcValues);
+
+ VirtualPathData vpd = routeCollection.GetVirtualPathForArea(requestContext, routeName, mergedRouteValues);
+ if (vpd == null) {
+ return null;
+ }
+
+ string modifiedUrl = PathHelpers.GenerateClientUrl(requestContext.HttpContext, vpd.VirtualPath);
+ return modifiedUrl;
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1055:UriReturnValuesShouldNotBeStrings",
+ Justification = "As the return value will used only for rendering, string return value is more appropriate.")]
+ public string RouteUrl(object routeValues) {
+ return RouteUrl(null /* routeName */, routeValues);
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1055:UriReturnValuesShouldNotBeStrings",
+ Justification = "As the return value will used only for rendering, string return value is more appropriate.")]
+ public string RouteUrl(RouteValueDictionary routeValues) {
+ return RouteUrl(null /* routeName */, routeValues);
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1055:UriReturnValuesShouldNotBeStrings",
+ Justification = "As the return value will used only for rendering, string return value is more appropriate.")]
+ public string RouteUrl(string routeName) {
+ return RouteUrl(routeName, (object)null /* routeValues */);
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1055:UriReturnValuesShouldNotBeStrings",
+ Justification = "As the return value will used only for rendering, string return value is more appropriate.")]
+ public string RouteUrl(string routeName, object routeValues) {
+ return RouteUrl(routeName, routeValues, null /* protocol */);
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1055:UriReturnValuesShouldNotBeStrings",
+ Justification = "As the return value will used only for rendering, string return value is more appropriate.")]
+ public string RouteUrl(string routeName, RouteValueDictionary routeValues) {
+ return RouteUrl(routeName, routeValues, null /* protocol */, null /* hostName */);
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1055:UriReturnValuesShouldNotBeStrings",
+ Justification = "As the return value will used only for rendering, string return value is more appropriate.")]
+ public string RouteUrl(string routeName, object routeValues, string protocol) {
+ return GenerateUrl(routeName, null /* actionName */, null /* controllerName */, protocol, null /* hostName */, null /* fragment */, new RouteValueDictionary(routeValues), RouteCollection, RequestContext, false /* includeImplicitMvcValues */);
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1055:UriReturnValuesShouldNotBeStrings",
+ Justification = "As the return value will used only for rendering, string return value is more appropriate.")]
+ public string RouteUrl(string routeName, RouteValueDictionary routeValues, string protocol, string hostName) {
+ return GenerateUrl(routeName, null /* actionName */, null /* controllerName */, protocol, hostName, null /* fragment */, routeValues, RouteCollection, RequestContext, false /* includeImplicitMvcValues */);
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/UrlParameter.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/UrlParameter.cs
new file mode 100644
index 00000000000..3d39c1eaa62
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/UrlParameter.cs
@@ -0,0 +1,27 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Diagnostics.CodeAnalysis;
+
+ public sealed class UrlParameter {
+
+ [SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes",
+ Justification = "This type is immutable.")]
+ public static readonly UrlParameter Optional = new UrlParameter();
+
+ // singleton constructor
+ private UrlParameter() { }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ValidateAntiForgeryTokenAttribute.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ValidateAntiForgeryTokenAttribute.cs
new file mode 100644
index 00000000000..0eedc812839
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ValidateAntiForgeryTokenAttribute.cs
@@ -0,0 +1,94 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Web;
+ using System.Web.Mvc.Resources;
+
+ [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
+ public sealed class ValidateAntiForgeryTokenAttribute : FilterAttribute, IAuthorizationFilter {
+
+ private string _salt;
+ private AntiForgeryDataSerializer _serializer;
+
+ public string Salt {
+ get {
+ return _salt ?? String.Empty;
+ }
+ set {
+ _salt = value;
+ }
+ }
+
+ internal AntiForgeryDataSerializer Serializer {
+ get {
+ if (_serializer == null) {
+ _serializer = new AntiForgeryDataSerializer();
+ }
+ return _serializer;
+ }
+ set {
+ _serializer = value;
+ }
+ }
+
+ private bool ValidateFormToken(AntiForgeryData token) {
+ return (String.Equals(Salt, token.Salt, StringComparison.Ordinal));
+ }
+
+ private static HttpAntiForgeryException CreateValidationException() {
+ return new HttpAntiForgeryException(MvcResources.AntiForgeryToken_ValidationFailed);
+ }
+
+ public void OnAuthorization(AuthorizationContext filterContext) {
+ if (filterContext == null) {
+ throw new ArgumentNullException("filterContext");
+ }
+
+ string fieldName = AntiForgeryData.GetAntiForgeryTokenName(null);
+ string cookieName = AntiForgeryData.GetAntiForgeryTokenName(filterContext.HttpContext.Request.ApplicationPath);
+
+ HttpCookie cookie = filterContext.HttpContext.Request.Cookies[cookieName];
+ if (cookie == null || String.IsNullOrEmpty(cookie.Value)) {
+ // error: cookie token is missing
+ throw CreateValidationException();
+ }
+ AntiForgeryData cookieToken = Serializer.Deserialize(cookie.Value);
+
+ string formValue = filterContext.HttpContext.Request.Form[fieldName];
+ if (String.IsNullOrEmpty(formValue)) {
+ // error: form token is missing
+ throw CreateValidationException();
+ }
+ AntiForgeryData formToken = Serializer.Deserialize(formValue);
+
+ if (!String.Equals(cookieToken.Value, formToken.Value, StringComparison.Ordinal)) {
+ // error: form token does not match cookie token
+ throw CreateValidationException();
+ }
+
+ string currentUsername = AntiForgeryData.GetUsername(filterContext.HttpContext.User);
+ if (!String.Equals(formToken.Username, currentUsername, StringComparison.OrdinalIgnoreCase)) {
+ // error: form token is not valid for this user
+ // (don't care about cookie token)
+ throw CreateValidationException();
+ }
+
+ if (!ValidateFormToken(formToken)) {
+ // error: custom validation failed
+ throw CreateValidationException();
+ }
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ValidateInputAttribute.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ValidateInputAttribute.cs
new file mode 100644
index 00000000000..7e07f99e604
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ValidateInputAttribute.cs
@@ -0,0 +1,40 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Diagnostics.CodeAnalysis;
+
+ [SuppressMessage("Microsoft.Performance", "CA1813:AvoidUnsealedAttributes",
+ Justification = "No compelling performance reason to seal this type.")]
+ [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
+ public class ValidateInputAttribute : FilterAttribute, IAuthorizationFilter {
+
+ public ValidateInputAttribute(bool enableValidation) {
+ EnableValidation = enableValidation;
+ }
+
+ public bool EnableValidation {
+ get;
+ private set;
+ }
+
+ public virtual void OnAuthorization(AuthorizationContext filterContext) {
+ if (filterContext == null) {
+ throw new ArgumentNullException("filterContext");
+ }
+
+ filterContext.Controller.ValidateRequest = EnableValidation;
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ValueProviderCollection.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ValueProviderCollection.cs
new file mode 100644
index 00000000000..46ec5aefcf0
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ValueProviderCollection.cs
@@ -0,0 +1,54 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Collections.Generic;
+ using System.Collections.ObjectModel;
+ using System.Linq;
+
+ public class ValueProviderCollection : Collection<IValueProvider>, IValueProvider {
+
+ public ValueProviderCollection() {
+ }
+
+ public ValueProviderCollection(IList<IValueProvider> list)
+ : base(list) {
+ }
+
+ public virtual bool ContainsPrefix(string prefix) {
+ return this.Any(vp => vp.ContainsPrefix(prefix));
+ }
+
+ public virtual ValueProviderResult GetValue(string key) {
+ return (from provider in this
+ let result = provider.GetValue(key)
+ where result != null
+ select result).FirstOrDefault();
+ }
+
+ protected override void InsertItem(int index, IValueProvider item) {
+ if (item == null) {
+ throw new ArgumentNullException("item");
+ }
+ base.InsertItem(index, item);
+ }
+
+ protected override void SetItem(int index, IValueProvider item) {
+ if (item == null) {
+ throw new ArgumentNullException("item");
+ }
+ base.SetItem(index, item);
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ValueProviderDictionary.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ValueProviderDictionary.cs
new file mode 100644
index 00000000000..a5ea7f0a9a5
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ValueProviderDictionary.cs
@@ -0,0 +1,208 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Collections;
+ using System.Collections.Generic;
+ using System.Collections.Specialized;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Globalization;
+ using System.Web.Routing;
+
+ [Obsolete("The recommended alternative is to use one of the specific ValueProvider types, such as FormValueProvider.")]
+ public class ValueProviderDictionary : IDictionary<string, ValueProviderResult>, IValueProvider {
+
+ private readonly Dictionary<string, ValueProviderResult> _dictionary = new Dictionary<string, ValueProviderResult>(StringComparer.OrdinalIgnoreCase);
+
+ public ValueProviderDictionary(ControllerContext controllerContext) {
+ ControllerContext = controllerContext;
+ if (controllerContext != null) {
+ PopulateDictionary();
+ }
+ }
+
+ public ControllerContext ControllerContext {
+ get;
+ private set;
+ }
+
+ public int Count {
+ get {
+ return ((ICollection<KeyValuePair<string, ValueProviderResult>>)Dictionary).Count;
+ }
+ }
+
+ internal Dictionary<string, ValueProviderResult> Dictionary {
+ get {
+ return _dictionary;
+ }
+ }
+
+ public bool IsReadOnly {
+ get {
+ return ((ICollection<KeyValuePair<string, ValueProviderResult>>)Dictionary).IsReadOnly;
+ }
+ }
+
+ public ICollection<string> Keys {
+ get {
+ return Dictionary.Keys;
+ }
+ }
+
+ public ValueProviderResult this[string key] {
+ get {
+ ValueProviderResult result;
+ Dictionary.TryGetValue(key, out result);
+ return result;
+ }
+ set {
+ Dictionary[key] = value;
+ }
+ }
+
+ public ICollection<ValueProviderResult> Values {
+ get {
+ return Dictionary.Values;
+ }
+ }
+
+ public void Add(KeyValuePair<string, ValueProviderResult> item) {
+ ((ICollection<KeyValuePair<string, ValueProviderResult>>)Dictionary).Add(item);
+ }
+
+ public void Add(string key, object value) {
+ string attemptedValue = Convert.ToString(value, CultureInfo.InvariantCulture);
+ ValueProviderResult vpResult = new ValueProviderResult(value, attemptedValue, CultureInfo.InvariantCulture);
+ Add(key, vpResult);
+ }
+
+ public void Add(string key, ValueProviderResult value) {
+ Dictionary.Add(key, value);
+ }
+
+ private void AddToDictionaryIfNotPresent(string key, ValueProviderResult result) {
+ if (!String.IsNullOrEmpty(key)) {
+ if (!Dictionary.ContainsKey(key)) {
+ Dictionary.Add(key, result);
+ }
+ }
+ }
+
+ public void Clear() {
+ ((ICollection<KeyValuePair<string, ValueProviderResult>>)Dictionary).Clear();
+ }
+
+ public bool Contains(KeyValuePair<string, ValueProviderResult> item) {
+ return ((ICollection<KeyValuePair<string, ValueProviderResult>>)Dictionary).Contains(item);
+ }
+
+ public bool ContainsKey(string key) {
+ return Dictionary.ContainsKey(key);
+ }
+
+ public void CopyTo(KeyValuePair<string, ValueProviderResult>[] array, int arrayIndex) {
+ ((ICollection<KeyValuePair<string, ValueProviderResult>>)Dictionary).CopyTo(array, arrayIndex);
+ }
+
+ public IEnumerator<KeyValuePair<string, ValueProviderResult>> GetEnumerator() {
+ return ((IEnumerable<KeyValuePair<string, ValueProviderResult>>)Dictionary).GetEnumerator();
+ }
+
+ private void PopulateDictionary() {
+ CultureInfo currentCulture = CultureInfo.CurrentCulture;
+ CultureInfo invariantCulture = CultureInfo.InvariantCulture;
+
+ // We use this order of precedence to populate the dictionary:
+ // 1. Request form submission (should be culture-aware)
+ // 2. Values from the RouteData (could be from the typed-in URL or from the route's default values)
+ // 3. URI query string
+
+ NameValueCollection form = ControllerContext.HttpContext.Request.Form;
+ if (form != null) {
+ string[] keys = form.AllKeys;
+ foreach (string key in keys) {
+ string[] rawValue = form.GetValues(key);
+ string attemptedValue = form[key];
+ ValueProviderResult result = new ValueProviderResult(rawValue, attemptedValue, currentCulture);
+ AddToDictionaryIfNotPresent(key, result);
+ }
+ }
+
+ RouteValueDictionary routeValues = ControllerContext.RouteData.Values;
+ if (routeValues != null) {
+ foreach (var kvp in routeValues) {
+ string key = kvp.Key;
+ object rawValue = kvp.Value;
+ string attemptedValue = Convert.ToString(rawValue, invariantCulture);
+ ValueProviderResult result = new ValueProviderResult(rawValue, attemptedValue, invariantCulture);
+ AddToDictionaryIfNotPresent(key, result);
+ }
+ }
+
+ NameValueCollection queryString = ControllerContext.HttpContext.Request.QueryString;
+ if (queryString != null) {
+ string[] keys = queryString.AllKeys;
+ foreach (string key in keys) {
+ string[] rawValue = queryString.GetValues(key);
+ string attemptedValue = queryString[key];
+ ValueProviderResult result = new ValueProviderResult(rawValue, attemptedValue, invariantCulture);
+ AddToDictionaryIfNotPresent(key, result);
+ }
+ }
+ }
+
+ public bool Remove(KeyValuePair<string, ValueProviderResult> item) {
+ return ((ICollection<KeyValuePair<string, ValueProviderResult>>)Dictionary).Remove(item);
+ }
+
+ public bool Remove(string key) {
+ return Dictionary.Remove(key);
+ }
+
+ public bool TryGetValue(string key, out ValueProviderResult value) {
+ return Dictionary.TryGetValue(key, out value);
+ }
+
+ #region IEnumerable Members
+ IEnumerator IEnumerable.GetEnumerator() {
+ return ((IEnumerable)Dictionary).GetEnumerator();
+ }
+ #endregion
+
+ #region IValueProvider Members
+ [SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes",
+ Justification = "The declaring type is obsolete, so there is little benefit to exposing this as a virtual method.")]
+ bool IValueProvider.ContainsPrefix(string prefix) {
+ if (prefix == null) {
+ throw new ArgumentNullException("prefix");
+ }
+
+ return ValueProviderUtil.CollectionContainsPrefix(Keys, prefix);
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes",
+ Justification = "The declaring type is obsolete, so there is little benefit to exposing this as a virtual method.")]
+ ValueProviderResult IValueProvider.GetValue(string key) {
+ if (key == null) {
+ throw new ArgumentNullException("key");
+ }
+
+ ValueProviderResult vpResult;
+ TryGetValue(key, out vpResult);
+ return vpResult;
+ }
+ #endregion
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ValueProviderFactories.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ValueProviderFactories.cs
new file mode 100644
index 00000000000..f926e58cb23
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ValueProviderFactories.cs
@@ -0,0 +1,32 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+
+ public static class ValueProviderFactories {
+
+ private static readonly ValueProviderFactoryCollection _factories = new ValueProviderFactoryCollection() {
+ new FormValueProviderFactory(),
+ new RouteDataValueProviderFactory(),
+ new QueryStringValueProviderFactory(),
+ new HttpFileCollectionValueProviderFactory()
+ };
+
+ public static ValueProviderFactoryCollection Factories {
+ get {
+ return _factories;
+ }
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ValueProviderFactory.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ValueProviderFactory.cs
new file mode 100644
index 00000000000..3d2e42a793d
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ValueProviderFactory.cs
@@ -0,0 +1,19 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+
+ public abstract class ValueProviderFactory {
+ public abstract IValueProvider GetValueProvider(ControllerContext controllerContext);
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ValueProviderFactoryCollection.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ValueProviderFactoryCollection.cs
new file mode 100644
index 00000000000..2d797e26fb2
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ValueProviderFactoryCollection.cs
@@ -0,0 +1,53 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Collections.Generic;
+ using System.Collections.ObjectModel;
+ using System.Linq;
+
+ public class ValueProviderFactoryCollection : Collection<ValueProviderFactory> {
+
+ public ValueProviderFactoryCollection() {
+ }
+
+ public ValueProviderFactoryCollection(IList<ValueProviderFactory> list)
+ : base(list) {
+ }
+
+ public IValueProvider GetValueProvider(ControllerContext controllerContext) {
+ var valueProviders = from factory in this
+ let valueProvider = factory.GetValueProvider(controllerContext)
+ where valueProvider != null
+ select valueProvider;
+
+ return new ValueProviderCollection(valueProviders.ToList());
+ }
+
+
+ protected override void InsertItem(int index, ValueProviderFactory item) {
+ if (item == null) {
+ throw new ArgumentNullException("item");
+ }
+ base.InsertItem(index, item);
+ }
+
+ protected override void SetItem(int index, ValueProviderFactory item) {
+ if (item == null) {
+ throw new ArgumentNullException("item");
+ }
+ base.SetItem(index, item);
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ValueProviderResult.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ValueProviderResult.cs
new file mode 100644
index 00000000000..790a04a47b9
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ValueProviderResult.cs
@@ -0,0 +1,147 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Collections;
+ using System.ComponentModel;
+ using System.Globalization;
+ using System.Web.Mvc.Resources;
+
+ [Serializable]
+ public class ValueProviderResult {
+
+ private static readonly CultureInfo _staticCulture = CultureInfo.InvariantCulture;
+ private CultureInfo _instanceCulture;
+
+ // default constructor so that subclassed types can set the properties themselves
+ protected ValueProviderResult() {
+ }
+
+ public ValueProviderResult(object rawValue, string attemptedValue, CultureInfo culture) {
+ RawValue = rawValue;
+ AttemptedValue = attemptedValue;
+ Culture = culture;
+ }
+
+ public string AttemptedValue {
+ get;
+ protected set;
+ }
+
+ public CultureInfo Culture {
+ get {
+ if (_instanceCulture == null) {
+ _instanceCulture = _staticCulture;
+ }
+ return _instanceCulture;
+ }
+ protected set {
+ _instanceCulture = value;
+ }
+ }
+
+ public object RawValue {
+ get;
+ protected set;
+ }
+
+ private static object ConvertSimpleType(CultureInfo culture, object value, Type destinationType) {
+ if (value == null || destinationType.IsInstanceOfType(value)) {
+ return value;
+ }
+
+ // if this is a user-input value but the user didn't type anything, return no value
+ string valueAsString = value as string;
+ if (valueAsString != null && valueAsString.Trim().Length == 0) {
+ return null;
+ }
+
+ TypeConverter converter = TypeDescriptor.GetConverter(destinationType);
+ bool canConvertFrom = converter.CanConvertFrom(value.GetType());
+ if (!canConvertFrom) {
+ converter = TypeDescriptor.GetConverter(value.GetType());
+ }
+ if (!(canConvertFrom || converter.CanConvertTo(destinationType))) {
+ string message = String.Format(CultureInfo.CurrentUICulture, MvcResources.ValueProviderResult_NoConverterExists,
+ value.GetType().FullName, destinationType.FullName);
+ throw new InvalidOperationException(message);
+ }
+
+ try {
+ object convertedValue = (canConvertFrom) ?
+ converter.ConvertFrom(null /* context */, culture, value) :
+ converter.ConvertTo(null /* context */, culture, value, destinationType);
+ return convertedValue;
+ }
+ catch (Exception ex) {
+ string message = String.Format(CultureInfo.CurrentUICulture, MvcResources.ValueProviderResult_ConversionThrew,
+ value.GetType().FullName, destinationType.FullName);
+ throw new InvalidOperationException(message, ex);
+ }
+ }
+
+ public object ConvertTo(Type type) {
+ return ConvertTo(type, null /* culture */);
+ }
+
+ public virtual object ConvertTo(Type type, CultureInfo culture) {
+ if (type == null) {
+ throw new ArgumentNullException("type");
+ }
+
+ CultureInfo cultureToUse = culture ?? Culture;
+ return UnwrapPossibleArrayType(cultureToUse, RawValue, type);
+ }
+
+ private static object UnwrapPossibleArrayType(CultureInfo culture, object value, Type destinationType) {
+ if (value == null || destinationType.IsInstanceOfType(value)) {
+ return value;
+ }
+
+ // array conversion results in four cases, as below
+ Array valueAsArray = value as Array;
+ if (destinationType.IsArray) {
+ Type destinationElementType = destinationType.GetElementType();
+ if (valueAsArray != null) {
+ // case 1: both destination + source type are arrays, so convert each element
+ IList converted = Array.CreateInstance(destinationElementType, valueAsArray.Length);
+ for (int i = 0; i < valueAsArray.Length; i++) {
+ converted[i] = ConvertSimpleType(culture, valueAsArray.GetValue(i), destinationElementType);
+ }
+ return converted;
+ }
+ else {
+ // case 2: destination type is array but source is single element, so wrap element in array + convert
+ object element = ConvertSimpleType(culture, value, destinationElementType);
+ IList converted = Array.CreateInstance(destinationElementType, 1);
+ converted[0] = element;
+ return converted;
+ }
+ }
+ else if (valueAsArray != null) {
+ // case 3: destination type is single element but source is array, so extract first element + convert
+ if (valueAsArray.Length > 0) {
+ value = valueAsArray.GetValue(0);
+ return ConvertSimpleType(culture, value, destinationType);
+ }
+ else {
+ // case 3(a): source is empty array, so can't perform conversion
+ return null;
+ }
+ }
+ // case 4: both destination + source type are single elements, so convert
+ return ConvertSimpleType(culture, value, destinationType);
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ValueProviderUtil.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ValueProviderUtil.cs
new file mode 100644
index 00000000000..4bbad407123
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ValueProviderUtil.cs
@@ -0,0 +1,63 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+
+ internal static class ValueProviderUtil {
+
+ // Given "foo.bar[baz].quux", this method will return:
+ // - "foo.bar[baz].quux"
+ // - "foo.bar[baz]"
+ // - "foo.bar"
+ // - "foo"
+ public static IEnumerable<string> GetPrefixes(string key) {
+ yield return key;
+ for (int i = key.Length - 1; i >= 0; i--) {
+ switch (key[i]) {
+ case '.':
+ case '[':
+ yield return key.Substring(0, i);
+ break;
+ }
+ }
+ }
+
+ public static bool CollectionContainsPrefix(IEnumerable<string> collection, string prefix) {
+ foreach (string key in collection) {
+ if (key != null) {
+ if (prefix.Length == 0) {
+ return true; // shortcut - non-null key matches empty prefix
+ }
+
+ if (key.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) {
+ if (key.Length == prefix.Length) {
+ return true; // exact match
+ }
+ else {
+ switch (key[prefix.Length]) {
+ case '.': // known separator characters
+ case '[':
+ return true;
+ }
+ }
+ }
+ }
+ }
+
+ return false; // nothing found
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewContext.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewContext.cs
new file mode 100644
index 00000000000..882c6df5839
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewContext.cs
@@ -0,0 +1,153 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Collections;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Globalization;
+ using System.IO;
+ using System.Web.Script.Serialization;
+
+ public class ViewContext : ControllerContext {
+
+ private const string _clientValidationScript = @"<script type=""text/javascript"">
+//<![CDATA[
+if (!window.mvcClientValidationMetadata) {{ window.mvcClientValidationMetadata = []; }}
+window.mvcClientValidationMetadata.push({0});
+//]]>
+</script>";
+
+ // Some values have to be stored in HttpContext.Items in order to be propagated between calls
+ // to RenderPartial(), RenderAction(), etc.
+ private static readonly object _clientValidationEnabledKey = new object();
+ private static readonly object _formContextKey = new object();
+ private static readonly object _lastFormNumKey = new object();
+
+ private Func<string> _formIdGenerator;
+
+ // parameterless constructor used for mocking
+ public ViewContext() {
+ }
+
+ [SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors",
+ Justification = "The virtual property setters are only to support mocking frameworks, in which case this constructor shouldn't be called anyway.")]
+ public ViewContext(ControllerContext controllerContext, IView view, ViewDataDictionary viewData, TempDataDictionary tempData, TextWriter writer)
+ : base(controllerContext) {
+ if (controllerContext == null) {
+ throw new ArgumentNullException("controllerContext");
+ }
+ if (view == null) {
+ throw new ArgumentNullException("view");
+ }
+ if (viewData == null) {
+ throw new ArgumentNullException("viewData");
+ }
+ if (tempData == null) {
+ throw new ArgumentNullException("tempData");
+ }
+ if (writer == null) {
+ throw new ArgumentNullException("writer");
+ }
+
+ View = view;
+ ViewData = viewData;
+ Writer = writer;
+ TempData = tempData;
+ }
+
+ public virtual bool ClientValidationEnabled {
+ get {
+ return (HttpContext.Items[_clientValidationEnabledKey] as bool?).GetValueOrDefault();
+ }
+ set {
+ HttpContext.Items[_clientValidationEnabledKey] = value;
+ }
+ }
+
+ public virtual FormContext FormContext {
+ get {
+ return HttpContext.Items[_formContextKey] as FormContext;
+ }
+ set {
+ HttpContext.Items[_formContextKey] = value;
+ }
+ }
+
+ internal Func<string> FormIdGenerator {
+ get {
+ if (_formIdGenerator == null) {
+ _formIdGenerator = DefaultFormIdGenerator;
+ }
+ return _formIdGenerator;
+ }
+ set {
+ _formIdGenerator = value;
+ }
+ }
+
+ [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly",
+ Justification = "The property setter is only here to support mocking this type and should not be called at runtime.")]
+ public virtual TempDataDictionary TempData {
+ get;
+ set;
+ }
+
+ public virtual IView View {
+ get;
+ set;
+ }
+
+ [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly",
+ Justification = "The property setter is only here to support mocking this type and should not be called at runtime.")]
+ public virtual ViewDataDictionary ViewData {
+ get;
+ set;
+ }
+
+ public virtual TextWriter Writer {
+ get;
+ set;
+ }
+
+ private string DefaultFormIdGenerator() {
+ int formNum = IncrementFormCount(HttpContext.Items);
+ return String.Format(CultureInfo.InvariantCulture, "form{0}", formNum);
+ }
+
+ internal FormContext GetFormContextForClientValidation() {
+ return (ClientValidationEnabled) ? FormContext : null;
+ }
+
+ private static int IncrementFormCount(IDictionary items) {
+ object lastFormNum = items[_lastFormNumKey];
+ int newFormNum = (lastFormNum != null) ? ((int)lastFormNum) + 1 : 0;
+ items[_lastFormNumKey] = newFormNum;
+ return newFormNum;
+ }
+
+ public void OutputClientValidation() {
+ FormContext formContext = GetFormContextForClientValidation();
+ if (formContext == null) {
+ return; // do nothing
+ }
+
+ string scriptWithCorrectNewLines = _clientValidationScript.Replace("\r\n", Environment.NewLine);
+ string validationJson = formContext.GetJsonValidationMetadata();
+ string formatted = String.Format(CultureInfo.InvariantCulture, scriptWithCorrectNewLines, validationJson);
+
+ Writer.Write(formatted);
+ FormContext = null;
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewDataDictionary.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewDataDictionary.cs
new file mode 100644
index 00000000000..28b25dd382a
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewDataDictionary.cs
@@ -0,0 +1,356 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Collections;
+ using System.Collections.Generic;
+ using System.ComponentModel;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Globalization;
+ using System.Reflection;
+ using System.Web.Mvc.Resources;
+
+ // TODO: Unit test ModelState interaction with VDD
+
+ public class ViewDataDictionary : IDictionary<string, object> {
+
+ private readonly Dictionary<string, object> _innerDictionary = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
+ private object _model;
+ private ModelMetadata _modelMetadata;
+ private readonly ModelStateDictionary _modelState = new ModelStateDictionary();
+ private TemplateInfo _templateMetadata;
+
+ public ViewDataDictionary()
+ : this((object)null) {
+ }
+
+ [SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors",
+ Justification = "See note on SetModel() method.")]
+ public ViewDataDictionary(object model) {
+ Model = model;
+ }
+
+ [SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors",
+ Justification = "See note on SetModel() method.")]
+ public ViewDataDictionary(ViewDataDictionary dictionary) {
+ if (dictionary == null) {
+ throw new ArgumentNullException("dictionary");
+ }
+
+ foreach (var entry in dictionary) {
+ _innerDictionary.Add(entry.Key, entry.Value);
+ }
+ foreach (var entry in dictionary.ModelState) {
+ ModelState.Add(entry.Key, entry.Value);
+ }
+
+ Model = dictionary.Model;
+ TemplateInfo = dictionary.TemplateInfo;
+
+ // PERF: Don't unnecessarily instantiate the model metadata
+ _modelMetadata = dictionary._modelMetadata;
+ }
+
+ public int Count {
+ get {
+ return _innerDictionary.Count;
+ }
+ }
+
+ public bool IsReadOnly {
+ get {
+ return ((IDictionary<string, object>)_innerDictionary).IsReadOnly;
+ }
+ }
+
+ public ICollection<string> Keys {
+ get {
+ return _innerDictionary.Keys;
+ }
+ }
+
+ public object Model {
+ get {
+ return _model;
+ }
+ set {
+ _modelMetadata = null;
+ SetModel(value);
+ }
+ }
+
+ public virtual ModelMetadata ModelMetadata {
+ get {
+ if (_modelMetadata == null && _model != null) {
+ _modelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => _model, _model.GetType());
+ }
+ return _modelMetadata;
+ }
+ set {
+ _modelMetadata = value;
+ }
+ }
+
+ public ModelStateDictionary ModelState {
+ get {
+ return _modelState;
+ }
+ }
+
+ public object this[string key] {
+ get {
+ object value;
+ _innerDictionary.TryGetValue(key, out value);
+ return value;
+ }
+ set {
+ _innerDictionary[key] = value;
+ }
+ }
+
+ public TemplateInfo TemplateInfo {
+ get {
+ if (_templateMetadata == null) {
+ _templateMetadata = new TemplateInfo();
+ }
+ return _templateMetadata;
+ }
+ set {
+ _templateMetadata = value;
+ }
+ }
+
+ public ICollection<object> Values {
+ get {
+ return _innerDictionary.Values;
+ }
+ }
+
+ public void Add(KeyValuePair<string, object> item) {
+ ((IDictionary<string, object>)_innerDictionary).Add(item);
+ }
+
+ public void Add(string key, object value) {
+ _innerDictionary.Add(key, value);
+ }
+
+ public void Clear() {
+ _innerDictionary.Clear();
+ }
+
+ public bool Contains(KeyValuePair<string, object> item) {
+ return ((IDictionary<string, object>)_innerDictionary).Contains(item);
+ }
+
+ public bool ContainsKey(string key) {
+ return _innerDictionary.ContainsKey(key);
+ }
+
+ public void CopyTo(KeyValuePair<string, object>[] array, int arrayIndex) {
+ ((IDictionary<string, object>)_innerDictionary).CopyTo(array, arrayIndex);
+ }
+
+ [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Eval",
+ Justification = "Commonly used shorthand for Evaluate.")]
+ public object Eval(string expression) {
+ ViewDataInfo info = GetViewDataInfo(expression);
+ return (info != null) ? info.Value : null;
+ }
+
+ [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Eval",
+ Justification = "Commonly used shorthand for Evaluate.")]
+ public string Eval(string expression, string format) {
+ object value = Eval(expression);
+
+ if (value == null) {
+ return String.Empty;
+ }
+
+ if (String.IsNullOrEmpty(format)) {
+ return Convert.ToString(value, CultureInfo.CurrentCulture);
+ }
+ else {
+ return String.Format(CultureInfo.CurrentCulture, format, value);
+ }
+ }
+
+ public IEnumerator<KeyValuePair<string, object>> GetEnumerator() {
+ return _innerDictionary.GetEnumerator();
+ }
+
+ public ViewDataInfo GetViewDataInfo(string expression) {
+ if (String.IsNullOrEmpty(expression)) {
+ throw new ArgumentException(MvcResources.Common_NullOrEmpty, "expression");
+ }
+
+ return ViewDataEvaluator.Eval(this, expression);
+ }
+
+ public bool Remove(KeyValuePair<string, object> item) {
+ return ((IDictionary<string, object>)_innerDictionary).Remove(item);
+ }
+
+ public bool Remove(string key) {
+ return _innerDictionary.Remove(key);
+ }
+
+ // This method will execute before the derived type's instance constructor executes. Derived types must
+ // be aware of this and should plan accordingly. For example, the logic in SetModel() should be simple
+ // enough so as not to depend on the "this" pointer referencing a fully constructed object.
+ protected virtual void SetModel(object value) {
+ _model = value;
+ }
+
+ public bool TryGetValue(string key, out object value) {
+ return _innerDictionary.TryGetValue(key, out value);
+ }
+
+ internal static class ViewDataEvaluator {
+
+ public static ViewDataInfo Eval(ViewDataDictionary vdd, string expression) {
+ //Given an expression "foo.bar.baz" we look up the following (pseudocode):
+ // this["foo.bar.baz.quux"]
+ // this["foo.bar.baz"]["quux"]
+ // this["foo.bar"]["baz.quux]
+ // this["foo.bar"]["baz"]["quux"]
+ // this["foo"]["bar.baz.quux"]
+ // this["foo"]["bar.baz"]["quux"]
+ // this["foo"]["bar"]["baz.quux"]
+ // this["foo"]["bar"]["baz"]["quux"]
+
+ ViewDataInfo evaluated = EvalComplexExpression(vdd, expression);
+ return evaluated;
+ }
+
+ private static ViewDataInfo EvalComplexExpression(object indexableObject, string expression) {
+ foreach (ExpressionPair expressionPair in GetRightToLeftExpressions(expression)) {
+ string subExpression = expressionPair.Left;
+ string postExpression = expressionPair.Right;
+
+ ViewDataInfo subTargetInfo = GetPropertyValue(indexableObject, subExpression);
+ if (subTargetInfo != null) {
+ if (String.IsNullOrEmpty(postExpression)) {
+ return subTargetInfo;
+ }
+
+ if (subTargetInfo.Value != null) {
+ ViewDataInfo potential = EvalComplexExpression(subTargetInfo.Value, postExpression);
+ if (potential != null) {
+ return potential;
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ private static IEnumerable<ExpressionPair> GetRightToLeftExpressions(string expression) {
+ // Produces an enumeration of all the combinations of complex property names
+ // given a complex expression. See the list above for an example of the result
+ // of the enumeration.
+
+ yield return new ExpressionPair(expression, String.Empty);
+
+ int lastDot = expression.LastIndexOf('.');
+
+ string subExpression = expression;
+ string postExpression = string.Empty;
+
+ while (lastDot > -1) {
+ subExpression = expression.Substring(0, lastDot);
+ postExpression = expression.Substring(lastDot + 1);
+ yield return new ExpressionPair(subExpression, postExpression);
+
+ lastDot = subExpression.LastIndexOf('.');
+ }
+ }
+
+ private static ViewDataInfo GetIndexedPropertyValue(object indexableObject, string key) {
+ IDictionary<string, object> dict = indexableObject as IDictionary<string, object>;
+ object value = null;
+ bool success = false;
+
+ if (dict != null) {
+ success = dict.TryGetValue(key, out value);
+ }
+ else {
+ TryGetValueDelegate tgvDel = TypeHelpers.CreateTryGetValueDelegate(indexableObject.GetType());
+ if (tgvDel != null) {
+ success = tgvDel(indexableObject, key, out value);
+ }
+ }
+
+ if (success) {
+ return new ViewDataInfo() {
+ Container = indexableObject,
+ Value = value
+ };
+ }
+
+ return null;
+ }
+
+ private static ViewDataInfo GetPropertyValue(object container, string propertyName) {
+ // This method handles one "segment" of a complex property expression
+
+ // First, we try to evaluate the property based on its indexer
+ ViewDataInfo value = GetIndexedPropertyValue(container, propertyName);
+ if (value != null) {
+ return value;
+ }
+
+ // If the indexer didn't return anything useful, continue...
+
+ // If the container is a ViewDataDictionary then treat its Model property
+ // as the container instead of the ViewDataDictionary itself.
+ ViewDataDictionary vdd = container as ViewDataDictionary;
+ if (vdd != null) {
+ container = vdd.Model;
+ }
+
+ // If the container is null, we're out of options
+ if (container == null) {
+ return null;
+ }
+
+ // Second, we try to use PropertyDescriptors and treat the expression as a property name
+ PropertyDescriptor descriptor = TypeDescriptor.GetProperties(container).Find(propertyName, true);
+ if (descriptor == null) {
+ return null;
+ }
+
+ return new ViewDataInfo(() => descriptor.GetValue(container)) {
+ Container = container,
+ PropertyDescriptor = descriptor
+ };
+ }
+
+ private struct ExpressionPair {
+ public readonly string Left;
+ public readonly string Right;
+
+ public ExpressionPair(string left, string right) {
+ Left = left;
+ Right = right;
+ }
+ }
+ }
+
+ #region IEnumerable Members
+ IEnumerator IEnumerable.GetEnumerator() {
+ return ((IEnumerable)_innerDictionary).GetEnumerator();
+ }
+ #endregion
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewDataDictionary`1.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewDataDictionary`1.cs
new file mode 100644
index 00000000000..a0d09f220df
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewDataDictionary`1.cs
@@ -0,0 +1,66 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+
+ public class ViewDataDictionary<TModel> : ViewDataDictionary {
+ public ViewDataDictionary() :
+ base(default(TModel)) {
+ }
+
+ public ViewDataDictionary(TModel model) :
+ base(model) {
+ }
+
+ public ViewDataDictionary(ViewDataDictionary viewDataDictionary) :
+ base(viewDataDictionary) {
+ }
+
+ public new TModel Model {
+ get {
+ return (TModel)base.Model;
+ }
+ set {
+ SetModel(value);
+ }
+ }
+
+ public override ModelMetadata ModelMetadata {
+ get {
+ ModelMetadata result = base.ModelMetadata;
+ if (result == null) {
+ result = base.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, typeof(TModel));
+ }
+ return result;
+ }
+ set {
+ base.ModelMetadata = value;
+ }
+ }
+
+ protected override void SetModel(object value) {
+ bool castWillSucceed = TypeHelpers.IsCompatibleObject<TModel>(value);
+
+ if (castWillSucceed) {
+ base.SetModel((TModel)value);
+ }
+ else {
+ InvalidOperationException exception = (value != null)
+ ? Error.ViewDataDictionary_WrongTModelType(value.GetType(), typeof(TModel))
+ : Error.ViewDataDictionary_ModelCannotBeNull(typeof(TModel));
+ throw exception;
+ }
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewDataInfo.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewDataInfo.cs
new file mode 100644
index 00000000000..8cc480b52d9
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewDataInfo.cs
@@ -0,0 +1,55 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.ComponentModel;
+
+ public class ViewDataInfo {
+
+ private object _value;
+ private Func<object> _valueAccessor;
+
+ public ViewDataInfo() {
+ }
+
+ public ViewDataInfo(Func<object> valueAccessor) {
+ _valueAccessor = valueAccessor;
+ }
+
+ public object Container {
+ get;
+ set;
+ }
+
+ public PropertyDescriptor PropertyDescriptor {
+ get;
+ set;
+ }
+
+ public object Value {
+ get {
+ if (_valueAccessor != null) {
+ _value = _valueAccessor();
+ _valueAccessor = null;
+ }
+
+ return _value;
+ }
+ set {
+ _value = value;
+ _valueAccessor = null;
+ }
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewEngineCollection.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewEngineCollection.cs
new file mode 100644
index 00000000000..1fce9653ccf
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewEngineCollection.cs
@@ -0,0 +1,95 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System.Collections.Generic;
+ using System.Collections.ObjectModel;
+ using System.Web.Mvc.Resources;
+
+ public class ViewEngineCollection : Collection<IViewEngine> {
+
+ public ViewEngineCollection() {
+ }
+
+ public ViewEngineCollection(IList<IViewEngine> list)
+ : base(list) {
+ }
+
+ protected override void InsertItem(int index, IViewEngine item) {
+ if (item == null) {
+ throw new ArgumentNullException("item");
+ }
+ base.InsertItem(index, item);
+ }
+
+ protected override void SetItem(int index, IViewEngine item) {
+ if (item == null) {
+ throw new ArgumentNullException("item");
+ }
+ base.SetItem(index, item);
+ }
+
+ private ViewEngineResult Find(Func<IViewEngine, ViewEngineResult> cacheLocator, Func<IViewEngine, ViewEngineResult> locator) {
+ ViewEngineResult result;
+
+ foreach (IViewEngine engine in Items) {
+ if (engine != null) {
+ result = cacheLocator(engine);
+
+ if (result.View != null) {
+ return result;
+ }
+ }
+ }
+
+ List<string> searched = new List<string>();
+
+ foreach (IViewEngine engine in Items) {
+ if (engine != null) {
+ result = locator(engine);
+
+ if (result.View != null) {
+ return result;
+ }
+
+ searched.AddRange(result.SearchedLocations);
+ }
+ }
+
+ return new ViewEngineResult(searched);
+ }
+
+ public virtual ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName) {
+ if (controllerContext == null) {
+ throw new ArgumentNullException("controllerContext");
+ }
+ if (string.IsNullOrEmpty(partialViewName)) {
+ throw new ArgumentException(MvcResources.Common_NullOrEmpty, "partialViewName");
+ }
+ Func<IViewEngine, ViewEngineResult> cacheLocator = e => e.FindPartialView(controllerContext, partialViewName, true);
+ Func<IViewEngine, ViewEngineResult> locator = e => e.FindPartialView(controllerContext, partialViewName, false);
+ return Find(cacheLocator, locator);
+ }
+
+ public virtual ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName) {
+ if (controllerContext == null) {
+ throw new ArgumentNullException("controllerContext");
+ }
+ if (string.IsNullOrEmpty(viewName)) {
+ throw new ArgumentException(MvcResources.Common_NullOrEmpty, "viewName");
+ }
+ Func<IViewEngine, ViewEngineResult> cacheLocator = e => e.FindView(controllerContext, viewName, masterName, true);
+ Func<IViewEngine, ViewEngineResult> locator = e => e.FindView(controllerContext, viewName, masterName, false);
+ return Find(cacheLocator, locator);
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewEngineResult.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewEngineResult.cs
new file mode 100644
index 00000000000..97a1b316fdd
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewEngineResult.cs
@@ -0,0 +1,54 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Collections.Generic;
+
+ public class ViewEngineResult {
+
+ public ViewEngineResult(IEnumerable<string> searchedLocations) {
+ if (searchedLocations == null) {
+ throw new ArgumentNullException("searchedLocations");
+ }
+
+ SearchedLocations = searchedLocations;
+ }
+
+ public ViewEngineResult(IView view, IViewEngine viewEngine) {
+ if (view == null) {
+ throw new ArgumentNullException("view");
+ }
+ if (viewEngine == null) {
+ throw new ArgumentNullException("viewEngine");
+ }
+
+ View = view;
+ ViewEngine = viewEngine;
+ }
+
+ public IEnumerable<string> SearchedLocations {
+ get;
+ private set;
+ }
+
+ public IView View {
+ get;
+ private set;
+ }
+
+ public IViewEngine ViewEngine {
+ get;
+ private set;
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewEngines.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewEngines.cs
new file mode 100644
index 00000000000..af4c016c77a
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewEngines.cs
@@ -0,0 +1,27 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+
+ public static class ViewEngines {
+
+ private readonly static ViewEngineCollection _engines = new ViewEngineCollection {
+ new WebFormViewEngine()
+ };
+
+ public static ViewEngineCollection Engines {
+ get {
+ return _engines;
+ }
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewMasterPage.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewMasterPage.cs
new file mode 100644
index 00000000000..837dabf0029
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewMasterPage.cs
@@ -0,0 +1,77 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System.Globalization;
+ using System.Web.Mvc.Resources;
+ using System.Web.UI;
+
+ public class ViewMasterPage : MasterPage {
+ public AjaxHelper<object> Ajax {
+ get {
+ return ViewPage.Ajax;
+ }
+ }
+
+ public HtmlHelper<object> Html {
+ get {
+ return ViewPage.Html;
+ }
+ }
+
+ public object Model {
+ get {
+ return ViewData.Model;
+ }
+ }
+
+ public TempDataDictionary TempData {
+ get {
+ return ViewPage.TempData;
+ }
+ }
+
+ public UrlHelper Url {
+ get {
+ return ViewPage.Url;
+ }
+ }
+
+ public ViewContext ViewContext {
+ get {
+ return ViewPage.ViewContext;
+ }
+ }
+
+ public ViewDataDictionary ViewData {
+ get {
+ return ViewPage.ViewData;
+ }
+ }
+
+ internal ViewPage ViewPage {
+ get {
+ ViewPage viewPage = Page as ViewPage;
+ if (viewPage == null) {
+ throw new InvalidOperationException(String.Format(CultureInfo.CurrentUICulture, MvcResources.ViewMasterPage_RequiresViewPage));
+ }
+ return viewPage;
+ }
+ }
+
+ public HtmlTextWriter Writer {
+ get {
+ return ViewPage.Writer;
+ }
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewMasterPage`1.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewMasterPage`1.cs
new file mode 100644
index 00000000000..c3c95d189bc
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewMasterPage`1.cs
@@ -0,0 +1,53 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+
+ public class ViewMasterPage<TModel> : ViewMasterPage {
+ private AjaxHelper<TModel> _ajaxHelper;
+ private HtmlHelper<TModel> _htmlHelper;
+ private ViewDataDictionary<TModel> _viewData;
+
+ public new AjaxHelper<TModel> Ajax {
+ get {
+ if (_ajaxHelper == null) {
+ _ajaxHelper = new AjaxHelper<TModel>(ViewContext, ViewPage);
+ }
+ return _ajaxHelper;
+ }
+ }
+
+ public new HtmlHelper<TModel> Html {
+ get {
+ if (_htmlHelper == null) {
+ _htmlHelper = new HtmlHelper<TModel>(ViewContext, ViewPage);
+ }
+ return _htmlHelper;
+ }
+ }
+
+ public new TModel Model {
+ get {
+ return ViewData.Model;
+ }
+ }
+
+ public new ViewDataDictionary<TModel> ViewData {
+ get {
+ if (_viewData == null) {
+ _viewData = new ViewDataDictionary<TModel>(ViewPage.ViewData);
+ }
+ return _viewData;
+ }
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewPage.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewPage.cs
new file mode 100644
index 00000000000..6e1891cff19
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewPage.cs
@@ -0,0 +1,378 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Globalization;
+ using System.IO;
+ using System.Text;
+ using System.Web;
+ using System.Web.UI;
+
+ [FileLevelControlBuilder(typeof(ViewPageControlBuilder))]
+ public class ViewPage : Page, IViewDataContainer {
+
+ private string _masterLocation;
+ [ThreadStatic]
+ private static int _nextId;
+ private ViewDataDictionary _viewData;
+
+ public AjaxHelper<object> Ajax {
+ get;
+ set;
+ }
+
+ public HtmlHelper<object> Html {
+ get;
+ set;
+ }
+
+ public string MasterLocation {
+ get {
+ return _masterLocation ?? String.Empty;
+ }
+ set {
+ _masterLocation = value;
+ }
+ }
+
+ public object Model {
+ get {
+ return ViewData.Model;
+ }
+ }
+
+ public TempDataDictionary TempData {
+ get {
+ return ViewContext.TempData;
+ }
+ }
+
+ public UrlHelper Url {
+ get;
+ set;
+ }
+
+ public ViewContext ViewContext {
+ get;
+ set;
+ }
+
+ [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly",
+ Justification = "This is the mechanism by which the ViewPage gets its ViewDataDictionary object.")]
+ public ViewDataDictionary ViewData {
+ get {
+ if (_viewData == null) {
+ SetViewData(new ViewDataDictionary());
+ }
+ return _viewData;
+ }
+ set {
+ SetViewData(value);
+ }
+ }
+
+ public HtmlTextWriter Writer {
+ get;
+ private set;
+ }
+
+ public virtual void InitHelpers() {
+ Ajax = new AjaxHelper<object>(ViewContext, this);
+ Html = new HtmlHelper<object>(ViewContext, this);
+ Url = new UrlHelper(ViewContext.RequestContext);
+ }
+
+ internal static string NextId() {
+ return (++_nextId).ToString(CultureInfo.InvariantCulture);
+ }
+
+ [SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers")]
+ protected override void OnPreInit(EventArgs e) {
+ base.OnPreInit(e);
+
+ if (!String.IsNullOrEmpty(MasterLocation)) {
+ MasterPageFile = MasterLocation;
+ }
+ }
+
+ public override void ProcessRequest(HttpContext context) {
+ // Tracing requires IDs to be unique.
+ ID = NextId();
+
+ base.ProcessRequest(context);
+ }
+
+ protected override void Render(HtmlTextWriter writer) {
+ Writer = writer;
+ try {
+ base.Render(writer);
+ }
+ finally {
+ Writer = null;
+ }
+ }
+
+ public virtual void RenderView(ViewContext viewContext) {
+ ViewContext = viewContext;
+ InitHelpers();
+
+ bool needServerExecute = false;
+
+ SwitchWriter switchWriter = viewContext.HttpContext.Response.Output as SwitchWriter;
+ if (switchWriter == null) {
+ switchWriter = new SwitchWriter();
+ needServerExecute = true;
+ }
+
+ using (switchWriter.Scope(viewContext.Writer)) {
+ if (needServerExecute) {
+ // It's safe to reset the _nextId within a Server.Execute() since it pushes a new TraceContext onto
+ // the stack, so there won't be an ID conflict.
+ int originalNextId = _nextId;
+ try {
+ _nextId = 0;
+ viewContext.HttpContext.Server.Execute(HttpHandlerUtil.WrapForServerExecute(this), switchWriter, true /* preserveForm */);
+ }
+ finally {
+ // Restore the original _nextId in case this isn't actually the outermost view, since resetting
+ // the _nextId may now cause trace ID conflicts in the outer view.
+ _nextId = originalNextId;
+ }
+ }
+ else {
+ ProcessRequest(HttpContext.Current);
+ }
+ }
+ }
+
+ [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "textWriter",
+ Justification = "This method existed in MVC 1.0 and has been deprecated.")]
+ [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic",
+ Justification = "This method existed in MVC 1.0 and has been deprecated.")]
+ [Obsolete("The TextWriter is now provided by the ViewContext object passed to the RenderView method.", true /* error */)]
+ public void SetTextWriter(TextWriter textWriter) {
+ // this is now a no-op
+ }
+
+ protected virtual void SetViewData(ViewDataDictionary viewData) {
+ _viewData = viewData;
+ }
+
+ internal class SwitchWriter : TextWriter {
+ public SwitchWriter()
+ : base(CultureInfo.CurrentCulture) {
+ }
+
+ public override Encoding Encoding {
+ get {
+ return InnerWriter.Encoding;
+ }
+ }
+
+ public override IFormatProvider FormatProvider {
+ get {
+ return InnerWriter.FormatProvider;
+ }
+ }
+
+ internal TextWriter InnerWriter {
+ get;
+ set;
+ }
+
+ public override string NewLine {
+ get {
+ return InnerWriter.NewLine;
+ }
+ set {
+ InnerWriter.NewLine = value;
+ }
+ }
+
+ public override void Close() {
+ InnerWriter.Close();
+ }
+
+ public override void Flush() {
+ InnerWriter.Flush();
+ }
+
+ public IDisposable Scope(TextWriter writer) {
+ WriterScope scope = new WriterScope(this, InnerWriter);
+
+ if (writer != this) {
+ InnerWriter = writer;
+ }
+
+ return scope;
+ }
+
+ public override void Write(bool value) {
+ InnerWriter.Write(value);
+ }
+
+ public override void Write(char value) {
+ InnerWriter.Write(value);
+ }
+
+ public override void Write(char[] buffer) {
+ InnerWriter.Write(buffer);
+ }
+
+ public override void Write(char[] buffer, int index, int count) {
+ InnerWriter.Write(buffer, index, count);
+ }
+
+ public override void Write(decimal value) {
+ InnerWriter.Write(value);
+ }
+
+ public override void Write(double value) {
+ InnerWriter.Write(value);
+ }
+
+ public override void Write(float value) {
+ InnerWriter.Write(value);
+ }
+
+ public override void Write(int value) {
+ InnerWriter.Write(value);
+ }
+
+ public override void Write(long value) {
+ InnerWriter.Write(value);
+ }
+
+ public override void Write(object value) {
+ InnerWriter.Write(value);
+ }
+
+ public override void Write(string format, object arg0) {
+ InnerWriter.Write(format, arg0);
+ }
+
+ public override void Write(string format, object arg0, object arg1) {
+ InnerWriter.Write(format, arg0, arg1);
+ }
+
+ public override void Write(string format, object arg0, object arg1, object arg2) {
+ InnerWriter.Write(format, arg0, arg1, arg2);
+ }
+
+ public override void Write(string format, params object[] arg) {
+ InnerWriter.Write(format, arg);
+ }
+
+ public override void Write(string value) {
+ InnerWriter.Write(value);
+ }
+
+ public override void Write(uint value) {
+ InnerWriter.Write(value);
+ }
+
+ public override void Write(ulong value) {
+ InnerWriter.Write(value);
+ }
+
+ public override void WriteLine() {
+ InnerWriter.WriteLine();
+ }
+
+ public override void WriteLine(bool value) {
+ InnerWriter.WriteLine(value);
+ }
+
+ public override void WriteLine(char value) {
+ InnerWriter.WriteLine(value);
+ }
+
+ public override void WriteLine(char[] buffer) {
+ InnerWriter.WriteLine(buffer);
+ }
+
+ public override void WriteLine(char[] buffer, int index, int count) {
+ InnerWriter.WriteLine(buffer, index, count);
+ }
+
+ public override void WriteLine(decimal value) {
+ InnerWriter.WriteLine(value);
+ }
+
+ public override void WriteLine(double value) {
+ InnerWriter.WriteLine(value);
+ }
+
+ public override void WriteLine(float value) {
+ InnerWriter.WriteLine(value);
+ }
+
+ public override void WriteLine(int value) {
+ InnerWriter.WriteLine(value);
+ }
+
+ public override void WriteLine(long value) {
+ InnerWriter.WriteLine(value);
+ }
+
+ public override void WriteLine(object value) {
+ InnerWriter.WriteLine(value);
+ }
+
+ public override void WriteLine(string format, object arg0) {
+ InnerWriter.WriteLine(format, arg0);
+ }
+
+ public override void WriteLine(string format, object arg0, object arg1) {
+ InnerWriter.WriteLine(format, arg0, arg1);
+ }
+
+ public override void WriteLine(string format, object arg0, object arg1, object arg2) {
+ InnerWriter.WriteLine(format, arg0, arg1, arg2);
+ }
+
+ public override void WriteLine(string format, params object[] arg) {
+ InnerWriter.WriteLine(format, arg);
+ }
+
+ public override void WriteLine(string value) {
+ InnerWriter.WriteLine(value);
+ }
+
+ public override void WriteLine(uint value) {
+ InnerWriter.WriteLine(value);
+ }
+
+ public override void WriteLine(ulong value) {
+ InnerWriter.WriteLine(value);
+ }
+
+ private sealed class WriterScope : IDisposable {
+ private SwitchWriter _switchWriter;
+ private TextWriter _writerToRestore;
+
+ public WriterScope(SwitchWriter switchWriter, TextWriter writerToRestore) {
+ _switchWriter = switchWriter;
+ _writerToRestore = writerToRestore;
+ }
+
+ public void Dispose() {
+ _switchWriter.InnerWriter = _writerToRestore;
+ }
+ }
+
+ }
+
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewPageControlBuilder.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewPageControlBuilder.cs
new file mode 100644
index 00000000000..cf494c26e62
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewPageControlBuilder.cs
@@ -0,0 +1,36 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System.CodeDom;
+ using System.Web.UI;
+
+ internal sealed class ViewPageControlBuilder : FileLevelPageControlBuilder {
+ public string PageBaseType {
+ get;
+ set;
+ }
+
+ public override void ProcessGeneratedCode(
+ CodeCompileUnit codeCompileUnit,
+ CodeTypeDeclaration baseType,
+ CodeTypeDeclaration derivedType,
+ CodeMemberMethod buildMethod,
+ CodeMemberMethod dataBindingMethod) {
+
+ // If we find got a base class string, use it
+ if (PageBaseType != null) {
+ derivedType.BaseTypes[0] = new CodeTypeReference(PageBaseType);
+ }
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewPage`1.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewPage`1.cs
new file mode 100644
index 00000000000..3f555395c99
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewPage`1.cs
@@ -0,0 +1,62 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System.Diagnostics.CodeAnalysis;
+
+ public class ViewPage<TModel> : ViewPage {
+
+ private ViewDataDictionary<TModel> _viewData;
+
+ public new AjaxHelper<TModel> Ajax {
+ get;
+ set;
+ }
+
+ public new HtmlHelper<TModel> Html {
+ get;
+ set;
+ }
+
+ public new TModel Model {
+ get {
+ return ViewData.Model;
+ }
+ }
+
+ [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
+ public new ViewDataDictionary<TModel> ViewData {
+ get {
+ if (_viewData == null) {
+ SetViewData(new ViewDataDictionary<TModel>());
+ }
+ return _viewData;
+ }
+ set {
+ SetViewData(value);
+ }
+ }
+
+ public override void InitHelpers() {
+ base.InitHelpers();
+
+ Ajax = new AjaxHelper<TModel>(ViewContext, this);
+ Html = new HtmlHelper<TModel>(ViewContext, this);
+ }
+
+ protected override void SetViewData(ViewDataDictionary viewData) {
+ _viewData = new ViewDataDictionary<TModel>(viewData);
+
+ base.SetViewData(_viewData);
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewResult.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewResult.cs
new file mode 100644
index 00000000000..49b8a85041d
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewResult.cs
@@ -0,0 +1,47 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Globalization;
+ using System.Text;
+ using System.Web.Mvc.Resources;
+
+ public class ViewResult : ViewResultBase {
+ private string _masterName;
+
+ public string MasterName {
+ get {
+ return _masterName ?? String.Empty;
+ }
+ set {
+ _masterName = value;
+ }
+ }
+
+ protected override ViewEngineResult FindView(ControllerContext context) {
+ ViewEngineResult result = ViewEngineCollection.FindView(context, ViewName, MasterName);
+ if (result.View != null) {
+ return result;
+ }
+
+ // we need to generate an exception containing all the locations we searched
+ StringBuilder locationsText = new StringBuilder();
+ foreach (string location in result.SearchedLocations) {
+ locationsText.AppendLine();
+ locationsText.Append(location);
+ }
+ throw new InvalidOperationException(String.Format(CultureInfo.CurrentUICulture,
+ MvcResources.Common_ViewNotFound, ViewName, locationsText));
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewResultBase.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewResultBase.cs
new file mode 100644
index 00000000000..c4fc2ccd76e
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewResultBase.cs
@@ -0,0 +1,103 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Diagnostics.CodeAnalysis;
+ using System.IO;
+
+ public abstract class ViewResultBase : ActionResult {
+ private TempDataDictionary _tempData;
+ private ViewDataDictionary _viewData;
+ private ViewEngineCollection _viewEngineCollection;
+ private string _viewName;
+
+ [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly",
+ Justification = "This entire type is meant to be mutable.")]
+ public TempDataDictionary TempData {
+ get {
+ if (_tempData == null) {
+ _tempData = new TempDataDictionary();
+ }
+ return _tempData;
+ }
+ set {
+ _tempData = value;
+ }
+ }
+
+ public IView View {
+ get;
+ set;
+ }
+
+ [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly",
+ Justification = "This entire type is meant to be mutable.")]
+ public ViewDataDictionary ViewData {
+ get {
+ if (_viewData == null) {
+ _viewData = new ViewDataDictionary();
+ }
+ return _viewData;
+ }
+ set {
+ _viewData = value;
+ }
+ }
+
+ [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly",
+ Justification = "This entire type is meant to be mutable.")]
+ public ViewEngineCollection ViewEngineCollection {
+ get {
+ return _viewEngineCollection ?? ViewEngines.Engines;
+ }
+ set {
+ _viewEngineCollection = value;
+ }
+ }
+
+ public string ViewName {
+ get {
+ return _viewName ?? String.Empty;
+ }
+ set {
+ _viewName = value;
+ }
+ }
+
+ public override void ExecuteResult(ControllerContext context) {
+ if (context == null) {
+ throw new ArgumentNullException("context");
+ }
+ if (String.IsNullOrEmpty(ViewName)) {
+ ViewName = context.RouteData.GetRequiredString("action");
+ }
+
+ ViewEngineResult result = null;
+
+ if (View == null) {
+ result = FindView(context);
+ View = result.View;
+ }
+
+ TextWriter writer = context.HttpContext.Response.Output;
+ ViewContext viewContext = new ViewContext(context, View, ViewData, TempData, writer);
+ View.Render(viewContext, writer);
+
+ if (result != null) {
+ result.ViewEngine.ReleaseView(context, View);
+ }
+ }
+
+ protected abstract ViewEngineResult FindView(ControllerContext context);
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewTemplateUserControl.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewTemplateUserControl.cs
new file mode 100644
index 00000000000..3ccc4fbcdea
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewTemplateUserControl.cs
@@ -0,0 +1,15 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ public class ViewTemplateUserControl : ViewTemplateUserControl<object> { }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewTemplateUserControl`1.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewTemplateUserControl`1.cs
new file mode 100644
index 00000000000..f2ae5db7041
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewTemplateUserControl`1.cs
@@ -0,0 +1,19 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ public class ViewTemplateUserControl<TModel> : ViewUserControl<TModel> {
+ protected string FormattedModelValue {
+ get { return ViewData.TemplateInfo.FormattedModelValue.ToString(); }
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewType.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewType.cs
new file mode 100644
index 00000000000..39afcc92136
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewType.cs
@@ -0,0 +1,32 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System.ComponentModel;
+ using System.Web.UI;
+
+ [ControlBuilder(typeof(ViewTypeControlBuilder))]
+ [NonVisualControl]
+ public class ViewType : Control {
+ private string _typeName;
+
+ [DefaultValue("")]
+ public string TypeName {
+ get {
+ return _typeName ?? String.Empty;
+ }
+ set {
+ _typeName = value;
+ }
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewTypeControlBuilder.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewTypeControlBuilder.cs
new file mode 100644
index 00000000000..0a306530ff3
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewTypeControlBuilder.cs
@@ -0,0 +1,39 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.CodeDom;
+ using System.Collections;
+ using System.Web.UI;
+
+ internal sealed class ViewTypeControlBuilder : ControlBuilder {
+ private string _typeName;
+
+ public override void Init(TemplateParser parser, ControlBuilder parentBuilder, Type type, string tagName, string id, IDictionary attribs) {
+ base.Init(parser, parentBuilder, type, tagName, id, attribs);
+
+ _typeName = (string)attribs["typename"];
+ }
+
+ public override void ProcessGeneratedCode(
+ CodeCompileUnit codeCompileUnit,
+ CodeTypeDeclaration baseType,
+ CodeTypeDeclaration derivedType,
+ CodeMemberMethod buildMethod,
+ CodeMemberMethod dataBindingMethod) {
+
+ // Override the view's base type with the explicit base type
+ derivedType.BaseTypes[0] = new CodeTypeReference(_typeName);
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewTypeParserFilter.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewTypeParserFilter.cs
new file mode 100644
index 00000000000..4fb159a4209
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewTypeParserFilter.cs
@@ -0,0 +1,156 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Collections;
+ using System.Web.UI;
+
+ internal class ViewTypeParserFilter : PageParserFilter {
+
+ private string _viewBaseType;
+ private DirectiveType _directiveType = DirectiveType.Unknown;
+ private bool _viewTypeControlAdded;
+
+ public override void PreprocessDirective(string directiveName, IDictionary attributes) {
+ base.PreprocessDirective(directiveName, attributes);
+
+ string defaultBaseType = null;
+
+ // If we recognize the directive, keep track of what it was. If we don't recognize
+ // the directive then just stop.
+ switch (directiveName) {
+ case "page":
+ _directiveType = DirectiveType.Page;
+ defaultBaseType = typeof(ViewPage).FullName;
+ break;
+ case "control":
+ _directiveType = DirectiveType.UserControl;
+ defaultBaseType = typeof(ViewUserControl).FullName;
+ break;
+ case "master":
+ _directiveType = DirectiveType.Master;
+ defaultBaseType = typeof(ViewMasterPage).FullName;
+ break;
+ }
+
+ if (_directiveType == DirectiveType.Unknown) {
+ // If we're processing an unknown directive (e.g. a register directive), stop processing
+ return;
+ }
+
+ // Look for an inherit attribute
+ string inherits = (string)attributes["inherits"];
+ if (!String.IsNullOrEmpty(inherits)) {
+ // If it doesn't look like a generic type, don't do anything special,
+ // and let the parser do its normal processing
+ if (IsGenericTypeString(inherits)) {
+ // Remove the inherits attribute so the parser doesn't blow up
+ attributes["inherits"] = defaultBaseType;
+
+ // Remember the full type string so we can later give it to the ControlBuilder
+ _viewBaseType = inherits;
+ }
+ }
+ }
+
+ private static bool IsGenericTypeString(string typeName) {
+ // Detect C# and VB generic syntax
+ // REVIEW: what about other languages?
+ return typeName.IndexOfAny(new char[] { '<', '(' }) >= 0;
+ }
+
+ public override void ParseComplete(ControlBuilder rootBuilder) {
+ base.ParseComplete(rootBuilder);
+
+ // If it's our page ControlBuilder, give it the base type string
+ ViewPageControlBuilder pageBuilder = rootBuilder as ViewPageControlBuilder;
+ if (pageBuilder != null) {
+ pageBuilder.PageBaseType = _viewBaseType;
+ }
+ ViewUserControlControlBuilder userControlBuilder = rootBuilder as ViewUserControlControlBuilder;
+ if (userControlBuilder != null) {
+ userControlBuilder.UserControlBaseType = _viewBaseType;
+ }
+ }
+
+ public override bool ProcessCodeConstruct(CodeConstructType codeType, string code) {
+ if (!_viewTypeControlAdded &&
+ _viewBaseType != null &&
+ _directiveType == DirectiveType.Master) {
+
+ // If we're dealing with a master page that needs to have its base type set, do it here.
+ // It's done by adding the ViewType control, which has a builder that sets the base type.
+
+ // The code currently assumes that the file in question contains a code snippet, since
+ // that's the item we key off of in order to know when to add the ViewType control.
+
+ Hashtable attribs = new Hashtable();
+ attribs["typename"] = _viewBaseType;
+ AddControl(typeof(ViewType), attribs);
+ _viewTypeControlAdded = true;
+ }
+
+ return base.ProcessCodeConstruct(codeType, code);
+ }
+
+ // Everything else in this class is unrelated to our 'inherits' handling.
+ // Since PageParserFilter blocks everything by default, we need to unblock it
+
+ public override bool AllowCode {
+ get {
+ return true;
+ }
+ }
+
+ public override bool AllowBaseType(Type baseType) {
+ return true;
+ }
+
+ public override bool AllowControl(Type controlType, ControlBuilder builder) {
+ return true;
+ }
+
+ public override bool AllowVirtualReference(string referenceVirtualPath, VirtualReferenceType referenceType) {
+ return true;
+ }
+
+ public override bool AllowServerSideInclude(string includeVirtualPath) {
+ return true;
+ }
+
+ public override int NumberOfControlsAllowed {
+ get {
+ return -1;
+ }
+ }
+
+ public override int NumberOfDirectDependenciesAllowed {
+ get {
+ return -1;
+ }
+ }
+
+ public override int TotalNumberOfDependenciesAllowed {
+ get {
+ return -1;
+ }
+ }
+
+ private enum DirectiveType {
+ Unknown,
+ Page,
+ UserControl,
+ Master,
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewUserControl.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewUserControl.cs
new file mode 100644
index 00000000000..d1ec1e14ff9
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewUserControl.cs
@@ -0,0 +1,200 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.ComponentModel;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Globalization;
+ using System.IO;
+ using System.Web.Mvc.Resources;
+ using System.Web.UI;
+
+ [FileLevelControlBuilder(typeof(ViewUserControlControlBuilder))]
+ public class ViewUserControl : UserControl, IViewDataContainer {
+ private AjaxHelper<object> _ajaxHelper;
+ private HtmlHelper<object> _htmlHelper;
+ private ViewContext _viewContext;
+ private ViewDataDictionary _viewData;
+ private string _viewDataKey;
+
+ public AjaxHelper<object> Ajax {
+ get {
+ if (_ajaxHelper == null) {
+ _ajaxHelper = new AjaxHelper<object>(ViewContext, this);
+ }
+ return _ajaxHelper;
+ }
+ }
+
+ public HtmlHelper<object> Html {
+ get {
+ if (_htmlHelper == null) {
+ _htmlHelper = new HtmlHelper<object>(ViewContext, this);
+ }
+ return _htmlHelper;
+ }
+ }
+
+ public object Model {
+ get {
+ return ViewData.Model;
+ }
+ }
+
+ public TempDataDictionary TempData {
+ get {
+ return ViewPage.TempData;
+ }
+ }
+
+ public UrlHelper Url {
+ get {
+ return ViewPage.Url;
+ }
+ }
+
+ [Browsable(false)]
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public ViewContext ViewContext {
+ get {
+ return _viewContext ?? ViewPage.ViewContext;
+ }
+ set {
+ _viewContext = value;
+ }
+ }
+
+ [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly",
+ Justification = "This is the mechanism by which the ViewUserControl gets its ViewDataDictionary object.")]
+ [Browsable(false)]
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public ViewDataDictionary ViewData {
+ get {
+ EnsureViewData();
+ return _viewData;
+ }
+ set {
+ SetViewData(value);
+ }
+ }
+
+ [DefaultValue("")]
+ public string ViewDataKey {
+ get {
+ return _viewDataKey ?? String.Empty;
+ }
+ set {
+ _viewDataKey = value;
+ }
+ }
+
+ internal ViewPage ViewPage {
+ get {
+ ViewPage viewPage = Page as ViewPage;
+ if (viewPage == null) {
+ throw new InvalidOperationException(MvcResources.ViewUserControl_RequiresViewPage);
+ }
+ return viewPage;
+ }
+ }
+
+ public HtmlTextWriter Writer {
+ get {
+ return ViewPage.Writer;
+ }
+ }
+
+ protected virtual void SetViewData(ViewDataDictionary viewData) {
+ _viewData = viewData;
+ }
+
+ protected void EnsureViewData() {
+ if (_viewData != null) {
+ return;
+ }
+
+ // Get the ViewData for this ViewUserControl, optionally using the specified ViewDataKey
+ IViewDataContainer vdc = GetViewDataContainer(this);
+ if (vdc == null) {
+ throw new InvalidOperationException(
+ String.Format(
+ CultureInfo.CurrentUICulture,
+ MvcResources.ViewUserControl_RequiresViewDataProvider,
+ AppRelativeVirtualPath));
+ }
+
+ ViewDataDictionary myViewData = vdc.ViewData;
+
+ // If we have a ViewDataKey, try to extract the ViewData from the dictionary, otherwise
+ // return the container's ViewData.
+ if (!String.IsNullOrEmpty(ViewDataKey)) {
+ object target = myViewData.Eval(ViewDataKey);
+ myViewData = target as ViewDataDictionary ?? new ViewDataDictionary(myViewData) { Model = target };
+ }
+
+ SetViewData(myViewData);
+ }
+
+ private static IViewDataContainer GetViewDataContainer(Control control) {
+ // Walk up the control hierarchy until we find someone that implements IViewDataContainer
+ while (control != null) {
+ control = control.Parent;
+ IViewDataContainer vdc = control as IViewDataContainer;
+ if (vdc != null) {
+ return vdc;
+ }
+ }
+ return null;
+ }
+
+ public virtual void RenderView(ViewContext viewContext) {
+ ViewUserControlContainerPage containerPage = new ViewUserControlContainerPage(this);
+
+ RenderViewAndRestoreContentType(containerPage, viewContext);
+ }
+
+ internal static void RenderViewAndRestoreContentType(ViewPage containerPage, ViewContext viewContext) {
+ // We need to restore the Content-Type since Page.SetIntrinsics() will reset it. It's not possible
+ // to work around the call to SetIntrinsics() since the control's render method requires the
+ // containing page's Response property to be non-null, and SetIntrinsics() is the only way to set
+ // this.
+ string savedContentType = viewContext.HttpContext.Response.ContentType;
+ containerPage.RenderView(viewContext);
+ viewContext.HttpContext.Response.ContentType = savedContentType;
+ }
+
+ [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "textWriter",
+ Justification = "This method existed in MVC 1.0 and has been deprecated.")]
+ [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic",
+ Justification = "This method existed in MVC 1.0 and has been deprecated.")]
+ [Obsolete("The TextWriter is now provided by the ViewContext object passed to the RenderView method.", true /* error */)]
+ public void SetTextWriter(TextWriter textWriter) {
+ // this is now a no-op
+ }
+
+ private sealed class ViewUserControlContainerPage : ViewPage {
+ private readonly ViewUserControl _userControl;
+
+ public ViewUserControlContainerPage(ViewUserControl userControl) {
+ _userControl = userControl;
+ }
+
+ public override void ProcessRequest(HttpContext context) {
+ _userControl.ID = ViewPage.NextId();
+ Controls.Add(_userControl);
+
+ base.ProcessRequest(context);
+ }
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewUserControlControlBuilder.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewUserControlControlBuilder.cs
new file mode 100644
index 00000000000..8bd473ab037
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewUserControlControlBuilder.cs
@@ -0,0 +1,36 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System.CodeDom;
+ using System.Web.UI;
+
+ internal sealed class ViewUserControlControlBuilder : FileLevelUserControlBuilder {
+ internal string UserControlBaseType {
+ get;
+ set;
+ }
+
+ public override void ProcessGeneratedCode(
+ CodeCompileUnit codeCompileUnit,
+ CodeTypeDeclaration baseType,
+ CodeTypeDeclaration derivedType,
+ CodeMemberMethod buildMethod,
+ CodeMemberMethod dataBindingMethod) {
+
+ // If we find got a base class string, use it
+ if (UserControlBaseType != null) {
+ derivedType.BaseTypes[0] = new CodeTypeReference(UserControlBaseType);
+ }
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewUserControl`1.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewUserControl`1.cs
new file mode 100644
index 00000000000..1a36f6843fb
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/ViewUserControl`1.cs
@@ -0,0 +1,62 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System.Diagnostics.CodeAnalysis;
+
+ public class ViewUserControl<TModel> : ViewUserControl {
+ private AjaxHelper<TModel> _ajaxHelper;
+ private HtmlHelper<TModel> _htmlHelper;
+ private ViewDataDictionary<TModel> _viewData;
+
+ public new AjaxHelper<TModel> Ajax {
+ get {
+ if (_ajaxHelper == null) {
+ _ajaxHelper = new AjaxHelper<TModel>(ViewContext, this);
+ }
+ return _ajaxHelper;
+ }
+ }
+
+ public new HtmlHelper<TModel> Html {
+ get {
+ if (_htmlHelper == null) {
+ _htmlHelper = new HtmlHelper<TModel>(ViewContext, this);
+ }
+ return _htmlHelper;
+ }
+ }
+
+ public new TModel Model {
+ get {
+ return ViewData.Model;
+ }
+ }
+
+ [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
+ public new ViewDataDictionary<TModel> ViewData {
+ get {
+ EnsureViewData();
+ return _viewData;
+ }
+ set {
+ SetViewData(value);
+ }
+ }
+
+ protected override void SetViewData(ViewDataDictionary viewData) {
+ _viewData = new ViewDataDictionary<TModel>(viewData);
+
+ base.SetViewData(_viewData);
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/VirtualPathProviderViewEngine.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/VirtualPathProviderViewEngine.cs
new file mode 100644
index 00000000000..5dd74baf750
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/VirtualPathProviderViewEngine.cs
@@ -0,0 +1,266 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Globalization;
+ using System.Linq;
+ using System.Web;
+ using System.Web.Hosting;
+ using System.Web.Mvc.Resources;
+
+ public abstract class VirtualPathProviderViewEngine : IViewEngine {
+ // format is ":ViewCacheEntry:{cacheType}:{prefix}:{name}:{controllerName}:{areaName}:"
+ private const string _cacheKeyFormat = ":ViewCacheEntry:{0}:{1}:{2}:{3}:{4}:";
+ private const string _cacheKeyPrefix_Master = "Master";
+ private const string _cacheKeyPrefix_Partial = "Partial";
+ private const string _cacheKeyPrefix_View = "View";
+ private static readonly string[] _emptyLocations = new string[0];
+
+ private VirtualPathProvider _vpp;
+
+ [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]
+ public string[] AreaMasterLocationFormats {
+ get;
+ set;
+ }
+
+ [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]
+ public string[] AreaPartialViewLocationFormats {
+ get;
+ set;
+ }
+
+ [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]
+ public string[] AreaViewLocationFormats {
+ get;
+ set;
+ }
+
+ [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]
+ public string[] MasterLocationFormats {
+ get;
+ set;
+ }
+
+ [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]
+ public string[] PartialViewLocationFormats {
+ get;
+ set;
+ }
+
+ public IViewLocationCache ViewLocationCache {
+ get;
+ set;
+ }
+
+ [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]
+ public string[] ViewLocationFormats {
+ get;
+ set;
+ }
+
+ protected VirtualPathProvider VirtualPathProvider {
+ get {
+ if (_vpp == null) {
+ _vpp = HostingEnvironment.VirtualPathProvider;
+ }
+ return _vpp;
+ }
+ set {
+ _vpp = value;
+ }
+ }
+
+ protected VirtualPathProviderViewEngine() {
+ if (HttpContext.Current == null || HttpContext.Current.IsDebuggingEnabled) {
+ ViewLocationCache = DefaultViewLocationCache.Null;
+ }
+ else {
+ ViewLocationCache = new DefaultViewLocationCache();
+ }
+ }
+
+ private string CreateCacheKey(string prefix, string name, string controllerName, string areaName) {
+ return String.Format(CultureInfo.InvariantCulture, _cacheKeyFormat,
+ GetType().AssemblyQualifiedName, prefix, name, controllerName, areaName);
+ }
+
+ protected abstract IView CreatePartialView(ControllerContext controllerContext, string partialPath);
+
+ protected abstract IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath);
+
+ protected virtual bool FileExists(ControllerContext controllerContext, string virtualPath) {
+ return VirtualPathProvider.FileExists(virtualPath);
+ }
+
+ public virtual ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache) {
+ if (controllerContext == null) {
+ throw new ArgumentNullException("controllerContext");
+ }
+ if (String.IsNullOrEmpty(partialViewName)) {
+ throw new ArgumentException(MvcResources.Common_NullOrEmpty, "partialViewName");
+ }
+
+ string[] searched;
+ string controllerName = controllerContext.RouteData.GetRequiredString("controller");
+ string partialPath = GetPath(controllerContext, PartialViewLocationFormats, AreaPartialViewLocationFormats, "PartialViewLocationFormats", partialViewName, controllerName, _cacheKeyPrefix_Partial, useCache, out searched);
+
+ if (String.IsNullOrEmpty(partialPath)) {
+ return new ViewEngineResult(searched);
+ }
+
+ return new ViewEngineResult(CreatePartialView(controllerContext, partialPath), this);
+ }
+
+ public virtual ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache) {
+ if (controllerContext == null) {
+ throw new ArgumentNullException("controllerContext");
+ }
+ if (String.IsNullOrEmpty(viewName)) {
+ throw new ArgumentException(MvcResources.Common_NullOrEmpty, "viewName");
+ }
+
+ string[] viewLocationsSearched;
+ string[] masterLocationsSearched;
+
+ string controllerName = controllerContext.RouteData.GetRequiredString("controller");
+ string viewPath = GetPath(controllerContext, ViewLocationFormats, AreaViewLocationFormats, "ViewLocationFormats", viewName, controllerName, _cacheKeyPrefix_View, useCache, out viewLocationsSearched);
+ string masterPath = GetPath(controllerContext, MasterLocationFormats, AreaMasterLocationFormats, "MasterLocationFormats", masterName, controllerName, _cacheKeyPrefix_Master, useCache, out masterLocationsSearched);
+
+ if (String.IsNullOrEmpty(viewPath) || (String.IsNullOrEmpty(masterPath) && !String.IsNullOrEmpty(masterName))) {
+ return new ViewEngineResult(viewLocationsSearched.Union(masterLocationsSearched));
+ }
+
+ return new ViewEngineResult(CreateView(controllerContext, viewPath, masterPath), this);
+ }
+
+ private string GetPath(ControllerContext controllerContext, string[] locations, string[] areaLocations, string locationsPropertyName, string name, string controllerName, string cacheKeyPrefix, bool useCache, out string[] searchedLocations) {
+ searchedLocations = _emptyLocations;
+
+ if (String.IsNullOrEmpty(name)) {
+ return String.Empty;
+ }
+
+ string areaName = AreaHelpers.GetAreaName(controllerContext.RouteData);
+ bool usingAreas = !String.IsNullOrEmpty(areaName);
+ List<ViewLocation> viewLocations = GetViewLocations(locations, (usingAreas) ? areaLocations : null);
+
+ if (viewLocations.Count == 0) {
+ throw new InvalidOperationException(String.Format(CultureInfo.CurrentUICulture,
+ MvcResources.Common_PropertyCannotBeNullOrEmpty, locationsPropertyName));
+ }
+
+ bool nameRepresentsPath = IsSpecificPath(name);
+ string cacheKey = CreateCacheKey(cacheKeyPrefix, name, (nameRepresentsPath) ? String.Empty : controllerName, areaName);
+
+ if (useCache) {
+ return ViewLocationCache.GetViewLocation(controllerContext.HttpContext, cacheKey);
+ }
+
+ return (nameRepresentsPath) ?
+ GetPathFromSpecificName(controllerContext, name, cacheKey, ref searchedLocations) :
+ GetPathFromGeneralName(controllerContext, viewLocations, name, controllerName, areaName, cacheKey, ref searchedLocations);
+ }
+
+ private string GetPathFromGeneralName(ControllerContext controllerContext, List<ViewLocation> locations, string name, string controllerName, string areaName, string cacheKey, ref string[] searchedLocations) {
+ string result = String.Empty;
+ searchedLocations = new string[locations.Count];
+
+ for (int i = 0; i < locations.Count; i++) {
+ ViewLocation location = locations[i];
+ string virtualPath = location.Format(name, controllerName, areaName);
+
+ if (FileExists(controllerContext, virtualPath)) {
+ searchedLocations = _emptyLocations;
+ result = virtualPath;
+ ViewLocationCache.InsertViewLocation(controllerContext.HttpContext, cacheKey, result);
+ break;
+ }
+
+ searchedLocations[i] = virtualPath;
+ }
+
+ return result;
+ }
+
+ private string GetPathFromSpecificName(ControllerContext controllerContext, string name, string cacheKey, ref string[] searchedLocations) {
+ string result = name;
+
+ if (!FileExists(controllerContext, name)) {
+ result = String.Empty;
+ searchedLocations = new[] { name };
+ }
+
+ ViewLocationCache.InsertViewLocation(controllerContext.HttpContext, cacheKey, result);
+ return result;
+ }
+
+ private static List<ViewLocation> GetViewLocations(string[] viewLocationFormats, string[] areaViewLocationFormats) {
+ List<ViewLocation> allLocations = new List<ViewLocation>();
+
+ if (areaViewLocationFormats != null) {
+ foreach (string areaViewLocationFormat in areaViewLocationFormats) {
+ allLocations.Add(new AreaAwareViewLocation(areaViewLocationFormat));
+ }
+ }
+
+ if (viewLocationFormats != null) {
+ foreach (string viewLocationFormat in viewLocationFormats) {
+ allLocations.Add(new ViewLocation(viewLocationFormat));
+ }
+ }
+
+ return allLocations;
+ }
+
+ private static bool IsSpecificPath(string name) {
+ char c = name[0];
+ return (c == '~' || c == '/');
+ }
+
+ public virtual void ReleaseView(ControllerContext controllerContext, IView view) {
+ IDisposable disposable = view as IDisposable;
+ if (disposable != null) {
+ disposable.Dispose();
+ }
+ }
+
+ private class ViewLocation {
+
+ protected string _virtualPathFormatString;
+
+ public ViewLocation(string virtualPathFormatString) {
+ _virtualPathFormatString = virtualPathFormatString;
+ }
+
+ public virtual string Format(string viewName, string controllerName, string areaName) {
+ return String.Format(CultureInfo.InvariantCulture, _virtualPathFormatString, viewName, controllerName);
+ }
+
+ }
+
+ private class AreaAwareViewLocation : ViewLocation {
+
+ public AreaAwareViewLocation(string virtualPathFormatString)
+ : base(virtualPathFormatString) {
+ }
+
+ public override string Format(string viewName, string controllerName, string areaName) {
+ return String.Format(CultureInfo.InvariantCulture, _virtualPathFormatString, viewName, controllerName, areaName);
+ }
+
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/WebFormView.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/WebFormView.cs
new file mode 100644
index 00000000000..7167d8c75aa
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/WebFormView.cs
@@ -0,0 +1,109 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System;
+ using System.Globalization;
+ using System.IO;
+ using System.Web.Mvc.Resources;
+
+ public class WebFormView : IView {
+
+ private IBuildManager _buildManager;
+
+ public WebFormView(string viewPath)
+ : this(viewPath, null) {
+ }
+
+ public WebFormView(string viewPath, string masterPath) {
+ if (String.IsNullOrEmpty(viewPath)) {
+ throw new ArgumentException(MvcResources.Common_NullOrEmpty, "viewPath");
+ }
+
+ ViewPath = viewPath;
+ MasterPath = masterPath ?? String.Empty;
+ }
+
+ internal IBuildManager BuildManager {
+ get {
+ if (_buildManager == null) {
+ _buildManager = new BuildManagerWrapper();
+ }
+ return _buildManager;
+ }
+ set {
+ _buildManager = value;
+ }
+ }
+
+ public string MasterPath {
+ get;
+ private set;
+ }
+
+ public string ViewPath {
+ get;
+ private set;
+ }
+
+ public virtual void Render(ViewContext viewContext, TextWriter writer) {
+ if (viewContext == null) {
+ throw new ArgumentNullException("viewContext");
+ }
+
+ object viewInstance = BuildManager.CreateInstanceFromVirtualPath(ViewPath, typeof(object));
+ if (viewInstance == null) {
+ throw new InvalidOperationException(
+ String.Format(
+ CultureInfo.CurrentUICulture,
+ MvcResources.WebFormViewEngine_ViewCouldNotBeCreated,
+ ViewPath));
+ }
+
+ ViewPage viewPage = viewInstance as ViewPage;
+ if (viewPage != null) {
+ RenderViewPage(viewContext, viewPage);
+ return;
+ }
+
+ ViewUserControl viewUserControl = viewInstance as ViewUserControl;
+ if (viewUserControl != null) {
+ RenderViewUserControl(viewContext, viewUserControl);
+ return;
+ }
+
+ throw new InvalidOperationException(
+ String.Format(
+ CultureInfo.CurrentUICulture,
+ MvcResources.WebFormViewEngine_WrongViewBase,
+ ViewPath));
+ }
+
+ private void RenderViewPage(ViewContext context, ViewPage page) {
+ if (!String.IsNullOrEmpty(MasterPath)) {
+ page.MasterLocation = MasterPath;
+ }
+
+ page.ViewData = context.ViewData;
+ page.RenderView(context);
+ }
+
+ private void RenderViewUserControl(ViewContext context, ViewUserControl control) {
+ if (!String.IsNullOrEmpty(MasterPath)) {
+ throw new InvalidOperationException(MvcResources.WebFormViewEngine_UserControlCannotHaveMaster);
+ }
+
+ control.ViewData = context.ViewData;
+ control.RenderView(context);
+ }
+ }
+}
diff --git a/mcs/class/System.Web.Mvc2/System.Web.Mvc/WebFormViewEngine.cs b/mcs/class/System.Web.Mvc2/System.Web.Mvc/WebFormViewEngine.cs
new file mode 100644
index 00000000000..9f2d080a3aa
--- /dev/null
+++ b/mcs/class/System.Web.Mvc2/System.Web.Mvc/WebFormViewEngine.cs
@@ -0,0 +1,98 @@
+/* ****************************************************************************
+ *
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *
+ * This software is subject to the Microsoft Public License (Ms-PL).
+ * A copy of the license can be found in the license.htm file included
+ * in this distribution.
+ *
+ * You must not remove this notice, or any other, from this software.
+ *
+ * ***************************************************************************/
+
+namespace System.Web.Mvc {
+ using System.Diagnostics.CodeAnalysis;
+ using System.Net;
+ using System.Web;
+
+ public class WebFormViewEngine : VirtualPathProviderViewEngine {
+
+ private IBuildManager _buildManager;
+
+ public WebFormViewEngine() {
+ MasterLocationFormats = new[] {
+ "~/Views/{1}/{0}.master",
+ "~/Views/Shared/{0}.master"
+ };
+
+ AreaMasterLocationFormats = new[] {
+ "~/Areas/{2}/Views/{1}/{0}.master",
+ "~/Areas/{2}/Views/Shared/{0}.master",
+ };
+
+ ViewLocationFormats = new[] {
+ "~/Views/{1}/{0}.aspx",
+ "~/Views/{1}/{0}.ascx",
+ "~/Views/Shared/{0}.aspx",
+ "~/Views/Shared/{0}.ascx"
+ };
+
+ AreaViewLocationFormats = new[] {
+ "~/Areas/{2}/Views/{1}/{0}.aspx",
+ "~/Areas/{2}/Views/{1}/{0}.ascx",
+ "~/Areas/{2}/Views/Shared/{0}.aspx",
+ "~/Areas/{2}/Views/Shared/{0}.ascx",
+ };
+
+ PartialViewLocationFormats = ViewLocationFormats;
+ AreaPartialViewLocationFormats = AreaViewLocationFormats;
+ }
+
+ internal IBuildManager BuildManager {
+ get {
+ if (_buildManager == null) {
+ _buildManager = new BuildManagerWrapper();
+ }
+ return _buildManager;
+ }
+ set {
+ _buildManager = value;
+ }
+ }
+
+ protected override IView CreatePartialView(ControllerContext controllerContext, string partialPath) {
+ return new WebFormView(partialPath, null);
+ }
+
+ protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath) {
+ return new WebFormView(viewPath, masterPath);
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes",
+ Justification = "Exceptions are interpreted as indicating that the file does not exist.")]
+ protected override bool FileExists(ControllerContext controllerContext, string virtualPath) {
+ try {
+ object viewInstance = BuildManager.CreateInstanceFromVirtualPath(virtualPath, typeof(object));
+ return viewInstance != null;
+ }
+ catch (HttpException he) {
+ if (he is HttpParseException) {
+ // The build manager found something, but instantiation failed due to a runtime lookup failure
+ throw;
+ }
+
+ if (he.GetHttpCode() == (int)HttpStatusCode.NotFound) {
+ // If BuildManager returns a 404 (Not Found) that means that a file did not exist.
+ // If the view itself doesn't exist, then this method should report that rather than throw an exception.
+ if (!base.FileExists(controllerContext, virtualPath)) {
+ return false;
+ }
+ }
+
+ // All other error codes imply other errors such as compilation or parsing errors
+ throw;
+ }
+ }
+
+ }
+}
diff --git a/mcs/class/System.XML/ChangeLog b/mcs/class/System.XML/ChangeLog
index cc6b0faa22c..366c0dd5810 100644
--- a/mcs/class/System.XML/ChangeLog
+++ b/mcs/class/System.XML/ChangeLog
@@ -1,3 +1,8 @@
+2010-03-16 Jb Evain <jbevain@novell.com>
+
+ * Makefile: rename the net_2_1 profile to moonlight.
+ * net_2_1_*.dll.sources: rename to moonlight_*.dll.sources.
+
2009-11-30 Jonathan Pryor <jpryor@novell.com>
* monotouch_System.Xml.dll.sources: Add System.Xml/NamespaceHandling.cs
diff --git a/mcs/class/System.XML/Makefile b/mcs/class/System.XML/Makefile
index 6dd6a391add..ff68bdcee1a 100644
--- a/mcs/class/System.XML/Makefile
+++ b/mcs/class/System.XML/Makefile
@@ -94,7 +94,7 @@ Mono.Xml.Xsl/PatternTokenizer.cs: System.Xml.XPath/Tokenizer.cs
echo "#define XSLT_PATTERN" > $@
cat $< >>$@
-ifneq (net_2_1_raw, $(PROFILE))
+ifneq (moonlight_raw, $(PROFILE))
BUILT_SOURCES = System.Xml.XPath/Parser.cs \
Mono.Xml.Xsl/PatternParser.cs \
Mono.Xml.Xsl/PatternTokenizer.cs
diff --git a/mcs/class/System.XML/System.Xml.Schema/ChangeLog b/mcs/class/System.XML/System.Xml.Schema/ChangeLog
index c2edf583dca..fc5f9a2936f 100644
--- a/mcs/class/System.XML/System.Xml.Schema/ChangeLog
+++ b/mcs/class/System.XML/System.Xml.Schema/ChangeLog
@@ -1,3 +1,8 @@
+2010-03-15 Atsushi Enomoto <atsushi@ximian.com>
+
+ * XmlSchemaComplexType.cs : quick fix for bug #584664. Fill base
+ content type particles in prior to filling its own.
+
2010-02-16 Atsushi Enomoto <atsushi@ximian.com>
* XmlSchemaComplexType.cs : in modern .NET, xs:anyType has the
diff --git a/mcs/class/System.XML/System.Xml.Schema/XmlSchemaComplexType.cs b/mcs/class/System.XML/System.Xml.Schema/XmlSchemaComplexType.cs
index 2fc6e0db289..9ee6609a6a0 100644
--- a/mcs/class/System.XML/System.Xml.Schema/XmlSchemaComplexType.cs
+++ b/mcs/class/System.XML/System.Xml.Schema/XmlSchemaComplexType.cs
@@ -414,6 +414,14 @@ namespace System.Xml.Schema
void FillContentTypeParticle (ValidationEventHandler h, XmlSchema schema)
{
+ if (CollectProcessId == schema.CompilationId)
+ return;
+ CollectProcessId = schema.CompilationId;
+
+ var ct = BaseXmlSchemaType as XmlSchemaComplexType;
+ if (ct != null)
+ ct.FillContentTypeParticle (h, schema);
+
// {content type} => ContentType and ContentTypeParticle (later)
if (ContentModel != null) {
CollectContentTypeFromContentModel (h, schema);
@@ -423,8 +431,6 @@ namespace System.Xml.Schema
contentTypeParticle = validatableParticle.GetOptimizedParticle (true);
if (contentTypeParticle == XmlSchemaParticle.Empty && resolvedContentType == XmlSchemaContentType.ElementOnly)
resolvedContentType = XmlSchemaContentType.Empty;
-
- CollectProcessId = schema.CompilationId;
}
#region {content type}
@@ -480,7 +486,7 @@ namespace System.Xml.Schema
if (BaseSchemaTypeName == XmlSchemaComplexType.AnyTypeName)
baseComplexType = XmlSchemaComplexType.AnyType;
- // On error case, it simple reject any contents
+ // On error case, it simply rejects any contents
if (baseComplexType == null) {
validatableParticle = XmlSchemaParticle.Empty;
resolvedContentType = XmlSchemaContentType.Empty;
@@ -488,6 +494,7 @@ namespace System.Xml.Schema
}
// 3.4.2 complex content {content type}
+ // FIXME: this part is looking different than the spec. sections.
if (cce.Particle == null || cce.Particle == XmlSchemaParticle.Empty) {
// - 2.1
if (baseComplexType == null) {
diff --git a/mcs/class/System.XML/Test/System.Xml.Schema/ChangeLog b/mcs/class/System.XML/Test/System.Xml.Schema/ChangeLog
index 39c4dec4398..b5a4f30dc55 100644
--- a/mcs/class/System.XML/Test/System.Xml.Schema/ChangeLog
+++ b/mcs/class/System.XML/Test/System.Xml.Schema/ChangeLog
@@ -1,3 +1,7 @@
+2010-03-15 Atsushi Enomoto <atsushi@ximian.com>
+
+ * XmlSchemaValidatorTests.cs : added test for bug #584664.
+
2010-01-27 Atsushi Enomoto <atsushi@ximian.com>
* XmlSchemaValidatorTests.cs : added test for bug #557452, by
diff --git a/mcs/class/System.XML/Test/System.Xml.Schema/XmlSchemaValidatorTests.cs b/mcs/class/System.XML/Test/System.Xml.Schema/XmlSchemaValidatorTests.cs
index 853a3f4f117..e58e24c3590 100644
--- a/mcs/class/System.XML/Test/System.Xml.Schema/XmlSchemaValidatorTests.cs
+++ b/mcs/class/System.XML/Test/System.Xml.Schema/XmlSchemaValidatorTests.cs
@@ -290,6 +290,13 @@ namespace MonoTests.System.Xml
doc.Schemas.Add (XmlSchema.Read (XmlReader.Create (new StringReader (xsd)), null));
doc.Validate (null);
}
+
+ [Test]
+ public void Bug584664 ()
+ {
+ Validate (File.ReadAllText ("Test/XmlFiles/xsd/584664a.xml"), File.ReadAllText ("Test/XmlFiles/xsd/584664a.xsd"));
+ Validate (File.ReadAllText ("Test/XmlFiles/xsd/584664b.xml"), File.ReadAllText ("Test/XmlFiles/xsd/584664b.xsd"));
+ }
}
}
diff --git a/mcs/class/System.XML/Test/XmlFiles/xsd/584664a.xml b/mcs/class/System.XML/Test/XmlFiles/xsd/584664a.xml
new file mode 100644
index 00000000000..50db974b338
--- /dev/null
+++ b/mcs/class/System.XML/Test/XmlFiles/xsd/584664a.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0"?>
+<root xmlns="http://a">
+ <leaf/>
+</root> \ No newline at end of file
diff --git a/mcs/class/System.XML/Test/XmlFiles/xsd/584664a.xsd b/mcs/class/System.XML/Test/XmlFiles/xsd/584664a.xsd
new file mode 100644
index 00000000000..7260c0c7ce2
--- /dev/null
+++ b/mcs/class/System.XML/Test/XmlFiles/xsd/584664a.xsd
@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="http://a" xmlns="http://a" elementFormDefault="qualified" attributeFormDefault="unqualified">
+
+<xs:complexType name="base">
+ <xs:sequence>
+ <xs:element name="leaf" type="xs:string"/>
+ <xs:element name="nested" type="derived" minOccurs="0" maxOccurs="unbounded"/>
+ </xs:sequence>
+</xs:complexType>
+
+<xs:complexType name="derived">
+ <xs:complexContent>
+ <xs:extension base="base"/>
+ </xs:complexContent>
+</xs:complexType>
+
+<xs:element name="root" type="derived"/>
+
+</xs:schema>
diff --git a/mcs/class/System.XML/Test/XmlFiles/xsd/584664b.xml b/mcs/class/System.XML/Test/XmlFiles/xsd/584664b.xml
new file mode 100644
index 00000000000..58c59074388
--- /dev/null
+++ b/mcs/class/System.XML/Test/XmlFiles/xsd/584664b.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0"?>
+<root xmlns="http://b">
+ <leaf/>
+</root> \ No newline at end of file
diff --git a/mcs/class/System.XML/Test/XmlFiles/xsd/584664b.xsd b/mcs/class/System.XML/Test/XmlFiles/xsd/584664b.xsd
new file mode 100644
index 00000000000..da5e5ca9cce
--- /dev/null
+++ b/mcs/class/System.XML/Test/XmlFiles/xsd/584664b.xsd
@@ -0,0 +1,18 @@
+<?xml version="1.0"?>
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="http://b" xmlns="http://b" elementFormDefault="qualified" attributeFormDefault="unqualified">
+
+<xs:complexType name="base">
+ <xs:sequence>
+ <xs:element name="leaf" type="xs:string"/>
+ </xs:sequence>
+</xs:complexType>
+
+<xs:complexType name="derived">
+ <xs:complexContent>
+ <xs:extension base="base"/>
+ </xs:complexContent>
+</xs:complexType>
+
+<xs:element name="root" type="derived"/>
+
+</xs:schema> \ No newline at end of file
diff --git a/mcs/class/System.XML/Test/XmlFiles/xsd/ChangeLog b/mcs/class/System.XML/Test/XmlFiles/xsd/ChangeLog
index 64bdb6c1fa6..1ee824d36c5 100644
--- a/mcs/class/System.XML/Test/XmlFiles/xsd/ChangeLog
+++ b/mcs/class/System.XML/Test/XmlFiles/xsd/ChangeLog
@@ -1,3 +1,8 @@
+2010-03-15 Atsushi Enomoto <atsushi@ximian.com>
+
+ * 584664b.xsd, 584664a.xml, 584664b.xml, 584664a.xsd: new test files
+ by Florian Haag.
+
2009-05-21 Gert Driesen <drieseng@users.sourceforge.net>
* extension-attr-redefine-*.xsd: new test files.
diff --git a/mcs/class/System.XML/net_2_1_raw_System.Xml.dll.sources b/mcs/class/System.XML/moonlight_raw_System.Xml.dll.sources
index 867f13f1412..867f13f1412 100644
--- a/mcs/class/System.XML/net_2_1_raw_System.Xml.dll.sources
+++ b/mcs/class/System.XML/moonlight_raw_System.Xml.dll.sources
diff --git a/mcs/class/System.Xml.Linq/System.Xml.Linq/ChangeLog b/mcs/class/System.Xml.Linq/System.Xml.Linq/ChangeLog
index 15210973344..d02e9bf2c17 100644
--- a/mcs/class/System.Xml.Linq/System.Xml.Linq/ChangeLog
+++ b/mcs/class/System.Xml.Linq/System.Xml.Linq/ChangeLog
@@ -1,3 +1,7 @@
+2010-03-15 Rolf Bjarne Kvinge <RKvinge@novell.com>
+
+ * XElement.cs: Added two new Load overloads for NET 4.0 and Moonlight.
+
2010-02-12 Miguel de Icaza <miguel@novell.com>
* XStreamingElement.cs: Avoid an extra indirect call, by calling
diff --git a/mcs/class/System.Xml.Linq/System.Xml.Linq/XElement.cs b/mcs/class/System.Xml.Linq/System.Xml.Linq/XElement.cs
index 92c6e7f8455..6c9e925b936 100644
--- a/mcs/class/System.Xml.Linq/System.Xml.Linq/XElement.cs
+++ b/mcs/class/System.Xml.Linq/System.Xml.Linq/XElement.cs
@@ -454,6 +454,23 @@ namespace System.Xml.Linq
}
}
+#if NET_4_0 || (NET_2_1 && !MONOTOUCH)
+ public static XElement Load (Stream stream)
+ {
+ return Load (stream, LoadOptions.None);
+ }
+
+ public static XElement Load (Stream stream, LoadOptions options)
+ {
+ XmlReaderSettings s = new XmlReaderSettings ();
+ DefineDefaultSettings (s, options);
+
+ using (XmlReader r = XmlReader.Create (stream, s)) {
+ return LoadCore (r, options);
+ }
+ }
+#endif
+
internal static XElement LoadCore (XmlReader r, LoadOptions options)
{
r.MoveToContent ();
diff --git a/mcs/class/System/ChangeLog b/mcs/class/System/ChangeLog
index e3474f10dc6..e58101cd20e 100644
--- a/mcs/class/System/ChangeLog
+++ b/mcs/class/System/ChangeLog
@@ -1,3 +1,7 @@
+2010-03-16 Jb Evain <jbevain@novell.com>
+
+ * net_2_1_*.dll.sources: rename to moonlight_*.dll.sources.
+
2010-03-11 Sebastien Pouliot <sebastien@ximian.com>
* net_2_1_raw_System.dll.sources: Add existing files that are
diff --git a/mcs/class/System/monotouch_System.dll.sources b/mcs/class/System/monotouch_System.dll.sources
index 98d578110cc..354eca3153d 100644
--- a/mcs/class/System/monotouch_System.dll.sources
+++ b/mcs/class/System/monotouch_System.dll.sources
@@ -1,4 +1,4 @@
-#include net_2_1_raw_System.dll.sources
+#include moonlight_raw_System.dll.sources
../../build/common/Consts.cs
../../build/common/Locale.cs
../../build/common/MonoTODOAttribute.cs
diff --git a/mcs/class/System/net_2_1_bootstrap_System.dll.sources b/mcs/class/System/moonlight_bootstrap_System.dll.sources
index 5482dc9d092..4c12aa1dff0 100644
--- a/mcs/class/System/net_2_1_bootstrap_System.dll.sources
+++ b/mcs/class/System/moonlight_bootstrap_System.dll.sources
@@ -1,4 +1,4 @@
../../build/common/Consts.cs
../../build/common/Locale.cs
../../build/common/MonoTODOAttribute.cs
-#include net_2_1_raw_System.dll.sources
+#include moonlight_raw_System.dll.sources
diff --git a/mcs/class/System/net_2_1_raw_System.dll.sources b/mcs/class/System/moonlight_raw_System.dll.sources
index 8f6c217fc1c..8f6c217fc1c 100644
--- a/mcs/class/System/net_2_1_raw_System.dll.sources
+++ b/mcs/class/System/moonlight_raw_System.dll.sources
diff --git a/mcs/class/corlib/ChangeLog b/mcs/class/corlib/ChangeLog
index 57e212107bf..f3156b68907 100644
--- a/mcs/class/corlib/ChangeLog
+++ b/mcs/class/corlib/ChangeLog
@@ -1,3 +1,8 @@
+2010-03-16 Jb Evain <jbevain@novell.com>
+
+ * Makefile: rename the net_2_1 profile to moonlight.
+ * net_2_1_*.dll.sources: rename to moonlight_*.dll.sources.
+
2010-03-11 Sebastien Pouliot <sebastien@ximian.com>
* net_2_1_raw_corlib.dll.sources: Add System.Diagnostics.Contracts
diff --git a/mcs/class/corlib/Makefile b/mcs/class/corlib/Makefile
index 820468c9380..4206ae2742a 100644
--- a/mcs/class/corlib/Makefile
+++ b/mcs/class/corlib/Makefile
@@ -75,11 +75,3 @@ $(cmp_makefrag): $(cmp_response)
$(cmp_response) $(cmp_makefrag): Makefile $(depsdir)/.stamp
endif
-
-#
-# qh21 = Quick Hack for 2.1, to be used by impatient developers to get
-# test builds done quickly
-#
-qh21:
- make PROFILE=net_2_1_raw
- (cd ../../tools/tuner; rm tune.stamp; make tune.stamp && make PROFILE=net_2_1 install)
diff --git a/mcs/class/corlib/Mono/Runtime.cs b/mcs/class/corlib/Mono/Runtime.cs
index 953c63fc2a1..920c545e6f7 100644
--- a/mcs/class/corlib/Mono/Runtime.cs
+++ b/mcs/class/corlib/Mono/Runtime.cs
@@ -59,7 +59,7 @@ namespace Mono {
which produces faster code.
*/
[MethodImplAttribute (MethodImplOptions.InternalCall)]
- internal static extern object NewObject (RuntimeTypeHandle h);
+ internal static extern object NewObject (IntPtr h);
}
}
diff --git a/mcs/class/corlib/System.IO/ChangeLog b/mcs/class/corlib/System.IO/ChangeLog
index 7cec76790ef..ce6e19179fb 100644
--- a/mcs/class/corlib/System.IO/ChangeLog
+++ b/mcs/class/corlib/System.IO/ChangeLog
@@ -1,3 +1,7 @@
+2010-03-15 Rolf Bjarne Kvinge <RKvinge@novell.com>
+
+ * SearchOption.cs: Make public for Moonlight, this type is in SL4.
+
2010-03-12 Sebastien Pouliot <sebastien@ximian.com>
* File.cs: Enable some NET_4_0 features in NET_2_1 since they are
diff --git a/mcs/class/corlib/System.IO/SearchOption.cs b/mcs/class/corlib/System.IO/SearchOption.cs
index d77ec7bf58c..9c2bfd341c0 100644
--- a/mcs/class/corlib/System.IO/SearchOption.cs
+++ b/mcs/class/corlib/System.IO/SearchOption.cs
@@ -31,11 +31,9 @@
using System.Runtime.InteropServices;
namespace System.IO {
-#if !NET_2_1 || MONOTOUCH
[ComVisible (true)]
[Serializable]
public
-#endif
enum SearchOption {
TopDirectoryOnly = 0,
AllDirectories = 1
diff --git a/mcs/class/corlib/System/ChangeLog b/mcs/class/corlib/System/ChangeLog
index b2c326eb421..a1c23c1080e 100644
--- a/mcs/class/corlib/System/ChangeLog
+++ b/mcs/class/corlib/System/ChangeLog
@@ -1,3 +1,17 @@
+2010-03-15 Carlos Alberto Cortez <calberto.cortez@gmail.com>
+
+ * TimeSpan.cs: In 4.0 if the part parsed as days exceeds the allowed
+ range -this is, 23-, then it is processed as days instead - opposed to
+ 2.0, where we are throwing an OverflowException.
+
+2010-03-15 Carlos Alberto Cortez <calberto.cortez@gmail.com>
+
+ * TimeSpan.cs: Make the colon parsing optional -and adjust the name to
+ reflect it-, so we can properly parse the case where we only have the
+ hours and minutes. This subtle bug was hidden before, since a string
+ such "10:12" would be parsed correctly, but "10:12 " (trailing white
+ space) was getting a FormatException.
+
2010-03-12 Sebastien Pouliot <sebastien@ximian.com>
* Tuple.cs, Tuples.cs: Add them to NET_2_1 since they are parts
diff --git a/mcs/class/corlib/System/TimeSpan.cs b/mcs/class/corlib/System/TimeSpan.cs
index d0ab4c2f78c..7b8f0dc7f9e 100644
--- a/mcs/class/corlib/System/TimeSpan.cs
+++ b/mcs/class/corlib/System/TimeSpan.cs
@@ -678,13 +678,12 @@ namespace System
}
#endif
- // Parse optional (LAMESPEC) colon
- private void ParseOptColon ()
+ private void ParseColon (bool optional)
{
if (!AtEnd) {
if (_src[_cur] == ':')
_cur++;
- else
+ else if (!optional)
formatError = true;
}
}
@@ -730,13 +729,21 @@ namespace System
if (ParseOptDot ()) {
hours = ParseInt (true);
}
+#if NET_4_0
+ // if the value that was going to be used as 'hours' exceeds the range,
+ // .net keeps it as days, even if there's a colon instead of a dot ahead
+ else if (days > 23) {
+ ParseColon (false);
+ hours = ParseInt (true);
+ }
+#endif
else if (!AtEnd) {
hours = days;
days = 0;
}
- ParseOptColon();
+ ParseColon(false);
minutes = ParseInt (true);
- ParseOptColon ();
+ ParseColon (true);
seconds = ParseInt (true);
#if NET_4_0
if ( ParseOptDecimalSeparator () ) {
diff --git a/mcs/class/corlib/moonlight_bootstrap_corlib.dll.sources b/mcs/class/corlib/moonlight_bootstrap_corlib.dll.sources
new file mode 100644
index 00000000000..346a84216bb
--- /dev/null
+++ b/mcs/class/corlib/moonlight_bootstrap_corlib.dll.sources
@@ -0,0 +1,2 @@
+#include moonlight_raw_corlib.dll.sources
+
diff --git a/mcs/class/corlib/net_2_1_raw_corlib.dll.sources b/mcs/class/corlib/moonlight_raw_corlib.dll.sources
index 111cace8ad3..111cace8ad3 100644
--- a/mcs/class/corlib/net_2_1_raw_corlib.dll.sources
+++ b/mcs/class/corlib/moonlight_raw_corlib.dll.sources
diff --git a/mcs/class/corlib/net_2_1_bootstrap_corlib.dll.sources b/mcs/class/corlib/net_2_1_bootstrap_corlib.dll.sources
deleted file mode 100644
index c2216ab9f1e..00000000000
--- a/mcs/class/corlib/net_2_1_bootstrap_corlib.dll.sources
+++ /dev/null
@@ -1,2 +0,0 @@
-#include net_2_1_raw_corlib.dll.sources
-
diff --git a/mcs/errors/ChangeLog b/mcs/errors/ChangeLog
index 6362b225827..98c9c1e06c7 100644
--- a/mcs/errors/ChangeLog
+++ b/mcs/errors/ChangeLog
@@ -1,3 +1,7 @@
+2010-03-16 Jb Evain <jbevain@novell.com>
+
+ * Makefile: rename the net_2_1 profile to moonlight.
+
2009-06-18 Raja R Harinath <harinath@hurrynot.org>
* Makefile (run-test-local): Make parallel-make safe.
diff --git a/mcs/errors/Makefile b/mcs/errors/Makefile
index 605731dd506..105862568c6 100644
--- a/mcs/errors/Makefile
+++ b/mcs/errors/Makefile
@@ -35,7 +35,7 @@ TEST_SUPPORT_FILES = \
# mention all targets
all-local $(STD_TARGETS:=-local):
-VALID_PROFILE := $(filter net_2_0 net_2_1 net_4_0, $(PROFILE))
+VALID_PROFILE := $(filter net_2_0 moonlight net_4_0, $(PROFILE))
ifdef VALID_PROFILE
check: run-mcs-tests
@@ -53,7 +53,7 @@ ifeq (net_4_0, $(PROFILE))
COMPILER_NAME = dmcs
TEST_PATTERN = 'v4'
endif
-ifeq (net_2_1, $(PROFILE))
+ifeq (moonlight, $(PROFILE))
COMPILER_NAME = smcs
TEST_PATTERN = 'v2'
LOCAL_RUNTIME_FLAGS = --security=temporary-smcs-hack
diff --git a/mcs/ilasm/parser/ChangeLog b/mcs/ilasm/parser/ChangeLog
index 5480e0f957c..4d3ff33de2f 100644
--- a/mcs/ilasm/parser/ChangeLog
+++ b/mcs/ilasm/parser/ChangeLog
@@ -1,3 +1,11 @@
+2010-03-14 Zoltan Varga <vargaz@gmail.com>
+
+ * ILParser.jay: Fix the float32(<long>) case in the previous change.
+
+2010-03-14 Zoltan Varga <vargaz@gmail.com>
+
+ * ILParser.jay: Fix support for hex float literals on big-endian platforms.
+
2009-04-20 Ankit Jain <jankit@novell.com>
Fix bug #494221.
diff --git a/mcs/ilasm/parser/ILParser.jay b/mcs/ilasm/parser/ILParser.jay
index 32247371fef..b94d64121b1 100644
--- a/mcs/ilasm/parser/ILParser.jay
+++ b/mcs/ilasm/parser/ILParser.jay
@@ -3274,7 +3274,7 @@ float64 : FLOAT64
{
int i = (int) $3;
byte[] intb = BitConverter.GetBytes (i);
- $$ = (double) BitConverter.ToSingle (intb, BitConverter.IsLittleEndian ? 0 : 4);
+ $$ = (double) BitConverter.ToSingle (intb, 0);
}
| K_FLOAT32 OPEN_PARENS INT64 CLOSE_PARENS
{
@@ -3285,12 +3285,12 @@ float64 : FLOAT64
| K_FLOAT64 OPEN_PARENS INT64 CLOSE_PARENS
{
byte[] intb = BitConverter.GetBytes ((long) $3);
- $$ = BitConverter.ToDouble (intb, BitConverter.IsLittleEndian ? 0 : 4);
+ $$ = BitConverter.ToDouble (intb, 0);
}
| K_FLOAT64 OPEN_PARENS INT32 CLOSE_PARENS
{
byte[] intb = BitConverter.GetBytes ((int) $3);
- $$ = (double) BitConverter.ToSingle (intb, BitConverter.IsLittleEndian ? 0 : 4);
+ $$ = (double) BitConverter.ToSingle (intb, 0);
}
;
diff --git a/mcs/mcs/ChangeLog b/mcs/mcs/ChangeLog
index 32230c2096a..e3f2443dc42 100644
--- a/mcs/mcs/ChangeLog
+++ b/mcs/mcs/ChangeLog
@@ -1,3 +1,7 @@
+2010-03-16 Jb Evain <jbevain@novell.com>
+
+ * Makefile: rename the net_2_1 profile to moonlight.
+
2010-03-11 Marek Safar <marek.safar@gmail.com>
* statement.cs, cs-parser.jay: Use correct location for empty
diff --git a/mcs/mcs/Makefile b/mcs/mcs/Makefile
index 6d496c57f5e..43a2780851e 100644
--- a/mcs/mcs/Makefile
+++ b/mcs/mcs/Makefile
@@ -18,7 +18,7 @@ ifeq (net_2_0, $(PROFILE))
INTERNAL_GMCS = $(RUNTIME) $(RUNTIME_FLAGS) $(topdir)/class/lib/$(BOOTSTRAP_PROFILE)/gmcs.exe
endif
-ifeq (net_2_1_bootstrap, $(PROFILE))
+ifeq (moonlight_bootstrap, $(PROFILE))
INTERNAL_GMCS = $(RUNTIME) $(RUNTIME_FLAGS) $(topdir)/class/lib/$(BOOTSTRAP_PROFILE)/gmcs.exe
endif
diff --git a/mcs/tests/Makefile b/mcs/tests/Makefile
index 4ddeabc7ca8..956b930e7b3 100644
--- a/mcs/tests/Makefile
+++ b/mcs/tests/Makefile
@@ -19,7 +19,7 @@ USE_MCS_FLAGS :=
# mention all targets
all-local $(STD_TARGETS:=-local):
-VALID_PROFILE := $(filter net_2_0 net_2_1 net_4_0, $(PROFILE))
+VALID_PROFILE := $(filter net_2_0 moonlight net_4_0, $(PROFILE))
ifdef VALID_PROFILE
# casts
bootstrap-cast.exe: gen-cast-test.cs
@@ -52,7 +52,7 @@ TEST_PATTERN = 'v4'
LOCAL_RUNTIME_FLAGS = --verify-all
#TOPTIONS += '-il:ver-il-dmcs.xml'
endif
-ifeq (net_2_1, $(PROFILE))
+ifeq (moonlight, $(PROFILE))
COMPILER_NAME = smcs
TEST_PATTERN = 'v2'
LOCAL_RUNTIME_FLAGS = --security=temporary-smcs-hack
diff --git a/mcs/tools/ChangeLog b/mcs/tools/ChangeLog
index c30da189978..6906d62759a 100644
--- a/mcs/tools/ChangeLog
+++ b/mcs/tools/ChangeLog
@@ -1,3 +1,12 @@
+2010-03-16 Jb Evain <jbevain@novell.com>
+
+ * Makefile: rename the net_2_1 profile to moonlight.
+
+2010-03-15 Andrew Jorgensen <ajorgensen@novell.com>
+
+ * Makefile: Add DIST_SUBDIRS with SUBDIRS and net_4_0_dirs so that all
+ that stuff gets dist'd
+
2010-03-03 Rolf Bjarne Kvinge <RKvinge@novell.com>
* gensources.sh: Reverted to plain bash, cygwin/make seems to end up confused
diff --git a/mcs/tools/Makefile b/mcs/tools/Makefile
index dd44aad6b0a..34abc62fc54 100644
--- a/mcs/tools/Makefile
+++ b/mcs/tools/Makefile
@@ -48,18 +48,19 @@ net_2_0_dirs := \
monodoc \
compiler-tester
-net_2_1_dirs := \
+moonlight_dirs := \
compiler-tester
basic_SUBDIRS = gacutil security
net_2_0_bootstrap_SUBDIRS = resgen culevel
net_2_0_SUBDIRS := $(basic_SUBDIRS) $(net_2_0_dirs)
-net_2_1_raw_SUBDIRS := $(net_2_1_dirs)
-net_2_1_SUBDIRS := $(net_2_1_dirs)
+moonlight_raw_SUBDIRS := $(moonlight_dirs)
+moonlight_SUBDIRS := $(moonlight_dirs)
net_4_0_bootstrap_SUBDIRS = resgen culevel
net_4_0_SUBDIRS := $(net_4_0_dirs) $(basic_SUBDIRS)
-SUBDIRS = $(basic_SUBDIRS) $(net_2_0_bootstrap_SUBDIRS) $(net_2_0_SUBDIRS) $(net_2_1_SUBDIRS)
+SUBDIRS = $(basic_SUBDIRS) $(net_2_0_bootstrap_SUBDIRS) $(net_2_0_SUBDIRS) $(moonlight_SUBDIRS)
+DIST_SUBDIRS = $(SUBDIRS) $(net_4_0_dirs)
include ../build/rules.make
diff --git a/mcs/tools/compiler-tester/Makefile b/mcs/tools/compiler-tester/Makefile
index ac3dd5e358d..062bc5acaa6 100644
--- a/mcs/tools/compiler-tester/Makefile
+++ b/mcs/tools/compiler-tester/Makefile
@@ -5,14 +5,14 @@ include ../../build/rules.make
PROGRAM = $(topdir)/class/lib/$(PROFILE)/compiler-tester.exe
NO_INSTALL = yes
-ifeq (net_2_1, $(PROFILE))
+ifeq (moonlight, $(PROFILE))
all-local: $(PROGRAM)
clean-local:
rm -f $(PROGRAM)
$(PROGRAM): $(dir $(PROGRAM))/.stamp
-$(PROGRAM): $(topdir)/class/lib/net_2_1_raw/compiler-tester.exe
+$(PROGRAM): $(topdir)/class/lib/moonlight_raw/compiler-tester.exe
cp $< $@
dist-local: dist-default