diff --git a/MR3/Castle.MonoRail.sln b/MR3/Castle.MonoRail.sln
index 0ff9961d..e0427f57 100644
--- a/MR3/Castle.MonoRail.sln
+++ b/MR3/Castle.MonoRail.sln
@@ -7,6 +7,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
ComponentsPerScope.fsx = ComponentsPerScope.fsx
ListAllExports.fsx = ListAllExports.fsx
rails thoughts.txt = rails thoughts.txt
+ rakefile.rb = rakefile.rb
TODO.txt = TODO.txt
EndProjectSection
EndProject
@@ -36,14 +37,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "TestingSites", "TestingSite
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{60349639-F39F-4556-8898-E64AEC0B22A4}"
EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Extensibility", "Extensibility", "{2F740D6F-95A0-44EE-8E2C-33EC3761EC25}"
-EndProject
-Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Castle.Extensibility", "..\..\Extensibility\src\Castle.Extensibility\Castle.Extensibility.fsproj", "{10FDDA68-3450-4179-819A-248D9D628454}"
-EndProject
-Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Castle.Extensibility.Windsor", "..\..\Extensibility\src\Castle.Extensibility.Windsor\Castle.Extensibility.Windsor.fsproj", "{6A43A495-8E52-4AF7-AFE3-51CA88900B18}"
-EndProject
-Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Castle.Extensibility.Services.FSStorage", "..\..\Extensibility\src\Castle.Extensibility.Services.FSStorage\Castle.Extensibility.Services.FSStorage.fsproj", "{FF8A9176-C810-4DA8-8470-DC925B2DEC3E}"
-EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Castle.MonoRail.Integration.Tests", "tests\Castle.MonoRail.Integration.Tests\Castle.MonoRail.Integration.Tests.csproj", "{A9D55E13-01F2-49B5-97C7-336E44B218F4}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebSiteForIntegration", "tests\WebSiteForIntegration\WebSiteForIntegration.csproj", "{B3C51C43-5E58-4CEE-8F02-9CFD3981BCCF}"
@@ -56,6 +49,14 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "OData", "OData", "{1FDBC6CA
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Data.OData", "Extensions\OData\3rd\odata\System.Data.OData.csproj", "{34B74905-DF66-4BF7-958B-62C50ED1203C}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ComposableHostWebSite", "tests\ComposableHostWebSite\ComposableHostWebSite.csproj", "{A5388E06-87EF-4894-B22A-4716839A165C}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BundledWebSite1", "tests\BundledWebSite1\BundledWebSite1.csproj", "{F70E9E7E-9955-42E6-B14F-7981EBD3F389}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Castle.MonoRail.Extension.OData.Integration.Tests", "Extensions\OData\tests\Castle.MonoRail.Extension.OData.Integration.Tests\Castle.MonoRail.Extension.OData.Integration.Tests.csproj", "{7AB66D00-A3FB-4B04-BA2D-4C60F0C0B6C0}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ODataTestWebSite", "Extensions\OData\tests\ODataTestWebSite\ODataTestWebSite.csproj", "{B0D83F48-B4B4-4F6B-867F-B9D91FAE7DAD}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -382,96 +383,6 @@ Global
{FB716680-F72C-4A77-84C9-C5A3A30DD909}.Release|Mixed Platforms.Build.0 = Release|x86
{FB716680-F72C-4A77-84C9-C5A3A30DD909}.Release|x86.ActiveCfg = Release|x86
{FB716680-F72C-4A77-84C9-C5A3A30DD909}.Release|x86.Build.0 = Release|x86
- {10FDDA68-3450-4179-819A-248D9D628454}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {10FDDA68-3450-4179-819A-248D9D628454}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {10FDDA68-3450-4179-819A-248D9D628454}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
- {10FDDA68-3450-4179-819A-248D9D628454}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
- {10FDDA68-3450-4179-819A-248D9D628454}.Debug|x86.ActiveCfg = Debug|Any CPU
- {10FDDA68-3450-4179-819A-248D9D628454}.MONO28-Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {10FDDA68-3450-4179-819A-248D9D628454}.MONO28-Debug|Any CPU.Build.0 = Debug|Any CPU
- {10FDDA68-3450-4179-819A-248D9D628454}.MONO28-Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
- {10FDDA68-3450-4179-819A-248D9D628454}.MONO28-Debug|Mixed Platforms.Build.0 = Debug|Any CPU
- {10FDDA68-3450-4179-819A-248D9D628454}.MONO28-Debug|x86.ActiveCfg = Debug|Any CPU
- {10FDDA68-3450-4179-819A-248D9D628454}.MONO28-Release|Any CPU.ActiveCfg = Release|Any CPU
- {10FDDA68-3450-4179-819A-248D9D628454}.MONO28-Release|Any CPU.Build.0 = Release|Any CPU
- {10FDDA68-3450-4179-819A-248D9D628454}.MONO28-Release|Mixed Platforms.ActiveCfg = Release|Any CPU
- {10FDDA68-3450-4179-819A-248D9D628454}.MONO28-Release|Mixed Platforms.Build.0 = Release|Any CPU
- {10FDDA68-3450-4179-819A-248D9D628454}.MONO28-Release|x86.ActiveCfg = Release|Any CPU
- {10FDDA68-3450-4179-819A-248D9D628454}.NET40-Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {10FDDA68-3450-4179-819A-248D9D628454}.NET40-Debug|Any CPU.Build.0 = Debug|Any CPU
- {10FDDA68-3450-4179-819A-248D9D628454}.NET40-Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
- {10FDDA68-3450-4179-819A-248D9D628454}.NET40-Debug|Mixed Platforms.Build.0 = Debug|Any CPU
- {10FDDA68-3450-4179-819A-248D9D628454}.NET40-Debug|x86.ActiveCfg = Debug|Any CPU
- {10FDDA68-3450-4179-819A-248D9D628454}.NET40-Release|Any CPU.ActiveCfg = Release|Any CPU
- {10FDDA68-3450-4179-819A-248D9D628454}.NET40-Release|Any CPU.Build.0 = Release|Any CPU
- {10FDDA68-3450-4179-819A-248D9D628454}.NET40-Release|Mixed Platforms.ActiveCfg = Release|Any CPU
- {10FDDA68-3450-4179-819A-248D9D628454}.NET40-Release|Mixed Platforms.Build.0 = Release|Any CPU
- {10FDDA68-3450-4179-819A-248D9D628454}.NET40-Release|x86.ActiveCfg = Release|Any CPU
- {10FDDA68-3450-4179-819A-248D9D628454}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {10FDDA68-3450-4179-819A-248D9D628454}.Release|Any CPU.Build.0 = Release|Any CPU
- {10FDDA68-3450-4179-819A-248D9D628454}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
- {10FDDA68-3450-4179-819A-248D9D628454}.Release|Mixed Platforms.Build.0 = Release|Any CPU
- {10FDDA68-3450-4179-819A-248D9D628454}.Release|x86.ActiveCfg = Release|Any CPU
- {6A43A495-8E52-4AF7-AFE3-51CA88900B18}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {6A43A495-8E52-4AF7-AFE3-51CA88900B18}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {6A43A495-8E52-4AF7-AFE3-51CA88900B18}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
- {6A43A495-8E52-4AF7-AFE3-51CA88900B18}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
- {6A43A495-8E52-4AF7-AFE3-51CA88900B18}.Debug|x86.ActiveCfg = Debug|Any CPU
- {6A43A495-8E52-4AF7-AFE3-51CA88900B18}.MONO28-Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {6A43A495-8E52-4AF7-AFE3-51CA88900B18}.MONO28-Debug|Any CPU.Build.0 = Debug|Any CPU
- {6A43A495-8E52-4AF7-AFE3-51CA88900B18}.MONO28-Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
- {6A43A495-8E52-4AF7-AFE3-51CA88900B18}.MONO28-Debug|Mixed Platforms.Build.0 = Debug|Any CPU
- {6A43A495-8E52-4AF7-AFE3-51CA88900B18}.MONO28-Debug|x86.ActiveCfg = Debug|Any CPU
- {6A43A495-8E52-4AF7-AFE3-51CA88900B18}.MONO28-Release|Any CPU.ActiveCfg = Release|Any CPU
- {6A43A495-8E52-4AF7-AFE3-51CA88900B18}.MONO28-Release|Any CPU.Build.0 = Release|Any CPU
- {6A43A495-8E52-4AF7-AFE3-51CA88900B18}.MONO28-Release|Mixed Platforms.ActiveCfg = Release|Any CPU
- {6A43A495-8E52-4AF7-AFE3-51CA88900B18}.MONO28-Release|Mixed Platforms.Build.0 = Release|Any CPU
- {6A43A495-8E52-4AF7-AFE3-51CA88900B18}.MONO28-Release|x86.ActiveCfg = Release|Any CPU
- {6A43A495-8E52-4AF7-AFE3-51CA88900B18}.NET40-Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {6A43A495-8E52-4AF7-AFE3-51CA88900B18}.NET40-Debug|Any CPU.Build.0 = Debug|Any CPU
- {6A43A495-8E52-4AF7-AFE3-51CA88900B18}.NET40-Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
- {6A43A495-8E52-4AF7-AFE3-51CA88900B18}.NET40-Debug|Mixed Platforms.Build.0 = Debug|Any CPU
- {6A43A495-8E52-4AF7-AFE3-51CA88900B18}.NET40-Debug|x86.ActiveCfg = Debug|Any CPU
- {6A43A495-8E52-4AF7-AFE3-51CA88900B18}.NET40-Release|Any CPU.ActiveCfg = Release|Any CPU
- {6A43A495-8E52-4AF7-AFE3-51CA88900B18}.NET40-Release|Any CPU.Build.0 = Release|Any CPU
- {6A43A495-8E52-4AF7-AFE3-51CA88900B18}.NET40-Release|Mixed Platforms.ActiveCfg = Release|Any CPU
- {6A43A495-8E52-4AF7-AFE3-51CA88900B18}.NET40-Release|Mixed Platforms.Build.0 = Release|Any CPU
- {6A43A495-8E52-4AF7-AFE3-51CA88900B18}.NET40-Release|x86.ActiveCfg = Release|Any CPU
- {6A43A495-8E52-4AF7-AFE3-51CA88900B18}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {6A43A495-8E52-4AF7-AFE3-51CA88900B18}.Release|Any CPU.Build.0 = Release|Any CPU
- {6A43A495-8E52-4AF7-AFE3-51CA88900B18}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
- {6A43A495-8E52-4AF7-AFE3-51CA88900B18}.Release|Mixed Platforms.Build.0 = Release|Any CPU
- {6A43A495-8E52-4AF7-AFE3-51CA88900B18}.Release|x86.ActiveCfg = Release|Any CPU
- {FF8A9176-C810-4DA8-8470-DC925B2DEC3E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {FF8A9176-C810-4DA8-8470-DC925B2DEC3E}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {FF8A9176-C810-4DA8-8470-DC925B2DEC3E}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
- {FF8A9176-C810-4DA8-8470-DC925B2DEC3E}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
- {FF8A9176-C810-4DA8-8470-DC925B2DEC3E}.Debug|x86.ActiveCfg = Debug|Any CPU
- {FF8A9176-C810-4DA8-8470-DC925B2DEC3E}.MONO28-Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {FF8A9176-C810-4DA8-8470-DC925B2DEC3E}.MONO28-Debug|Any CPU.Build.0 = Debug|Any CPU
- {FF8A9176-C810-4DA8-8470-DC925B2DEC3E}.MONO28-Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
- {FF8A9176-C810-4DA8-8470-DC925B2DEC3E}.MONO28-Debug|Mixed Platforms.Build.0 = Debug|Any CPU
- {FF8A9176-C810-4DA8-8470-DC925B2DEC3E}.MONO28-Debug|x86.ActiveCfg = Debug|Any CPU
- {FF8A9176-C810-4DA8-8470-DC925B2DEC3E}.MONO28-Release|Any CPU.ActiveCfg = Release|Any CPU
- {FF8A9176-C810-4DA8-8470-DC925B2DEC3E}.MONO28-Release|Any CPU.Build.0 = Release|Any CPU
- {FF8A9176-C810-4DA8-8470-DC925B2DEC3E}.MONO28-Release|Mixed Platforms.ActiveCfg = Release|Any CPU
- {FF8A9176-C810-4DA8-8470-DC925B2DEC3E}.MONO28-Release|Mixed Platforms.Build.0 = Release|Any CPU
- {FF8A9176-C810-4DA8-8470-DC925B2DEC3E}.MONO28-Release|x86.ActiveCfg = Release|Any CPU
- {FF8A9176-C810-4DA8-8470-DC925B2DEC3E}.NET40-Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {FF8A9176-C810-4DA8-8470-DC925B2DEC3E}.NET40-Debug|Any CPU.Build.0 = Debug|Any CPU
- {FF8A9176-C810-4DA8-8470-DC925B2DEC3E}.NET40-Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
- {FF8A9176-C810-4DA8-8470-DC925B2DEC3E}.NET40-Debug|Mixed Platforms.Build.0 = Debug|Any CPU
- {FF8A9176-C810-4DA8-8470-DC925B2DEC3E}.NET40-Debug|x86.ActiveCfg = Debug|Any CPU
- {FF8A9176-C810-4DA8-8470-DC925B2DEC3E}.NET40-Release|Any CPU.ActiveCfg = Release|Any CPU
- {FF8A9176-C810-4DA8-8470-DC925B2DEC3E}.NET40-Release|Any CPU.Build.0 = Release|Any CPU
- {FF8A9176-C810-4DA8-8470-DC925B2DEC3E}.NET40-Release|Mixed Platforms.ActiveCfg = Release|Any CPU
- {FF8A9176-C810-4DA8-8470-DC925B2DEC3E}.NET40-Release|Mixed Platforms.Build.0 = Release|Any CPU
- {FF8A9176-C810-4DA8-8470-DC925B2DEC3E}.NET40-Release|x86.ActiveCfg = Release|Any CPU
- {FF8A9176-C810-4DA8-8470-DC925B2DEC3E}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {FF8A9176-C810-4DA8-8470-DC925B2DEC3E}.Release|Any CPU.Build.0 = Release|Any CPU
- {FF8A9176-C810-4DA8-8470-DC925B2DEC3E}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
- {FF8A9176-C810-4DA8-8470-DC925B2DEC3E}.Release|Mixed Platforms.Build.0 = Release|Any CPU
- {FF8A9176-C810-4DA8-8470-DC925B2DEC3E}.Release|x86.ActiveCfg = Release|Any CPU
{A9D55E13-01F2-49B5-97C7-336E44B218F4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A9D55E13-01F2-49B5-97C7-336E44B218F4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A9D55E13-01F2-49B5-97C7-336E44B218F4}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
@@ -622,6 +533,126 @@ Global
{34B74905-DF66-4BF7-958B-62C50ED1203C}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{34B74905-DF66-4BF7-958B-62C50ED1203C}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{34B74905-DF66-4BF7-958B-62C50ED1203C}.Release|x86.ActiveCfg = Release|Any CPU
+ {A5388E06-87EF-4894-B22A-4716839A165C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A5388E06-87EF-4894-B22A-4716839A165C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A5388E06-87EF-4894-B22A-4716839A165C}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {A5388E06-87EF-4894-B22A-4716839A165C}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {A5388E06-87EF-4894-B22A-4716839A165C}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {A5388E06-87EF-4894-B22A-4716839A165C}.MONO28-Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A5388E06-87EF-4894-B22A-4716839A165C}.MONO28-Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A5388E06-87EF-4894-B22A-4716839A165C}.MONO28-Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {A5388E06-87EF-4894-B22A-4716839A165C}.MONO28-Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {A5388E06-87EF-4894-B22A-4716839A165C}.MONO28-Debug|x86.ActiveCfg = Debug|Any CPU
+ {A5388E06-87EF-4894-B22A-4716839A165C}.MONO28-Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A5388E06-87EF-4894-B22A-4716839A165C}.MONO28-Release|Any CPU.Build.0 = Release|Any CPU
+ {A5388E06-87EF-4894-B22A-4716839A165C}.MONO28-Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {A5388E06-87EF-4894-B22A-4716839A165C}.MONO28-Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {A5388E06-87EF-4894-B22A-4716839A165C}.MONO28-Release|x86.ActiveCfg = Release|Any CPU
+ {A5388E06-87EF-4894-B22A-4716839A165C}.NET40-Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A5388E06-87EF-4894-B22A-4716839A165C}.NET40-Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A5388E06-87EF-4894-B22A-4716839A165C}.NET40-Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {A5388E06-87EF-4894-B22A-4716839A165C}.NET40-Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {A5388E06-87EF-4894-B22A-4716839A165C}.NET40-Debug|x86.ActiveCfg = Debug|Any CPU
+ {A5388E06-87EF-4894-B22A-4716839A165C}.NET40-Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A5388E06-87EF-4894-B22A-4716839A165C}.NET40-Release|Any CPU.Build.0 = Release|Any CPU
+ {A5388E06-87EF-4894-B22A-4716839A165C}.NET40-Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {A5388E06-87EF-4894-B22A-4716839A165C}.NET40-Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {A5388E06-87EF-4894-B22A-4716839A165C}.NET40-Release|x86.ActiveCfg = Release|Any CPU
+ {A5388E06-87EF-4894-B22A-4716839A165C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A5388E06-87EF-4894-B22A-4716839A165C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A5388E06-87EF-4894-B22A-4716839A165C}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {A5388E06-87EF-4894-B22A-4716839A165C}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {A5388E06-87EF-4894-B22A-4716839A165C}.Release|x86.ActiveCfg = Release|Any CPU
+ {F70E9E7E-9955-42E6-B14F-7981EBD3F389}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F70E9E7E-9955-42E6-B14F-7981EBD3F389}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F70E9E7E-9955-42E6-B14F-7981EBD3F389}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {F70E9E7E-9955-42E6-B14F-7981EBD3F389}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {F70E9E7E-9955-42E6-B14F-7981EBD3F389}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {F70E9E7E-9955-42E6-B14F-7981EBD3F389}.MONO28-Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F70E9E7E-9955-42E6-B14F-7981EBD3F389}.MONO28-Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F70E9E7E-9955-42E6-B14F-7981EBD3F389}.MONO28-Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {F70E9E7E-9955-42E6-B14F-7981EBD3F389}.MONO28-Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {F70E9E7E-9955-42E6-B14F-7981EBD3F389}.MONO28-Debug|x86.ActiveCfg = Debug|Any CPU
+ {F70E9E7E-9955-42E6-B14F-7981EBD3F389}.MONO28-Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F70E9E7E-9955-42E6-B14F-7981EBD3F389}.MONO28-Release|Any CPU.Build.0 = Release|Any CPU
+ {F70E9E7E-9955-42E6-B14F-7981EBD3F389}.MONO28-Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {F70E9E7E-9955-42E6-B14F-7981EBD3F389}.MONO28-Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {F70E9E7E-9955-42E6-B14F-7981EBD3F389}.MONO28-Release|x86.ActiveCfg = Release|Any CPU
+ {F70E9E7E-9955-42E6-B14F-7981EBD3F389}.NET40-Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F70E9E7E-9955-42E6-B14F-7981EBD3F389}.NET40-Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F70E9E7E-9955-42E6-B14F-7981EBD3F389}.NET40-Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {F70E9E7E-9955-42E6-B14F-7981EBD3F389}.NET40-Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {F70E9E7E-9955-42E6-B14F-7981EBD3F389}.NET40-Debug|x86.ActiveCfg = Debug|Any CPU
+ {F70E9E7E-9955-42E6-B14F-7981EBD3F389}.NET40-Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F70E9E7E-9955-42E6-B14F-7981EBD3F389}.NET40-Release|Any CPU.Build.0 = Release|Any CPU
+ {F70E9E7E-9955-42E6-B14F-7981EBD3F389}.NET40-Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {F70E9E7E-9955-42E6-B14F-7981EBD3F389}.NET40-Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {F70E9E7E-9955-42E6-B14F-7981EBD3F389}.NET40-Release|x86.ActiveCfg = Release|Any CPU
+ {F70E9E7E-9955-42E6-B14F-7981EBD3F389}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F70E9E7E-9955-42E6-B14F-7981EBD3F389}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F70E9E7E-9955-42E6-B14F-7981EBD3F389}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {F70E9E7E-9955-42E6-B14F-7981EBD3F389}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {F70E9E7E-9955-42E6-B14F-7981EBD3F389}.Release|x86.ActiveCfg = Release|Any CPU
+ {7AB66D00-A3FB-4B04-BA2D-4C60F0C0B6C0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {7AB66D00-A3FB-4B04-BA2D-4C60F0C0B6C0}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {7AB66D00-A3FB-4B04-BA2D-4C60F0C0B6C0}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {7AB66D00-A3FB-4B04-BA2D-4C60F0C0B6C0}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {7AB66D00-A3FB-4B04-BA2D-4C60F0C0B6C0}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {7AB66D00-A3FB-4B04-BA2D-4C60F0C0B6C0}.MONO28-Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {7AB66D00-A3FB-4B04-BA2D-4C60F0C0B6C0}.MONO28-Debug|Any CPU.Build.0 = Debug|Any CPU
+ {7AB66D00-A3FB-4B04-BA2D-4C60F0C0B6C0}.MONO28-Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {7AB66D00-A3FB-4B04-BA2D-4C60F0C0B6C0}.MONO28-Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {7AB66D00-A3FB-4B04-BA2D-4C60F0C0B6C0}.MONO28-Debug|x86.ActiveCfg = Debug|Any CPU
+ {7AB66D00-A3FB-4B04-BA2D-4C60F0C0B6C0}.MONO28-Release|Any CPU.ActiveCfg = Release|Any CPU
+ {7AB66D00-A3FB-4B04-BA2D-4C60F0C0B6C0}.MONO28-Release|Any CPU.Build.0 = Release|Any CPU
+ {7AB66D00-A3FB-4B04-BA2D-4C60F0C0B6C0}.MONO28-Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {7AB66D00-A3FB-4B04-BA2D-4C60F0C0B6C0}.MONO28-Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {7AB66D00-A3FB-4B04-BA2D-4C60F0C0B6C0}.MONO28-Release|x86.ActiveCfg = Release|Any CPU
+ {7AB66D00-A3FB-4B04-BA2D-4C60F0C0B6C0}.NET40-Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {7AB66D00-A3FB-4B04-BA2D-4C60F0C0B6C0}.NET40-Debug|Any CPU.Build.0 = Debug|Any CPU
+ {7AB66D00-A3FB-4B04-BA2D-4C60F0C0B6C0}.NET40-Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {7AB66D00-A3FB-4B04-BA2D-4C60F0C0B6C0}.NET40-Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {7AB66D00-A3FB-4B04-BA2D-4C60F0C0B6C0}.NET40-Debug|x86.ActiveCfg = Debug|Any CPU
+ {7AB66D00-A3FB-4B04-BA2D-4C60F0C0B6C0}.NET40-Release|Any CPU.ActiveCfg = Release|Any CPU
+ {7AB66D00-A3FB-4B04-BA2D-4C60F0C0B6C0}.NET40-Release|Any CPU.Build.0 = Release|Any CPU
+ {7AB66D00-A3FB-4B04-BA2D-4C60F0C0B6C0}.NET40-Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {7AB66D00-A3FB-4B04-BA2D-4C60F0C0B6C0}.NET40-Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {7AB66D00-A3FB-4B04-BA2D-4C60F0C0B6C0}.NET40-Release|x86.ActiveCfg = Release|Any CPU
+ {7AB66D00-A3FB-4B04-BA2D-4C60F0C0B6C0}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {7AB66D00-A3FB-4B04-BA2D-4C60F0C0B6C0}.Release|Any CPU.Build.0 = Release|Any CPU
+ {7AB66D00-A3FB-4B04-BA2D-4C60F0C0B6C0}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {7AB66D00-A3FB-4B04-BA2D-4C60F0C0B6C0}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {7AB66D00-A3FB-4B04-BA2D-4C60F0C0B6C0}.Release|x86.ActiveCfg = Release|Any CPU
+ {B0D83F48-B4B4-4F6B-867F-B9D91FAE7DAD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B0D83F48-B4B4-4F6B-867F-B9D91FAE7DAD}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B0D83F48-B4B4-4F6B-867F-B9D91FAE7DAD}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {B0D83F48-B4B4-4F6B-867F-B9D91FAE7DAD}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {B0D83F48-B4B4-4F6B-867F-B9D91FAE7DAD}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {B0D83F48-B4B4-4F6B-867F-B9D91FAE7DAD}.MONO28-Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B0D83F48-B4B4-4F6B-867F-B9D91FAE7DAD}.MONO28-Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B0D83F48-B4B4-4F6B-867F-B9D91FAE7DAD}.MONO28-Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {B0D83F48-B4B4-4F6B-867F-B9D91FAE7DAD}.MONO28-Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {B0D83F48-B4B4-4F6B-867F-B9D91FAE7DAD}.MONO28-Debug|x86.ActiveCfg = Debug|Any CPU
+ {B0D83F48-B4B4-4F6B-867F-B9D91FAE7DAD}.MONO28-Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B0D83F48-B4B4-4F6B-867F-B9D91FAE7DAD}.MONO28-Release|Any CPU.Build.0 = Release|Any CPU
+ {B0D83F48-B4B4-4F6B-867F-B9D91FAE7DAD}.MONO28-Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {B0D83F48-B4B4-4F6B-867F-B9D91FAE7DAD}.MONO28-Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {B0D83F48-B4B4-4F6B-867F-B9D91FAE7DAD}.MONO28-Release|x86.ActiveCfg = Release|Any CPU
+ {B0D83F48-B4B4-4F6B-867F-B9D91FAE7DAD}.NET40-Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B0D83F48-B4B4-4F6B-867F-B9D91FAE7DAD}.NET40-Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B0D83F48-B4B4-4F6B-867F-B9D91FAE7DAD}.NET40-Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {B0D83F48-B4B4-4F6B-867F-B9D91FAE7DAD}.NET40-Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {B0D83F48-B4B4-4F6B-867F-B9D91FAE7DAD}.NET40-Debug|x86.ActiveCfg = Debug|Any CPU
+ {B0D83F48-B4B4-4F6B-867F-B9D91FAE7DAD}.NET40-Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B0D83F48-B4B4-4F6B-867F-B9D91FAE7DAD}.NET40-Release|Any CPU.Build.0 = Release|Any CPU
+ {B0D83F48-B4B4-4F6B-867F-B9D91FAE7DAD}.NET40-Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {B0D83F48-B4B4-4F6B-867F-B9D91FAE7DAD}.NET40-Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {B0D83F48-B4B4-4F6B-867F-B9D91FAE7DAD}.NET40-Release|x86.ActiveCfg = Release|Any CPU
+ {B0D83F48-B4B4-4F6B-867F-B9D91FAE7DAD}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B0D83F48-B4B4-4F6B-867F-B9D91FAE7DAD}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B0D83F48-B4B4-4F6B-867F-B9D91FAE7DAD}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {B0D83F48-B4B4-4F6B-867F-B9D91FAE7DAD}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {B0D83F48-B4B4-4F6B-867F-B9D91FAE7DAD}.Release|x86.ActiveCfg = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -629,17 +660,18 @@ Global
GlobalSection(NestedProjects) = preSolution
{84BCA422-D499-44A4-8AD3-5039238B77BE} = {382D966B-30DE-4849-ADBB-7286C248B287}
{A0731A18-51AF-43FE-9CB5-C13F870DC1BA} = {382D966B-30DE-4849-ADBB-7286C248B287}
+ {B3C51C43-5E58-4CEE-8F02-9CFD3981BCCF} = {382D966B-30DE-4849-ADBB-7286C248B287}
+ {A5388E06-87EF-4894-B22A-4716839A165C} = {382D966B-30DE-4849-ADBB-7286C248B287}
+ {F70E9E7E-9955-42E6-B14F-7981EBD3F389} = {382D966B-30DE-4849-ADBB-7286C248B287}
{D0F1F4F2-48B0-4C46-83E4-8909C92846C9} = {60349639-F39F-4556-8898-E64AEC0B22A4}
{A9D55E13-01F2-49B5-97C7-336E44B218F4} = {60349639-F39F-4556-8898-E64AEC0B22A4}
- {B3C51C43-5E58-4CEE-8F02-9CFD3981BCCF} = {60349639-F39F-4556-8898-E64AEC0B22A4}
{8DF9E981-2558-4EFD-A455-5459F36A9B03} = {FF1223B6-11F6-4EDB-AC3E-5DDF93B23C96}
{FB716680-F72C-4A77-84C9-C5A3A30DD909} = {FF1223B6-11F6-4EDB-AC3E-5DDF93B23C96}
{51FC792E-69D3-41C4-937E-4ABAC181D918} = {FF1223B6-11F6-4EDB-AC3E-5DDF93B23C96}
- {10FDDA68-3450-4179-819A-248D9D628454} = {2F740D6F-95A0-44EE-8E2C-33EC3761EC25}
- {6A43A495-8E52-4AF7-AFE3-51CA88900B18} = {2F740D6F-95A0-44EE-8E2C-33EC3761EC25}
- {FF8A9176-C810-4DA8-8470-DC925B2DEC3E} = {2F740D6F-95A0-44EE-8E2C-33EC3761EC25}
{702F84E9-CAD2-4B64-A4C5-F915BAD4A729} = {1FDBC6CA-942F-45B1-B841-B0808D16E1DE}
{EBA1D22A-F8D5-4884-9297-2C10AD2663E7} = {1FDBC6CA-942F-45B1-B841-B0808D16E1DE}
{34B74905-DF66-4BF7-958B-62C50ED1203C} = {1FDBC6CA-942F-45B1-B841-B0808D16E1DE}
+ {7AB66D00-A3FB-4B04-BA2D-4C60F0C0B6C0} = {1FDBC6CA-942F-45B1-B841-B0808D16E1DE}
+ {B0D83F48-B4B4-4F6B-867F-B9D91FAE7DAD} = {1FDBC6CA-942F-45B1-B841-B0808D16E1DE}
EndGlobalSection
EndGlobal
diff --git a/MR3/ConsoleApplication1/.gitignore b/MR3/ConsoleApplication1/.gitignore
deleted file mode 100644
index 8f99b635..00000000
--- a/MR3/ConsoleApplication1/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-/ConsoleApplication1/
-/ConsoleApplication1/**
diff --git a/MR3/Extensions/OData/3rd/odata/System/Data/Services/Providers/ResourceSet.cs b/MR3/Extensions/OData/3rd/odata/System/Data/Services/Providers/ResourceSet.cs
index 9d954ad6..c5066739 100644
--- a/MR3/Extensions/OData/3rd/odata/System/Data/Services/Providers/ResourceSet.cs
+++ b/MR3/Extensions/OData/3rd/odata/System/Data/Services/Providers/ResourceSet.cs
@@ -14,154 +14,155 @@
namespace System.Data.Services.Providers
{
- #region Namespaces.
- using System;
- using System.Data.OData;
- using System.Diagnostics;
- #endregion Namespaces.
-
- ///
- /// Class to keep information about a resource set.
- ///
- ///
- /// Custom providers can choose to use it as is or derive from it
- /// in order to flow provider-specific data.
- ///
- [DebuggerDisplay("{Name}: {ResourceType}")]
+ #region Namespaces.
+
+ using System;
+ using System.Data.OData;
+ using System.Diagnostics;
+
+ #endregion Namespaces.
+
+ ///
+ /// Class to keep information about a resource set.
+ ///
+ ///
+ /// Custom providers can choose to use it as is or derive from it
+ /// in order to flow provider-specific data.
+ ///
+ [DebuggerDisplay("{Name}: {ResourceType}")]
#if INTERNAL_DROP
internal class ResourceSet : ODataAnnotatable
#else
- public class ResourceSet : ODataAnnotatable
+ public class ResourceSet : ODataAnnotatable
#endif
- {
- #region Fields
- ///
- /// Reference to resource type that this resource set is a collection of.
- ///
- private readonly ResourceType elementType;
-
- ///
- /// Name of the resource set.
- ///
- private readonly string name;
-
- ///
- /// Is true, if the resource set is fully initialized and validated. No more changes can be made once its set to readonly.
- ///
- private bool isReadOnly;
-
- /// Is true, if key properties should be ordered as per declared order when used for constructing OrderBy queries.
- /// Otherwise the default alphabetical order is used.
- private bool useMetadataKeyOrder;
- #endregion Fields
-
- #region Constructors
- ///
- /// Constructs a new ResourceSet instance using the specified name and ResourceType instance.
- ///
- /// name of the resource set.
- /// Reference to clr type that this resource set is a collection of.
- public ResourceSet(string name, ResourceType elementType)
- {
- ExceptionUtils.CheckArgumentStringNotNullOrEmpty(name, "name");
- ExceptionUtils.CheckArgumentNotNull(elementType, "elementType");
-
- if (elementType.ResourceTypeKind != ResourceTypeKind.EntityType)
- {
- throw new ArgumentException(Strings.ResourceSet_ResourceSetMustBeAssociatedWithEntityType);
- }
-
- this.name = name;
- this.elementType = elementType;
- }
- #endregion Constructors
-
- #region Properties
- ///
- /// Name of the resource set.
- ///
- public string Name
- {
- get { return this.name; }
- }
-
- ///
- /// Reference to resource type that this resource set is a collection of.
- ///
- public ResourceType ResourceType
- {
- get { return this.elementType; }
- }
-
- ///
- /// PlaceHolder to hold custom state information about resource set.
- ///
- public object CustomState
- {
- get
- {
- return this.GetCustomState();
- }
-
- set
- {
- this.SetCustomState(value);
- }
- }
-
- ///
- /// Returns true, if this container has been set to read only. Otherwise returns false.
- ///
- public bool IsReadOnly
- {
- get { return this.isReadOnly; }
- }
-
- ///
- /// Is true, if key properties should be ordered as per declared order when used for constructing OrderBy queries.
- /// Otherwise the default alphabetical order is used.
- ///
- public bool UseMetadataKeyOrder
- {
- get
- {
- return this.useMetadataKeyOrder;
- }
-
- set
- {
- this.ThrowIfSealed();
- this.useMetadataKeyOrder = value;
- }
- }
- #endregion Properties
-
- #region Methods
- ///
- /// Sets the resource set to readonly mode. resource sets cannot be updated once this property is set.
- ///
- public void SetReadOnly()
- {
- // If its already set to readonly, then its a no-op
- if (this.isReadOnly)
- {
- return;
- }
-
- this.elementType.SetReadOnly();
- this.isReadOnly = true;
- }
-
- ///
- /// Checks if the resource set is sealed. If not, it throws an InvalidOperationException.
- ///
- private void ThrowIfSealed()
- {
- if (this.isReadOnly)
- {
- throw new InvalidOperationException(Strings.ResourceSet_Sealed(this.Name));
- }
- }
- #endregion Methods
- }
-}
+ {
+ #region Fields
+
+ ///
+ /// Reference to resource type that this resource set is a collection of.
+ ///
+ private readonly ResourceType elementType;
+
+ ///
+ /// Name of the resource set.
+ ///
+ private readonly string name;
+
+ ///
+ /// Is true, if the resource set is fully initialized and validated. No more changes can be made once its set to readonly.
+ ///
+ private bool isReadOnly;
+
+ /// Is true, if key properties should be ordered as per declared order when used for constructing OrderBy queries.
+ /// Otherwise the default alphabetical order is used.
+ private bool useMetadataKeyOrder;
+
+ #endregion Fields
+
+ #region Constructors
+
+ ///
+ /// Constructs a new ResourceSet instance using the specified name and ResourceType instance.
+ ///
+ /// name of the resource set.
+ /// Reference to clr type that this resource set is a collection of.
+ public ResourceSet(string name, ResourceType elementType)
+ {
+ ExceptionUtils.CheckArgumentStringNotNullOrEmpty(name, "name");
+ ExceptionUtils.CheckArgumentNotNull(elementType, "elementType");
+
+ if (elementType.ResourceTypeKind != ResourceTypeKind.EntityType)
+ {
+ throw new ArgumentException(Strings.ResourceSet_ResourceSetMustBeAssociatedWithEntityType);
+ }
+
+ this.name = name;
+ this.elementType = elementType;
+ }
+
+ #endregion Constructors
+
+ #region Properties
+
+ ///
+ /// Name of the resource set.
+ ///
+ public string Name
+ {
+ get { return this.name; }
+ }
+
+ ///
+ /// Reference to resource type that this resource set is a collection of.
+ ///
+ public ResourceType ResourceType
+ {
+ get { return this.elementType; }
+ }
+
+ ///
+ /// PlaceHolder to hold custom state information about resource set.
+ ///
+ public object CustomState
+ {
+ get { return this.GetCustomState(); }
+
+ set { this.SetCustomState(value); }
+ }
+
+ ///
+ /// Returns true, if this container has been set to read only. Otherwise returns false.
+ ///
+ public bool IsReadOnly
+ {
+ get { return this.isReadOnly; }
+ }
+
+ ///
+ /// Is true, if key properties should be ordered as per declared order when used for constructing OrderBy queries.
+ /// Otherwise the default alphabetical order is used.
+ ///
+ public bool UseMetadataKeyOrder
+ {
+ get { return this.useMetadataKeyOrder; }
+
+ set
+ {
+ this.ThrowIfSealed();
+ this.useMetadataKeyOrder = value;
+ }
+ }
+
+ #endregion Properties
+
+ #region Methods
+
+ ///
+ /// Sets the resource set to readonly mode. resource sets cannot be updated once this property is set.
+ ///
+ public void SetReadOnly()
+ {
+ // If its already set to readonly, then its a no-op
+ if (this.isReadOnly)
+ {
+ return;
+ }
+
+ this.elementType.SetReadOnly();
+ this.isReadOnly = true;
+ }
+
+ ///
+ /// Checks if the resource set is sealed. If not, it throws an InvalidOperationException.
+ ///
+ private void ThrowIfSealed()
+ {
+ if (this.isReadOnly)
+ {
+ throw new InvalidOperationException(Strings.ResourceSet_Sealed(this.Name));
+ }
+ }
+
+ #endregion Methods
+ }
+}
\ No newline at end of file
diff --git a/MR3/Extensions/OData/3rd/odata/System/Data/Services/Providers/ResourceType.cs b/MR3/Extensions/OData/3rd/odata/System/Data/Services/Providers/ResourceType.cs
index efe864b2..9404d155 100644
--- a/MR3/Extensions/OData/3rd/odata/System/Data/Services/Providers/ResourceType.cs
+++ b/MR3/Extensions/OData/3rd/odata/System/Data/Services/Providers/ResourceType.cs
@@ -39,10 +39,12 @@ public class ResourceType : ODataAnnotatable
#endif
{
#region Fields.
+
///
/// Empty list of properties.
///
- internal static readonly ReadOnlyCollection EmptyProperties = new ReadOnlyCollection(new ResourceProperty[0]);
+ internal static readonly ReadOnlyCollection EmptyProperties =
+ new ReadOnlyCollection(new ResourceProperty[0]);
///
/// ResourceTypeKind for the type that this structure represents.
@@ -128,9 +130,11 @@ public class ResourceType : ODataAnnotatable
/// True if the EPM info was initialized for this type.
///
private bool epmInfoInitialized;
+
#endregion Fields.
#region Constructors.
+
///
/// Constructs a new instance of Astoria type using the specified clr type.
///
@@ -160,7 +164,8 @@ public class ResourceType : ODataAnnotatable
if (baseType != null && baseType.ResourceTypeKind != resourceTypeKind)
{
throw new ArgumentException(
- Strings.ResourceType_InvalidResourceTypeKindInheritance(resourceTypeKind.ToString(), baseType.ResourceTypeKind.ToString()),
+ Strings.ResourceType_InvalidResourceTypeKindInheritance(resourceTypeKind.ToString(),
+ baseType.ResourceTypeKind.ToString()),
"resourceTypeKind");
}
@@ -238,19 +243,18 @@ internal ResourceType(Type type, ResourceTypeKind resourceTypeKind, string names
this.baseType = baseType;
}
}
+
#endregion Constructors.
#region Properties.
+
///
/// True if the resource type includes a default stream.
///
public bool IsMediaLinkEntry
{
[DebuggerStepThrough]
- get
- {
- return this.isMediaLinkEntry;
- }
+ get { return this.isMediaLinkEntry; }
set
{
@@ -296,27 +300,27 @@ public ResourceTypeKind ResourceTypeKind
///
public ReadOnlyCollection Properties
{
- get
- {
- return this.InitializeProperties();
- }
+ get { return this.InitializeProperties(); }
}
///
/// List of properties declared on this type.
///
- [System.Diagnostics.CodeAnalysis.SuppressMessage("DataWeb.Usage", "AC0014:DoNotHandleProhibitedExceptionsRule", Justification = "always rethrows the exception")]
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("DataWeb.Usage", "AC0014:DoNotHandleProhibitedExceptionsRule",
+ Justification = "always rethrows the exception")]
public ReadOnlyCollection PropertiesDeclaredOnThisType
{
get
{
- ReadOnlyCollection readOnlyProperties = this.propertiesDeclaredOnThisType as ReadOnlyCollection;
+ ReadOnlyCollection readOnlyProperties =
+ this.propertiesDeclaredOnThisType as ReadOnlyCollection;
if (readOnlyProperties == null)
{
// This method will call the virtual method, if that's not been called yet and add the list of properties
// returned by the virtual method to the properties collection.
this.GetPropertiesDeclaredOnThisType();
- readOnlyProperties = new ReadOnlyCollection(this.propertiesDeclaredOnThisType ?? ResourceType.EmptyProperties);
+ readOnlyProperties =
+ new ReadOnlyCollection(this.propertiesDeclaredOnThisType ?? ResourceType.EmptyProperties);
if (!this.isReadOnly)
{
@@ -397,7 +401,9 @@ public ReadOnlyCollection ETagProperties
{
if (this.etagProperties == null)
{
- ReadOnlyCollection etag = new ReadOnlyCollection(this.Properties.Where(p => p.IsOfKind(ResourcePropertyKind.ETag)).ToList());
+ ReadOnlyCollection etag =
+ new ReadOnlyCollection(
+ this.Properties.Where(p => p.IsOfKind(ResourcePropertyKind.ETag)).ToList());
if (!this.isReadOnly)
{
return etag;
@@ -449,10 +455,7 @@ public bool IsAbstract
public bool IsOpenType
{
[DebuggerStepThrough]
- get
- {
- return this.isOpenType;
- }
+ get { return this.isOpenType; }
set
{
@@ -474,10 +477,7 @@ public bool IsOpenType
public bool CanReflectOnInstanceType
{
[DebuggerStepThrough]
- get
- {
- return this.canReflectOnInstanceType;
- }
+ get { return this.canReflectOnInstanceType; }
set
{
@@ -491,15 +491,9 @@ public bool CanReflectOnInstanceType
///
public object CustomState
{
- get
- {
- return this.GetCustomState();
- }
+ get { return this.GetCustomState(); }
- set
- {
- this.SetCustomState(value);
- }
+ set { this.SetCustomState(value); }
}
///
@@ -522,9 +516,11 @@ internal IEnumerable NamedStreams
return this.Properties.Where(p => p.IsOfKind(ResourcePropertyKind.Stream));
}
}
+
#endregion Properties.
#region Methods.
+
///
/// Get a ResourceType representing a primitive type given a .NET System.Type object.
///
@@ -550,7 +546,8 @@ public static ResourceType GetPrimitiveResourceType(Type type)
///
/// The of a single item in the multiValue.
/// A object representing a multiValue of the specified items.
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704", Justification = "MultiValue is a Name")]
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704", Justification = "MultiValue is a Name")
+ ]
public static MultiValueResourceType GetMultiValueResourceType(ResourceType itemType)
{
ExceptionUtils.CheckArgumentNotNull(itemType, "itemType");
@@ -601,8 +598,8 @@ public void AddEntityPropertyMappingAttribute(EntityPropertyMappingAttribute att
epm.OwnEpmAttributes.Add(attribute);
}
- public IEnumerable OwnEpmAttributes
- {
+ public IEnumerable OwnEpmAttributes
+ {
get
{
EpmResourceTypeAnnotation epm = this.Epm();
@@ -611,7 +608,7 @@ public IEnumerable OwnEpmAttributes
return Enumerable.Empty();
}
return epm.OwnEpmAttributes;
- }
+ }
}
///
@@ -784,7 +781,7 @@ private static int ResourcePropertyComparison(ResourceProperty a, ResourceProper
private static void CheckResourceTypeKind(ResourceTypeKind kind, string parameterName)
{
if (kind < ResourceTypeKind.EntityType ||
- kind > ResourceTypeKind.MultiValue)
+ kind > ResourceTypeKind.MultiValue)
{
throw new ArgumentException(Strings.General_InvalidEnumValue(kind.GetType().Name), parameterName);
}
@@ -848,7 +845,8 @@ private void AddPropertyImplementation(ResourceProperty property)
{
if (resourceProperty.Name == property.Name)
{
- throw new InvalidOperationException(Strings.ResourceType_PropertyWithSameNameAlreadyExists(resourceProperty.Name, this.FullName));
+ throw new InvalidOperationException(Strings.ResourceType_PropertyWithSameNameAlreadyExists(resourceProperty.Name,
+ this.FullName));
}
}
@@ -862,7 +860,8 @@ private void AddPropertyImplementation(ResourceProperty property)
}
// NamedStream cannot be used as key or etag (you cannot create a property with a mixed flag that contains stream)
- Debug.Assert(!property.IsOfKind(ResourcePropertyKind.Key) && !property.IsOfKind(ResourcePropertyKind.ETag), "NamedStream property kind must be used alone");
+ Debug.Assert(!property.IsOfKind(ResourcePropertyKind.Key) && !property.IsOfKind(ResourcePropertyKind.ETag),
+ "NamedStream property kind must be used alone");
Debug.Assert(!property.CanReflectOnInstanceTypeProperty, "NamedStream properties must not be able to reflect");
}
else
@@ -879,9 +878,12 @@ private void AddPropertyImplementation(ResourceProperty property)
throw new InvalidOperationException(Strings.ResourceType_KeyPropertiesOnlyOnEntityTypes);
}
- Debug.Assert(property.ResourceType.ResourceTypeKind == ResourceTypeKind.Primitive, "This check must have been done in ResourceProperty.ValidatePropertyParameters method");
- Debug.Assert(!property.IsOfKind(ResourcePropertyKind.ETag), "This check must have been done in ResourceProperty.ValidatePropertyParameters method");
- Debug.Assert(property.IsOfKind(ResourcePropertyKind.Primitive), "This check must have been done in ResourceProperty.ValidatePropertyParameters method");
+ Debug.Assert(property.ResourceType.ResourceTypeKind == ResourceTypeKind.Primitive,
+ "This check must have been done in ResourceProperty.ValidatePropertyParameters method");
+ Debug.Assert(!property.IsOfKind(ResourcePropertyKind.ETag),
+ "This check must have been done in ResourceProperty.ValidatePropertyParameters method");
+ Debug.Assert(property.IsOfKind(ResourcePropertyKind.Primitive),
+ "This check must have been done in ResourceProperty.ValidatePropertyParameters method");
}
if (property.IsOfKind(ResourcePropertyKind.ETag))
@@ -891,12 +893,16 @@ private void AddPropertyImplementation(ResourceProperty property)
throw new InvalidOperationException(Strings.ResourceType_ETagPropertiesOnlyOnEntityTypes);
}
- Debug.Assert(property.ResourceType.ResourceTypeKind == ResourceTypeKind.Primitive, "This check must have been done in ResourceProperty.ValidatePropertyParameters method");
- Debug.Assert(property.IsOfKind(ResourcePropertyKind.Primitive), "This check must have been done in ResourceProperty.ValidatePropertyParameters method");
- Debug.Assert(!property.IsOfKind(ResourcePropertyKind.Key), "This check must have been done in ResourceProperty.ValidatePropertyParameters method");
+ Debug.Assert(property.ResourceType.ResourceTypeKind == ResourceTypeKind.Primitive,
+ "This check must have been done in ResourceProperty.ValidatePropertyParameters method");
+ Debug.Assert(property.IsOfKind(ResourcePropertyKind.Primitive),
+ "This check must have been done in ResourceProperty.ValidatePropertyParameters method");
+ Debug.Assert(!property.IsOfKind(ResourcePropertyKind.Key),
+ "This check must have been done in ResourceProperty.ValidatePropertyParameters method");
}
- Debug.Assert(property.ResourceType != GetPrimitiveResourceType(typeof(System.IO.Stream)), "Non NamedStream resource using Stream type");
+ Debug.Assert(property.ResourceType != GetPrimitiveResourceType(typeof (System.IO.Stream)),
+ "Non NamedStream resource using Stream type");
}
this.propertiesDeclaredOnThisType.Add(property);
@@ -934,7 +940,8 @@ private void GetPropertiesDeclaredOnThisType()
///
private void ValidateType()
{
- Debug.Assert(this.isLoadPropertiesMethodCalled && this.IsReadOnly, "This method must be invoked only if LoadPropertiesDeclaredOnThisType has been called and the type is set to ReadOnly");
+ Debug.Assert(this.isLoadPropertiesMethodCalled && this.IsReadOnly,
+ "This method must be invoked only if LoadPropertiesDeclaredOnThisType has been called and the type is set to ReadOnly");
if (this.BaseType != null)
{
@@ -1009,6 +1016,7 @@ private void MarkEpmInfoInitialized()
epm.EpmTargetTree.Validate();
}
}
+
#endregion Methods.
}
}
\ No newline at end of file
diff --git a/MR3/Extensions/OData/3rd/odata/System/Data/Services/Providers/ServiceOperation.cs b/MR3/Extensions/OData/3rd/odata/System/Data/Services/Providers/ServiceOperation.cs
index 2bcc6a07..ce5823cb 100644
--- a/MR3/Extensions/OData/3rd/odata/System/Data/Services/Providers/ServiceOperation.cs
+++ b/MR3/Extensions/OData/3rd/odata/System/Data/Services/Providers/ServiceOperation.cs
@@ -14,287 +14,283 @@
namespace System.Data.Services.Providers
{
- #region Namespaces.
- using System;
- using System.Collections.Generic;
- using System.Collections.ObjectModel;
- using System.Data.OData;
- using System.Diagnostics;
- #endregion Namespaces.
-
- ///
- /// Use this class to represent a custom service operation.
- ///
+ #region Namespaces.
+
+ using System;
+ using System.Collections.Generic;
+ using System.Collections.ObjectModel;
+ using System.Data.OData;
+ using System.Diagnostics;
+
+ #endregion Namespaces.
+
+ ///
+ /// Use this class to represent a custom service operation.
+ ///
#if !SILVERLIGHT && !WINDOWS_PHONE
- [DebuggerVisualizer("ServiceOperation={Name}")]
+ [DebuggerVisualizer("ServiceOperation={Name}")]
#endif
#if INTERNAL_DROP
internal class ServiceOperation : ODataAnnotatable
#else
- public class ServiceOperation : ODataAnnotatable
+ public class ServiceOperation : ODataAnnotatable
#endif
- {
- ///
- /// HTTP method the service operation responds to.
- ///
- private readonly string method;
-
- ///
- /// In-order parameters for this operation.
- ///
- private readonly ReadOnlyCollection parameters;
-
- ///
- /// Kind of result expected from this operation.
- ///
- private readonly ServiceOperationResultKind resultKind;
-
- ///
- /// Type of element of the method result.
- ///
- private readonly ResourceType resultType;
-
- ///
- /// Empty parameter collection.
- ///
- private static ReadOnlyCollection emptyParameterCollection = new ReadOnlyCollection(new ServiceOperationParameter[0]);
-
- ///
- /// MIME type specified on primitive results, possibly null.
- ///
- private string mimeType;
-
- ///
- /// Entity set from which entities are read, if applicable.
- ///
- private ResourceSet resourceSet;
-
- ///
- /// Name of the service operation.
- ///
- private string name;
-
- ///
- /// Is true, if the service operation is set to readonly i.e. fully initialized and validated. No more changes can be made,
- /// after the service operation is set to readonly.
- ///
- private bool isReadOnly;
-
- ///
- /// Initializes a new instance.
- ///
- /// name of the service operation.
- /// Kind of result expected from this operation.
- /// Type of element of the method result.
- /// EntitySet of the result expected from this operation.
- /// Protocol (for example HTTP) method the service operation responds to.
- /// In-order parameters for this operation.
- public ServiceOperation(
- string name,
- ServiceOperationResultKind resultKind,
- ResourceType resultType,
- ResourceSet resultSet,
- string method,
- IEnumerable parameters)
- {
- ExceptionUtils.CheckArgumentStringNotNullOrEmpty(name, "name");
- CheckServiceOperationResultKind(resultKind, "resultKind");
- ExceptionUtils.CheckArgumentStringNotNullOrEmpty(method, "method");
-
- if ((resultKind == ServiceOperationResultKind.Void && resultType != null) ||
- (resultKind != ServiceOperationResultKind.Void && resultType == null))
- {
- throw new ArgumentException(Strings.ServiceOperation_ResultTypeAndKindMustMatch("resultKind", "resultType", ServiceOperationResultKind.Void));
- }
-
- if ((resultType == null || resultType.ResourceTypeKind != ResourceTypeKind.EntityType) && resultSet != null)
- {
- throw new ArgumentException(Strings.ServiceOperation_ResultSetMustBeNull("resultSet", "resultType"));
- }
-
- if (resultType != null && resultType.ResourceTypeKind == ResourceTypeKind.EntityType && (resultSet == null || !resultSet.ResourceType.IsAssignableFrom(resultType)))
- {
- throw new ArgumentException(Strings.ServiceOperation_ResultTypeAndResultSetMustMatch("resultType", "resultSet"));
- }
-
- if (resultType != null && resultType.ResourceTypeKind == ResourceTypeKind.MultiValue)
- {
- throw new ArgumentException(Strings.ServiceOperation_InvalidResultType(resultType.FullName));
- }
-
- if (method != HttpConstants.HttpMethodGet && method != HttpConstants.HttpMethodPost)
- {
- throw new ArgumentException(Strings.ServiceOperation_NotSupportedProtocolMethod(method, name));
- }
-
- this.name = name;
- this.resultKind = resultKind;
- this.resultType = resultType;
- this.resourceSet = resultSet;
- this.method = method;
- if (parameters == null)
- {
- this.parameters = ServiceOperation.emptyParameterCollection;
- }
- else
- {
- this.parameters = new ReadOnlyCollection(new List(parameters));
- HashSet paramNames = new HashSet(StringComparer.Ordinal);
- foreach (ServiceOperationParameter p in this.parameters)
- {
- if (!paramNames.Add(p.Name))
- {
- throw new ArgumentException(Strings.ServiceOperation_DuplicateParameterName(p.Name), "parameters");
- }
- }
- }
- }
-
- ///
- /// Protocol (for example HTTP) method the service operation responds to.
- ///
- public string Method
- {
- get { return this.method; }
- }
-
- ///
- /// MIME type specified on primitive results, possibly null.
- ///
- public string MimeType
- {
- get
- {
- return this.mimeType;
- }
-
- set
- {
- this.ThrowIfSealed();
- if (String.IsNullOrEmpty(value))
- {
- throw new InvalidOperationException(Strings.ServiceOperation_MimeTypeCannotBeEmpty(this.Name));
- }
-
- if (!HttpUtils.IsValidMediaTypeName(value))
- {
- throw new InvalidOperationException(Strings.ServiceOperation_MimeTypeNotValid(value, this.Name));
- }
-
- this.mimeType = value;
- }
- }
-
- ///
- /// Name of the service operation.
- ///
- public string Name
- {
- get { return this.name; }
- }
-
- ///
- /// Returns all the parameters for the given service operations.
- ///
- public ReadOnlyCollection Parameters
- {
- get { return this.parameters; }
- }
-
- ///
- /// Kind of result expected from this operation.
- ///
- public ServiceOperationResultKind ResultKind
- {
- get { return this.resultKind; }
- }
-
- ///
- /// Element of result type.
- ///
- ///
- /// Note that if the method returns an IEnumerable<string>,
- /// this property will be typeof(string).
- ///
- public ResourceType ResultType
- {
- get { return this.resultType; }
- }
-
- ///
- /// PlaceHolder to hold custom state information about service operation.
- ///
- public object CustomState
- {
- get
- {
- return this.GetCustomState();
- }
-
- set
- {
- this.SetCustomState(value);
- }
- }
-
- ///
- /// Returns true, if this service operation has been set to read only. Otherwise returns false.
- ///
- public bool IsReadOnly
- {
- get { return this.isReadOnly; }
- }
-
- ///
- /// Entity set from which entities are read (possibly null).
- ///
- public ResourceSet ResourceSet
- {
- get { return this.resourceSet; }
- }
-
- ///
- /// Set this service operation to readonly.
- ///
- public void SetReadOnly()
- {
- if (this.isReadOnly)
- {
- return;
- }
-
- foreach (ServiceOperationParameter parameter in this.Parameters)
- {
- parameter.SetReadOnly();
- }
-
- this.isReadOnly = true;
- }
-
- ///
- /// Check whether the given value for ServiceOperationResultKind is valid. If not, throw argument exception.
- ///
- /// value for ServiceOperationResultKind
- /// name of the parameter
- /// if the value is not valid.
- private static void CheckServiceOperationResultKind(ServiceOperationResultKind kind, string parameterName)
- {
- if (kind < ServiceOperationResultKind.DirectValue ||
- kind > ServiceOperationResultKind.Void)
- {
- throw new ArgumentException(Strings.General_InvalidEnumValue(kind.GetType().Name), parameterName);
- }
- }
-
- ///
- /// Throws an InvalidOperationException if this service operation is already set to readonly.
- ///
- private void ThrowIfSealed()
- {
- if (this.isReadOnly)
- {
- throw new InvalidOperationException(Strings.ServiceOperation_Sealed(this.Name));
- }
- }
- }
-}
+ {
+ ///
+ /// HTTP method the service operation responds to.
+ ///
+ private readonly string method;
+
+ ///
+ /// In-order parameters for this operation.
+ ///
+ private readonly ReadOnlyCollection parameters;
+
+ ///
+ /// Kind of result expected from this operation.
+ ///
+ private readonly ServiceOperationResultKind resultKind;
+
+ ///
+ /// Type of element of the method result.
+ ///
+ private readonly ResourceType resultType;
+
+ ///
+ /// Empty parameter collection.
+ ///
+ private static ReadOnlyCollection emptyParameterCollection =
+ new ReadOnlyCollection(new ServiceOperationParameter[0]);
+
+ ///
+ /// MIME type specified on primitive results, possibly null.
+ ///
+ private string mimeType;
+
+ ///
+ /// Entity set from which entities are read, if applicable.
+ ///
+ private ResourceSet resourceSet;
+
+ ///
+ /// Name of the service operation.
+ ///
+ private string name;
+
+ ///
+ /// Is true, if the service operation is set to readonly i.e. fully initialized and validated. No more changes can be made,
+ /// after the service operation is set to readonly.
+ ///
+ private bool isReadOnly;
+
+ ///
+ /// Initializes a new instance.
+ ///
+ /// name of the service operation.
+ /// Kind of result expected from this operation.
+ /// Type of element of the method result.
+ /// EntitySet of the result expected from this operation.
+ /// Protocol (for example HTTP) method the service operation responds to.
+ /// In-order parameters for this operation.
+ public ServiceOperation(
+ string name,
+ ServiceOperationResultKind resultKind,
+ ResourceType resultType,
+ ResourceSet resultSet,
+ string method,
+ IEnumerable parameters)
+ {
+ ExceptionUtils.CheckArgumentStringNotNullOrEmpty(name, "name");
+ CheckServiceOperationResultKind(resultKind, "resultKind");
+ ExceptionUtils.CheckArgumentStringNotNullOrEmpty(method, "method");
+
+ if ((resultKind == ServiceOperationResultKind.Void && resultType != null) ||
+ (resultKind != ServiceOperationResultKind.Void && resultType == null))
+ {
+ throw new ArgumentException(Strings.ServiceOperation_ResultTypeAndKindMustMatch("resultKind", "resultType",
+ ServiceOperationResultKind.Void));
+ }
+
+ if ((resultType == null || resultType.ResourceTypeKind != ResourceTypeKind.EntityType) && resultSet != null)
+ {
+ throw new ArgumentException(Strings.ServiceOperation_ResultSetMustBeNull("resultSet", "resultType"));
+ }
+
+ if (resultType != null && resultType.ResourceTypeKind == ResourceTypeKind.EntityType &&
+ (resultSet == null || !resultSet.ResourceType.IsAssignableFrom(resultType)))
+ {
+ throw new ArgumentException(Strings.ServiceOperation_ResultTypeAndResultSetMustMatch("resultType", "resultSet"));
+ }
+
+ if (resultType != null && resultType.ResourceTypeKind == ResourceTypeKind.MultiValue)
+ {
+ throw new ArgumentException(Strings.ServiceOperation_InvalidResultType(resultType.FullName));
+ }
+
+ if (method != HttpConstants.HttpMethodGet && method != HttpConstants.HttpMethodPost)
+ {
+ throw new ArgumentException(Strings.ServiceOperation_NotSupportedProtocolMethod(method, name));
+ }
+
+ this.name = name;
+ this.resultKind = resultKind;
+ this.resultType = resultType;
+ this.resourceSet = resultSet;
+ this.method = method;
+ if (parameters == null)
+ {
+ this.parameters = ServiceOperation.emptyParameterCollection;
+ }
+ else
+ {
+ this.parameters = new ReadOnlyCollection(new List(parameters));
+ HashSet paramNames = new HashSet(StringComparer.Ordinal);
+ foreach (ServiceOperationParameter p in this.parameters)
+ {
+ if (!paramNames.Add(p.Name))
+ {
+ throw new ArgumentException(Strings.ServiceOperation_DuplicateParameterName(p.Name), "parameters");
+ }
+ }
+ }
+ }
+
+ ///
+ /// Protocol (for example HTTP) method the service operation responds to.
+ ///
+ public string Method
+ {
+ get { return this.method; }
+ }
+
+ ///
+ /// MIME type specified on primitive results, possibly null.
+ ///
+ public string MimeType
+ {
+ get { return this.mimeType; }
+
+ set
+ {
+ this.ThrowIfSealed();
+ if (String.IsNullOrEmpty(value))
+ {
+ throw new InvalidOperationException(Strings.ServiceOperation_MimeTypeCannotBeEmpty(this.Name));
+ }
+
+ if (!HttpUtils.IsValidMediaTypeName(value))
+ {
+ throw new InvalidOperationException(Strings.ServiceOperation_MimeTypeNotValid(value, this.Name));
+ }
+
+ this.mimeType = value;
+ }
+ }
+
+ ///
+ /// Name of the service operation.
+ ///
+ public string Name
+ {
+ get { return this.name; }
+ }
+
+ ///
+ /// Returns all the parameters for the given service operations.
+ ///
+ public ReadOnlyCollection Parameters
+ {
+ get { return this.parameters; }
+ }
+
+ ///
+ /// Kind of result expected from this operation.
+ ///
+ public ServiceOperationResultKind ResultKind
+ {
+ get { return this.resultKind; }
+ }
+
+ ///
+ /// Element of result type.
+ ///
+ ///
+ /// Note that if the method returns an IEnumerable<string>,
+ /// this property will be typeof(string).
+ ///
+ public ResourceType ResultType
+ {
+ get { return this.resultType; }
+ }
+
+ ///
+ /// PlaceHolder to hold custom state information about service operation.
+ ///
+ public object CustomState
+ {
+ get { return this.GetCustomState(); }
+
+ set { this.SetCustomState(value); }
+ }
+
+ ///
+ /// Returns true, if this service operation has been set to read only. Otherwise returns false.
+ ///
+ public bool IsReadOnly
+ {
+ get { return this.isReadOnly; }
+ }
+
+ ///
+ /// Entity set from which entities are read (possibly null).
+ ///
+ public ResourceSet ResourceSet
+ {
+ get { return this.resourceSet; }
+ }
+
+ ///
+ /// Set this service operation to readonly.
+ ///
+ public void SetReadOnly()
+ {
+ if (this.isReadOnly)
+ {
+ return;
+ }
+
+ foreach (ServiceOperationParameter parameter in this.Parameters)
+ {
+ parameter.SetReadOnly();
+ }
+
+ this.isReadOnly = true;
+ }
+
+ ///
+ /// Check whether the given value for ServiceOperationResultKind is valid. If not, throw argument exception.
+ ///
+ /// value for ServiceOperationResultKind
+ /// name of the parameter
+ /// if the value is not valid.
+ private static void CheckServiceOperationResultKind(ServiceOperationResultKind kind, string parameterName)
+ {
+ if (kind < ServiceOperationResultKind.DirectValue ||
+ kind > ServiceOperationResultKind.Void)
+ {
+ throw new ArgumentException(Strings.General_InvalidEnumValue(kind.GetType().Name), parameterName);
+ }
+ }
+
+ ///
+ /// Throws an InvalidOperationException if this service operation is already set to readonly.
+ ///
+ private void ThrowIfSealed()
+ {
+ if (this.isReadOnly)
+ {
+ throw new InvalidOperationException(Strings.ServiceOperation_Sealed(this.Name));
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/Api.fs b/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/Api.fs
index 0560a44d..2985327a 100644
--- a/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/Api.fs
+++ b/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/Api.fs
@@ -1,4 +1,18 @@
-
+// Copyright 2004-2012 Castle Project - http://www.castleproject.org/
+// Hamilton Verissimo de Oliveira and individual contributors as indicated.
+// See the committers.txt/contributors.txt in the distribution for a
+// full listing of individual contributors.
+//
+// This is free software; you can redistribute it and/or modify it
+// under the terms of the GNU Lesser General Public License as
+// published by the Free Software Foundation; either version 3 of
+// the License, or (at your option) any later version.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this software; if not, write to the Free
+// Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+// 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+
namespace Castle.MonoRail
open System
@@ -16,6 +30,7 @@ namespace Castle.MonoRail
let mutable _containerName = containerName
let _entities = List()
+ let _entSeq : EntitySetConfig seq = upcast _entities
let _resourcetypes = lazy ( let rts = ResourceMetadataBuilder.build(_schemaNs, _entities)
rts |> Seq.iter (fun rt -> rt.SetReadOnly() )
@@ -23,7 +38,8 @@ namespace Castle.MonoRail
let _resourcesets = lazy ( _resourcetypes.Force()
|> Seq.filter (fun rt -> rt.ResourceTypeKind = ResourceTypeKind.EntityType && (_entities |> Seq.exists (fun e -> e.EntityName === rt.Name) ) )
- |> Seq.map (fun rt -> (let rs = ResourceSet(rt.Name, rt)
+ |> Seq.map (fun rt -> (let name = (_entSeq |> Seq.find(fun e -> e.TargetType = rt.InstanceType)).EntitySetName
+ let rs = ResourceSet(name, rt)
rs.SetReadOnly()
rs ))
|> box :?> ResourceSet seq)
@@ -32,14 +48,14 @@ namespace Castle.MonoRail
member x.SchemaNamespace with get() = schemaNamespace
member x.ContainerName with get() = containerName
- member x.EntitySet<'a>(entityName:string, source:IQueryable<'a>) =
+ member x.EntitySet<'a>(entitySetName:string, source:IQueryable<'a>) =
if _resourcesets.IsValueCreated then raise(InvalidOperationException("Model is frozen since ResourceSets were built"))
- let cfg = EntitySetConfigurator(entityName, source)
+ let entityType = typeof<'a>
+ let cfg = EntitySetConfigurator(entitySetName, entityType.Name, source)
_entities.Add cfg
cfg
- member x.Entities : EntitySetConfig seq = upcast _entities
-
+ member x.Entities = _entSeq
member internal x.ResourceSets = _resourcesets.Force()
member internal x.ResourceTypes = _resourcetypes.Force()
member internal x.GetResourceType(name) =
@@ -48,10 +64,13 @@ namespace Castle.MonoRail
member internal x.GetResourceSet(name) =
x.ResourceSets
|> Seq.tryFind (fun rs -> StringComparer.OrdinalIgnoreCase.Equals( rs.Name, name ) )
- member internal x.GetQueryable(name) =
- match _entities |> Seq.tryFind (fun e -> StringComparer.OrdinalIgnoreCase.Equals(e.EntityName, name)) with
+ member internal x.GetQueryable(rs:ResourceSet) =
+ match _entities |> Seq.tryFind (fun e -> StringComparer.OrdinalIgnoreCase.Equals(e.EntitySetName, rs.Name)) with
| Some e -> e.Source
| _ -> null
+ member internal x.GetRelatedResourceSet(rt:ResourceType) =
+ x.ResourceSets
+ |> Seq.tryFind (fun rs -> rs.ResourceType = rt )
interface IDataServiceMetadataProvider with
member x.ContainerNamespace = x.SchemaNamespace
diff --git a/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/AstLinqTranslator.fs b/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/AstLinqTranslator.fs
new file mode 100644
index 00000000..15a26945
--- /dev/null
+++ b/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/AstLinqTranslator.fs
@@ -0,0 +1,185 @@
+// Copyright 2004-2012 Castle Project - http://www.castleproject.org/
+// Hamilton Verissimo de Oliveira and individual contributors as indicated.
+// See the committers.txt/contributors.txt in the distribution for a
+// full listing of individual contributors.
+//
+// This is free software; you can redistribute it and/or modify it
+// under the terms of the GNU Lesser General Public License as
+// published by the Free Software Foundation; either version 3 of
+// the License, or (at your option) any later version.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this software; if not, write to the Free
+// Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+// 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+
+namespace Castle.MonoRail.Extension.OData
+
+open System
+open System.Linq
+open System.Linq.Expressions
+open System.Collections
+open System.Collections.Generic
+open System.Collections.Specialized
+open System.Data.OData
+open System.Data.Services.Providers
+
+
+module AstLinqTranslator =
+
+ type This = static member Assembly = typeof.Assembly
+
+ let typed_select_methodinfo =
+ let m = This.Assembly.GetType("Castle.MonoRail.Extension.OData.AstLinqTranslator").GetMethod("typed_select")
+ System.Diagnostics.Debug.Assert(m <> null, "Could not get typed_select methodinfo")
+ m
+
+ let typed_queryable_filter_methodinfo =
+ let m = This.Assembly.GetType("Castle.MonoRail.Extension.OData.AstLinqTranslator").GetMethod("typed_queryable_filter")
+ System.Diagnostics.Debug.Assert(m <> null, "Could not get typed_queryable_filter methodinfo")
+ m
+
+ let typed_queryable_orderby_methodinfo =
+ let m = This.Assembly.GetType("Castle.MonoRail.Extension.OData.AstLinqTranslator").GetMethod("typed_queryable_orderby")
+ System.Diagnostics.Debug.Assert(m <> null, "Could not get typed_queryable_orderby methodinfo")
+ m
+
+ let select_by_key (rt:ResourceType) (source:IQueryable) (key:string) =
+ // for now support for a single key
+ let keyProp = Seq.head rt.KeyProperties
+
+ let keyVal =
+ // weak!!
+ System.Convert.ChangeType(key, keyProp.ResourceType.InstanceType)
+
+ let rtType = rt.InstanceType
+ let ``method`` = typed_select_methodinfo.MakeGenericMethod([|rtType|])
+ let result = ``method``.Invoke(null, [|source; keyVal; keyProp|])
+ if result = null then failwithf "Lookup of entity %s for key %s failed." rt.Name key
+ result
+
+ let apply_queryable_filter (rt:ResourceType) (items:IQueryable) (ast:QueryAst) =
+ let rtType = rt.InstanceType
+ let ``method`` = typed_queryable_filter_methodinfo.MakeGenericMethod([|rtType|])
+ ``method``.Invoke(null, [|items; ast|])
+
+ let apply_queryable_orderby (rt:ResourceType) (items:IQueryable) (ast:OrderByAst seq) =
+ let rtType = rt.InstanceType
+ let ``method`` = typed_queryable_orderby_methodinfo.MakeGenericMethod([|rtType|])
+ ``method``.Invoke(null, [|items; ast|])
+
+ let typed_select<'a> (source:IQueryable) (key:obj) (keyProp:ResourceProperty) =
+ let typedSource = source :?> IQueryable<'a>
+ let parameter = Expression.Parameter(source.ElementType, "element")
+ let e = Expression.Property(parameter, keyProp.Name)
+
+ let bExp = Expression.Equal(e, Expression.Constant(key))
+ let exp = Expression.Lambda(bExp, [parameter]) :?> Expression>
+ typedSource.FirstOrDefault(exp)
+
+
+ let internal build_linq_exp_tree (paramType:Type) (ast:QueryAst) =
+
+ let parameter = Expression.Parameter(paramType, "element")
+
+ let rec build_tree (node) : Expression =
+ match node with
+ | Element -> upcast parameter
+ | Null -> upcast Expression.Constant(null)
+ | Literal (t, v) -> upcast Expression.Constant(v, t)
+
+ | PropertyAccess (s, prop, rt) ->
+ let target = build_tree s
+ upcast Expression.Property(target, prop)
+
+ | UnaryExp (e, op, rt) ->
+ let exp = build_tree e
+ match op with
+ | UnaryOp.Negate -> upcast Expression.Negate (exp)
+ | UnaryOp.Not -> upcast Expression.Not (exp)
+ | UnaryOp.Cast -> upcast Expression.Convert(exp, rt.InstanceType)
+ // | UnaryOp.IsOf -> upcast Expression.TypeIs
+ | _ -> failwithf "Unsupported unary op %O" op
+
+ | BinaryExp (l, r, op, rt) ->
+ let leftExp = build_tree l
+ let rightExp = build_tree r
+ match op with
+ | BinaryOp.Eq -> upcast Expression.Equal(leftExp, rightExp)
+ | BinaryOp.Neq -> upcast Expression.NotEqual(leftExp, rightExp)
+ | BinaryOp.Add -> upcast Expression.Add(leftExp, rightExp)
+ | BinaryOp.And -> upcast Expression.And(leftExp, rightExp)
+ | BinaryOp.Or -> upcast Expression.Or(leftExp, rightExp)
+ | BinaryOp.Mul -> upcast Expression.Multiply(leftExp, rightExp)
+ | BinaryOp.Div -> upcast Expression.Divide(leftExp, rightExp)
+ | BinaryOp.Mod -> upcast Expression.Modulo(leftExp, rightExp)
+ | BinaryOp.Sub -> upcast Expression.Subtract(leftExp, rightExp)
+ | BinaryOp.LessT -> upcast Expression.LessThan(leftExp, rightExp)
+ | BinaryOp.GreatT -> upcast Expression.GreaterThan(leftExp, rightExp)
+ | BinaryOp.LessET -> upcast Expression.LessThanOrEqual(leftExp, rightExp)
+ | BinaryOp.GreatET -> upcast Expression.GreaterThanOrEqual(leftExp, rightExp)
+
+ | _ -> failwithf "Unsupported binary op %O" op
+
+ | _ -> failwithf "Unsupported node %O" node
+
+ let exp = build_tree ast
+ (exp, parameter)
+
+ // a predicate is a Func
+ let build_linq_exp_predicate<'a> (paramType:Type) (ast:QueryAst) =
+ let rootExp, parameter = build_linq_exp_tree paramType ast
+ Expression.Lambda(rootExp, [parameter]) :?> Expression>
+
+ let build_linq_exp_lambda (paramType:Type) (ast:QueryAst) =
+ let rootExp, parameter = build_linq_exp_tree paramType ast
+ Expression.Lambda(rootExp, [parameter])
+
+ (*
+ // a member access is a Func
+ let build_linq_exp_memberaccess<'a> (paramType:Type) (ast:QueryAst) =
+ let rootExp, parameter = build_linq_exp_tree paramType ast
+ Expression.Lambda(rootExp, [parameter]) :?> Expression>
+ *)
+
+ let typed_queryable_filter<'a> (source:IQueryable) (ast:QueryAst) : IQueryable =
+ let typedSource = source :?> IQueryable<'a>
+ let orExp = build_linq_exp_predicate<'a> source.ElementType ast
+ let exp : Expression = upcast Expression.Quote( orExp )
+ let where = Expression.Call(typeof, "Where", [|source.ElementType|], [|source.Expression; exp|])
+ typedSource.Provider.CreateQuery(where)
+
+
+ let typed_queryable_orderby<'a> (source:IQueryable) (nodes:OrderByAst seq) : IQueryable =
+ // let typedSource = source :?> IQueryable<'a>
+ let elemType = typeof<'a>
+ let isFirstCall = ref true
+
+ let applyOrder (source:IQueryable) node =
+ let build_lambda ast : Expression * Type =
+ let exp = build_linq_exp_lambda elemType ast
+ let retType = exp.Body.Type
+ upcast Expression.Quote exp, retType
+ let asc, desc =
+ if !isFirstCall
+ then "OrderBy", "OrderByDescending"
+ else "ThenBy", "ThenByDescending"
+ isFirstCall := false
+
+ let exp, retType, op =
+ match node with
+ | OrderByAst.Asc ast ->
+ let exp, retType = build_lambda ast
+ exp, retType, asc
+ | OrderByAst.Desc ast ->
+ let exp, retType = build_lambda ast
+ exp, retType, desc
+ | _ -> failwith "Unsupported node"
+
+ source.Provider.CreateQuery( Expression.Call(typeof, op, [|source.ElementType; retType|], [|source.Expression; exp|]) )
+
+ // applies expression, which returns a "new"
+ // queryable, which is then used on the next call
+ nodes |> Seq.fold (fun source c -> applyOrder source c ) source
+
+
diff --git a/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/Castle.MonoRail.Extension.OData.fsproj b/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/Castle.MonoRail.Extension.OData.fsproj
index 7e9ec099..d73b9a45 100644
--- a/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/Castle.MonoRail.Extension.OData.fsproj
+++ b/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/Castle.MonoRail.Extension.OData.fsproj
@@ -34,8 +34,17 @@
--keyfile:..\..\..\..\buildscripts\CastleKey.snk
+
+ ..\..\..\..\lib\FParsec.dll
+
+
+ ..\..\..\..\lib\FParsecCS.dll
+
+
+ ..\..\..\..\lib\Newtonsoft.Json.dll
+
..\..\..\..\lib\System.ComponentModel.Composition.Codeplex.dll
@@ -52,15 +61,23 @@
+
+
-
+
+
+
+
+
-
+
+
+
diff --git a/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/Configurators.fs b/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/Configurators.fs
new file mode 100644
index 00000000..11a17622
--- /dev/null
+++ b/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/Configurators.fs
@@ -0,0 +1,82 @@
+// Copyright 2004-2012 Castle Project - http://www.castleproject.org/
+// Hamilton Verissimo de Oliveira and individual contributors as indicated.
+// See the committers.txt/contributors.txt in the distribution for a
+// full listing of individual contributors.
+//
+// This is free software; you can redistribute it and/or modify it
+// under the terms of the GNU Lesser General Public License as
+// published by the Free Software Foundation; either version 3 of
+// the License, or (at your option) any later version.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this software; if not, write to the Free
+// Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+// 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+
+namespace Castle.MonoRail.OData
+
+ open System
+ open System.Collections.Generic
+ open System.Data.OData
+ open System.Data.Services.Providers
+ open System.Data.Services.Common
+ open System.Linq
+ open System.Linq.Expressions
+ open System.Reflection
+
+
+ []
+ type EntitySetConfig(entitySetName:string, entityName:string, source, targetType:Type) =
+ let _entMapAttrs : List = List()
+ let _customPropInfo = Dictionary()
+ let mutable _entityName = entityName
+ member x.TargetType = targetType
+ member x.EntitySetName : string = entitySetName
+ member x.EntityName with get() = _entityName and set(v) = _entityName <- v
+ member x.Source : IQueryable = source
+ member internal x.EntityPropertyAttributes : List = _entMapAttrs
+ member internal x.CustomPropConfig = _customPropInfo
+
+ and []
+ PropConfigurator(mappedType:Type) =
+ class
+ abstract member GetValue : instance:obj * source:obj -> obj
+ abstract member SetValue : instance:obj * value:obj -> unit
+ member x.MappedType = mappedType
+ end
+
+ and TypedPropConfigurator<'TSource,'TTarget>
+ (getter:Func<'TSource, 'TTarget>,
+ setter:Func<'TTarget, 'TSource>) =
+ class
+ inherit PropConfigurator(typeof<'TTarget>)
+
+ override x.GetValue(instance, source) =
+ null
+
+ override x.SetValue(instance, value) =
+ ()
+ end
+
+ and EntitySetConfigurator<'a>(entitySetName, entityName, source:IQueryable<'a>) =
+ inherit EntitySetConfig(entitySetName, entityName, source, typeof<'a>)
+
+ member x.TypedSource = source
+
+ member x.AddAttribute( att:EntityPropertyMappingAttribute ) =
+ x.EntityPropertyAttributes.Add att
+ x
+
+ member x.WithEntityName(name) =
+ x.EntityName <- name
+ x
+
+ member x.ForProperty<'TSource,'TTarget>(propSelector:Expression>,
+ getter:Func<'TSource, 'TTarget>,
+ setter:Func<'TTarget, 'TSource>) =
+ let propInfo = RefHelpers.lastpropinfo_from_exp(propSelector)
+ if propInfo = null then raise(ArgumentException())
+ let config = TypedPropConfigurator(getter, setter)
+ x.CustomPropConfig.[propInfo] <- config
+ x
+
diff --git a/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/Constants.fs b/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/Constants.fs
index 06a1beaf..fe097a12 100644
--- a/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/Constants.fs
+++ b/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/Constants.fs
@@ -1,4 +1,19 @@
-namespace Castle.MonoRail.Extension.OData
+// Copyright 2004-2012 Castle Project - http://www.castleproject.org/
+// Hamilton Verissimo de Oliveira and individual contributors as indicated.
+// See the committers.txt/contributors.txt in the distribution for a
+// full listing of individual contributors.
+//
+// This is free software; you can redistribute it and/or modify it
+// under the terms of the GNU Lesser General Public License as
+// published by the Free Software Foundation; either version 3 of
+// the License, or (at your option) any later version.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this software; if not, write to the Free
+// Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+// 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+
+namespace Castle.MonoRail.Extension.OData
open System.Data.OData
open System.Text.RegularExpressions
diff --git a/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/ControllerActionOperation.fs b/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/ControllerActionOperation.fs
new file mode 100644
index 00000000..4d4fcc5a
--- /dev/null
+++ b/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/ControllerActionOperation.fs
@@ -0,0 +1,42 @@
+// Copyright 2004-2012 Castle Project - http://www.castleproject.org/
+// Hamilton Verissimo de Oliveira and individual contributors as indicated.
+// See the committers.txt/contributors.txt in the distribution for a
+// full listing of individual contributors.
+//
+// This is free software; you can redistribute it and/or modify it
+// under the terms of the GNU Lesser General Public License as
+// published by the Free Software Foundation; either version 3 of
+// the License, or (at your option) any later version.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this software; if not, write to the Free
+// Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+// 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+
+namespace Castle.MonoRail.Extension.OData
+
+ open System
+ open System.Collections.Generic
+ open System.Data.OData
+ open System.Data.Services.Providers
+ open System.Data.Services.Common
+ open System.Linq
+ open System.Linq.Expressions
+ open System.Reflection
+ open Castle.MonoRail.OData
+ open Castle.MonoRail.Extension.OData
+ open Castle.MonoRail.Hosting.Mvc
+ open Castle.MonoRail.Hosting.Mvc.Typed
+
+
+ []
+ type ControllerActionOperation(rt:ResourceType, actionName:string) =
+ member x.ResourceType = rt
+ member x.Name = actionName
+
+
+ type internal SubControllerInfo = {
+ creator : Func;
+ desc : TypedControllerDescriptor;
+ } with
+ static member Empty = { creator = null; desc = null }
\ No newline at end of file
diff --git a/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/Internal.AssemblyInfo.fs b/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/Internal.AssemblyInfo.fs
index 927e05bc..bd674810 100644
--- a/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/Internal.AssemblyInfo.fs
+++ b/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/Internal.AssemblyInfo.fs
@@ -1,4 +1,18 @@
-
+// Copyright 2004-2012 Castle Project - http://www.castleproject.org/
+// Hamilton Verissimo de Oliveira and individual contributors as indicated.
+// See the committers.txt/contributors.txt in the distribution for a
+// full listing of individual contributors.
+//
+// This is free software; you can redistribute it and/or modify it
+// under the terms of the GNU Lesser General Public License as
+// published by the Free Software Foundation; either version 3 of
+// the License, or (at your option) any later version.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this software; if not, write to the Free
+// Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+// 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+
module AssemblyLevelDeclarations
open System.Reflection
diff --git a/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/Internal.fs b/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/Internal.fs
new file mode 100644
index 00000000..f091977f
--- /dev/null
+++ b/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/Internal.fs
@@ -0,0 +1,27 @@
+// Copyright 2004-2012 Castle Project - http://www.castleproject.org/
+// Hamilton Verissimo de Oliveira and individual contributors as indicated.
+// See the committers.txt/contributors.txt in the distribution for a
+// full listing of individual contributors.
+//
+// This is free software; you can redistribute it and/or modify it
+// under the terms of the GNU Lesser General Public License as
+// published by the Free Software Foundation; either version 3 of
+// the License, or (at your option) any later version.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this software; if not, write to the Free
+// Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+// 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+
+module InternalUtils
+
+ open System
+ open System.Reflection
+ open System.Collections.Generic
+
+
+ let getEnumerableElementType (possibleEnumerableType:Type) =
+ let found = possibleEnumerableType.FindInterfaces(TypeFilter(fun t o -> (o :?> Type).IsAssignableFrom(t)), typedefof>)
+ if found.Length = 0
+ then None
+ else Some(found.[0].GetGenericArguments().[0])
diff --git a/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/ODataController.fs b/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/ODataController.fs
index ce0d1549..0ae07f6b 100644
--- a/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/ODataController.fs
+++ b/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/ODataController.fs
@@ -1,4 +1,19 @@
-namespace Castle.MonoRail
+// Copyright 2004-2012 Castle Project - http://www.castleproject.org/
+// Hamilton Verissimo de Oliveira and individual contributors as indicated.
+// See the committers.txt/contributors.txt in the distribution for a
+// full listing of individual contributors.
+//
+// This is free software; you can redistribute it and/or modify it
+// under the terms of the GNU Lesser General Public License as
+// published by the Free Software Foundation; either version 3 of
+// the License, or (at your option) any later version.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this software; if not, write to the Free
+// Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+// 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+
+namespace Castle.MonoRail
open System
open System.Collections
@@ -15,15 +30,13 @@
open Castle.MonoRail.Extension.OData
-
-
-
/// Entry point for exposing EntitySets through OData
[]
type ODataController<'T when 'T :> ODataModel>(model:'T) =
-
+
let _provider = model :> IDataServiceMetadataProvider
let _wrapper = DataServiceMetadataProviderWrapper(_provider)
+ let _services : Ref = ref null
let resolveHttpOperation (httpMethod) =
match httpMethod with
@@ -33,41 +46,76 @@
| SegmentProcessor.HttpDelete -> SegmentOp.Delete
| _ -> failwithf "Unsupported http method %s" httpMethod
- // returns a function able execute a action (param to fun)
- // on the controller associated with the entity type
- let resource_controller_creator (services:IServiceRegistry) (entityType:Type) (routeMatch:RouteMatch) (context:HttpContextBase) paramCallback =
- // todo: caching
- let template = typedefof>
- let concrete = template.MakeGenericType([|entityType|])
- let spec = PredicateControllerCreationSpec(fun t -> concrete.IsAssignableFrom(t))
- let prototype = services.ControllerProvider.CreateController(spec)
- if prototype <> null then
- let executor = services.ControllerExecutorProvider.CreateExecutor(prototype)
- System.Diagnostics.Debug.Assert ( executor <> null && executor :? ODataEntitySubControllerExecutor )
-
+ let _executors = List()
+ let _invoker_cache = Dictionary bool -> IList -> RouteMatch -> HttpContextBase -> obj>()
+ // action isCollection params route context result
+
+ let get_action_invoker rt =
+ let create_controller_prototype (rt:ResourceType) =
+ let creator = model.GetControllerCreator (rt)
+ if creator <> null
+ then creator.Invoke()
+ else null
+
+ // we will have issues with object models with self referencies
+ // a better implementation would "consume" the items used, taking them off the list
+ let tryResolveParamValue (paramType:Type) isCollection (parameters:IList) =
+ let entryType =
+
+ if isCollection then
+ match InternalUtils.getEnumerableElementType paramType with
+ | Some t -> t
+ | _ -> paramType
+ elif paramType.IsGenericType then
+ paramType.GetGenericArguments().[0]
+ else paramType
+
+ match parameters |> Seq.tryFind (fun (ptype, _) -> ptype = entryType || entryType.IsAssignableFrom(ptype)) with
+ | Some (_, value) ->
+ // param is Model
+ if paramType.IsGenericType && paramType.GetGenericTypeDefinition() = typedefof>
+ then Activator.CreateInstance ((typedefof>).MakeGenericType(paramType.GetGenericArguments()), [|value|])
+ else // entryType <> paramType && paramType.IsAssignableFrom(entryType) then
+ value
+ | _ -> null
+
+ // returns a function able to invoke actions
+ let create_executor_fn (rt:ResourceType) prototype =
+ let executor = (!_services).ControllerExecutorProvider.CreateExecutor(prototype)
+ Diagnostics.Debug.Assert ( executor <> null && executor :? ODataEntitySubControllerExecutor )
+ _executors.Add executor
let odataExecutor = executor :?> ODataEntitySubControllerExecutor
- odataExecutor.GetParameterCallback <- Func(paramCallback)
- (fun action -> let result = executor.Execute(action, prototype, routeMatch, context)
- // if the return is an empty result, we treat it as null
- if result <> null && result :? EmptyResult
- then (null, true) else (result, true))
- else (fun _ -> (null, false))
-
- let tryResolveParamValue (paramType:Type) isCollection (rt:ResourceType) (value:obj) =
- let entryType =
- let found = paramType.FindInterfaces(TypeFilter(fun t o -> (o :?> Type).IsAssignableFrom(t)), typedefof>)
- if found.Length = 0
- then paramType
- else found.[0].GetGenericArguments().[0]
-
- // if param is Model
- if paramType.IsGenericType && paramType.GetGenericTypeDefinition() = typedefof>
- then
- Activator.CreateInstance ((typedefof>).MakeGenericType(paramType.GetGenericArguments()), [|value|])
- elif paramType.IsAssignableFrom(entryType) then
- value
+ (fun action isCollection parameters routeMatch context ->
+ let callback = Func(fun ptype -> tryResolveParamValue ptype isCollection parameters)
+ odataExecutor.GetParameterCallback <- callback
+ executor.Execute(action, prototype, routeMatch, context))
+ let succ, existing = _invoker_cache.TryGetValue rt
+ if succ then existing
+ else
+ let prototype = create_controller_prototype rt
+ let executor = create_executor_fn rt prototype
+ _invoker_cache.[rt] <- executor
+ executor
+
+ let invoke_action rt action parameters route context =
+ let invoker = get_action_invoker (rt)
+ invoker action parameters route context
+
+ let invoke_controller (action:string) isCollection (rt:ResourceType) parameters optional route context =
+ if model.SupportsAction(rt, action) then
+ let result = invoke_action rt action isCollection parameters route context
+ if result = null || ( result <> null && result :? EmptyResult )
+ // if the action didn't return anything meaningful, we consider it a success
+ then true
+ // else, the action took over, and we should therefore end our execution
+ else false
else
- null
+ // if we couldnt run the action, then the results
+ // depends on whether the call was optional or not
+ if optional then true else false
+
+ let clean_up =
+ _executors |> Seq.iter (fun exec -> (exec :> IDisposable).Dispose() )
member x.Model = model
member internal x.MetadataProvider = _provider
@@ -75,7 +123,13 @@
member x.Process(services:IServiceRegistry, httpMethod:string, greedyMatch:string,
routeMatch:RouteMatch, context:HttpContextBase) =
+
+
+ _services := services
+
+ model.SetServiceRegistry services
+
let request = context.Request
let response = context.Response
response.AddHeader("DataServiceVersion", "2.0")
@@ -83,61 +137,83 @@
let writer = response.Output
let qs = request.Url.Query
let baseUri = routeMatch.Uri
- let requestContentType = request.ContentType
-
- let invoke_controller (action:string) isCollection (rt:ResourceType) o optional =
- let paramCallback = fun (t:Type) -> tryResolveParamValue t isCollection rt o
- let actionExecutor = resource_controller_creator services rt.InstanceType routeMatch context paramCallback
- let result, executed = actionExecutor action
-
- if not optional && not executed then
- failwith "Non existent controller or action not found. Entity: %O action: %s. Make sure there's a controller inheriting from ODataEntitySubController" rt.InstanceType action
- else
- if result = null then true
- else
- // todo: execute result?
- false
+ let requestContentType, reqEncoding =
+ if request.ContentType.IndexOf(";", StringComparison.Ordinal) <> -1 then
+ let content = request.ContentType.Split([|';'|]).[0]
+ content, request.ContentEncoding
+ else request.ContentType, request.ContentEncoding
+
+ let negotiate_content (isSingle) =
+ let supported =
+ if isSingle
+ then [|"application/atom+xml";"application/json";"application/xml";"text/plain"|]
+ else [|"application/atom+xml";"application/json"|]
+ services.ContentNegotiator.ResolveBestContentType (request.AcceptTypes, supported)
+
+ let invoke action isColl (rt:ResourceType) (parameters:(Type*obj) seq) value isOptional =
+ let newParams = List(parameters)
+ if value <> null then
+ newParams.Add (rt.InstanceType, value)
+ invoke_controller action isColl rt newParams isOptional routeMatch context
let callbacks = {
- accessSingle = Func(fun rt o -> invoke_controller "Access" false rt o true);
- accessMany = Func(fun rt o -> invoke_controller "AccessMany" true rt o true);
- create = Func(fun rt o -> invoke_controller "Create" false rt o false);
- update = Func(fun rt o -> invoke_controller "Update" false rt o false);
- remove = Func(fun rt o -> invoke_controller "Remove" false rt o false);
- }
+ authorize = Func(fun rt ps o -> invoke "Authorize" false rt ps o true);
+ authorizeMany = Func(fun rt ps o -> invoke "AuthorizeMany" true rt ps o true);
+ view = Func(fun rt ps o -> invoke "View" false rt ps o true);
+ viewMany = Func(fun rt ps o -> invoke "ViewMany" true rt ps o true);
+ create = Func(fun rt ps o -> invoke "Create" false rt ps o false);
+ update = Func(fun rt ps o -> invoke "Update" false rt ps o false);
+ remove = Func(fun rt ps o -> invoke "Remove" false rt ps o false);
+ operation = Action(fun rt ps action -> invoke action false rt ps null false |> ignore);
+ negotiateContent = Func(negotiate_content)
+ }
+
let requestParams = {
- model = model;
- provider = x.MetadataProvider;
- wrapper = x.MetadataProviderWrapper;
- contentType = requestContentType;
- contentEncoding = request.ContentEncoding;
- input = request.InputStream;
- baseUri = baseUri;
- accept = request.AcceptTypes;
- }
+ model = model;
+ provider = x.MetadataProvider;
+ wrapper = x.MetadataProviderWrapper;
+ contentType = requestContentType;
+ contentEncoding = reqEncoding;
+ input = request.InputStream;
+ baseUri = baseUri;
+ accept = request.AcceptTypes;
+ }
let responseParams = {
- contentType = null ;
- contentEncoding = response.ContentEncoding;
- writer = writer;
- httpStatus = 200;
- }
+ contentType = null ;
+ contentEncoding = response.ContentEncoding;
+ writer = writer;
+ httpStatus = 200;
+ httpStatusDesc = "OK";
+ location = null;
+ }
try
- let op = resolveHttpOperation httpMethod
- let segments = SegmentParser.parse (greedyMatch, qs, model)
-
- SegmentProcessor.Process op segments callbacks requestParams responseParams
-
- if not <| String.IsNullOrEmpty responseParams.contentType then
- response.ContentType <- responseParams.contentType
- if responseParams.contentEncoding <> null then
- response.ContentEncoding <- responseParams.contentEncoding
-
- EmptyResult.Instance
-
+ try
+ let op = resolveHttpOperation httpMethod
+ let segments, meta, metaquery = SegmentParser.parse (greedyMatch, request.QueryString, model, baseUri)
+
+ SegmentProcessor.Process op segments meta metaquery request.QueryString callbacks requestParams responseParams
+
+ if responseParams.httpStatus <> 200 then
+ response.StatusCode <- responseParams.httpStatus
+ response.StatusDescription <- responseParams.httpStatusDesc
+ if not <| String.IsNullOrEmpty responseParams.contentType then
+ response.ContentType <- responseParams.contentType
+ if responseParams.contentEncoding <> null then
+ response.ContentEncoding <- responseParams.contentEncoding
+ if responseParams.location <> null then
+ response.AddHeader("Location", responseParams.location)
+
+ EmptyResult.Instance
+
+ finally
+ clean_up
with
| :? HttpException as ht -> reraise()
| exc ->
+
+ reraise()
+
// todo: instead of raising, we should serialize error e write it back
// TODO: use responseContentType to resolve output mime type
@@ -157,13 +233,12 @@
(*
-
- Resource not found for the segment 'People'.
+
+ Resource not found for the segment 'People'.
*)
// if html, let the exception filters handle it, otherwise, let it bubble to asp.net
- reraise()
diff --git a/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/ODataEntitySubController.fs b/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/ODataEntitySubController.fs
new file mode 100644
index 00000000..f2d7f42a
--- /dev/null
+++ b/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/ODataEntitySubController.fs
@@ -0,0 +1,51 @@
+// Copyright 2004-2012 Castle Project - http://www.castleproject.org/
+// Hamilton Verissimo de Oliveira and individual contributors as indicated.
+// See the committers.txt/contributors.txt in the distribution for a
+// full listing of individual contributors.
+//
+// This is free software; you can redistribute it and/or modify it
+// under the terms of the GNU Lesser General Public License as
+// published by the Free Software Foundation; either version 3 of
+// the License, or (at your option) any later version.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this software; if not, write to the Free
+// Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+// 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+
+namespace Castle.MonoRail
+
+ open System
+ open System.Linq
+ open System.Collections
+ open System.Collections.Generic
+
+ /// Entry point for exposing EntitySets through OData
+ []
+ type IODataEntitySubController<'TEntity when 'TEntity : not struct> =
+ interface
+ (*
+ member x.Authorize(ent:'TEntity) =
+ EmptyResult.Instance
+
+ member x.Authorize(ents:IEnumerable<'TEntity>) =
+ EmptyResult.Instance
+
+ member x.View(ent:'TEntity) =
+ EmptyResult.Instance
+
+ member x.ViewAll(ents:IEnumerable<'TEntity>) =
+ EmptyResult.Instance
+
+ member x.Post_Create(ent:'TEntity) =
+ EmptyResult.Instance
+
+ member x.Put_Update(ent:'TEntity) =
+ EmptyResult.Instance
+
+ member x.Delete_Remove(ent:'TEntity) =
+ EmptyResult.Instance
+ *)
+ end
+
+
diff --git a/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/ODataEntitySubControllerExecutor.fs b/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/ODataEntitySubControllerExecutor.fs
index 664445e6..f3543d8f 100644
--- a/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/ODataEntitySubControllerExecutor.fs
+++ b/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/ODataEntitySubControllerExecutor.fs
@@ -1,14 +1,17 @@
-namespace Castle.MonoRail
-
- /// Entry point for exposing EntitySets through OData
- []
- type ODataEntitySubController<'TEntity when 'TEntity : not struct>() =
- class
- // view
- // create
- // update
- // delete
- end
+// Copyright 2004-2012 Castle Project - http://www.castleproject.org/
+// Hamilton Verissimo de Oliveira and individual contributors as indicated.
+// See the committers.txt/contributors.txt in the distribution for a
+// full listing of individual contributors.
+//
+// This is free software; you can redistribute it and/or modify it
+// under the terms of the GNU Lesser General Public License as
+// published by the Free Software Foundation; either version 3 of
+// the License, or (at your option) any later version.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this software; if not, write to the Free
+// Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+// 02110-1301 USA, or see the FSF site: http://www.fsf.org.
namespace Castle.MonoRail.Extension.OData
@@ -101,7 +104,7 @@ namespace Castle.MonoRail.Extension.OData
}
let isSubController =
baseTypes
- |> Seq.exists (fun t -> t.IsGenericType && typedefof>.IsAssignableFrom( t.GetGenericTypeDefinition() ))
+ |> Seq.exists (fun t -> t.IsGenericType && typedefof>.IsAssignableFrom( t.GetGenericTypeDefinition() ))
if isSubController then
let exp = _execFactory.CreateExport()
diff --git a/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/ODataModel.fs b/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/ODataModel.fs
new file mode 100644
index 00000000..09f39ee9
--- /dev/null
+++ b/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/ODataModel.fs
@@ -0,0 +1,166 @@
+// Copyright 2004-2012 Castle Project - http://www.castleproject.org/
+// Hamilton Verissimo de Oliveira and individual contributors as indicated.
+// See the committers.txt/contributors.txt in the distribution for a
+// full listing of individual contributors.
+//
+// This is free software; you can redistribute it and/or modify it
+// under the terms of the GNU Lesser General Public License as
+// published by the Free Software Foundation; either version 3 of
+// the License, or (at your option) any later version.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this software; if not, write to the Free
+// Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+// 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+
+namespace Castle.MonoRail
+
+ open System
+ open System.Collections.Generic
+ open System.Data.OData
+ open System.Data.Services.Providers
+ open System.Linq
+ open Castle.MonoRail.OData
+ open Castle.MonoRail.Extension.OData
+ open Castle.MonoRail.Hosting.Mvc
+ open Castle.MonoRail.Hosting.Mvc.Typed
+
+
+ /// Access point to entities to be exposed by a single odata endpoint
+ type ODataModel(schemaNamespace, containerName) =
+
+ let mutable _schemaNs = schemaNamespace
+ let mutable _containerName = containerName
+ let _serviceRegistry : Ref = ref null
+
+ let _entities = List()
+ let _entSeq : EntitySetConfig seq = upcast _entities
+
+ let _resourcetypes = lazy ( let rts = ResourceMetadataBuilder.build(_schemaNs, _entities)
+ rts |> Seq.iter (fun rt -> rt.SetReadOnly() )
+ rts.ToList() |> box :?> ResourceType seq )
+
+ let _resourcesets = lazy ( _resourcetypes.Force()
+ |> Seq.filter (fun rt -> rt.ResourceTypeKind = ResourceTypeKind.EntityType && (_entities |> Seq.exists (fun e -> e.EntityName === rt.Name) ) )
+ |> Seq.map (fun rt -> (let name = (_entSeq |> Seq.find(fun e -> e.TargetType = rt.InstanceType)).EntitySetName
+ let rs = ResourceSet(name, rt)
+ rs.SetReadOnly()
+ rs ))
+ |> box :?> ResourceSet seq)
+
+ let _rt2ControllerCreator : Ref> = ref null
+
+ member x.SchemaNamespace with get() = schemaNamespace
+ member x.ContainerName with get() = containerName
+
+ member x.EntitySet<'a>(entitySetName:string, source:IQueryable<'a>) =
+ if _resourcesets.IsValueCreated then raise(InvalidOperationException("Model is frozen since ResourceSets were built"))
+ let entityType = typeof<'a>
+ let cfg = EntitySetConfigurator(entitySetName, entityType.Name, source)
+ _entities.Add cfg
+ cfg
+
+ member x.Entities = _entSeq
+ member internal x.ResourceSets = _resourcesets.Force()
+ member internal x.ResourceTypes = _resourcetypes.Force()
+ member internal x.GetResourceType(name) =
+ x.ResourceTypes
+ |> Seq.tryFind (fun rs -> StringComparer.OrdinalIgnoreCase.Equals( rs.Name, name ) )
+ member internal x.GetResourceSet(name) =
+ x.ResourceSets
+ |> Seq.tryFind (fun rs -> StringComparer.OrdinalIgnoreCase.Equals( rs.Name, name ) )
+ member internal x.GetQueryable(rs:ResourceSet) =
+ match _entities |> Seq.tryFind (fun e -> StringComparer.OrdinalIgnoreCase.Equals(e.EntitySetName, rs.Name)) with
+ | Some e -> e.Source
+ | _ -> null
+ member internal x.GetRelatedResourceSet (rt:ResourceType) =
+ x.ResourceSets
+ |> Seq.tryFind (fun rs -> rs.ResourceType = rt)
+
+ member internal x.SupportsAction (rt:ResourceType, name:string) =
+ if !_rt2ControllerCreator <> null then
+ let succ, info = (!_rt2ControllerCreator).TryGetValue rt
+ succ && info.desc.HasAction name
+ else false
+
+ member internal x.GetControllerCreator (rt:ResourceType) =
+ if !_rt2ControllerCreator <> null then
+ let succ, info = (!_rt2ControllerCreator).TryGetValue rt
+ if succ then info.creator
+ else null
+ else null
+
+ member internal x.GetNestedOperation (rt:ResourceType, name:string) : ControllerActionOperation =
+ if !_rt2ControllerCreator <> null then
+ let succ, info = (!_rt2ControllerCreator).TryGetValue rt
+ if succ && info.desc.HasAction name // TODO: should use HTTP VERB to narrow options
+ then ControllerActionOperation(rt, name)
+ else null
+ else null
+
+ member internal x.SetServiceRegistry(services:IServiceRegistry) =
+
+ if _serviceRegistry.Value = null then
+ _serviceRegistry := services
+
+ let resolve_subcontrollerinfo (entityType:Type) =
+ let template = typedefof>
+ let concrete = template.MakeGenericType([|entityType|])
+ let spec = PredicateControllerCreationSpec(fun t -> concrete.IsAssignableFrom(t))
+ let creator = services.ControllerProvider.CreateController(spec)
+ if creator <> null then
+ let prototype = creator.Invoke() :?> TypedControllerPrototype
+ { creator = creator; desc = prototype.Descriptor :?> TypedControllerDescriptor }
+ else
+ SubControllerInfo.Empty
+
+ let build_subcontrollers_map () =
+ let dict = Dictionary()
+ _resourcetypes.Force()
+ |> Seq.map (fun rt -> rt, resolve_subcontrollerinfo rt.InstanceType)
+ |> Seq.filter (fun t -> snd t <> SubControllerInfo.Empty)
+ |> Seq.iter (fun t -> dict.Add (fst t, snd t))
+ dict
+
+ _rt2ControllerCreator := build_subcontrollers_map ()
+
+
+ interface IDataServiceMetadataProvider with
+ member x.ContainerNamespace = x.SchemaNamespace
+ member x.ContainerName = x.ContainerName
+ member x.ResourceSets = x.ResourceSets
+ member x.Types = x.ResourceTypes
+ member x.ServiceOperations =
+ // we dont support ops yet
+ Seq.empty
+ member x.GetDerivedTypes(resType) =
+ // we dont support hierarchies yet
+ Seq.empty
+ member x.HasDerivedTypes(resType) =
+ // we dont support hierarchies yet
+ false
+ member x.TryResolveResourceSet(name, rtToReturn) =
+ match x.GetResourceSet(name) with
+ | Some rt -> rtToReturn <- rt; true
+ | None -> false
+ member x.TryResolveResourceType(name, rtToReturn) =
+ match x.GetResourceType(name) with
+ | Some rt -> rtToReturn <- rt; true
+ | None -> false
+ member x.TryResolveServiceOperation(name, opToReturn) =
+ // we dont support ops yet
+ false
+ member x.GetResourceAssociationSet(resSet, resType, property) =
+ let targetResType = property.ResourceType
+ match x.ResourceSets |> Seq.tryFind (fun rs -> targetResType.InstanceType.IsAssignableFrom(rs.ResourceType.InstanceType)) with
+ | Some containerResSet ->
+ ResourceAssociationSet(resType.Name + "_" + property.Name,
+ ResourceAssociationSetEnd(resSet, resType, property),
+ ResourceAssociationSetEnd(containerResSet, targetResType, null))
+ | _ -> null
+
+
+
+
+
+
diff --git a/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/QueryExpressionParser.fs b/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/QueryExpressionParser.fs
new file mode 100644
index 00000000..6cd3ba00
--- /dev/null
+++ b/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/QueryExpressionParser.fs
@@ -0,0 +1,457 @@
+// Copyright 2004-2012 Castle Project - http://www.castleproject.org/
+// Hamilton Verissimo de Oliveira and individual contributors as indicated.
+// See the committers.txt/contributors.txt in the distribution for a
+// full listing of individual contributors.
+//
+// This is free software; you can redistribute it and/or modify it
+// under the terms of the GNU Lesser General Public License as
+// published by the Free Software Foundation; either version 3 of
+// the License, or (at your option) any later version.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this software; if not, write to the Free
+// Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+// 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+
+namespace Castle.MonoRail.Extension.OData
+
+open System
+open System.Collections
+open System.Collections.Specialized
+open System.Collections.Generic
+open System.Data.OData
+open System.Data.Services.Providers
+open System.Linq
+open System.Linq.Expressions
+open System.Text
+open System.Globalization
+open System.Web
+open Castle.MonoRail
+open FParsec
+open FParsec.Primitives
+open FParsec.CharParsers
+
+
+type BinaryOp =
+ | And = 0
+ | Or = 1
+ | Eq = 2
+ | Neq = 3
+ | Mul = 4
+ | Div = 5
+ | Mod = 6
+ | Add = 7
+ | Sub = 8
+ | LessT = 9
+ | GreatT = 10
+ | LessET = 11
+ | GreatET= 12
+
+type UnaryOp =
+ | Negate = 0
+ | Not = 1
+ | Cast = 2
+ // | IsOf
+
+type EdmPrimitives =
+ | Null = 0
+ | Binary = 1
+ | Boolean = 2
+ | Byte = 3
+ | DateTime = 4
+ | Decimal = 5
+ | Double = 6
+ | Single = 7
+ | Guid = 8
+ | Int16 = 9
+ | Int32 = 10
+ | Int64 = 11
+ | SByte = 12
+ | SString = 13
+ | Time = 14
+ | String = 15
+ | DateTimeOffset = 16
+
+
+type Exp =
+ | All
+ | Identifier of string
+ | Element
+ | Literal of EdmPrimitives * string * obj
+ | MemberAccess of Exp * Exp // string * (string list) option
+ | Unary of UnaryOp * Exp
+ | Binary of Exp * BinaryOp * Exp
+ // | MethodCall
+ // | Cast
+ // | IsOf
+ // | FuncCall
+ with
+ member x.ToStringTree() =
+ let b = StringBuilder()
+ let rec print (n) (level:int) =
+ b.AppendLine() |> ignore
+ for x in 1..level do b.Append(" ") |> ignore
+ match n with
+ | Identifier f -> b.Append (sprintf "Id %s" f) |> ignore
+ | Element -> b.Append (sprintf "Element") |> ignore
+ | Literal (t,s,v) ->
+ if v = null
+ then b.Append (sprintf "Literal %O [%s]" t s ) |> ignore
+ else
+ if v :? DateTime then
+ let dt = v :?> DateTime
+ b.Append (sprintf "Literal %O [%O]" t (dt.ToString(CultureInfo.InvariantCulture))) |> ignore
+ else b.Append (sprintf "Literal %O [%O]" t v ) |> ignore
+
+ | MemberAccess (ex,n) ->
+ b.Append (sprintf "MemberAccess " ) |> ignore
+ print ex (level + 1)
+ print n (level + 1)
+ | Unary (op,ex) ->
+ b.Append (sprintf "Unary %O " op) |> ignore
+ print ex (level + 1)
+ | Binary (ex1, op, ex2) ->
+ b.Append (sprintf "Binary %O " op) |> ignore
+ print ex1 (level + 1)
+ print ex2 (level + 1)
+ | _ -> failwithf "Unsupported node type? Need to update this match, dude"
+ print x 1
+ b.ToString()
+
+
+type OrderByExp =
+ | Asc of Exp
+ | Desc of Exp
+
+
+// rename to queryparser instead?
+module QueryExpressionParser =
+ begin
+ (*
+ OData/Operator Precedence
+ Grouping (x) parenExpression, boolParenExpression
+ Primary x/m memberExpression
+ Primary x(...) methodCallExpression, boolMethodCallExpression
+ Unary -x negateExpression
+ Unary not x notExpression
+ Unary cast(T), cast(x, T) castExpression
+ Multiplicative x mul y mulExpression
+ Multiplicative x div y divExpression
+ Multiplicative x mod y modExpression
+ Additive x add y addExpression
+ Additive x sub y subExpression
+ Relational/ttesting x lt y ltExpression
+ Relational/ttesting x gt y gtExpression
+ Relational/ttesting x le y leExpression
+ Relational/ttesting x ge y geExpression
+ Relational/ttesting isof(T), isof(x, T) isofExpression
+ Equality x eq y eqExpression
+ Equality x ne y neExpression
+ Conditional AND x and y andExpression
+ Conditional OR x or y orExpression
+
+ *)
+
+ // Rewrites the tree to be rooted with MemberAccess(Element instead)
+ // not ideal, but couldnt get fparsec to produce the desired tree
+ let rec rebuildMemberAccessTree exp =
+ match exp with
+ | MemberAccess (r, arg) -> MemberAccess(rebuildMemberAccessTree r, arg)
+ | Identifier i -> MemberAccess(Exp.Element, exp)
+ | _ -> failwithf "Not supposed to traverse any other node type, but got into %O" exp
+
+ let ws = spaces
+ // let nospace = preturn ()
+ let pc c = ws >>. pchar c
+ let pstr s = ws >>. pstring s
+ let pstrCI s = ws >>. pstringCI s
+ let lparen = pstring "(" >>. ws
+ let rparen = pstring ")" >>. ws
+ let opp = new OperatorPrecedenceParser<_,_,_>()
+ let ida = identifier(IdentifierOptions())
+ let entity = ws >>. ida .>> manyChars (noneOf "/")
+ let squote = pstring "'"
+ let shyphen = pstring "-"
+ let scolon = pstring ":"
+
+ // Address MemberAccess(element, PriceAddress)
+ // Address/Name MemberAccess(MemberAccess(element, Address), Name)
+ // Address/Name/Length MemberAccess(MemberAccess(MemberAccess(element, Address), Name), Length)
+ let combine = stringReturn "/" (fun x y -> Exp.MemberAccess(x, y))
+ let idAsExp = ida .>> ws |>> (fun id -> Exp.Identifier(id))
+ let star = pchar '*' .>> ws |>> (fun _ -> Exp.Identifier("*"))
+ let memberAccessExp = chainl1 (idAsExp) (combine)
+ let selMemberAccessExp = chainl1 (idAsExp <|> star) (combine)
+
+ let sign = pstr "-" <|>% ""
+
+ let nullLiteral = pstr "null" .>> ws |>> fun _ -> Exp.Literal(EdmPrimitives.Null, null, null)
+
+ // guidUriLiteral= "guid" SQUOTE guidLiteral SQUOTE
+ // guidLiteral = 8*HEXDIG "-" 4*HEXDIG "-" 4*HEXDIG "-" 12*HEXDIG
+ // 80749f18-d2f1-47e5-b1d0-4169c10125b5
+ let guidLiteral = pstring "guid" .>> squote >>.
+ pipe5
+ (manyMinMaxSatisfy 8 8 isHex .>> shyphen)
+ (manyMinMaxSatisfy 4 4 isHex .>> shyphen)
+ (manyMinMaxSatisfy 4 4 isHex .>> shyphen)
+ (manyMinMaxSatisfy 4 4 isHex .>> shyphen)
+ (manyMinMaxSatisfy 12 12 isHex)
+ (fun v1 v2 v3 v4 v5 -> sprintf "%s-%s-%s-%s-%s" v1 v2 v3 v4 v5) .>> squote .>> ws
+ |>> fun g -> Exp.Literal(EdmPrimitives.Guid, null, Guid.Parse(g))
+
+ let toInt v = Int32.Parse(v)
+
+ // year "-" month "-" day
+ let datePart = pipe3
+ (manyMinMaxSatisfy 4 4 isDigit .>> shyphen |>> toInt)
+ (manyMinMaxSatisfy 1 2 isDigit .>> shyphen |>> toInt)
+ (manyMinMaxSatisfy 1 2 isDigit |>> toInt)
+ (fun year month day -> DateTime(year, month, day))
+
+ // [":" second ["." nanoSeconds]]
+ let secondsNanoPart = opt ( pchar ':' >>.
+ (manyMinMaxSatisfy 1 2 isDigit |>> toInt) .>>.
+ (opt (pchar '.' >>. manyMinMaxSatisfy 1 7 isDigit |>> toInt)))
+ |>> fun (sec) ->
+ match sec with
+ | Some (secs, nano) ->
+ match nano with | Some n -> (secs, n) | _ -> (secs, 0)
+ | _ -> (0,0)
+
+ // hour ":" minute [":" second ["." nanoSeconds]]
+ let timePart = pipe3 (manyMinMaxSatisfy 1 2 isDigit .>> scolon |>> toInt)
+ (manyMinMaxSatisfy 1 2 isDigit |>> toInt)
+ secondsNanoPart
+ ( fun hour minute secs -> DateTime(1999, 1, 1, hour, minute, fst secs, snd secs))
+
+
+ // dateTimeLiteral = year "-" month "-" day "T" hour ":" minute [":" second ["." nanoSeconds]]
+ let datetimeLiteral = pstring "datetime" .>> squote >>.
+ pipe2 (datePart .>> pchar 'T') timePart
+ (fun date time -> DateTime(date.Year, date.Month, date.Day, time.Hour, time.Minute, time.Second, time.Millisecond)) .>> squote .>> ws
+ |>> fun g -> Exp.Literal(EdmPrimitives.DateTime, null, g)
+
+ let signedIntPart = (sign .>>. many1Chars digit) |>> fun (s,v) -> Single.Parse(s + v)
+ let optDecimalPart = (pchar '.' >>. many1Chars digit |>> fun v -> Single.Parse("0." + v))
+ <|>% 0.0f
+ let decimalPart = (pchar '.' >>. many1Chars digit |>> fun v -> Single.Parse("0." + v))
+
+ let intLiteral = (sign .>>. many1Chars digit) .>> ws
+ |>> fun (c,v) -> Exp.Literal(EdmPrimitives.Int32, null, Int32.Parse(c + v))
+ let int64Literal = (sign .>>. many1Chars digit .>> pstringCI "l") .>> ws
+ |>> fun (c,v) -> Exp.Literal(EdmPrimitives.Int64, null, Int64.Parse(c + v))
+
+ // decimalUriLiteral = decimalLiteral ("M"/"m")
+ // decimalLiteral = sign 1*29DIGIT ["." 1*29DIGIT]
+ let decLiteral = pipe3 signedIntPart optDecimalPart (pstringCI "m")
+ (fun i d _ -> (decimal) (i + d)) .>> ws
+ |>> fun d -> Exp.Literal(EdmPrimitives.Decimal, null, d)
+
+ (*
+ singleLiteral = nonDecimalPoint
+ / nonExp
+ / exp
+ / nan
+ / negativeInfinity
+ / postiveInfinity
+ nonDecimalPoint = sign 1*8DIGIT
+ nonExpDecimal = sign *DIGIT "." *DIGIT
+ expDecimal = sign 1*DIGIT "." 8DIGIT ("e" / "E") sign 1*2DIGIT
+ *)
+ let singleLiteral = ((attempt(pipe2 signedIntPart decimalPart (fun i d -> (float32) (i + d))))
+ <|> (signedIntPart .>> pstrCI "f" |>> fun i -> (float32) i) ) .>> ws
+ |>> fun d -> Exp.Literal(EdmPrimitives.Single, null, d)
+
+ (*
+ doubleLiteral = nonDecimalPoint
+ / nonExp
+ / exp
+ / nan
+ / negativeInfinity
+ / postiveInfinity
+ ("D" / "d")
+ nonDecimalPoint = sign 1*17DIGIT
+ nonExpDecimal = sign* DIGIT "." *DIGIT
+ expDecimal = sign 1*DIGIT "." 16DIGIT ("e" / "E") sign 1*3DIGIT
+ *)
+ let doubleLiteral = pipe3 signedIntPart optDecimalPart (pstringCI "d")
+ (fun i d _ -> (float) (i + d)) .>> ws
+ |>> fun d -> Exp.Literal(EdmPrimitives.Double, null, d)
+
+ let stringLiteral = between (pc '\'') (pchar '\'')
+ (many1Chars (noneOf "'")) .>> ws |>> fun en -> Exp.Literal(EdmPrimitives.SString, en, null)
+
+ let boolLiteral = (pstr "true" <|> pstr "false") .>> ws |>> fun v -> Exp.Literal(EdmPrimitives.Boolean, v, null)
+ let binaryLiteral = (pstr "X" <|> pstrCI "binary") >>. squote >>. (many1Chars hex) .>> squote .>> ws
+ |>> fun v -> Exp.Literal(EdmPrimitives.Binary, v, null)
+
+ let literalExp = choice [
+ nullLiteral
+ binaryLiteral
+ datetimeLiteral
+ guidLiteral
+ attempt(decLiteral)
+ attempt(doubleLiteral)
+ attempt(singleLiteral)
+ attempt(int64Literal)
+ intLiteral
+ stringLiteral
+ boolLiteral
+ ]
+
+ let tryBetweenParens p = lparen >>? (p .>>? rparen)
+
+ let exp = opp.ExpressionParser
+ let memberAccess = memberAccessExp |>> rebuildMemberAccessTree
+ let selMemberAccess = memberAccessExp |>> rebuildMemberAccessTree
+ let units = literalExp <|> tryBetweenParens exp <|> memberAccess
+
+ opp.TermParser <- units
+
+ let term = exp .>> eof
+
+ let expandExp = sepBy1 memberAccess (pc ',' .>> ws)
+
+ let orderbyTerm = memberAccess .>>.
+ ( pstringCI "asc" |>> (fun _ -> "asc") <|> pstringCI "desc" |>> (fun _ -> "desc") <|>% "asc")
+ |>> fun (m,o) -> if o = "asc" then OrderByExp.Asc(m) else OrderByExp.Desc(m)
+
+ let orderByUnit = sepBy1 orderbyTerm (pc ',' .>> ws)
+
+ // ShipCountry , Else [asc|desc]
+ // ShipCountry ne 'France' desc
+ // "=" [WSP] commonExpression [WSP] [asc / desc]
+ // *( "," [WSP] commonExpression [WSP] [asc / desc])
+ let orderby = orderByUnit .>> eof
+
+
+ // $select=CustomerID,CompanyName,Address
+ // select=CustomerID,Orders
+ // select=CustomerID,Orders & $expand=Orders/OrderDetails
+ // $select= *
+ // $select=CustomerID,Orders/* & $expand=Orders/OrderDetails
+ let selectTerm = (pc '*' .>> ws |>> fun _ -> Exp.All) <|> selMemberAccessExp
+ let selectExp = (sepBy1 selectTerm (pc ',' .>> ws)) .>> eof
+
+
+
+ opp.AddOperator(InfixOperator("and", ws, 1 , Associativity.Right, fun x y -> Exp.Binary(x, BinaryOp.And, y)))
+ opp.AddOperator(InfixOperator("or", ws, 2 , Associativity.Right, fun x y -> Exp.Binary(x, BinaryOp.Or, y)))
+
+ opp.AddOperator(InfixOperator("eq", ws, 3 , Associativity.Right, fun x y -> Exp.Binary(x, BinaryOp.Eq, y)))
+ opp.AddOperator(InfixOperator("ne", ws, 4 , Associativity.Right, fun x y -> Exp.Binary(x, BinaryOp.Neq, y)))
+
+ opp.AddOperator(InfixOperator("lt" , ws, 5, Associativity.Right, fun x y -> Exp.Binary(x, BinaryOp.LessT, y)))
+ opp.AddOperator(InfixOperator("gt" , ws, 6, Associativity.Right, fun x y -> Exp.Binary(x, BinaryOp.GreatT, y)))
+ opp.AddOperator(InfixOperator("le" , ws, 7, Associativity.Right, fun x y -> Exp.Binary(x, BinaryOp.LessET, y)))
+ opp.AddOperator(InfixOperator("ge" , ws, 8, Associativity.Right, fun x y -> Exp.Binary(x, BinaryOp.GreatET, y)))
+ // opp.AddOperator(PrefixOperator("isof",nospace, 70, false, fun x -> Exp.Unary(UnaryOp.IsOf, x))
+
+ opp.AddOperator(InfixOperator("add", ws, 9, Associativity.Left, fun x y -> Exp.Binary(x, BinaryOp.Add, y)))
+ opp.AddOperator(InfixOperator("sub", ws, 10, Associativity.Left, fun x y -> Exp.Binary(x, BinaryOp.Sub, y)))
+
+ opp.AddOperator(InfixOperator("mul", ws, 11, Associativity.Left, fun x y -> Exp.Binary(x, BinaryOp.Mul, y)))
+ opp.AddOperator(InfixOperator("div", ws, 12, Associativity.Left, fun x y -> Exp.Binary(x, BinaryOp.Div, y)))
+ opp.AddOperator(InfixOperator("mod", ws, 13, Associativity.Left, fun x y -> Exp.Binary(x, BinaryOp.Mod, y)))
+
+ opp.AddOperator(PrefixOperator("-", ws, 14, false, fun x -> Exp.Unary(UnaryOp.Negate, x)))
+ opp.AddOperator(PrefixOperator("not", ws, 15, false, fun x -> Exp.Unary(UnaryOp.Not, x)))
+ // opp.AddOperator(PrefixOperator("cast", nospace, 100, false, fun x -> Exp.Unary(UnaryOp.Cast, x))
+
+ let internal parse (original:string) p =
+ match run p original with
+ | Success(result, _, _) -> result
+ | Failure(errorMsg, _, _) -> (raise(ArgumentException(errorMsg)))
+
+ let parse_orderby (original:string) =
+ parse original orderby |> Array.ofList
+
+ let parse_filter (original:string) =
+ parse original term
+
+ let parse_expand (original:string) =
+ parse original expandExp |> Array.ofList
+
+ let parse_select (original:string) =
+ parse original selectExp |> Array.ofList
+
+ end
+
+(*
+isofExpression = "isof" [WSP] "("[[WSP] commonExpression [WSP] ","][WSP]stringLiteral [WSP] ")"
+castExpression = "cast" [WSP] "("[[WSP] commonExpression [WSP] ","][WSP]stringLiteral [WSP] ")"
+boolCastExpression = "cast" [WSP] "("[[WSP] commonExpression [WSP] ","][WSP] "Edm.Boolean" [WSP] ")"
+
+methodCallExpression = boolMethodExpression
+ / indexOfMethodCallExpression
+ / replaceMethodCallExpression
+ / toLowerMethodCallExpression
+ / toUpperMethodCallExpression
+ / trimMethodCallExpression
+ / substringMethodCallExpression
+ / concatMethodCallExpression
+ / lengthMethodCallExpression
+ / yearMethodCallExpression
+ / monthMethodCallExpression
+ / dayMethodCallExpression
+ / hourMethodCallExpression
+ / minuteMethodCallExpression
+ / secondMethodCallExpression
+ / roundMethodCallExpression
+ / floorMethodCallExpression
+ / ceilingMethodCallExpression
+
+boolMethodExpression = endsWithMethodCallExpression
+ / startsWithMethodCallExpression
+ / substringOfMethodCallExpression
+
+endsWithMethodCallExpression = "endswith" [WSP]
+ "(" [WSP] commonexpression [WSP]
+ "," [WSP] commonexpression [WSP] ")"
+indexOfMethodCallExpression = "indexof" [WSP]
+ "(" [WSP] commonexpression [WSP]
+ "," [WSP] commonexpression [WSP] ")"
+replaceMethodCallExpression = "replace" [WSP]
+ "(" [WSP] commonexpression [WSP]
+ "," [WSP] commonexpression [WSP]
+ "," [WSP] commonexpression [WSP] ")"
+startsWithMethodCallExpression = "startswith" [WSP]
+ "(" [WSP] commonexpression [WSP]
+ "," [WSP] commonexpression [WSP] ")"
+toLowerMethodCallExpression = "tolower" [WSP]
+ "(" [WSP] commonexpression [WSP] ")"
+toUpperMethodCallExpression = "toupper" [WSP]
+ "(" [WSP] commonexpression [WSP] ")"
+trimMethodCallExpression = "trim" [WSP]
+ "(" [WSP] commonexpression [WSP] ")"
+substringMethodCallExpression = "substring" [WSP]
+ "(" [WSP] commonexpression [WSP]
+ [ "," [WSP] commonexpression [WSP] ] ")"
+substringOfMethodCallExpression = "substringof" [WSP]
+ "(" [WSP] commonexpression [WSP]
+ [ "," [WSP] commonexpression [WSP] ] ")"
+concatMethodCallExpression = "concat" [WSP]
+ "(" [WSP] commonexpression [WSP]
+ [ "," [WSP] commonexpression [WSP] ] ")"
+lengthMethodCallExpression = "length" [WSP]
+ "(" [WSP] commonexpression [WSP] ")"
+yearMethodCallExpression = "year" [WSP]
+ "(" [WSP] commonexpression [WSP] ")"
+monthMethodCallExpression = "month" [WSP]
+ "(" [WSP] commonexpression [WSP] ")"
+dayMethodCallExpression = "day" [WSP]
+ "(" [WSP] commonexpression [WSP] ")"
+hourMethodCallExpression = "hour" [WSP]
+ "(" [WSP] commonexpression [WSP] ")"
+minuteMethodCallExpression = "minute" [WSP]
+ "(" [WSP] commonexpression [WSP] ")"
+secondMethodCallExpression = "second" [WSP]
+ "(" [WSP] commonexpression [WSP] ")"
+roundMethodCallExpression = "round" [WSP]
+ "(" [WSP] commonexpression [WSP] ")"
+floorMethodCallExpression = "floor" [WSP]
+ "(" [WSP] commonexpression [WSP] ")"
+ceilingMethodCallExpression = "ceiling" [WSP]
+ "(" [WSP] commonexpression [WSP] ")"
+*)
\ No newline at end of file
diff --git a/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/QuerySemanticAnalysis.fs b/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/QuerySemanticAnalysis.fs
new file mode 100644
index 00000000..c78208c1
--- /dev/null
+++ b/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/QuerySemanticAnalysis.fs
@@ -0,0 +1,303 @@
+// Copyright 2004-2012 Castle Project - http://www.castleproject.org/
+// Hamilton Verissimo de Oliveira and individual contributors as indicated.
+// See the committers.txt/contributors.txt in the distribution for a
+// full listing of individual contributors.
+//
+// This is free software; you can redistribute it and/or modify it
+// under the terms of the GNU Lesser General Public License as
+// published by the Free Software Foundation; either version 3 of
+// the License, or (at your option) any later version.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this software; if not, write to the Free
+// Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+// 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+
+namespace Castle.MonoRail.Extension.OData
+
+open System
+open System.Collections
+open System.Collections.Specialized
+open System.Collections.Generic
+open System.Data.OData
+open System.Data.Services.Providers
+open System.Linq
+open System.Linq.Expressions
+open System.Text
+open System.Reflection
+open System.Web
+open Castle.MonoRail
+open FParsec
+open FParsec.Primitives
+open FParsec.CharParsers
+
+
+type QueryAst =
+ | Element
+ | Null
+ | Literal of Type * obj
+ | PropertyAccess of QueryAst * PropertyInfo * ResourceType
+ | BinaryExp of QueryAst * QueryAst * BinaryOp * ResourceType
+ | UnaryExp of QueryAst * UnaryOp * ResourceType
+ with
+ member x.GetExpType(root:ResourceType) =
+ match x with
+ | Element -> root.InstanceType
+ | Null -> typeof
+ | Literal (t,_) -> t
+ | PropertyAccess (_,_,rt) -> rt.InstanceType
+ | UnaryExp (_,_,rt) -> rt.InstanceType
+ | BinaryExp (_,_,_,rt) -> rt.InstanceType
+
+ // member x.IsDecimal()
+
+ member x.ToStringTree() =
+ let b = StringBuilder()
+ let rec print (n) (level:int) =
+ b.AppendLine() |> ignore
+ for x in 1..level do b.Append(" ") |> ignore
+ match n with
+ | Null -> b.Append (sprintf "Null") |> ignore
+ | Element -> b.Append (sprintf "Element") |> ignore
+ | Literal (t,v) -> b.Append (sprintf "Literal %s [%O]" t.Name v) |> ignore
+ | PropertyAccess (ex,pinfo,rt) ->
+ b.Append (sprintf "PropertyAccess [%s] = %s" pinfo.Name rt.FullName) |> ignore
+ print ex (level + 1)
+
+ | UnaryExp (ex,op,rt) ->
+ b.Append (sprintf "Unary %O %s" op rt.FullName) |> ignore
+ print ex (level + 1)
+
+ | BinaryExp (ex1,ex2,op,rt) ->
+ b.Append (sprintf "Binary %O %s" op rt.FullName) |> ignore
+ print ex1 (level + 1)
+ print ex2 (level + 1)
+
+ | _ -> failwithf "Unsupported node type? Need to update this match, dude"
+ print x 1
+ b.ToString()
+
+type OrderByAst =
+ | Nothing
+ | Asc of QueryAst
+ | Desc of QueryAst
+
+module QuerySemanticAnalysis =
+ begin
+
+ // recursively process the raw exp tree transforming it
+ // into a QueryAst bound to types and RTs
+ let rec private r_analyze e (rt:ResourceType) =
+ match e with
+ | Exp.Element -> QueryAst.Element, rt
+ | Exp.Literal (edm, v, o) ->
+ let literal =
+ match edm with
+ | EdmPrimitives.Null -> QueryAst.Null
+ | EdmPrimitives.SString -> QueryAst.Literal (typeof, v)
+ | EdmPrimitives.Int16 -> QueryAst.Literal (typeof, o)
+ | EdmPrimitives.Int32 -> QueryAst.Literal (typeof, o)
+ | EdmPrimitives.Int64 -> QueryAst.Literal (typeof, o)
+ | EdmPrimitives.Single -> QueryAst.Literal (typeof, o)
+ | EdmPrimitives.Decimal -> QueryAst.Literal (typeof, o)
+ | EdmPrimitives.Double -> QueryAst.Literal (typeof, o)
+ | EdmPrimitives.DateTime -> QueryAst.Literal (typeof, o)
+ | EdmPrimitives.Boolean -> QueryAst.Literal (typeof, Convert.ToBoolean(v))
+ | EdmPrimitives.Guid -> QueryAst.Literal (typeof, o)
+ | _ -> failwithf "Unsupported edm primitive type %O" edm
+
+ match literal with
+ | QueryAst.Literal (t,v) -> literal, ResourceType.GetPrimitiveResourceType(t)
+ | QueryAst.Null -> literal, null
+ | _ -> failwith "What kind of literal is that?!"
+
+ | Exp.MemberAccess (ex, id) ->
+ let name =
+ match id with
+ | Identifier i -> i
+ | _ -> failwith "Only Identifier nodes are supported as the rhs of a MemberAccess node"
+
+ let get_prop (name:string) (rt:ResourceType) =
+ match rt.Properties |> Seq.tryFind (fun p -> p.Name === name) with
+ | Some p -> p
+ | _ -> failwith "Property not found?"
+
+ let root, nestedRt = r_analyze ex rt
+
+ // rt.InstanceType.GetProperty(p.Name, BindingFlags.Public ||| BindingFlags.Instance)
+ let prop = get_prop name nestedRt
+ let propInfo = nestedRt.InstanceType.GetProperty(prop.Name, BindingFlags.Public ||| BindingFlags.Instance)
+
+ QueryAst.PropertyAccess(root, propInfo, prop.ResourceType), prop.ResourceType
+
+
+ | Exp.Binary (ex1, op, ex2) ->
+
+ let texp1, t1 = r_analyze ex1 rt
+ let texp2, t2 = r_analyze ex2 rt
+
+ // Unary Numeric promotions
+ // A data service MAY support unary numeric promotions for the negation operator
+ // (negateExpression common expressions). Unary promotions consist of converting
+ // operands of type Edm.Byte or Edm.Int16 to Edm.Int32 and of type Edm.Single to Edm.Double.
+
+ // Binary Numeric promotions
+ // If supported, binary numeric promotion SHOULD implicitly convert both operands to a
+ // common type and, in the case of the nonrelational operators, also become the return type.
+ // If supported, a data service SHOULD support binary numeric promotion for the following
+ // Entity Data Model (EDM) primitive types
+
+ let cast_exp (exp:QueryAst) targetType =
+ // * If binary numeric promotion is supported, a data service SHOULD use a castExpression to
+ // promote an operand to the target type.
+ if exp.GetExpType(rt) = targetType
+ then exp
+ else QueryAst.UnaryExp(exp, UnaryOp.Cast, ResourceType.GetPrimitiveResourceType(targetType))
+
+ let convert_to_bool (e1:QueryAst) (e2:QueryAst) =
+ if e1.GetExpType(rt) <> typeof || e2.GetExpType(rt) <> typeof then
+ let newe1 =
+ if e1.GetExpType(rt) = typeof
+ then QueryAst.UnaryExp(e1, UnaryOp.Cast, ResourceType.GetPrimitiveResourceType(typeof))
+ else e1
+ let newe2 =
+ if e2.GetExpType(rt) = typeof
+ then QueryAst.UnaryExp(e2, UnaryOp.Cast, ResourceType.GetPrimitiveResourceType(typeof))
+ else e2
+ newe1, newe2
+ else e1, e2
+
+ let binary_numeric_promote (e1:QueryAst) (e2:QueryAst) originalRt =
+ // If supported, binary numeric promotion SHOULD consist of the application of the
+ // following rules in the order specified:
+ // * If either operand is of type Edm.Decimal, the other operand is converted
+ // to Edm.Decimal unless it is of type Edm.Single or Edm.Double.
+ // * Otherwise, if either operand is Edm.Double, the other operand is converted to type Edm.Double.
+ // * Otherwise, if either operand is Edm.Single, the other operand is converted to type Edm.Single.
+ // * Otherwise, if either operand is Edm.Int64, the other operand is converted to type Edm.Int64.
+ // * Otherwise, if either operand is Edm.Int32, the other operand is converted to type Edm.Int32
+ // * Otherwise, if either operand is Edm.Int16, the other operand is converted to type Edm.Int16.
+
+ if e1.GetExpType(rt) = typeof || e2.GetExpType(rt) = typeof then
+ cast_exp e1 typeof, cast_exp e2 typeof, ResourceType.GetPrimitiveResourceType (typeof)
+
+ elif e1.GetExpType(rt) = typeof || e2.GetExpType(rt) = typeof then
+ cast_exp e1 typeof, cast_exp e2 typeof, ResourceType.GetPrimitiveResourceType (typeof)
+
+ elif e1.GetExpType(rt) = typeof || e2.GetExpType(rt) = typeof then
+ cast_exp e1 typeof, cast_exp e2 typeof, ResourceType.GetPrimitiveResourceType (typeof)
+
+ elif e1.GetExpType(rt) = typeof || e2.GetExpType(rt) = typeof then
+ cast_exp e1 typeof, cast_exp e2 typeof, ResourceType.GetPrimitiveResourceType (typeof)
+
+ elif e1.GetExpType(rt) = typeof || e2.GetExpType(rt) = typeof then
+ cast_exp e1 typeof, cast_exp e2 typeof, ResourceType.GetPrimitiveResourceType (typeof)
+
+ elif e1.GetExpType(rt) = typeof || e2.GetExpType(rt) = typeof then
+ cast_exp e1 typeof, cast_exp e2 typeof, ResourceType.GetPrimitiveResourceType (typeof)
+
+ else e1, e2, originalRt
+
+ let assert_isnumeric (t:Type) =
+ //t.IsPrimitive &&
+ ()
+
+ let newExp1, newExp2, eqRt =
+ match op with
+ | BinaryOp.And
+ | BinaryOp.Or ->
+ // suports booleans
+ let new1, new2 = convert_to_bool texp1 texp2
+ new1, new2, ResourceType.GetPrimitiveResourceType(typeof)
+
+ | BinaryOp.Add
+ | BinaryOp.Mul
+ | BinaryOp.Div
+ | BinaryOp.Mod
+ | BinaryOp.Sub ->
+ // suports decimal, double single int32 and int64
+ // need to promote members if necessary
+ let new1, new2, newRt = binary_numeric_promote texp1 texp2 t1
+ assert_isnumeric (newRt.InstanceType)
+ assert_isnumeric (new1.GetExpType(rt))
+ assert_isnumeric (new2.GetExpType(rt))
+ new1, new2, newRt
+
+ | BinaryOp.Neq
+ | BinaryOp.Eq
+ | BinaryOp.LessT
+ | BinaryOp.GreatT
+ | BinaryOp.LessET
+ | BinaryOp.GreatET ->
+ // suports double single int32 int64 string datetime guid binary
+ let boolRt = ResourceType.GetPrimitiveResourceType(typeof)
+ let new1, new2, newRt = binary_numeric_promote texp1 texp2 boolRt
+ new1, new2, boolRt
+
+ | _ -> failwith "Unknown binary operation"
+
+ QueryAst.BinaryExp(newExp1, newExp2, op, eqRt), eqRt
+
+ | Exp.Unary (op, exp) ->
+
+ let exp1, expRt = r_analyze exp rt
+
+ let newExp, eqRt =
+ match op with
+ | UnaryOp.Negate ->
+ // suports decimal, double single int32 and int64
+ // need to promote members if necessary
+ exp1, expRt
+
+ | UnaryOp.Not ->
+ // suports bool only
+ exp1, ResourceType.GetPrimitiveResourceType(typeof)
+
+ // TODO: isofExpression
+ // TODO: cast
+ | _ -> failwith "Unknown unary operation"
+
+ QueryAst.UnaryExp(newExp, op, eqRt), eqRt
+
+ | _ -> failwithf "Unsupported exp type %O" e
+
+
+ let analyze_and_convert (exp:Exp) (rt:ResourceType) : QueryAst =
+
+ let newTree, _ = r_analyze exp rt
+ newTree
+
+ let analyze_and_convert_orderby (exps:OrderByExp[]) (rt:ResourceType) : OrderByAst seq =
+
+ let convert exp =
+ match exp with
+ | OrderByExp.Asc e -> let ast, _ = r_analyze e rt in OrderByAst.Asc(ast)
+ | OrderByExp.Desc e -> let ast, _ = r_analyze e rt in OrderByAst.Desc(ast)
+ | _ -> failwithf "Unsupported OrderByExp type %O" exp
+
+ exps |> Seq.map convert
+
+ let analyze_and_convert_expand (exps:Exp[]) (rt:ResourceType) (properties:HashSet) =
+
+ let rec resolve_property ast (rt:ResourceType) =
+ match ast with
+ | QueryAst.Element ->
+ rt
+ | QueryAst.PropertyAccess (source, name, res) ->
+ let target = resolve_property source rt
+ let prop = target.Properties |> Seq.find (fun p -> p.Name = name.Name)
+ properties.Add prop |> ignore
+ res
+ | _ -> failwithf "Unsupported QueryAst type %O" ast
+
+ let properties = HashSet()
+
+ let convert exp =
+ let ast, _ = r_analyze exp rt
+ resolve_property ast rt
+
+ exps |> Seq.iter (fun e -> convert e |> ignore)
+
+
+ end
+
diff --git a/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/ResourceMetadataBuilder.fs b/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/ResourceMetadataBuilder.fs
index 31188c8b..e107a3d6 100644
--- a/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/ResourceMetadataBuilder.fs
+++ b/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/ResourceMetadataBuilder.fs
@@ -1,38 +1,17 @@
-
-namespace Castle.MonoRail.OData
-
- open System
- open System.Collections.Generic
- open System.Data.OData
- open System.Data.Services.Providers
- open System.Data.Services.Common
- open System.Linq
- open System.Linq.Expressions
- open System.Reflection
-
- []
- type EntitySetConfig(entityName, source, targetType:Type) =
- let _entMapAttrs : List = List()
- member x.TargetType = targetType
- member x.EntityName : string = entityName
- member x.Source : IQueryable = source
- member internal x.EntityPropertyAttributes : List = _entMapAttrs
-
-
- and EntitySetConfigurator<'a>(entityName, source:IQueryable<'a>) =
- inherit EntitySetConfig(entityName, source, typeof<'a>)
-
- member x.TypedSource = source
-
- member x.AddAttribute( att:EntityPropertyMappingAttribute ) =
- //let memberAccess = exp.Body :?> MemberExpression
- //let prop = memberAccess.Member :?> PropertyInfo
- //let res, list = x.EntityPropertyAttributes.TryGetValue prop
- //if res
- //then list.Add att
- //else x.EntityPropertyAttributes.[prop] <- List([att])
- x.EntityPropertyAttributes.Add att
-
+// Copyright 2004-2012 Castle Project - http://www.castleproject.org/
+// Hamilton Verissimo de Oliveira and individual contributors as indicated.
+// See the committers.txt/contributors.txt in the distribution for a
+// full listing of individual contributors.
+//
+// This is free software; you can redistribute it and/or modify it
+// under the terms of the GNU Lesser General Public License as
+// published by the Free Software Foundation; either version 3 of
+// the License, or (at your option) any later version.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this software; if not, write to the Free
+// Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+// 02110-1301 USA, or see the FSF site: http://www.fsf.org.
namespace Castle.MonoRail.Extension.OData
@@ -62,7 +41,7 @@ namespace Castle.MonoRail.Extension.OData
let private resolve_resourceTypeKind_based_on_properties (entType:Type) =
let hasKeyOrNonPrimitives =
entType.GetProperties(PropertiesBindingFlags)
- |> Seq.exists (fun p -> p.IsDefined(typeof, true) || not <| is_primitive p.PropertyType)
+ |> Seq.exists (fun p -> p.IsDefined(typeof, true))
if hasKeyOrNonPrimitives
then ResourceTypeKind.EntityType
else ResourceTypeKind.ComplexType
@@ -76,7 +55,6 @@ namespace Castle.MonoRail.Extension.OData
then pType.GetGenericArguments().[0]
else null
-
let rec private resolveRT (pType) (knownTypes:Dictionary) (builderFn) =
// maybe it's a primitive
let resourceType = ResourceType.GetPrimitiveResourceType(pType)
@@ -103,7 +81,6 @@ namespace Castle.MonoRail.Extension.OData
then Some(result, false)
else None
-
let private resolve_propertKind (resource:ResourceType) (propertyInfo:PropertyInfo) (isCollection) =
if resource.ResourceTypeKind = ResourceTypeKind.Primitive then
@@ -122,35 +99,40 @@ namespace Castle.MonoRail.Extension.OData
failwithf "Unsupported resource type (kind) for property"
- let private build_property (resource:ResourceType) (prop:PropertyInfo) (knownTypes:Dictionary) builderFn =
+ let private build_property (resource:ResourceType) (prop:PropertyInfo) (knownTypes:Dictionary)
+ (customPropMapping:Dictionary<_,_>) builderFn =
if not prop.DeclaringType.IsInterface && prop.CanRead && prop.GetIndexParameters().Length = 0 then
- let propType = prop.PropertyType
- match resolveRT propType knownTypes builderFn with
+ let propType =
+ let succ, config : bool * PropConfigurator = customPropMapping.TryGetValue(prop)
+ if not succ then prop.PropertyType
+ else config.MappedType
+
+ match resolveRT propType knownTypes builderFn with
| Some (resolvedType, isColl) ->
- let kind = resolve_propertKind resolvedType prop isColl
+ let kind = resolve_propertKind resolvedType prop isColl
let resProp = ResourceProperty(prop.Name, kind, resolvedType)
resource.AddProperty resProp
| _ -> ()
let private build_properties (resource:ResourceType) (knownTypes:Dictionary) (type2CustomName:Dictionary)
- resourceBuilderFn (entType:Type) =
+ customPropMapping resourceBuilderFn (entType:Type) =
entType.GetProperties(PropertiesBindingFlags)
- |> Seq.iter (fun prop -> build_property resource prop knownTypes resourceBuilderFn)
+ |> Seq.iter (fun prop -> build_property resource prop knownTypes customPropMapping resourceBuilderFn)
let rec private build_resource_type schemaNs (knownTypes:Dictionary) (type2CustomName:Dictionary)
- (entMapAttributes:List) (entType:Type) =
+ (entMapAttributes:List) customPropMapping (entType:Type) =
if entType.IsValueType || not entType.IsVisible || entType.IsArray || entType.IsPointer || entType.IsCOMObject || entType.IsInterface ||
entType = typeof || entType = typeof || entType = typeof || entType = typeof ||
entType = typeof || entType = typeof || entType.IsEnum then
null
- elif knownTypes.ContainsKey(entType) then
+ elif knownTypes.ContainsKey(entType) then
knownTypes.[entType]
- else
+ else
// note: no support for hierarchies of resource types yet
let kind = resolve_resourceTypeKind_based_on_properties entType
@@ -159,13 +141,14 @@ namespace Castle.MonoRail.Extension.OData
knownTypes.[entType] <- resource
entMapAttributes |> Seq.iter (fun e -> resource.AddEntityPropertyMappingAttribute e)
- build_properties resource knownTypes type2CustomName (build_resource_type schemaNs knownTypes type2CustomName entMapAttributes) entType
+ build_properties resource knownTypes type2CustomName customPropMapping (build_resource_type schemaNs knownTypes type2CustomName entMapAttributes customPropMapping) entType
resource
/// Asserts that the return from build_resource_type is non null and EntityType
- let private build_entity_resource schemaNs (config:EntitySetConfig) (knownTypes) (type2CustomName) =
- let resource = build_resource_type schemaNs knownTypes type2CustomName config.EntityPropertyAttributes config.TargetType
+ let private build_entity_resource schemaNs (config:EntitySetConfig) propMappings (knownTypes) (type2CustomName) =
+
+ let resource = build_resource_type schemaNs knownTypes type2CustomName config.EntityPropertyAttributes propMappings config.TargetType
if resource = null || resource.ResourceTypeKind <> ResourceTypeKind.EntityType
then failwithf "Expecting an entity to be constructed from %O but instead got something else" config.TargetType
@@ -173,13 +156,21 @@ namespace Castle.MonoRail.Extension.OData
let build(schemaNs:string, configs:EntitySetConfig seq) =
+ // aggregates the custom mapping for all properties
+ let propMappings =
+ let dict = Dictionary()
+ configs
+ |> Seq.collect (fun c -> c.CustomPropConfig)
+ |> Seq.iter (fun kv -> dict.[kv.Key] <- kv.Value)
+ dict
+
let type2CustomName =
Enumerable.ToDictionary(configs,
(fun (c:EntitySetConfig) -> c.TargetType),
(fun (c:EntitySetConfig) -> c.EntityName))
let knownTypes = Dictionary()
- configs |> Seq.iter (fun c -> build_entity_resource schemaNs c knownTypes type2CustomName)
+ configs |> Seq.iter (fun c -> build_entity_resource schemaNs c propMappings knownTypes type2CustomName)
knownTypes.Values |> box :?> ResourceType seq
diff --git a/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/SegmentParser.fs b/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/SegmentParser.fs
index 677f7869..fe3a2ff2 100644
--- a/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/SegmentParser.fs
+++ b/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/SegmentParser.fs
@@ -1,24 +1,45 @@
-
+// Copyright 2004-2012 Castle Project - http://www.castleproject.org/
+// Hamilton Verissimo de Oliveira and individual contributors as indicated.
+// See the committers.txt/contributors.txt in the distribution for a
+// full listing of individual contributors.
+//
+// This is free software; you can redistribute it and/or modify it
+// under the terms of the GNU Lesser General Public License as
+// published by the Free Software Foundation; either version 3 of
+// the License, or (at your option) any later version.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this software; if not, write to the Free
+// Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+// 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+
namespace Castle.MonoRail.Extension.OData
open System
open System.Collections
+open System.Collections.Specialized
open System.Collections.Generic
open System.Data.OData
open System.Data.Services.Providers
open System.Linq
+open System.Linq.Expressions
open System.Web
open Castle.MonoRail
-type EntityDetails = {
+type EntityAccessInfo = {
+ RawPathSegment : string;
+ Uri : Uri;
mutable ManyResult : IQueryable;
mutable SingleResult : obj;
+ ResSet : ResourceSet;
ResourceType : ResourceType;
Name : string;
- Key : string
+ Key : string;
}
-type PropertyAccessDetails = {
+type PropertyAccessInfo = {
+ RawPathSegment : string;
+ Uri : Uri;
mutable ManyResult : IEnumerable;
mutable SingleResult : obj;
ResourceType : ResourceType;
@@ -27,31 +48,169 @@ type PropertyAccessDetails = {
}
type MetaSegment =
+ | Nothing
| Metadata
- | Batch
| Count
| Value
- | Links of UriSegment[]
+ // | Links // of UriSegment[]
+ | Batch
+
+and MetaQuerySegment =
+ | Nothing
| Format of string
| Skip of int
| Top of int
- | OrderBy of string[]
- | Expand of string[]
- | Select of string[]
- | InlineCount
+ | OrderBy of string
+ | Expand of string
+ | Select of string
+ | InlineCount of string
| Filter of string
and UriSegment =
- | Meta of MetaSegment
- | ServiceDirectory
- | EntitySet of EntityDetails
- | EntityType of EntityDetails
- | ComplexType of PropertyAccessDetails
- | PropertyAccessSingle of PropertyAccessDetails
- | PropertyAccessCollection of PropertyAccessDetails
- | ServiceOperation
+ // | Links
| Nothing
-
+ | ServiceDirectory
+ | EntitySet of EntityAccessInfo
+ | EntityType of EntityAccessInfo
+ | ComplexType of PropertyAccessInfo
+ | PropertyAccessSingle of PropertyAccessInfo
+ | PropertyAccessCollection of PropertyAccessInfo
+ | RootServiceOperation
+ | ActionOperation of ControllerActionOperation
+
+
+(*
+ Uri Syntax spec. See ms-odata.pdf, section 2.2.3.1
+ ==================================================
+
+serviceRoot = *( "/" segment-nz ) ; section 3.3 of [RFC3986]
+ ; segment-nz = the non empty sequence of characters
+ ; outside the set of URI reserved
+ ; characters as specified in [RFC3986]
+
+pathPrefix = *( "/" segment-nz ) ; zero or more URI path segments
+
+resourcePath = "/"
+ ( ([entityContainer "."] entitySet)
+ / serviceOperation-collEt
+ [ paren ] [ navPath ] [ count ]
+ )
+ / serviceOperation
+
+paren = "()"
+
+serviceOperation = serviceOperation-et
+ / serviceOperation-collCt
+ / serviceOperation-ct
+ / serviceOperation-collPrim
+ / serviceOperation-prim [ value ]
+
+count = "/$count"
+
+navPath = "("keyPredicate")" [navPath-options]
+
+navPath-options = [ navPath-np / propertyPath / propertyPath-ct / value ]
+
+navPath-np = "/"
+ ( ("$links" / entityNavProperty )
+ / (entityNavProperty-es [ paren ] [ navPath ])
+ / (entityNavProperty-et [ navPath-options ])
+ )
+
+entityNavProperty = (entityNavProperty-es [ paren ])
+ / entityNavProperty-et
+
+propertyPath = "/" entityProperty [ value ]
+propertyPath-ct = 1 * ("/" entityComplexProperty) [ propertyPath ]
+
+keyPredicate = keyPredicate-single
+ / keyPredicate-cmplx
+
+keyPredicate-single = 1*DIGIT ; section B.1 of [RFC5234]
+ / ([1*unreserved] "’" 1*unreserved "’") ; section 2.3 of [RFC3986]
+ / 1*(HEXDIG HEXDIG)) ; section B.1 of [RFC5234]
+
+keyPredicate-cmplx = entityProperty "=" keyPredicate-single
+ ["," keyPredicate-cmplx]
+
+value = "/$value"
+
+queryOptions = sysQueryOption ; see section 2.2.3.6.1
+ / customQueryOption ; section 2.2.3.6.2
+ / serviceOpParam ; see section 2.2.3.6.3
+ *("&"(sysQueryOption / serviceOpParam / customQueryOption))
+
+sysQueryOption = expandQueryOp
+ / filterQueryOp
+ / orderbyQueryOp
+ / skipQueryOp
+ / topQueryOp
+ / formatQueryOp
+ / countQueryOp
+ / selectQueryOp
+ / skiptokenQueryOp
+
+customQueryOption = *pchar ; section 3.3 of [RFC3986]
+expandQueryOp = ; see section 2.2.3.6.1.3
+filterQueryOp = ; see section 2.2.3.6.1.4
+orderbyQueryOp = ; see section 2.2.3.6.1.6
+skipQueryOp = ; see section 2.2.3.6.1.7
+serviceOpParam = ; see section 2.2.3.6.3
+topQueryOp = ; see section 2.2.3.6.1.8
+formatQueryOp = ; see section 2.2.3.6.1.5
+countQueryOp = ; see section 2.2.3.6.1.10
+selectQueryOp = ; see section 2.2.3.6.1.11
+skiptokenQueryOp = ; see section 2.2.3.6.1.9
+
+;Note: The semantic meaning, relationship to Entity Data Model
+; (EDM) constructs and additional URI construction
+; constraints for the following grammar rules are further
+; defined in (section 2.2.3.4) and (section 2.2.3.5)
+; See [MC-CSDL] for further scoping rules regarding the value
+; of each of the rules below
+
+entityContainer = *pchar ; section 3.3 of [RFC3986]
+ ; the name of an Entity Container in the EDM model
+
+entitySet = *pchar ; section 3.3 of [RFC3986]
+ ; the name of an Entity Set in the EDM model
+
+entityType = *pchar ; section 3.3 of [RFC3986]
+ ; the name of an Entity Type in the EDM model
+
+entityProperty = *pchar ; section 3.3 of [RFC3986]
+ ; the name of a property (of type EDMSimpleType) on an
+ ; Entity Type in the EDM
+ ; model associated with the data service
+
+entityComplexProperty = *pchar ; section 3.3 of [RFC3986]
+ ; the name of a property (of type ComplexType) on an
+ ; Entity Type in the EDM
+ ; model associated with the data service
+
+entityNavProperty-es= *pchar ; section 3.3 of [RFC3986]
+ ; the name of a Navigation Property on an Entity Type in
+ ; the EDM model associated with the data service. The
+ ; Navigation Property MUST identify an Entity Set.
+
+entityNavProperty-et= *pchar ; section 3.3 of [RFC3986]
+ ; the name of a Navigation Property on an Entity Type
+ ; in the EDM model associated with the data service.
+ ; The Navigation Property MUST identify an entity.
+
+serviceOperation-collEt = *pchar ; section 3.3 of [RFC3986]
+ ; the name of a Function Import in the EDM model which returns a
+ ; collection of entities from the same Entity Set
+
+serviceOperation-et = *pchar ; section 3.3 of [RFC3986]
+ ; the name of a Function Import which returns a single Entity
+ ; Type instance
+
+serviceOperation-collCt = *pchar ; section 3.3 of [RFC3986]
+ ; the name of a Function Import which returns a collection of
+ ; Complex Type [MC-CSDL] instances. Each member of the
+ ; collection is of the same type.
+*)
module SegmentParser =
begin
@@ -93,11 +252,16 @@ module SegmentParser =
then Some(arg)
else None
- let (|RootOperationAccess|_|) (model:ODataModel) (arg:string) =
+ let (|RootOperationAccess|_|) (model:ODataModel) (arg:string) =
None
- let (|OperationAccess|_|) (rt:ResourceType option) (arg:string) =
- None
+ let (|OperationAccess|_|) (model:ODataModel) (rt:ResourceType option) (arg:string) =
+ if rt.IsNone then None
+ else
+ let op = model.GetNestedOperation(rt.Value, arg)
+ if op = null
+ then None
+ else Some(op)
let (|PropertyAccess|_|) (rt:ResourceType option) (arg:string) =
let name, key =
@@ -114,7 +278,7 @@ module SegmentParser =
let (|EntityTypeAccess|_|) (model:ODataModel) (arg:string) =
match arg with
| SegmentWithKey (name, key) ->
- match model.GetResourceType(name) with
+ match model.GetResourceSet(name) with
| Some rt -> Some(rt, name, key)
| _ -> None
| _ -> None
@@ -122,60 +286,147 @@ module SegmentParser =
let (|EntitySetAccess|_|) (model:ODataModel) (arg:string) =
match arg with
| SegmentWithoutKey name ->
- match model.GetResourceType(name) with
- | Some rt -> Some(rt, name)
+ match model.GetResourceSet(name) with
+ | Some rs -> Some(rs, name)
| _ -> None
| _ -> None
-
+
+ let internal process_first (firstSeg:string) model (svcUri:Uri) (resourceType:Ref) (meta:Ref) =
+ match firstSeg with
+ | "" ->
+ UriSegment.ServiceDirectory
+ | Meta m -> // todo: semantic validation
+ Diagnostics.Debug.Assert (!meta = MetaSegment.Nothing)
+ meta := m
+ UriSegment.Nothing
+
+ | RootOperationAccess model o ->
+ UriSegment.RootServiceOperation
+
+ // todo: support for:
+ // | OperationAccess within ResourceType
+
+ | EntitySetAccess model (rs, name) ->
+ resourceType := rs.ResourceType
+ UriSegment.EntitySet({ Uri=Uri(svcUri, name); RawPathSegment=firstSeg; ResSet = rs;
+ ResourceType = !resourceType; Name = name; Key = null;
+ SingleResult = null; ManyResult = null; })
+
+ | EntityTypeAccess model (rs, name, key) ->
+ resourceType := rs.ResourceType
+ UriSegment.EntityType({ Uri=Uri(svcUri, firstSeg); RawPathSegment=firstSeg; ResSet = rs;
+ ResourceType = !resourceType; Name = name; Key = key;
+ SingleResult = null; ManyResult = null; })
+
+ | _ -> raise(HttpException(400, "First segment of uri could not be parsed"))
+
+
+ let internal build_segment_for_property kind (baseUri:Uri) (rawSegment:string) (prop:ResourceProperty) key =
+ match kind with
+ | ResourcePropertyKind.Primitive ->
+ // todo: assert key is null
+ let info = { Uri=Uri(baseUri, rawSegment); RawPathSegment=rawSegment; ResourceType=prop.ResourceType;
+ Property=prop; Key = null; SingleResult = null; ManyResult = null }
+ UriSegment.PropertyAccessSingle(info)
+
+ | ResourcePropertyKind.ComplexType ->
+ // todo: assert key is null
+ let info = { Uri=Uri(baseUri, rawSegment); RawPathSegment=rawSegment; ResourceType=prop.ResourceType;
+ Property=prop; Key = null; SingleResult = null; ManyResult = null }
+ UriSegment.ComplexType(info)
+
+ | ResourcePropertyKind.ResourceReference ->
+ let info = { Uri=Uri(baseUri, rawSegment); RawPathSegment=rawSegment; ResourceType=prop.ResourceType;
+ Property=prop; Key = key; SingleResult = null; ManyResult = null }
+ UriSegment.PropertyAccessSingle(info)
+
+ | ResourcePropertyKind.ResourceSetReference ->
+ if key = null then
+ let info = { Uri=Uri(baseUri, rawSegment); RawPathSegment=rawSegment; ResourceType=prop.ResourceType;
+ Property=prop; Key = null; SingleResult = null; ManyResult = null }
+ UriSegment.PropertyAccessCollection(info)
+ else
+ let info = { Uri=Uri(baseUri, rawSegment); RawPathSegment=rawSegment; ResourceType=prop.ResourceType;
+ Property=prop; Key = key; SingleResult = null; ManyResult = null }
+ UriSegment.PropertyAccessSingle(info)
+
+ | _ -> raise(HttpException(500, "Unsupported property kind for segment "))
+
+ let partition_qs_parameters (qs:NameValueCollection) =
+ let odataParams, ordinaryParams =
+ let odata, ordinary =
+ qs.AllKeys
+ |> List.ofSeq |> List.partition (fun k -> k.StartsWith("$", StringComparison.Ordinal))
+ let ordinaryParams = NameValueCollection(StringComparer.OrdinalIgnoreCase)
+ ordinary |> List.iter (fun i -> ordinaryParams.[i] <- qs.[i])
+ let odataparms = NameValueCollection(StringComparer.OrdinalIgnoreCase)
+ odata |> List.iter (fun i -> odataparms.[i] <- qs.[i])
+ odataparms, ordinary
+ odataParams, ordinaryParams
+
// we also need to parse QS
// ex url/Suppliers?$filter=Address/City eq 'Redmond'
- let public parse(path:string, qs:string, model:ODataModel) : UriSegment[] =
-
- let rec parse_segment (all:UriSegment list) (previous:UriSegment) (contextRT:ResourceType option) (rawSegments:string[]) (index:int) : UriSegment[] =
+ let parse(path:string, qs:NameValueCollection, model:ODataModel, svcUri:Uri) : UriSegment[] * MetaSegment * MetaQuerySegment[] =
+
+ let odataParams, ordinaryParams = partition_qs_parameters qs
+
+ // tracks the meta we will discover later
+ let meta = ref MetaSegment.Nothing
+
+ // tracks the last segment where (is a collection type = true)
+ let lastCollAccessSegment : Ref = ref UriSegment.Nothing
+
+ // this rec function parses all segments but the first
+ let rec parse_segment (all:UriSegment list) (previous:UriSegment)
+ (contextRT:ResourceType option) (rawSegments:string[]) (index:int) : UriSegment[] =
- // check for empty is temporary, should find better solution
if index < rawSegments.Length && (rawSegments.[index] <> String.Empty && index <= rawSegments.Length - 1) then
let rawSegment = rawSegments.[index]
let resourceType : Ref = ref null
+ let baseUri =
+ match previous with
+ | UriSegment.EntitySet d
+ | UriSegment.EntityType d -> Uri(d.Uri.AbsoluteUri + "/")
+ | UriSegment.PropertyAccessCollection d
+ | UriSegment.PropertyAccessSingle d -> Uri(d.Uri.AbsoluteUri + "/")
+ | _ -> svcUri
let newSegment =
match rawSegment with
| Meta m ->
- // todo: semantic validation
- UriSegment.Meta(m)
- | OperationAccess contextRT o -> UriSegment.ServiceOperation
+ // todo: semantic validation
+ // (e.g. at this point, $metadata is not accepted)
+ Diagnostics.Debug.Assert (!meta = MetaSegment.Nothing)
+ meta := m
+ UriSegment.Nothing
+
+ | OperationAccess model contextRT o ->
+ UriSegment.ActionOperation(o)
| PropertyAccess contextRT (prop, key) ->
resourceType := prop.ResourceType
-
match prop.Kind with
| ResourcePropKind kind ->
- match kind with
- | ResourcePropertyKind.Primitive ->
- // todo: assert key is null
- UriSegment.PropertyAccessSingle({ ResourceType=prop.ResourceType; Property=prop; Key = null; SingleResult = null; ManyResult = null })
-
- | ResourcePropertyKind.ComplexType ->
- // todo: assert key is null
- UriSegment.ComplexType({ ResourceType=prop.ResourceType; Property=prop; Key = null; SingleResult = null; ManyResult = null })
+ build_segment_for_property kind baseUri rawSegment prop key
+ | _ -> raise(HttpException(500, "Unsupported property kind for segment "))
- | ResourcePropertyKind.ResourceReference ->
- UriSegment.PropertyAccessSingle({ ResourceType=prop.ResourceType; Property=prop; Key = key; SingleResult = null; ManyResult = null })
+ | _ -> raise(HttpException(400, "Segment does not match a property or operation"))
+
+ match newSegment with
+ | UriSegment.EntitySet _
+ | UriSegment.PropertyAccessCollection _ ->
+ lastCollAccessSegment := newSegment
+ | _ -> ()
- | ResourcePropertyKind.ResourceSetReference ->
- if key = null then
- UriSegment.PropertyAccessCollection({ ResourceType=prop.ResourceType; Property=prop; Key = null; SingleResult = null; ManyResult = null })
- else
- UriSegment.PropertyAccessSingle({ ResourceType=prop.ResourceType; Property=prop; Key = key; SingleResult = null; ManyResult = null })
+ let rt = if !resourceType <> null then Some(!resourceType) else None
- | _ -> raise(HttpException(500, "Unsupported property kind for segment "))
- | _ -> raise(HttpException(500, "Unsupported property kind for segment "))
- | _ -> raise(HttpException(400, "Segment does not match a property or operation"))
+ let newList =
+ all @ (if newSegment = UriSegment.Nothing then [] else [newSegment])
- parse_segment (all @ [newSegment]) newSegment (if !resourceType <> null then Some(!resourceType) else None) rawSegments (index + 1)
- else
- all |> Array.ofList
+ parse_segment newList newSegment rt rawSegments (index + 1)
+
+ else all |> Array.ofList
let normalizedPath =
if path.StartsWith("/", StringComparison.Ordinal)
@@ -186,67 +437,31 @@ module SegmentParser =
let firstSeg = rawSegments.[0]
let resourceType : Ref = ref null
- let segment =
- match firstSeg with
- | "" ->
- UriSegment.ServiceDirectory
- | Meta m ->
- // todo: semantic validation
- UriSegment.Meta(m)
- | RootOperationAccess model o ->
- UriSegment.ServiceOperation
- | EntitySetAccess model (rt, name) ->
- resourceType := rt
- UriSegment.EntitySet({ ResourceType = rt; Name = name; Key = null; SingleResult = null; ManyResult = null })
- | EntityTypeAccess model (rt, name, key) ->
- resourceType := rt
- UriSegment.EntityType({ ResourceType = rt; Name = name; Key = key; SingleResult = null; ManyResult = null })
- | _ -> raise(HttpException(400, "First segment of uri could not be parsed"))
-
- parse_segment [segment] segment (if !resourceType <> null then Some(!resourceType) else None) rawSegments 1
-
-
-
- (*
- http://services.odata.org/OData/OData.svc/Categories
- Identifies all Categories Collection.
- Is described by the Entity Set named "Categories" in the service metadata document.
- http://services.odata.org/OData/OData.svc/Categories(1)
- Identifies a single Category Entry with key value 1.
- Is described by the Entity Type named "Categories" in the service metadata document.
- http://services.odata.org/OData/OData.svc/Categories(1)/Name
- Identifies the Name property of the Categories Entry with key value 1.
- Is described by the Property named "Name" on the "Categories" Entity Type in the service metadata document.
- http://services.odata.org/OData/OData.svc/Categories(1)/Products
- Identifies the collection of Products associated with Category Entry with key value 1.
- Is described by the Navigation Property named "Products" on the "Category" Entity Type in the service metadata document.
- http://services.odata.org/OData/OData.svc/Categories(1)/Products/$count
- Identifies the number of Product Entries associated with Category 1.
- Is described by the Navigation Property named "Products" on the "Category" Entity Type in the service metadata document.
- http://services.odata.org/OData/OData.svc/Categories(1)/Products(1)/Supplier/Address/City
- Identifies the City of the Supplier for Product 1 which is associated with Category 1.
- Is described by the Property named "City" on the "Address" Complex Type in the service metadata document.
- http://services.odata.org/OData/OData.svc/Categories(1)/Products(1)/Supplier/Address/City/$value
- Same as the URI above, but identifies the "raw value" of the City property.
- http://services.odata.org/OData/OData.svc/Categories(1)/$links/Products
- Identifies the set of Products related to Category 1.
- Is described by the Navigation Property named "Products" on the "Category" Entity Type in the associated service metadata document.
- http://services.odata.org/OData/OData.svc/Products(1)/$links/Category
- Identifies the Category related to Product 1.
- Is described by the Navigation Property named "Category" on the "Product" Entity Type in the associated service metadata document.
- *)
-
-
-// need to process segments from an endpoint
-// Example localhost/vpath/odata.svc/Products(1)/Categories
-
-// http://odata.research.microsoft.com/FAQ.aspx
-// http://services.odata.org/%28S%28zjtwckq5iumy0qno2wbf413y%29%29/OData/OData.svc/
-
-// http://odata.netflix.com/v2/Catalog/
-// http://odata.netflix.com/v2/Catalog/$metadata
-// http://odata.netflix.com/v2/Catalog/Movies
-
-// http://vancouverdataservice.cloudapp.net/v1/
-
- end
\ No newline at end of file
+ // first segment is a special situation
+ let segment = process_first firstSeg model svcUri resourceType meta
+
+ // calls the parser
+ let uriSegments = parse_segment [segment] segment (if !resourceType <> null then Some(!resourceType) else None) rawSegments 1
+
+ // process odata query parameters, if any
+ let metaQuerySegments =
+ let list = List()
+ for key in odataParams.Keys do
+ let value = odataParams.[key]
+ match key with
+ | "$filter" -> MetaQuerySegment.Filter (value)
+ | "$orderby" -> MetaQuerySegment.OrderBy (value)
+ | "$top" -> MetaQuerySegment.Top (Int32.Parse(value))
+ | "$skip" -> MetaQuerySegment.Skip (Int32.Parse(value))
+ | "$expand" -> MetaQuerySegment.Expand (value)
+ | "$format" -> MetaQuerySegment.Format (value)
+ | "$inlinecount"-> MetaQuerySegment.InlineCount (value)
+ | "$select" -> MetaQuerySegment.Select (value)
+ | _ -> failwithf "special query parameter is not supported: %s (note that these parameters are case sensitive)" key
+ |> list.Add
+ list |> Array.ofSeq
+
+ uriSegments, !meta, metaQuerySegments
+
+ end
+
diff --git a/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/SegmentProcessor.fs b/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/SegmentProcessor.fs
index d4608d8d..f64983f8 100644
--- a/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/SegmentProcessor.fs
+++ b/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/SegmentProcessor.fs
@@ -1,4 +1,19 @@
-namespace Castle.MonoRail.Extension.OData
+// Copyright 2004-2012 Castle Project - http://www.castleproject.org/
+// Hamilton Verissimo de Oliveira and individual contributors as indicated.
+// See the committers.txt/contributors.txt in the distribution for a
+// full listing of individual contributors.
+//
+// This is free software; you can redistribute it and/or modify it
+// under the terms of the GNU Lesser General Public License as
+// published by the Free Software Foundation; either version 3 of
+// the License, or (at your option) any later version.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this software; if not, write to the Free
+// Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+// 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+
+namespace Castle.MonoRail.Extension.OData
open System
open System.IO
@@ -6,6 +21,7 @@ open System.Linq
open System.Linq.Expressions
open System.Collections
open System.Collections.Generic
+open System.Collections.Specialized
open System.Data.OData
open System.Data.Services.Providers
open System.ServiceModel.Syndication
@@ -13,6 +29,7 @@ open System.Text
open System.Xml
open System.Xml.Linq
open Castle.MonoRail
+open Castle.MonoRail.Extension.OData.Serialization
// http://msdn.microsoft.com/en-us/library/dd233205.aspx
@@ -24,12 +41,24 @@ type SegmentOp =
// | Merge = 5
type ProcessorCallbacks = {
- accessSingle : Func;
- accessMany : Func;
- create : Func;
- update : Func;
- remove : Func;
-}
+ authorize : Func;
+ authorizeMany : Func;
+ view : Func;
+ viewMany : Func;
+ create : Func;
+ update : Func;
+ remove : Func;
+ operation : Action;
+ negotiateContent : Func;
+} with
+ member x.Auth (rt, parameters, item) = x.authorize.Invoke(rt, parameters, item)
+ member x.Auth (rt, parameters, item) = x.authorizeMany.Invoke(rt, parameters, item)
+ member x.View (rt, parameters, item) = x.view.Invoke(rt, parameters, item)
+ member x.View (rt, parameters, item) = x.viewMany.Invoke(rt, parameters, item)
+ member x.Create (rt, parameters, item) = x.create.Invoke(rt, parameters, item)
+ member x.Update (rt, parameters, item) = x.update.Invoke(rt, parameters, item)
+ member x.Remove (rt, parameters, item) = x.remove.Invoke(rt, parameters, item)
+ member x.Operation (rt, parameters, action) = x.operation.Invoke(rt, parameters, action)
type RequestParameters = {
model : ODataModel;
@@ -47,18 +76,18 @@ type ResponseParameters = {
mutable contentEncoding : Encoding;
writer : TextWriter;
mutable httpStatus : int;
-}
-
-type ResponseToSend = {
- mutable QItems : IQueryable;
- mutable EItems : IEnumerable;
- mutable SingleResult : obj;
- ResType : ResourceType;
+ mutable httpStatusDesc : string;
+ mutable location : string;
}
module SegmentProcessor =
begin
- let internal emptyResponse = { QItems = null; EItems = null; SingleResult = null; ResType = null }
+ type ResponseParameters with
+ member x.SetStatus(code:int, desc:string) =
+ x.httpStatus <- code
+ x.httpStatusDesc <- desc
+
+ let internal emptyResponse = { QItems = null; SingleResult = null; ResType = null; FinalResourceUri=null; ResProp = null; PropertiesToExpand = HashSet() }
let (|HttpGet|HttpPost|HttpPut|HttpDelete|HttpMerge|HttpHead|) (arg:string) =
match arg.ToUpperInvariant() with
@@ -70,47 +99,38 @@ module SegmentProcessor =
| "GET" -> HttpGet
| _ -> failwithf "Could not understand method %s" arg
- type This = static member Assembly = typeof.Assembly
-
- let typed_select_methodinfo =
- let m = This.Assembly.GetType("Castle.MonoRail.Extension.OData.SegmentProcessor").GetMethod("typed_select")
- System.Diagnostics.Debug.Assert(m <> null, "Could not get typed_select methodinfo")
- m
-
- let typed_select<'a> (source:IQueryable) (key:obj) (keyProp:ResourceProperty) =
- let typedSource = source :?> IQueryable<'a>
- let parameter = Expression.Parameter(source.ElementType, "element")
- let e = Expression.Property(parameter, keyProp.Name)
- let bExp = Expression.Equal(e, Expression.Constant(key))
- let exp = Expression.Lambda(bExp, [parameter]) :?> Expression>
- typedSource.FirstOrDefault(exp)
-
- let private select_by_key (rt:ResourceType) (source:IQueryable) (key:string) =
-
- // for now support for a single key
- let keyProp = Seq.head rt.KeyProperties
- let keyVal =
- // weak!!
- System.Convert.ChangeType(key, keyProp.ResourceType.InstanceType)
+ let private assert_entitytype_without_entityset op (rt:ResourceType) (model:ODataModel) =
+ if rt.ResourceTypeKind <> ResourceTypeKind.EntityType then
+ failwithf "Unsupported operation %O" op
+ match model.GetRelatedResourceSet(rt) with
+ | Some rs -> failwithf "Unsupported operation %O" op
+ | _ -> ()
- let rtType = rt.InstanceType
- let ``method`` = typed_select_methodinfo.MakeGenericMethod([|rtType|])
- let result = ``method``.Invoke(null, [|source; keyVal; keyProp|])
- if result = null then failwithf "Lookup of entity %s for key %s failed." rt.Name key
- result
- let internal serialize_result (items:IEnumerable) (item:obj) (rt:ResourceType) (request:RequestParameters) (response:ResponseParameters) =
- let s = SerializerFactory.Create(response.contentType)
+ let internal serialize_result (formatOverrider:string) (reply:ResponseToSend)
+ (request:RequestParameters) (response:ResponseParameters) (containerUri:Uri) =
- if items <> null then
- s.SerializeMany (request.baseUri, rt, items, response.writer, response.contentEncoding)
- else
- s.SerializeSingle (request.baseUri, rt, item, response.writer, response.contentEncoding)
+ response.contentType <-
+ if formatOverrider <> null then
+ match formatOverrider.ToLowerInvariant() with
+ | "json" -> MediaTypes.JSon
+ | "xml" -> MediaTypes.Xml
+ | "atom" -> MediaTypes.Atom
+ | "simplejson" -> MediaTypes.JSon;
+ | _ -> failwithf "Unsupported format value %O" formatOverrider
+ else response.contentType
+
+ let wrapper = request.wrapper
+ let s = SerializerFactory.Create(response.contentType, formatOverrider,
+ wrapper, request.baseUri, containerUri,
+ reply.ResType, reply.PropertiesToExpand,
+ response.writer, response.contentEncoding )
+ Diagnostics.Debug.Assert( s <> null )
+ s.Serialize reply
let internal deserialize_input (rt:ResourceType) (request:RequestParameters) =
let s = DeserializerFactory.Create(request.contentType)
-
s.DeserializeSingle (rt, new StreamReader(request.input), request.contentEncoding)
let internal get_property_value (container:obj) (property:ResourceProperty) =
@@ -123,124 +143,242 @@ module SegmentProcessor =
value
- let internal process_collection_property op container (p:PropertyAccessDetails) (previous:UriSegment) hasMoreSegments (model:ODataModel) (shouldContinue:Ref) =
+ let internal process_collection_property op container (p:PropertyAccessInfo) (previous:UriSegment) hasMoreSegments
+ (model:ODataModel) (callbacks:ProcessorCallbacks)
+ (request:RequestParameters) (response:ResponseParameters) parameters
+ (shouldContinue:Ref) =
System.Diagnostics.Debug.Assert ((match previous with | UriSegment.Nothing -> false | _ -> true), "cannot be root")
if op = SegmentOp.View || (hasMoreSegments && op = SegmentOp.Update) then
let value = (get_property_value container p.Property ) :?> IEnumerable
- //if intercept_many op value p.ResourceType shouldContinue then
+ // if callbacks.accessMany.Invoke(p.ResourceType, value) then
p.ManyResult <- value
+ { ResType = p.ResourceType;
+ QItems = value.AsQueryable(); SingleResult = null;
+ FinalResourceUri = p.Uri; ResProp = p.Property; PropertiesToExpand = HashSet() }
+ // else emptyResponse
+
else
match op with
- | SegmentOp.Update ->
- // deserialize
- // process
- // result
- raise(NotImplementedException("Update for property not supported yet"))
+ | SegmentOp.Create ->
+
+ assert_entitytype_without_entityset op p.ResourceType model
+
+ let input = deserialize_input p.ResourceType request
+
+ let succ= callbacks.create.Invoke(p.ResourceType, parameters, input)
+ if succ then
+ response.SetStatus(201, "Created")
+ // we dont have enough data to build it
+ // response.location <- Uri(request.baseUri, p.Uri.OriginalString + "(" + key + ")").AbsoluteUri
+
+ p.SingleResult <- input
+
+ { ResType = p.ResourceType;
+ QItems = null; SingleResult = input;
+ FinalResourceUri = p.Uri; ResProp = null; PropertiesToExpand = HashSet() }
+ else
+ shouldContinue := false
+ emptyResponse
+
| _ -> failwithf "Unsupported operation %O" op
- let internal process_item_property op container (p:PropertyAccessDetails) (previous:UriSegment) hasMoreSegments (model:ODataModel) (shouldContinue:Ref) =
+ let internal process_item_property op container (p:PropertyAccessInfo) (previous:UriSegment) hasMoreSegments
+ (model:ODataModel) (callbacks:ProcessorCallbacks) (shouldContinue:Ref)
+ (requestParams:RequestParameters) (response:ResponseParameters) parameters =
System.Diagnostics.Debug.Assert ((match previous with | UriSegment.Nothing -> false | _ -> true), "cannot be root")
- if op = SegmentOp.View || (hasMoreSegments && op = SegmentOp.Update) then
+ let auth_item (item:obj) =
+ let succ = callbacks.Auth(p.ResourceType, parameters, item)
+ if not succ then shouldContinue := false
+ succ
+
+ let get_property_value () =
let propValue = get_property_value container p.Property
- if p.Key <> null then
- let collAsQueryable = (propValue :?> IEnumerable).AsQueryable()
- let value = select_by_key p.ResourceType collAsQueryable p.Key
- //if intercept_single op value p.ResourceType shouldContinue then
- p.SingleResult <- value
+ let finalVal =
+ if p.Key <> null then
+ let collAsQueryable = (propValue :?> IEnumerable).AsQueryable()
+ let value = AstLinqTranslator.select_by_key p.ResourceType collAsQueryable p.Key
+ value
+ else propValue
+ if auth_item finalVal
+ then finalVal
+ else null
+
+ if op = SegmentOp.View || hasMoreSegments then
+
+ let singleResult = get_property_value ()
+
+ if singleResult <> null then
+ if not hasMoreSegments && not <| callbacks.View(p.ResourceType, parameters, singleResult) then
+ shouldContinue := false
else
- //if intercept_single op propValue p.ResourceType shouldContinue then
- p.SingleResult <- propValue
+ shouldContinue := false
+
+ if !shouldContinue then
+ p.SingleResult <- singleResult
+ { ResType = p.ResourceType;
+ QItems = null; SingleResult = singleResult;
+ FinalResourceUri = p.Uri; ResProp = p.Property; PropertiesToExpand = HashSet() }
+ else emptyResponse
+
else
+ System.Diagnostics.Debug.Assert (not hasMoreSegments)
+
match op with
| SegmentOp.Update ->
- // if primitive...
- raise(NotImplementedException("Update for property not supported yet"))
+
+ if p.Property.IsOfKind(ResourcePropertyKind.Primitive) then
+ // if primitive...
+ raise(NotImplementedException("Update for property is not supported yet"))
- // | SegmentOp.Delete -> is the property a relationship? should delete through a $link instead
- | _ -> ()
+ elif p.Property.IsOfKind(ResourcePropertyKind.ResourceSetReference) ||
+ p.Property.IsOfKind(ResourcePropertyKind.ResourceReference) then
+
+ // only supported for the case below, otherwise one should use $link instead
+ assert_entitytype_without_entityset op p.ResourceType model
+ let finalValue = get_property_value ()
- let internal process_entityset op (d:EntityDetails) (previous:UriSegment) hasMoreSegments
- (model:ODataModel) (callbacks:ProcessorCallbacks) (shouldContinue:Ref) requestParams =
- System.Diagnostics.Debug.Assert ((match previous with | UriSegment.Nothing -> true | _ -> false), "must be root")
+ if callbacks.update.Invoke(p.ResourceType, parameters, finalValue) then
+ response.SetStatus(204, "No Content")
- // System.Diagnostics.Debug.Assert (not hasMoreSegments)
+ emptyResponse
+
+ else failwithf "Operation not supported for this entity type"
+
+ | SegmentOp.Delete ->
+
+ if p.Property.IsOfKind(ResourcePropertyKind.Primitive) then
+ failwithf "Cannot delete a primitive value in a property"
+
+ elif p.Property.IsOfKind(ResourcePropertyKind.ResourceSetReference) ||
+ p.Property.IsOfKind(ResourcePropertyKind.ResourceReference) then
+
+ // only supported for the case below, otherwise one should use $link instead
+ assert_entitytype_without_entityset op p.ResourceType model
+
+ let finalValue = get_property_value ()
+
+ if callbacks.remove.Invoke(p.ResourceType, parameters, finalValue) then
+ response.SetStatus(204, "No Content")
+
+ emptyResponse
+
+ else failwithf "Operation not supported for this resource type"
+
+ | _ -> failwithf "Operation not supported for this resource type"
+
+
+ let internal process_entityset op (d:EntityAccessInfo) (previous:UriSegment) hasMoreSegments
+ (model:ODataModel) (callbacks:ProcessorCallbacks) (shouldContinue:Ref)
+ (request:RequestParameters) (response:ResponseParameters) parameters =
+ System.Diagnostics.Debug.Assert ((match previous with | UriSegment.Nothing -> true | _ -> false), "must be root")
+
+ let get_values () =
+ let value = model.GetQueryable (d.ResSet)
+ if not <| callbacks.Auth(d.ResourceType, parameters, value) then
+ shouldContinue := false; null
+ else value
match op with
| SegmentOp.View ->
// acceptable next segments: $count, $orderby, $top, $skip, $format, $inlinecount
- let value = model.GetQueryable (d.Name)
- if callbacks.accessMany.Invoke(d.ResourceType, value) then
- d.ManyResult <- value
- { ResType = d.ResourceType; QItems = value; EItems = null; SingleResult = null }
- else
- shouldContinue := false
- emptyResponse
+ let values = get_values ()
+ d.ManyResult <- values
- | SegmentOp.Create ->
- System.Diagnostics.Debug.Assert (not hasMoreSegments)
+ if values <> null then
+ if not hasMoreSegments && not <| callbacks.View( d.ResourceType, parameters, values ) then
+ shouldContinue := false
- let item = deserialize_input d.ResourceType requestParams
+ // remember: this ! is not NOT, it's a de-ref
+ if !shouldContinue then
+ { ResType = d.ResourceType; QItems = values; SingleResult = null; FinalResourceUri = d.Uri; ResProp = null; PropertiesToExpand = HashSet() }
+ else emptyResponse
- if callbacks.create.Invoke(d.ResourceType, item) then
- ()
- else
- shouldContinue := false
-
- emptyResponse
- | SegmentOp.Update ->
+ | SegmentOp.Create ->
System.Diagnostics.Debug.Assert (not hasMoreSegments)
- // deserialize
- // process
- // result
- emptyResponse
- | SegmentOp.Delete ->
- System.Diagnostics.Debug.Assert (not hasMoreSegments)
- // process
- // result
- emptyResponse
+ let item = deserialize_input d.ResourceType request
+
+ let succ = callbacks.Create(d.ResourceType, parameters, item)
+ if succ then
+ response.SetStatus(201, "Created")
+ // not enough info to build location
+ // response.location <- Uri(request.baseUri, d.Uri.OriginalString + "(" + key + ")").AbsoluteUri
+
+ { ResType = d.ResourceType;
+ QItems = null; SingleResult = item;
+ FinalResourceUri = d.Uri; ResProp = null; PropertiesToExpand = HashSet() }
+ else
+ shouldContinue := false
+ emptyResponse
| _ -> failwithf "Unsupported operation %O" op
- let internal process_entitytype op (d:EntityDetails) (previous:UriSegment) hasMoreSegments (model:ODataModel) (shouldContinue:Ref) stream =
+ let internal process_entityset_single op (d:EntityAccessInfo) (previous:UriSegment) hasMoreSegments
+ (model:ODataModel) (callbacks:ProcessorCallbacks) (shouldContinue:Ref)
+ (request:RequestParameters) (response:ResponseParameters) parameters =
System.Diagnostics.Debug.Assert ((match previous with | UriSegment.Nothing -> true | _ -> false), "must be root")
- if op = SegmentOp.View || (hasMoreSegments && op = SegmentOp.Update) then
- System.Diagnostics.Debug.Assert (not (op = SegmentOp.Delete), "should not be delete")
+ let auth_item (item:obj) =
+ let succ = callbacks.Auth(d.ResourceType, parameters, item)
+ if not succ then shouldContinue := false
+ succ
+
+ let get_single_result () =
+ let wholeSet = model.GetQueryable (d.ResSet)
+ let singleResult = AstLinqTranslator.select_by_key d.ResourceType wholeSet d.Key
+ if auth_item singleResult
+ then singleResult
+ else null
+
+ if op = SegmentOp.View || hasMoreSegments then
+ if not hasMoreSegments then Diagnostics.Debug.Assert (not (op = SegmentOp.Delete), "should not be delete")
+
+ let singleResult = get_single_result ()
- // if there are more segments, consider this a read
- let wholeSet = model.GetQueryable (d.Name)
- let singleResult = select_by_key d.ResourceType wholeSet d.Key
- //if intercept_single op singleResult d.ResourceType shouldContinue then
d.SingleResult <- singleResult
- { ResType = d.ResourceType; QItems = null; EItems = null; SingleResult = singleResult }
+ if singleResult <> null then
+ if not hasMoreSegments && not <| callbacks.View(d.ResourceType, parameters, singleResult) then
+ shouldContinue := false
+ else
+ shouldContinue := false
+
+ if !shouldContinue then
+ { ResType = d.ResourceType; QItems = null; SingleResult = singleResult; FinalResourceUri = d.Uri; ResProp = null; PropertiesToExpand = HashSet() }
+ else emptyResponse
- else
+ else
match op with
| SegmentOp.Update ->
- // deserialize
- // process
- // result
- emptyResponse
+ // runs auth
+ let single = get_single_result()
+ if single <> null then
+ // todo: shouldn't it deserialize into 'single'?
+ let item = deserialize_input d.ResourceType request
+ let succ = callbacks.Update(d.ResourceType, parameters, item)
+ if succ
+ then response.SetStatus(204, "No Content")
+ else shouldContinue := false
| SegmentOp.Delete ->
// http://www.odata.org/developers/protocols/operations#DeletingEntries
// Entries are deleted by executing an HTTP DELETE request against a URI that points at the Entry.
// If the operation executed successfully servers should return 200 (OK) with no response body.
-
- // process
- // result
- emptyResponse
+ let single = get_single_result()
+ if single <> null then
+ if callbacks.Remove(d.ResourceType, parameters, single) then
+ response.SetStatus(204, "No Content")
+ else shouldContinue := false
| _ -> failwithf "Unsupported operation %O at this level" op
+ emptyResponse
let internal serialize_directory op hasMoreSegments (previous:UriSegment) writer baseUri metadataProviderWrapper (response:ResponseParameters) =
@@ -254,9 +392,8 @@ module SegmentProcessor =
| _ -> failwithf "Unsupported operation %O at this level" op
- let internal serialize_metadata op hasMoreSegments (previous:UriSegment) writer baseUri metadataProviderWrapper (response:ResponseParameters) =
+ let internal serialize_metadata op (previous:UriSegment) writer baseUri metadataProviderWrapper (response:ResponseParameters) =
System.Diagnostics.Debug.Assert ((match previous with | UriSegment.Nothing -> true | _ -> false), "must be root")
- System.Diagnostics.Debug.Assert (not hasMoreSegments, "needs to be the only segment")
match op with
| SegmentOp.View ->
@@ -264,25 +401,42 @@ module SegmentProcessor =
MetadataSerializer.serialize (writer, metadataProviderWrapper, response.contentEncoding)
| _ -> failwithf "Unsupported operation %O at this level" op
- let internal resolveResponseContentType (segments:UriSegment[]) (acceptTypes:string[]) =
- match segments |> Array.tryPick (fun s -> match s with | UriSegment.Meta m -> (match m with | MetaSegment.Format f -> Some(f) | _ -> None ) | _ -> None) with
- | Some f ->
- match f.ToLowerInvariant() with
- | "atom" -> "application/atom+xml"
- | "xml" -> "application/xml"
- | "json" -> "application/json"
- | _ -> f
- | _ ->
- // should be more sophisticate than this..
- if acceptTypes = null || acceptTypes.Length = 0
- then "application/atom+xml" // defaults to atom
- else
- if acceptTypes |> Array.exists (fun at -> at.StartsWith("*/*", StringComparison.OrdinalIgnoreCase) )
- then "application/atom+xml"
- else acceptTypes.[0]
+ let private process_operation_value (previous:UriSegment) (result:ResponseToSend) (response:ResponseParameters) =
+ if result = emptyResponse || result.SingleResult = null
+ || result.ResProp = null
+ || not <| result.ResProp.IsOfKind(ResourcePropertyKind.Primitive) then
+ raise(InvalidOperationException("$value can only operate if a previous segment produced a primitive value"))
+
+ // change the response type
+ response.contentType <- "text/plain"
+
+ // return the exact same result as the previous
+ result
+
+ let private apply_filter (response:ResponseToSend) (rawExpression:string) =
+ let ast = QueryExpressionParser.parse_filter rawExpression
+ let typedAst = QuerySemanticAnalysis.analyze_and_convert ast response.ResType
+
+ if response.QItems <> null then
+ response.QItems <- AstLinqTranslator.apply_queryable_filter response.ResType response.QItems typedAst :?> IQueryable
+
+ let private apply_orderby (response:ResponseToSend) (rawExpression:string) =
+ let exps = QueryExpressionParser.parse_orderby rawExpression
+ let typedNodes = QuerySemanticAnalysis.analyze_and_convert_orderby exps response.ResType
- let public Process (op:SegmentOp) (segments:UriSegment[]) (callbacks:ProcessorCallbacks) (request:RequestParameters) (response:ResponseParameters) =
+ if response.QItems <> null then
+ response.QItems <- AstLinqTranslator.apply_queryable_orderby response.ResType response.QItems typedNodes :?> IQueryable
+
+ let private apply_expand (response:ResponseToSend) (rawExpression:string) =
+ let exps = QueryExpressionParser.parse_expand rawExpression
+ QuerySemanticAnalysis.analyze_and_convert_expand exps response.ResType response.PropertiesToExpand
+
+ let public Process (op:SegmentOp)
+ (segments:UriSegment[]) (meta:MetaSegment) (metaQueries:MetaQuerySegment[])
+ (ordinaryParams:NameValueCollection)
+ (callbacks:ProcessorCallbacks)
+ (request:RequestParameters) (response:ResponseParameters) =
// missing support for operations, value, filters, links, batch, ...
@@ -295,80 +449,103 @@ module SegmentProcessor =
// in case of exception, serialized error is sent
let model = request.model
- let stream = request.input
let baseUri = request.baseUri
let writer = response.writer
- do response.contentType <- resolveResponseContentType segments request.accept
+ let parameters = List()
+ let lastSegment = segments.[segments.Length - 1]
- let rec rec_process (index:int) (previous:UriSegment) (result:ResponseToSend) =
+ let rec rec_process (index:int) (previous:UriSegment) (result:ResponseToSend) =
let shouldContinue = ref true
if index < segments.Length then
- let container, prevRt =
+ let container, prevRt, containerUri =
match previous with
- | UriSegment.EntityType d -> d.SingleResult, d.ResourceType
+ | UriSegment.EntityType d -> d.SingleResult, d.ResourceType, d.Uri
| UriSegment.ComplexType d
- | UriSegment.PropertyAccessSingle d -> d.SingleResult, d.ResourceType
- | _ -> null, null
+ | UriSegment.PropertyAccessSingle d -> d.SingleResult, d.ResourceType, d.Uri
+ | _ -> null, null, null
+
+ // builds list of contextual parameters. used when calling back controllers
+ if container <> null then parameters.Add (prevRt.InstanceType, container)
let hasMoreSegments = index + 1 < segments.Length
let segment = segments.[index]
let toSerialize =
match segment with
- | UriSegment.Meta m ->
- match m with
- | MetaSegment.Metadata ->
- serialize_metadata op hasMoreSegments previous writer baseUri request.wrapper response
- emptyResponse
- | _ -> failwithf "Unsupported meta instruction %O" m
-
| UriSegment.ServiceDirectory ->
serialize_directory op hasMoreSegments previous writer baseUri request.wrapper response
emptyResponse
- | UriSegment.ServiceOperation ->
- ()
+ | UriSegment.ActionOperation actionOp ->
+ callbacks.Operation(actionOp.ResourceType, parameters, actionOp.Name)
+ // it's understood that the action took care of the result
emptyResponse
+ | UriSegment.RootServiceOperation -> emptyResponse
+
| UriSegment.EntitySet d ->
- process_entityset op d previous hasMoreSegments model callbacks shouldContinue request
+ process_entityset op d previous hasMoreSegments model callbacks shouldContinue request response parameters
| UriSegment.EntityType d ->
- process_entitytype op d previous hasMoreSegments model shouldContinue stream
+ process_entityset_single op d previous hasMoreSegments model callbacks shouldContinue request response parameters
| UriSegment.PropertyAccessCollection d ->
- process_collection_property op container d previous hasMoreSegments model shouldContinue
- emptyResponse
+ process_collection_property op container d previous hasMoreSegments model callbacks request response parameters shouldContinue
| UriSegment.ComplexType d | UriSegment.PropertyAccessSingle d ->
- process_item_property op container d previous hasMoreSegments model shouldContinue
- emptyResponse
+ process_item_property op container d previous hasMoreSegments model callbacks shouldContinue request response parameters
| _ -> Unchecked.defaultof
- // if !shouldContinue then
- rec_process (index+1) segment toSerialize
- // else
+ if !shouldContinue
+ then rec_process (index+1) segment toSerialize
+ else result
else result
- // process segments recursively.
+ let result =
+ // process segments recursively.
+ let navResult = rec_process 0 UriSegment.Nothing emptyResponse
+ match meta with
+ | MetaSegment.Nothing ->
+ navResult
+ | MetaSegment.Metadata ->
+ serialize_metadata op lastSegment writer baseUri request.wrapper response
+ emptyResponse
+ | MetaSegment.Value ->
+ process_operation_value lastSegment navResult response
+ | _ -> failwithf "Unsupported meta instruction %O" meta
+
+ let formatOverrider : Ref = ref null
+
+ // I'm starting to think that ordering may be important here:
+ // select > expand > everything else
+ for metaQuery in metaQueries do
+ match metaQuery with
+ | MetaQuerySegment.Select exp ->
+ ()
+ | MetaQuerySegment.Filter exp ->
+ apply_filter result exp
+ | MetaQuerySegment.OrderBy exp ->
+ apply_orderby result exp
+ | MetaQuerySegment.Expand exp ->
+ apply_expand result exp
+ | MetaQuerySegment.Format fmt ->
+ formatOverrider := fmt
+ | MetaQuerySegment.InlineCount cf ->
+ ()
+ | MetaQuerySegment.Skip howMany ->
+ ()
+ | MetaQuerySegment.Top count ->
+ ()
+ | _ -> failwithf "Unsupported metaQuery instruction %O" metaQuery
+
// we ultimately need to serialize a result back
- let result = rec_process 0 UriSegment.Nothing emptyResponse
-
if result <> emptyResponse then
- let items : IEnumerable =
- if result.QItems <> null
- then upcast result.QItems
- else result.EItems
- let item = result.SingleResult
- let rt = result.ResType
-
- serialize_result items item rt request response
-
- ()
-
+ if response.contentType = null then
+ response.contentType <- callbacks.negotiateContent.Invoke( result.SingleResult <> null )
+ serialize_result !formatOverrider result request response result.FinalResourceUri
end
diff --git a/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/Serialization/Serialization.Atom.fs b/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/Serialization/Serialization.Atom.fs
index f923b872..b7d09959 100644
--- a/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/Serialization/Serialization.Atom.fs
+++ b/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/Serialization/Serialization.Atom.fs
@@ -1,4 +1,19 @@
-namespace Castle.MonoRail.Extension.OData
+// Copyright 2004-2012 Castle Project - http://www.castleproject.org/
+// Hamilton Verissimo de Oliveira and individual contributors as indicated.
+// See the committers.txt/contributors.txt in the distribution for a
+// full listing of individual contributors.
+//
+// This is free software; you can redistribute it and/or modify it
+// under the terms of the GNU Lesser General Public License as
+// published by the Free Software Foundation; either version 3 of
+// the License, or (at your option) any later version.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this software; if not, write to the Free
+// Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+// 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+
+namespace Castle.MonoRail.Extension.OData.Serialization
open System
open System.Collections
@@ -21,35 +36,13 @@ module AtomSerialization =
let private linkRelResource = Uri("http://schemas.microsoft.com/ado/2007/08/dataservices/related/")
let private categoryScheme = "http://schemas.microsoft.com/ado/2007/08/dataservices/scheme"
-
- type System.Data.Services.Providers.ResourceProperty
- with
- member x.GetValue(instance:obj) =
- let prop = instance.GetType().GetProperty(x.Name)
- prop.GetValue(instance, null)
- member x.GetValueAsStr(instance:obj) =
- let prop = instance.GetType().GetProperty(x.Name)
- let value = prop.GetValue(instance, null)
- // XmlConvert.ToString(value)
- if value = null
- then null
- else value.ToString()
-
- type System.Data.Services.Providers.ResourceType
- with
- member x.PathWithKey(instance:obj) =
- let keyValue =
- if x.KeyProperties.Count = 1
- then x.KeyProperties.[0].GetValueAsStr(instance)
- else failwith "Composite keys are not supported"
- sprintf "%s(%s)" x.Name keyValue
-
-
+ /// The content of a SyndicationItem can be anything that inherits from SyndicationContent
+ /// This class exposes one that is based entries, like a dictionary
type ContentDict (items:(string*string*obj) seq) =
inherit SyndicationContent()
let _items = List(items)
- let rec write_primitive_prop (writer:XmlWriter) name typename (value:obj) =
+ let rec write_property_value (writer:XmlWriter) name typename (value:obj) =
writer.WriteStartElement (name, "http://schemas.microsoft.com/ado/2007/08/dataservices")
if typename <> "Edm.String" then
@@ -67,141 +60,257 @@ module AtomSerialization =
new () = ContentDict(Seq.empty)
+ /// Adds an entry to the content. value may be null
member x.Add(name, typename, value) =
_items.Add( (name, typename, value) )
member internal x.InternalWrite (writer, name) =
- _items |> Seq.iter (fun (name,typename,value) -> write_primitive_prop writer name typename value)
+ _items |> Seq.iter (fun (name,typename,value) -> write_property_value writer name typename value)
override x.Type = "application/xml"
override x.Clone() = upcast ContentDict(_items)
override x.WriteContentsTo (writer) =
if _items.Count > 0 then
writer.WriteStartElement("properties", "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata")
- _items |> Seq.iter (fun (name,typename,value) -> write_primitive_prop writer name typename value)
+ _items |> Seq.iter (fun (name,typename,value) -> write_property_value writer name typename value)
writer.WriteEndElement()
- let rec private build_content_from_properties (relResUri:Uri) instance (rt:ResourceType) (item:SyndicationItem) =
- let content = ContentDict()
- let skipContent = ref false
-
- for prop in rt.Properties do
- let otherRt = prop.ResourceType
- skipContent := false
-
- let atts = rt.OwnEpmAttributes |> Seq.filter (fun epm -> epm.SourcePath = prop.Name)
-
- for att in atts do
- skipContent := not att.KeepInContent
- match att.TargetSyndicationItem with
- // todo, lots of extra cases
- | SyndicationItemProperty.Title ->
- item.Title <- TextSyndicationContent (prop.GetValueAsStr(instance))
- | _ -> ()
-
- if prop.IsOfKind ResourcePropertyKind.ResourceReference then
- //
- item.Links.Add (SyndicationLink(Uri(relResUri.OriginalString + "/" + prop.Name, UriKind.Relative),
- linkRelResource.AbsoluteUri + otherRt.Name,
- otherRt.Name,
- "application/atom+xml;type=entry", 0L))
-
-
- elif prop.IsOfKind ResourcePropertyKind.ResourceSetReference then
- //
- item.Links.Add (SyndicationLink(Uri(relResUri.OriginalString + "/" + prop.Name, UriKind.Relative),
- linkRelResource.AbsoluteUri + otherRt.Name,
- otherRt.Name,
- "application/atom+xml;type=feed", 0L))
-
-
- elif prop.IsOfKind ResourcePropertyKind.ComplexType then
- // ...
-
- let innerinstance = prop.GetValue(instance)
- let inner = build_content_from_properties relResUri innerinstance otherRt item
- content.Add (prop.Name, prop.ResourceType.FullName, inner)
-
- elif prop.IsOfKind ResourcePropertyKind.Primitive && not !skipContent then
- content.Add (prop.Name, prop.ResourceType.FullName, (prop.GetValue(instance)))
-
- content
-
-
- let internal build_item (instance) (baseUri:Uri) (rt:ResourceType) addNs =
- (*
-
-
-
-
-
-
- 0
- 1992-01-01T00:00:00
-
- 4
- 2.5
-
- *)
- let item = SyndicationItem()
- let relResUri = Uri(rt.PathWithKey(instance), UriKind.Relative)
- let fullResUri = Uri(baseUri, relResUri)
-
- if addNs then
- item.AttributeExtensions.Add (qualifiedDataWebPrefix, "http://schemas.microsoft.com/ado/2007/08/dataservices")
- item.AttributeExtensions.Add (qualifiedDataWebMetadataPrefix, "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata")
-
- item.BaseUri <- baseUri
- // item.AttributeExtensions.Add (qualifiedDataWebPrefix, "http://schemas.microsoft.com/ado/2007/08/dataservices")
- // item.AttributeExtensions.Add (qualifiedDataWebMetadataPrefix, "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata")
-
- item.Title <- TextSyndicationContent(String.Empty)
- item.Id <- fullResUri.AbsoluteUri
- item.Links.Add(SyndicationLink(relResUri, "edit", rt.InstanceType.Name, null, 0L))
-
- item.Authors.Add emptyPerson
-
- item.Categories.Add(SyndicationCategory(rt.Namespace + "." + rt.InstanceType.Name, categoryScheme, null))
-
- item.Content <- build_content_from_properties relResUri instance rt item
+ /// Custom serializer for Atom.
+ /// Mapped properties of the entity type are written in the content element as
+ /// properties in the dataservice namespace.
+ type AtomSerializer(wrapper, serviceBaseUri, containerUri, rt, propertiesToExpand, writer, enc) as self =
+ class
+ inherit Serializer(wrapper, serviceBaseUri, containerUri, rt, propertiesToExpand, writer, enc)
- item
+ let _xmlWriter = SerializerCommons.create_xmlwriter writer enc
+ let rec build_content_from_properties (relResUri:Uri) instance (rt:ResourceType) (item:SyndicationItem) =
+ let content = ContentDict()
+ let skipContent = ref false
- let internal write_items (baseUri:Uri) (rt:ResourceType) (items:IEnumerable) (writer:TextWriter) (enc:Encoding) =
- let resUri = Uri(rt.Name, UriKind.Relative)
+ for prop in rt.Properties do
+ let otherRt = prop.ResourceType
+ skipContent := false
+
+ let atts = rt.OwnEpmAttributes |> Seq.filter (fun epm -> epm.SourcePath = prop.Name)
- let syndicationItems =
- let lst = List()
- for item in items do
- lst.Add (build_item item baseUri rt false)
- lst
+ for att in atts do
+ skipContent := not att.KeepInContent
+ match att.TargetSyndicationItem with
+ // todo: lots of extra cases
+ | SyndicationItemProperty.Title ->
+ item.Title <- TextSyndicationContent (prop.GetValueAsStr(instance))
+ | _ -> ()
- let feed = SyndicationFeed(syndicationItems)
+ if prop.IsOfKind ResourcePropertyKind.ResourceReference then
- feed.BaseUri <- baseUri
- feed.AttributeExtensions.Add (qualifiedDataWebPrefix, "http://schemas.microsoft.com/ado/2007/08/dataservices")
- feed.AttributeExtensions.Add (qualifiedDataWebMetadataPrefix, "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata")
+ let link = SyndicationLink(Uri(relResUri.OriginalString + "/" + prop.Name, UriKind.Relative),
+ linkRelResource.AbsoluteUri + otherRt.Name,
+ otherRt.Name, "application/atom+xml;type=entry", 0L)
- feed.Title <- TextSyndicationContent(rt.Name)
- feed.Id <- Uri(baseUri, resUri).AbsoluteUri
-
- // temporary
- feed.LastUpdatedTime <- DateTimeOffset.MinValue
+ if self.ShouldExpand prop then
+ ()
+ //
+ //
+ //
- feed.Links.Add(SyndicationLink(Uri(rt.Name, UriKind.Relative), "self", rt.Name, null, 0L))
+ item.Links.Add link
+
+ elif prop.IsOfKind ResourcePropertyKind.ResourceSetReference then
+
+ //
+ let link = SyndicationLink(Uri(relResUri.OriginalString + "/" + prop.Name, UriKind.Relative),
+ linkRelResource.AbsoluteUri + otherRt.Name,
+ otherRt.Name,
+ "application/atom+xml;type=feed", 0L)
+
+ if self.ShouldExpand prop then
+ ()
+ //
+ //
+ //
+ //
+ //
+
+ item.Links.Add link
+
+ elif prop.IsOfKind ResourcePropertyKind.ComplexType then
+ // ...
+ // todo: add case for collection of complex types
+
+ match InternalUtils.getEnumerableElementType prop.ResourceType.InstanceType with
+ | Some elementType ->
+ let innerContent = ContentDict()
+ let elements = prop.GetValue(instance) :?> IEnumerable
+ // start Properties
+ for element in elements do
+ let contentElement = build_content_from_properties relResUri element otherRt item
+ innerContent.Add ("element", prop.ResourceType.FullName, contentElement)
+ // end Properties
+ content.Add (prop.Name, prop.ResourceType.FullName, innerContent)
+
+ | _ ->
+ let innerinstance = prop.GetValue(instance)
+ let inner = build_content_from_properties relResUri innerinstance otherRt item
+ content.Add (prop.Name, prop.ResourceType.FullName, inner)
+
+ elif prop.IsOfKind ResourcePropertyKind.Primitive && not !skipContent then
+ let originalVal = (prop.GetValue(instance))
+ if originalVal <> null then
+ let strVal = XmlSerialization.to_xml_string prop.ResourceType.InstanceType originalVal
+ content.Add (prop.Name, prop.ResourceType.FullName, strVal)
+ else
+ content.Add (prop.Name, prop.ResourceType.FullName, null)
+ content
+
+ let build_item (instance) addNs appendKey =
+ let item = SyndicationItem()
+ let resourceSet = wrapper.ResourceSets |> Seq.tryFind (fun rs -> rs.ResourceType = rt)
+
+ if addNs then
+ item.BaseUri <- serviceBaseUri
+ item.AttributeExtensions.Add (qualifiedDataWebPrefix, "http://schemas.microsoft.com/ado/2007/08/dataservices")
+ item.AttributeExtensions.Add (qualifiedDataWebMetadataPrefix, "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata")
+
+ item.Title <- TextSyndicationContent(String.Empty)
+
+ let resourceUri =
+ match resourceSet with
+ | Some rs ->
+ // for this case, we always want to append the key
+ Uri(serviceBaseUri, rs.Name + rt.GetKey(instance))
+ | _ ->
+ System.Diagnostics.Debug.Assert (containerUri <> null)
+ if appendKey
+ then Uri(containerUri.AbsoluteUri + rt.GetKey(instance))
+ else containerUri
+ let relativeUri = serviceBaseUri.MakeRelativeUri(resourceUri)
+
+ item.Id <- resourceUri.AbsoluteUri
+
+ item.Links.Add(SyndicationLink(relativeUri, "edit", rt.InstanceType.Name, null, 0L))
+ item.Authors.Add emptyPerson
+ item.Categories.Add(SyndicationCategory(rt.Namespace + "." + rt.InstanceType.Name, categoryScheme, null))
+ item.Content <- build_content_from_properties relativeUri instance rt item
+ item
+
+ let build_feed (items:IEnumerable) =
+ let rootUri = if containerUri <> null then containerUri else serviceBaseUri
+ let resourceSet = wrapper.ResourceSets |> Seq.tryFind (fun rs -> rs.ResourceType = rt)
+
+ System.Diagnostics.Debug.Assert (rootUri <> null)
+
+ let syndicationItems =
+ let lst = List()
+ for item in items do
+ lst.Add (build_item item false true)
+ lst
+
+ let feed = SyndicationFeed(syndicationItems)
+
+ feed.BaseUri <- serviceBaseUri
+ feed.AttributeExtensions.Add (qualifiedDataWebPrefix, "http://schemas.microsoft.com/ado/2007/08/dataservices")
+ feed.AttributeExtensions.Add (qualifiedDataWebMetadataPrefix, "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata")
+
+ feed.Title <- TextSyndicationContent(rt.Name)
+ feed.Id <- rootUri.AbsoluteUri
- let xmlWriter = SerializerCommons.create_xmlwriter writer enc
- feed.GetAtom10Formatter().WriteTo( xmlWriter )
- xmlWriter.Flush()
-
- let internal write_item (baseUri:Uri) (rt:ResourceType) (item:obj) (writer:TextWriter) (enc:Encoding) =
- let syndicationItem = build_item item baseUri rt true
-
- let xmlWriter = SerializerCommons.create_xmlwriter writer enc
- syndicationItem.GetAtom10Formatter().WriteTo( xmlWriter )
- xmlWriter.Flush()
+ let selfLink =
+ match resourceSet with
+ | Some rs -> Uri(rs.Name, UriKind.Relative)
+ | _ -> containerUri
+
+ feed.Links.Add(SyndicationLink(selfLink, "self", rt.Name, null, 0L))
+ feed
+
+ override x.SerializeMany(items) =
+ let feed = build_feed items
+ feed.GetAtom10Formatter().WriteTo _xmlWriter
+ _xmlWriter.Flush()
+
+ override x.SerializeSingle(item) =
+ let item = build_item item true false
+ item.GetAtom10Formatter().WriteTo _xmlWriter
+ _xmlWriter.Flush()
+
+ override x.SerializeProperty(prop:ResourceProperty, value) =
+ raise(NotImplementedException())
+
+ end
+
+
+ let private get_string_value (reader:XmlReader) =
+ let doContinue = ref true
+ let buffer = StringBuilder()
+ while !doContinue && reader.Read() do
+ match reader.NodeType with
+ | XmlNodeType.SignificantWhitespace | XmlNodeType.CDATA | XmlNodeType.Text ->
+ buffer.Append reader.Value |> ignore
+ | XmlNodeType.Comment | XmlNodeType.Whitespace -> ()
+ | XmlNodeType.EndElement -> doContinue := false
+ | _ -> failwithf "Unexpected token parsing element value"
+ buffer.ToString()
+
+ let private populate_properties (reader:XmlReader) (rt:ResourceType) (instance:obj) =
+
+ while reader.ReadToElement() do
+ let doContinue = ref true
+
+ while !doContinue do
+ if reader.NodeType = XmlNodeType.None then
+ doContinue := false
+
+ elif reader.NodeType <> XmlNodeType.Element then
+ doContinue := true
+ reader.Skip()
+ else
+ match rt.Properties |> Seq.tryFind (fun p -> p.Name = reader.LocalName) with
+ | Some prop ->
+ let value : obj =
+ let rawStringVal =
+ let att = reader.GetAttribute("null", "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata")
+ if att = null || XmlConvert.ToBoolean(att) = false then
+ if reader.IsEmptyElement
+ then String.Empty
+ else get_string_value reader
+ else null
+
+ if rawStringVal <> null then
+ match prop.ResourceType.ResourceTypeKind with
+ | ResourceTypeKind.Primitive ->
+ let targetType =
+ let nulType = Nullable.GetUnderlyingType(prop.ResourceType.InstanceType)
+ if nulType = null then prop.ResourceType.InstanceType
+ else nulType
+
+ if targetType = typeof then rawStringVal :> obj
+ elif targetType = typeof then XmlConvert.ToInt32 rawStringVal |> box
+ elif targetType = typeof then XmlConvert.ToInt16 rawStringVal |> box
+ elif targetType = typeof then XmlConvert.ToInt64 rawStringVal |> box
+ elif targetType = typeof then XmlConvert.ToByte rawStringVal |> box
+ elif targetType = typeof then XmlConvert.ToBoolean rawStringVal |> box
+ elif targetType = typeof then XmlConvert.ToDateTime (rawStringVal, XmlDateTimeSerializationMode.RoundtripKind) |> box
+ elif targetType = typeof then XmlConvert.ToDecimal rawStringVal |> box
+ elif targetType = typeof then XmlConvert.ToSingle rawStringVal |> box
+ else null
+
+ | ResourceTypeKind.ComplexType -> failwithf "complex type support needs to be implemented"
+ | ResourceTypeKind.EntityType -> failwithf "entity type is not a supported property type"
+ | _ -> failwithf "Unsupported type"
+
+ else null
+
+ prop.SetValue(instance, value)
+
+ doContinue := reader.Read()
+
+ | _ ->
+ // could not find property: should this be an error?
+ doContinue := false
let internal read_item (rt:ResourceType) (reader:TextReader) (enc:Encoding) =
let reader = SerializerCommons.create_xmlreader reader enc
@@ -209,30 +318,33 @@ module AtomSerialization =
fmt.ReadFrom(reader)
let item = fmt.Item
+ let instance = Activator.CreateInstance rt.InstanceType
+ let content =
+ if item.Content :? XmlSyndicationContent
+ then item.Content :?> XmlSyndicationContent
+ else null
+
+ // todo: remapping of atom attributes
for prop in rt.PropertiesDeclaredOnThisType do
// rt.OwnEpmAttributes
()
- Activator.CreateInstance rt.InstanceType
+ if content <> null then
+ let reader = content.GetReaderAtContent()
+ reader.ReadStartElement ("content", "http://www.w3.org/2005/Atom")
+ if reader.IsStartElement ("properties", "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata") then
+ reader.ReadStartElement("properties", "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata")
+ populate_properties reader rt instance
- let internal read_feed (rt:ResourceType) (reader:TextReader) (enc:Encoding) =
- raise(NotImplementedException())
+ instance
- let CreateDeserializer () =
+ let CreateDeserializer =
{ new Deserializer() with
override x.DeserializeMany (rt, reader, enc) =
- read_feed rt reader enc
+ raise(NotImplementedException())
override x.DeserializeSingle (rt, reader, enc) =
read_item rt reader enc
}
- let CreateSerializer () =
- { new Serializer() with
- override x.SerializeMany(baseUri, rt, items, writer, enc) =
- write_items baseUri rt items writer enc
- override x.SerializeSingle(baseUri, rt, item, writer, enc) =
- write_item baseUri rt item writer enc
- }
-
end
diff --git a/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/Serialization/Serialization.Factory.fs b/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/Serialization/Serialization.Factory.fs
index a322d7f7..81c3539e 100644
--- a/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/Serialization/Serialization.Factory.fs
+++ b/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/Serialization/Serialization.Factory.fs
@@ -1,4 +1,19 @@
-namespace Castle.MonoRail.Extension.OData
+// Copyright 2004-2012 Castle Project - http://www.castleproject.org/
+// Hamilton Verissimo de Oliveira and individual contributors as indicated.
+// See the committers.txt/contributors.txt in the distribution for a
+// full listing of individual contributors.
+//
+// This is free software; you can redistribute it and/or modify it
+// under the terms of the GNU Lesser General Public License as
+// published by the Free Software Foundation; either version 3 of
+// the License, or (at your option) any later version.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this software; if not, write to the Free
+// Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+// 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+
+namespace Castle.MonoRail.Extension.OData.Serialization
open System
open System.Xml
@@ -6,36 +21,39 @@ open System.Xml
type SerializerFactory() =
- static member Create(contentType:string) : Serializer =
+ static member Create(contentType:string, overriding:string, wrapper, serviceBaseUri, containerUri, rt, propertiesToExpand, writer, enc) : Serializer =
match contentType.ToLowerInvariant() with
| "application/atom+xml" ->
- AtomSerialization.CreateSerializer()
+ upcast AtomSerialization.AtomSerializer(wrapper, serviceBaseUri, containerUri, rt, propertiesToExpand, writer, enc)
| "application/json" ->
- JSonSerialization.CreateSerializer()
-
- | "application/xml"
- | "text/xml" ->
- XmlSerialization.CreateSerializer()
-
- | _ -> failwithf "unsupported content type %s" contentType
+ let useSimplerFormat = overriding === "simplejson"
+ upcast JSonSerialization.JsonSerializer(wrapper, serviceBaseUri, containerUri, rt, propertiesToExpand, writer, enc, useSimplerFormat)
+
+ | "text/xml"
+ | "application/xml" ->
+ upcast XmlSerialization.XmlSerializer(wrapper, serviceBaseUri, containerUri, rt, propertiesToExpand, writer, enc)
+
+ | "text/plain" ->
+ upcast PlainTextSerialization.PlainTextSerializer(wrapper, serviceBaseUri, containerUri, rt, propertiesToExpand, writer, enc)
+ | _ -> failwithf "unsupported content type %s" contentType
type DeserializerFactory() =
static member Create(contentType:string) : Deserializer =
-
match contentType.ToLowerInvariant() with
| "application/atom+xml" ->
- AtomSerialization.CreateDeserializer()
-
+ AtomSerialization.CreateDeserializer
+
| "application/json" ->
- JSonSerialization.CreateDeserializer()
-
+ JSonSerialization.CreateDeserializer
+
| "application/xml"
| "text/xml" ->
XmlSerialization.CreateDeserializer()
-
- | _ -> failwithf "unsupported content type %s" contentType
\ No newline at end of file
+
+ | _ -> failwithf "unsupported content type %s" contentType
+
diff --git a/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/Serialization/Serialization.Json.fs b/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/Serialization/Serialization.Json.fs
index 7f02ad0e..05105f54 100644
--- a/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/Serialization/Serialization.Json.fs
+++ b/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/Serialization/Serialization.Json.fs
@@ -1,13 +1,264 @@
-namespace Castle.MonoRail.Extension.OData
+// Copyright 2004-2012 Castle Project - http://www.castleproject.org/
+// Hamilton Verissimo de Oliveira and individual contributors as indicated.
+// See the committers.txt/contributors.txt in the distribution for a
+// full listing of individual contributors.
+//
+// This is free software; you can redistribute it and/or modify it
+// under the terms of the GNU Lesser General Public License as
+// published by the Free Software Foundation; either version 3 of
+// the License, or (at your option) any later version.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this software; if not, write to the Free
+// Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+// 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+
+namespace Castle.MonoRail.Extension.OData.Serialization
open System
+open System.Collections
+open System.Collections.Generic
+open System.Linq
open System.Xml
+open System.IO
+open System.Text
+open System.ServiceModel.Syndication
+open System.Data.OData
+open System.Data.Services.Providers
+open System.Data.Services.Common
+open Newtonsoft.Json
+
+
module JSonSerialization =
begin
- let CreateDeserializer () = null
+ let private set_up (writer:JsonTextWriter) =
+ writer.DateFormatHandling <- DateFormatHandling.IsoDateFormat
+ writer.Formatting <- Formatting.Indented
+ writer.IndentChar <- '\t'
+ writer.Indentation <- 1
+
+
+ type JsonSerializer(wrapper, serviceBaseUri, containerUri, rt, propertiesToExpand, writer, enc, useSimplerFormat:bool) as self =
+ class
+ inherit Serializer(wrapper, serviceBaseUri, containerUri, rt, propertiesToExpand, writer, enc)
+
+ let jwriter = new JsonTextWriter(writer)
+
+ do
+ set_up jwriter
+
+ let write_meta (uri:Uri) (rt:ResourceType) =
+ if not useSimplerFormat then
+ jwriter.WritePropertyName "__metadata"
+ jwriter.WriteStartObject()
+ if uri <> null then
+ jwriter.WritePropertyName "uri"
+ jwriter.WriteValue uri.AbsoluteUri
+ jwriter.WritePropertyName "type"
+ jwriter.WriteValue rt.FullName
+ jwriter.WriteEndObject()
+
+ let rec write_primitive_and_complex_properties (instance) (uri:Uri) (rt:ResourceType) =
+
+ for prop in rt.Properties do
+ let otherRt = prop.ResourceType
+
+ if prop.IsOfKind ResourcePropertyKind.ComplexType then
+ // todo: add case for collection of complex types
+
+ jwriter.WritePropertyName prop.Name
+
+ // TODO: is collection?
+ // if prop.ResourceType.InstanceType.IsColl then
+
+ let innerinstance = prop.GetValue(instance)
+
+ write_complextype innerinstance uri otherRt
+
+ elif prop.IsOfKind ResourcePropertyKind.Primitive then
+
+ jwriter.WritePropertyName prop.Name
+
+ let originalVal = prop.GetValue(instance)
+
+ jwriter.WriteValue originalVal
+
+
+ and write_ref_properties (instance) (uri:Uri) (rt:ResourceType) =
+
+ for prop in rt.Properties do
+ let otherRt = prop.ResourceType
+
+ if prop.IsOfKind ResourcePropertyKind.ResourceReference || prop.IsOfKind ResourcePropertyKind.ResourceSetReference then
+ jwriter.WritePropertyName prop.Name
+ if not useSimplerFormat then jwriter.WriteStartObject ()
+
+ // spec wise, we need to output additional metadata in the end (after the properties)
+ // to reference the associations used for the expanded properties, but I'm skipping that for now
+
+ if self.ShouldExpand (prop) then
+ if prop.IsOfKind ResourcePropertyKind.ResourceSetReference then
+ if not useSimplerFormat then jwriter.WritePropertyName "results"
+
+ let innerItems = prop.GetValue(instance) :?> IEnumerable
+ if innerItems <> null then
+ write_set (Uri(uri.AbsoluteUri + "/" + prop.Name)) prop.ResourceType innerItems true
+ else
+ jwriter.WriteStartArray ()
+ jwriter.WriteEndArray ()
+ else
+ if not useSimplerFormat then jwriter.WritePropertyName "result"
+
+ let inner = prop.GetValue(instance)
+ if inner <> null then
+ write_js_item inner (Uri(uri.AbsoluteUri + "/" + prop.Name)) prop.ResourceType true
+ else
+ jwriter.WriteNull()
+
+ else
+ if not useSimplerFormat then
+ jwriter.WritePropertyName "__deferred"
+ jwriter.WriteStartObject ()
+ jwriter.WritePropertyName "uri"
+ jwriter.WriteValue (uri.AbsoluteUri + "/" + prop.Name)
+ jwriter.WriteEndObject ()
+ else
+ jwriter.WriteStartObject ()
+ jwriter.WriteEndObject ()
+
+
+ if not useSimplerFormat then jwriter.WriteEndObject ()
+
+
+ and write_complextype (instance) (uri:Uri) (rt:ResourceType) =
+
+ if instance = null then
+ jwriter.WriteNull()
+ else
+ jwriter.WriteStartObject()
+ write_meta null rt
+ write_primitive_and_complex_properties instance uri rt
+ jwriter.WriteEndObject()
+
+
+ and write_js_item (instance) (containerUri:Uri) (rt:ResourceType) appendKey =
+ jwriter.WriteStartObject()
+
+ let resourceSet = wrapper.ResourceSets |> Seq.tryFind (fun rs -> rs.ResourceType = rt)
+ let resourceUri =
+ match resourceSet with
+ | Some rs ->
+ // for this case, we always want to append the key
+ Uri(serviceBaseUri, rs.Name + rt.GetKey(instance))
+ | _ ->
+ System.Diagnostics.Debug.Assert (containerUri <> null)
+ if appendKey
+ then Uri(containerUri.AbsoluteUri + rt.GetKey(instance))
+ else containerUri
+
+ write_meta resourceUri rt
+ write_primitive_and_complex_properties instance resourceUri rt
+ write_ref_properties instance resourceUri rt
+
+ jwriter.WriteEndObject()
+
+ and write_set (containerUri:Uri) (rt:ResourceType) (items:IEnumerable) appendKey =
+
+ jwriter.WriteStartArray()
+
+ for item in items do
+ write_js_item item containerUri rt appendKey
+
+ jwriter.WriteEndArray()
+
+ let wrap_in_d (f) =
+ if not useSimplerFormat then
+ jwriter.WriteStartObject()
+ jwriter.WritePropertyName "d"
+
+ f()
+
+ if not useSimplerFormat then
+ jwriter.WriteEndObject()
+
+ override x.SerializeMany(items) =
+ wrap_in_d (fun _ -> write_set containerUri rt items true )
+
+ override x.SerializeSingle(item) =
+ wrap_in_d (fun _ -> write_js_item item containerUri rt false )
+
+ override x.SerializeProperty(prop:ResourceProperty, value) =
+
+ let write_d () =
+ jwriter.WriteStartObject()
+ jwriter.WritePropertyName prop.Name
+ jwriter.WriteValue value
+ jwriter.WriteEndObject()
+
+ wrap_in_d (fun _ -> write_d () )
+
+ end
+
+
+
+ let internal read_item (rt:ResourceType) (reader:TextReader) (enc:Encoding) =
+
+ use jsonReader = new JsonTextReader(reader)
+ let instance = Activator.CreateInstance rt.InstanceType
+
+ // { "d": { Prop: a, Prop2: 2 } }
+ // { Prop: a, Prop2: 2 }
+
+ let getToPropertyStart () =
+ let doContinue = ref true
+ while !doContinue && jsonReader.Read() do
+ if jsonReader.TokenType = JsonToken.PropertyName && jsonReader.Value.ToString() <> "d" then
+ doContinue := false
+
+ getToPropertyStart()
+
+ let doContinue = ref true
+ while !doContinue do
+ if jsonReader.TokenType = JsonToken.PropertyName then
+ match rt.Properties |> Seq.tryFind (fun p -> p.Name = jsonReader.Value.ToString()) with
+ | Some prop ->
+ jsonReader.Read() |> ignore
+ // todo: assert is not comment or property name
+
+ let value = jsonReader.Value
+
+ if prop.IsOfKind (ResourcePropertyKind.Primitive) then
+
+ let sanitizedVal = Convert.ChangeType(value, prop.ResourceType.InstanceType)
+
+ prop.SetValue(instance, sanitizedVal)
+
+ elif prop.IsOfKind (ResourcePropertyKind.ComplexType) then
+
+ ()
+ else
+ ()
+
+ doContinue := jsonReader.Read()
+
+ | _ ->
+ // could not find property: should this be an error?
+ doContinue := false
+
+ else
+ doContinue := jsonReader.Read()
+
+ instance
- let CreateSerializer () = null
+ let CreateDeserializer =
+ {
+ new Deserializer() with
+ override x.DeserializeMany (rt, reader, enc) =
+ raise(NotImplementedException())
+ override x.DeserializeSingle (rt, reader, enc) =
+ read_item rt reader enc
+ }
end
diff --git a/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/Serialization/Serialization.Metadata.fs b/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/Serialization/Serialization.Metadata.fs
index 85457bb1..013fe511 100644
--- a/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/Serialization/Serialization.Metadata.fs
+++ b/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/Serialization/Serialization.Metadata.fs
@@ -1,4 +1,19 @@
-namespace Castle.MonoRail.Extension.OData
+// Copyright 2004-2012 Castle Project - http://www.castleproject.org/
+// Hamilton Verissimo de Oliveira and individual contributors as indicated.
+// See the committers.txt/contributors.txt in the distribution for a
+// full listing of individual contributors.
+//
+// This is free software; you can redistribute it and/or modify it
+// under the terms of the GNU Lesser General Public License as
+// published by the Free Software Foundation; either version 3 of
+// the License, or (at your option) any later version.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this software; if not, write to the Free
+// Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+// 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+
+namespace Castle.MonoRail.Extension.OData.Serialization
open System
open System.Collections.Generic
@@ -43,6 +58,7 @@ module AtomServiceDocSerializer =
end
// used when /$metadata is accessed with GET
+// see http://msdn.microsoft.com/en-us/library/bb399292.aspx
module MetadataSerializer =
begin
@@ -54,15 +70,27 @@ module MetadataSerializer =
static member EpmNsPrefix = "FC_NsPrefix"
static member EpmNsUri = "FC_NsUri"
- let private associationSetsCache = Dictionary()
- let private associationTypesCache = Dictionary()
-
let private get_associationtype_lookupname (rt:ResourceType) (prop:ResourceProperty) =
rt.Name +
if prop <> null
then "_" + prop.Name
else ""
+ let private get_associationtype_name (end1:ResourceType) (prop1:ResourceProperty) (end2:ResourceType) (prop2:ResourceProperty) =
+ end1.Name + "_" + prop1.Name
+
+ (*
+ let actualend1 = if prop1 <> null then end1 else end2
+ let actualend2 = if actualend1 = end1
+ then (if prop2 <> null then end2 else null)
+ else null
+ actualend1.Name + "_" + prop1.Name +
+ if actualend2 <> null
+ then "_" + actualend2.Name + "_" + prop2.Name
+ else ""
+ *)
+
+ (*
let private get_associationtype_name (association:ResourceAssociationSet) =
let end1 = if association.End1.ResourceProperty <> null then association.End1 else association.End2
let end2 = if end1 = association.End1
@@ -72,6 +100,7 @@ module MetadataSerializer =
if end2 <> null
then "_" + end2.ResourceType.Name + "_" + end2.ResourceProperty.Name
else ""
+ *)
// http://msdn.microsoft.com/en-us/library/dd942559%28v=prot.10%29.aspx
let write_epm_properties (xmlWriter:XmlWriter) skipSourcePath removePrefix (items:EntityPropertyMappingAttribute seq) =
@@ -138,7 +167,7 @@ module MetadataSerializer =
items |> Seq.iter write_epm_attributes
- let private write_property (xmlWriter:XmlWriter) (resRt:ResourceType) (property:ResourceProperty) =
+ let private write_property (xmlWriter:XmlWriter) (resRt:ResourceType) (property:ResourceProperty) (associationTypesCache:Dictionary) =
let write_facets () =
let pType = property.Type
@@ -152,7 +181,7 @@ module MetadataSerializer =
|> Seq.filter (fun epm -> (Array.get (epm.SourcePath.Split([|'/'|])) 0) = property.Name)
|> write_epm_properties xmlWriter skipSourcePath removePrefix
- if (property.ResourceType.ResourceTypeKind = ResourceTypeKind.Primitive) then
+ if property.ResourceType.ResourceTypeKind = ResourceTypeKind.Primitive then
xmlWriter.WriteStartElement("Property")
xmlWriter.WriteAttributeString("Name", property.Name)
xmlWriter.WriteAttributeString("Type", property.ResourceType.FullName)
@@ -166,7 +195,7 @@ module MetadataSerializer =
write_epm_attributes false
- elif (property.Kind = ResourcePropertyKind.ComplexType) then
+ elif property.Kind = ResourcePropertyKind.ComplexType then
xmlWriter.WriteStartElement("Property")
xmlWriter.WriteAttributeString("Name", property.Name)
@@ -188,17 +217,14 @@ module MetadataSerializer =
xmlWriter.WriteAttributeString("Relationship", assocType.FullName)
xmlWriter.WriteAttributeString("FromRole", typeend.Name)
xmlWriter.WriteAttributeString("ToRole", otherside.Name)
-
xmlWriter.WriteEndElement()
- ()
-
- let private write_properties (writer:XmlWriter) (resRt:ResourceType) =
+ let private write_properties (writer:XmlWriter) (resRt:ResourceType) (associationTypesCache:Dictionary) =
resRt.PropertiesDeclaredOnThisType
- |> Seq.iter (fun p -> write_property writer resRt p)
+ |> Seq.iter (fun p -> write_property writer resRt p associationTypesCache)
- let private write_entity (writer:XmlWriter) (resRt:ResourceType) =
+ let private write_entity (writer:XmlWriter) (resRt:ResourceType) (associationTypesCache:Dictionary) =
let write_key () =
writer.WriteStartElement("Key")
@@ -209,7 +235,7 @@ module MetadataSerializer =
writer.WriteEndElement()
writer.WriteStartElement("EntityType")
- writer.WriteAttributeString("Name", XmlConvert.EncodeName(resRt.Name))
+ writer.WriteAttributeString("Name", XmlConvert.EncodeName(resRt.InstanceType.Name))
if resRt.IsAbstract || resRt.BaseType <> null then raise(NotImplementedException("Abstract/Inheritance for ResourceTypes is not supported"))
if resRt.IsOpenType then raise(NotImplementedException("OpenTypes aren't supported"))
@@ -222,26 +248,26 @@ module MetadataSerializer =
write_epm_properties writer false false resRt.OwnEpmAttributes // resRt.InheritedEpmInfo
write_key ()
- write_properties writer resRt
+ write_properties writer resRt associationTypesCache
writer.WriteEndElement()
- let private write_complex (writer:XmlWriter) (resRt:ResourceType) =
+ let private write_complex (writer:XmlWriter) (resRt:ResourceType) (associationTypesCache:Dictionary) =
writer.WriteStartElement("ComplexType")
- writer.WriteAttributeString("Name", XmlConvert.EncodeName(resRt.Name))
- write_properties writer resRt
+ writer.WriteAttributeString("Name", XmlConvert.EncodeName(resRt.InstanceType.Name))
+ write_properties writer resRt associationTypesCache
writer.WriteEndElement()
- let private write_type (writer:XmlWriter) (resRt:ResourceType) =
+ let private write_type (writer:XmlWriter) (resRt:ResourceType) (associationTypesCache:Dictionary) =
if resRt.ResourceTypeKind = ResourceTypeKind.EntityType then
- write_entity writer resRt
+ write_entity writer resRt associationTypesCache
else
- write_complex writer resRt
-
+ write_complex writer resRt associationTypesCache
-
- let private prepare (wrapper:DataServiceMetadataProviderWrapper) =
+ let private prepare (wrapper:DataServiceMetadataProviderWrapper)
+ (associationSetsCache:Dictionary)
+ (associationTypesCache:Dictionary) =
- let get_main_association (rs:ResourceSetWrapper) (rt:ResourceType) (prop:ResourceProperty) =
+ let get_res_association_set (rs:ResourceSetWrapper) (rt:ResourceType) (prop:ResourceProperty) =
let key = rs.Name + "_" + rt.FullName + "_" + prop.Name
let r, association = associationSetsCache.TryGetValue key
@@ -255,124 +281,145 @@ module MetadataSerializer =
let associationSet = wrapper.GetResourceAssociationSet(wrapper.ValidateResourceSet(associationEnd.ResourceSet), associationEnd.ResourceType, associationEnd.ResourceProperty)
if associationSet = null || association.Name <> associationSet.Name
then raise(InvalidOperationException("Invalid bi-directional assocation"))
-
+ let key2 = sprintf "%s_%s_%s" associationEnd.ResourceSet.Name associationEnd.ResourceProperty.ResourceType.FullName associationEnd.ResourceProperty.Name
+ associationSetsCache.Add (key2, association)
+ (*
let key2 =
if associationEnd.ResourceProperty <> null
then sprintf "%s_%s_%s" associationEnd.ResourceSet.Name associationEnd.ResourceProperty.ResourceType.FullName associationEnd.ResourceProperty.Name
else sprintf "%s_Null_%s_%s" associationEnd.ResourceSet.Name rt.FullName prop.Name
associationSetsCache.Add (key2, association)
+ *)
associationSetsCache.Add (key, association)
association
- let get_association_type (association:ResourceAssociationSet) (rs:ResourceSetWrapper) (rt:ResourceType) (prop:ResourceProperty) =
- let associationTypes = associationTypesCache
- let name = get_associationtype_name association
+ let get_association_type (association:ResourceAssociationSet) (rs:ResourceSetWrapper)
+ (rt:ResourceType) (prop:ResourceProperty) =
+
+ let lookupName = get_associationtype_lookupname rt prop
+ if not <| associationTypesCache.ContainsKey lookupName then
+ failwithf "Since RT associations are processed first, it was expected that the association name %s would already be created by now. However, it wasn't." lookupName
+ else associationTypesCache.[lookupName]
+
+ let build_association_info (rs:ResourceSetWrapper) (rt:ResourceType) (prop:ResourceProperty) =
+ let association = get_res_association_set rs rt prop
+ if association <> null then
+ association.ResourceAssociationType <- get_association_type association rs rt prop
+
+ let populate_association_set (rs:ResourceSetWrapper) =
+ let rt = rs.ResourceType
+ rt.PropertiesDeclaredOnThisType
+ |> Seq.filter (fun p -> p.ResourceType.ResourceTypeKind = ResourceTypeKind.EntityType)
+ |> Seq.iter (fun p -> build_association_info rs rt p)
+
+ let ensure_association_type_exists (rt:ResourceType) (prop:ResourceProperty) =
+ let end1 = rt
+ let end2 = prop.ResourceType
+ let otherProp =
+ match end2.PropertiesDeclaredOnThisType |> Seq.tryPick (fun p -> if p.ResourceType = end1 then Some(p) else None) with
+ | Some p -> p
+ | _ -> null
+
+ let name = get_associationtype_name end1 prop end2 otherProp
let lookupName = get_associationtype_lookupname rt prop
- if not <| associationTypes.ContainsKey lookupName then
- let bidirectional = association.End1.ResourceProperty <> null && association.End2.ResourceProperty <> null
+ if not <| associationTypesCache.ContainsKey lookupName then
+ let bidirectional = otherProp <> null
let end1Name, end2Name =
if not bidirectional then
- if association.End1.ResourceProperty <> null
+ if otherProp = null
then rt.Name, prop.Name
else prop.Name, rt.Name
else
- (get_associationtype_lookupname association.End1.ResourceType association.End1.ResourceProperty),
- (get_associationtype_lookupname association.End2.ResourceType association.End2.ResourceProperty)
+ (get_associationtype_lookupname end1 prop),
+ (get_associationtype_lookupname end2 otherProp)
+
let resourceAssociationType =
ResourceAssociationType(
name,
rt.Namespace,
- new ResourceAssociationTypeEnd(end1Name, association.End1.ResourceType, association.End1.ResourceProperty, association.End2.ResourceProperty),
- new ResourceAssociationTypeEnd(end2Name, association.End2.ResourceType, association.End2.ResourceProperty, association.End1.ResourceProperty))
- associationTypes.Add (lookupName, resourceAssociationType)
- if bidirectional then
- let otherside = association.GetRelatedResourceAssociationSetEnd (rs, rt, prop)
- let name = get_associationtype_lookupname otherside.ResourceType otherside.ResourceProperty
- associationTypes.Add (name, resourceAssociationType)
- resourceAssociationType
- else
- associationTypes.[lookupName]
-
-
+ new ResourceAssociationTypeEnd(end1Name, end1, prop, otherProp),
+ new ResourceAssociationTypeEnd(end2Name, end2, otherProp, prop))
+ associationTypesCache.Add (lookupName, resourceAssociationType)
- let build_association_info (rs:ResourceSetWrapper) (rt:ResourceType) (prop:ResourceProperty) =
- let association = get_main_association rs rt prop
-
- if association <> null then
- association.ResourceAssociationType <- get_association_type association rs rt prop
-
- ()
+ if bidirectional then
+ let name = get_associationtype_lookupname end2 otherProp
+ if not <| associationTypesCache.ContainsKey name then
+ associationTypesCache.Add (name, ResourceAssociationType(name, rt.Namespace, resourceAssociationType.End2, resourceAssociationType.End1))
- let populate_association (rs:ResourceSetWrapper) =
- let rt = rs.ResourceType
+ let populate_association_types (rt:ResourceType) =
rt.PropertiesDeclaredOnThisType
|> Seq.filter (fun p -> p.ResourceType.ResourceTypeKind = ResourceTypeKind.EntityType)
- |> Seq.iter (fun p -> build_association_info rs rt p)
-
- wrapper.ResourceSets |> Seq.iter populate_association
- ()
+ |> Seq.iter (fun p -> ensure_association_type_exists rt p)
- let private write_associations (writer:XmlWriter) =
+ wrapper.ResourceTypes |> Seq.iter populate_association_types
+ wrapper.ResourceSets |> Seq.iter populate_association_set
- let write_association (association:ResourceAssociationType) =
- let write_association_end (``end``:ResourceAssociationTypeEnd) =
- writer.WriteStartElement "End"
- writer.WriteAttributeString ("Role", ``end``.Name)
- writer.WriteAttributeString ("Type", ``end``.ResourceType.FullName)
- writer.WriteAttributeString ("Multiplicity", ``end``.Multiplicity)
- writer.WriteEndElement()
- writer.WriteStartElement "Association"
- writer.WriteAttributeString("Name", association.Name)
- write_association_end association.End1
- write_association_end association.End2
- writer.WriteEndElement ()
- associationTypesCache.Values |> Seq.iter write_association
-
- let private write_entitycontainer (writer:XmlWriter) (wrapper:DataServiceMetadataProviderWrapper) =
+ let public serialize(writer:TextWriter, wrapper:DataServiceMetadataProviderWrapper, enc:Encoding) =
+ let xmlWriter = SerializerCommons.create_xmlwriter writer enc
- let write_entity (rs:ResourceSetWrapper) =
- writer.WriteStartElement "EntitySet"
- writer.WriteAttributeString ("Name", rs.Name)
- writer.WriteAttributeString ("EntityType", rs.ResourceType.FullName)
- writer.WriteEndElement()
+ let associationSetsCache = Dictionary()
+ let associationTypesCache = Dictionary()
- let write_association (association:ResourceAssociationSet) =
+ prepare wrapper associationSetsCache associationTypesCache
- let write_end (``end``:ResourceAssociationTypeEnd) (aSetEnd:ResourceAssociationSetEnd) =
- writer.WriteStartElement "End"
- writer.WriteAttributeString ("Role", ``end``.Name)
- writer.WriteAttributeString ("EntitySet", aSetEnd.ResourceSet.Name)
+ let write_associations (writer:XmlWriter) =
+
+ let write_association_type (association:ResourceAssociationType) =
+ let write_association_end (``end``:ResourceAssociationTypeEnd) =
+ writer.WriteStartElement "End"
+ writer.WriteAttributeString ("Role", ``end``.Name)
+ writer.WriteAttributeString ("Type", ``end``.ResourceType.Namespace + "." + ``end``.ResourceType.InstanceType.Name)
+ writer.WriteAttributeString ("Multiplicity", ``end``.Multiplicity)
+ writer.WriteEndElement()
+
+ writer.WriteStartElement "Association"
+ writer.WriteAttributeString("Name", association.Name)
+ write_association_end association.End1
+ if association.End2.Name <> association.End1.Name then
+ write_association_end association.End2
writer.WriteEndElement ()
- writer.WriteStartElement "AssociationSet"
- writer.WriteAttributeString ("Name", association.Name)
- writer.WriteAttributeString("Association", association.ResourceAssociationType.FullName)
- let associationTypeEnd1 = association.ResourceAssociationType.GetResourceAssociationTypeEnd(association.End1.ResourceType, association.End1.ResourceProperty)
- let associationTypeEnd2 = association.ResourceAssociationType.GetResourceAssociationTypeEnd(association.End2.ResourceType, association.End2.ResourceProperty)
- write_end associationTypeEnd1 association.End1
- write_end associationTypeEnd2 association.End2
- writer.WriteEndElement ()
-
- writer.WriteStartElement "EntityContainer"
- writer.WriteAttributeString ("Name", (XmlConvert.EncodeName(wrapper.ContainerName)))
- writer.WriteAttributeString ("m", "IsDefaultEntityContainer", "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata", "true")
+ associationTypesCache.Values |> Seq.iter write_association_type
+
+ let write_entitycontainer (writer:XmlWriter) (wrapper:DataServiceMetadataProviderWrapper) =
- wrapper.ResourceSets |> Seq.iter write_entity
- associationSetsCache.Values |> Seq.iter write_association
- // write service operations
+ let write_entity (rs:ResourceSetWrapper) =
+ writer.WriteStartElement "EntitySet"
+ writer.WriteAttributeString ("Name", rs.Name)
+ writer.WriteAttributeString ("EntityType", rs.ResourceType.Namespace + "." + rs.ResourceType.InstanceType.Name)
+ writer.WriteEndElement()
- writer.WriteEndElement ()
+ let write_association_set (association:ResourceAssociationSet) =
+
+ let write_end (``end``:ResourceAssociationTypeEnd) (aSetEnd:ResourceAssociationSetEnd) =
+ if ``end`` <> null && aSetEnd.ResourceSet <> null then
+ writer.WriteStartElement "End"
+ writer.WriteAttributeString ("Role", ``end``.Name)
+ writer.WriteAttributeString ("EntitySet", aSetEnd.ResourceSet.Name)
+ writer.WriteEndElement ()
+
+ writer.WriteStartElement "AssociationSet"
+ writer.WriteAttributeString ("Name", association.Name)
+ writer.WriteAttributeString("Association", association.ResourceAssociationType.FullName)
+ let associationTypeEnd1 = association.ResourceAssociationType.GetResourceAssociationTypeEnd(association.End1.ResourceType, association.End1.ResourceProperty)
+ let associationTypeEnd2 = association.ResourceAssociationType.GetResourceAssociationTypeEnd(association.End2.ResourceType, association.End2.ResourceProperty)
+ write_end associationTypeEnd1 association.End1
+ write_end associationTypeEnd2 association.End2
+ writer.WriteEndElement ()
- ()
+ writer.WriteStartElement "EntityContainer"
+ writer.WriteAttributeString ("Name", (XmlConvert.EncodeName(wrapper.ContainerName)))
+ writer.WriteAttributeString ("m", "IsDefaultEntityContainer", "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata", "true")
- let public serialize(writer:TextWriter, wrapper:DataServiceMetadataProviderWrapper, enc:Encoding) =
- let xmlWriter = SerializerCommons.create_xmlwriter writer enc
+ wrapper.ResourceSets |> Seq.iter write_entity
+ associationSetsCache.Values |> Seq.iter write_association_set
+ // write service operations
- prepare(wrapper)
+ writer.WriteEndElement ()
xmlWriter.WriteStartElement("edmx", "Edmx", "http://schemas.microsoft.com/ado/2007/06/edmx")
xmlWriter.WriteAttributeString("Version", "1.0")
@@ -386,7 +433,7 @@ module MetadataSerializer =
xmlWriter.WriteAttributeString("xmlns", "m", null, "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata")
wrapper.ResourceTypes
- |> Seq.iter (fun rs -> write_type xmlWriter rs )
+ |> Seq.iter (fun rs -> write_type xmlWriter rs associationTypesCache)
write_associations xmlWriter
diff --git a/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/Serialization/Serialization.PlainTextSerialization.fs b/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/Serialization/Serialization.PlainTextSerialization.fs
new file mode 100644
index 00000000..6c42de0a
--- /dev/null
+++ b/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/Serialization/Serialization.PlainTextSerialization.fs
@@ -0,0 +1,46 @@
+// Copyright 2004-2012 Castle Project - http://www.castleproject.org/
+// Hamilton Verissimo de Oliveira and individual contributors as indicated.
+// See the committers.txt/contributors.txt in the distribution for a
+// full listing of individual contributors.
+//
+// This is free software; you can redistribute it and/or modify it
+// under the terms of the GNU Lesser General Public License as
+// published by the Free Software Foundation; either version 3 of
+// the License, or (at your option) any later version.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this software; if not, write to the Free
+// Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+// 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+
+namespace Castle.MonoRail.Extension.OData.Serialization
+
+open System
+open System.IO
+open System.Xml
+open System.Data.Services.Providers
+
+module PlainTextSerialization =
+ begin
+
+ type PlainTextSerializer(wrapper, serviceBaseUri, containerUri, rt, propertiesToExpand, writer, enc) =
+ class
+ inherit Serializer(wrapper, serviceBaseUri, containerUri, rt, propertiesToExpand, writer, enc)
+
+ let write_primitive_value (rt:ResourceType) (prop:ResourceProperty) value (writer:TextWriter) =
+ if value = null
+ then writer.Write "null"
+ else writer.Write (value.ToString())
+
+ override x.SerializeMany(items) =
+ raise(NotImplementedException())
+
+ override x.SerializeSingle(item) =
+ raise(NotImplementedException())
+
+ override x.SerializeProperty(prop:ResourceProperty, value) =
+ write_primitive_value rt prop value writer
+
+ end
+
+ end
\ No newline at end of file
diff --git a/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/Serialization/Serialization.PlainXml.fs b/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/Serialization/Serialization.PlainXml.fs
index 3bc6021a..43499c41 100644
--- a/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/Serialization/Serialization.PlainXml.fs
+++ b/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/Serialization/Serialization.PlainXml.fs
@@ -1,12 +1,92 @@
-namespace Castle.MonoRail.Extension.OData
+// Copyright 2004-2012 Castle Project - http://www.castleproject.org/
+// Hamilton Verissimo de Oliveira and individual contributors as indicated.
+// See the committers.txt/contributors.txt in the distribution for a
+// full listing of individual contributors.
+//
+// This is free software; you can redistribute it and/or modify it
+// under the terms of the GNU Lesser General Public License as
+// published by the Free Software Foundation; either version 3 of
+// the License, or (at your option) any later version.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this software; if not, write to the Free
+// Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+// 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+
+namespace Castle.MonoRail.Extension.OData.Serialization
open System
open System.Xml
+open System.Data.Services.Providers
+
module XmlSerialization =
begin
- let CreateDeserializer () = null
+ let internal to_xml_string (valType:Type) (originalVal:obj) =
+ let targetType =
+ let nulType = Nullable.GetUnderlyingType(valType)
+ if nulType = null then valType else nulType
+
+ System.Diagnostics.Debug.Assert( originalVal <> null )
+
+ if targetType = typeof then string(originalVal)
+ elif targetType = typeof then XmlConvert.ToString(originalVal :?> bool)
+ elif targetType = typeof then XmlConvert.ToString(originalVal :?> float)
+ elif targetType = typeof then XmlConvert.ToString(originalVal :?> double)
+ elif targetType = typeof then XmlConvert.ToString(originalVal :?> int8)
+ elif targetType = typeof then XmlConvert.ToString(originalVal :?> int16)
+ elif targetType = typeof then XmlConvert.ToString(originalVal :?> int32)
+ elif targetType = typeof then XmlConvert.ToString(originalVal :?> int64)
+ elif targetType = typeof then XmlConvert.ToString(originalVal :?> DateTime, XmlDateTimeSerializationMode.RoundtripKind)
+ elif targetType = typeof then XmlConvert.ToString(originalVal :?> decimal)
+ elif targetType = typeof then Convert.ToBase64String(originalVal :?> byte[])
+ elif targetType = typeof then XmlConvert.ToString(originalVal :?> byte)
+ else raise(InvalidOperationException("primitive value conversion to its xml representation is not supported. " + valType.FullName))
+
+ let internal write_primitive_value (rt:ResourceType) (prop:ResourceProperty) value (writer:XmlWriter) =
+ let name = prop.Name
+
+ writer.WriteStartElement (name, "http://schemas.microsoft.com/ado/2007/08/dataservices")
+
+ let typename = rt.FullName
+
+ if typename <> "Edm.String" then
+ writer.WriteAttributeString ("type", "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata", typename)
+
+ if value = null
+ then writer.WriteAttributeString("null", "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata", "true")
+ else writer.WriteString(value.ToString())
+
+ writer.WriteEndElement()
+
+ let private write_primitive (rt:ResourceType) (prop:ResourceProperty) (value:obj) (txtwriter) (enc) =
+ use writer = SerializerCommons.create_xmlwriter (txtwriter) enc
+ write_primitive_value rt prop value writer
+
+
+ type XmlSerializer(wrapper, serviceBaseUri, containerUri, rt, propertiesToExpand, writer, enc) =
+ class
+ inherit Serializer(wrapper, serviceBaseUri, containerUri, rt, propertiesToExpand, writer, enc)
+
+ override x.SerializeMany(items) =
+ raise(NotImplementedException())
+
+ override x.SerializeSingle(item) =
+ raise(NotImplementedException())
+
+ override x.SerializeProperty(prop:ResourceProperty, value) =
+ write_primitive rt prop value writer enc
- let CreateSerializer () = null
+ end
+
+ let CreateDeserializer () =
+ { new Deserializer() with
+ override x.DeserializeMany (rt, reader, enc) =
+ // read_feed rt reader enc
+ raise(NotImplementedException())
+ override x.DeserializeSingle (rt, reader, enc) =
+ // read_item rt reader enc
+ raise(NotImplementedException())
+ }
- end
\ No newline at end of file
+ end
diff --git a/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/Serialization/Serialization.fs b/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/Serialization/Serialization.fs
index 3798c757..e51c5506 100644
--- a/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/Serialization/Serialization.fs
+++ b/MR3/Extensions/OData/src/Castle.MonoRail.Extension.ODataFs/Serialization/Serialization.fs
@@ -1,4 +1,19 @@
-namespace Castle.MonoRail.Extension.OData
+// Copyright 2004-2012 Castle Project - http://www.castleproject.org/
+// Hamilton Verissimo de Oliveira and individual contributors as indicated.
+// See the committers.txt/contributors.txt in the distribution for a
+// full listing of individual contributors.
+//
+// This is free software; you can redistribute it and/or modify it
+// under the terms of the GNU Lesser General Public License as
+// published by the Free Software Foundation; either version 3 of
+// the License, or (at your option) any later version.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this software; if not, write to the Free
+// Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+// 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+
+namespace Castle.MonoRail.Extension.OData.Serialization
open System
open System.Collections
@@ -13,6 +28,15 @@ open System.Data.OData
open System.Data.Services.Providers
+type ResponseToSend = {
+ mutable QItems : IQueryable;
+ mutable SingleResult : obj;
+ ResType : ResourceType;
+ FinalResourceUri : Uri;
+ ResProp : ResourceProperty;
+ PropertiesToExpand : HashSet;
+}
+
[]
type Deserializer() =
class
@@ -21,15 +45,118 @@ type Deserializer() =
end
[]
-type Serializer() =
- class
- abstract member SerializeMany : baseUri:Uri * rt:ResourceType * items:IEnumerable * writer:TextWriter * enc:Encoding -> unit
- abstract member SerializeSingle : baseUri:Uri * rt:ResourceType * items:obj * writer:TextWriter * enc:Encoding -> unit
+type Serializer(wrapper:DataServiceMetadataProviderWrapper, serviceBaseUri:Uri, containerUri:Uri, rt:ResourceType,
+ propertiesToExpand:HashSet, writer:TextWriter, enc:Encoding) =
+
+ abstract member SerializeMany : items:IEnumerable -> unit
+ abstract member SerializeSingle : item:obj -> unit
+ abstract member SerializeProperty : prop:ResourceProperty * value:obj -> unit
+
+ member x.ShouldExpand (property:ResourceProperty) = propertiesToExpand.Contains property
+
+ member x.Serialize (response:ResponseToSend) =
+ let items : IQueryable = response.QItems
+ let item = response.SingleResult
+ let rt = response.ResType
+
+ if items <> null then
+ x.SerializeMany (items)
+ elif response.ResProp <> null && response.ResProp.IsOfKind(ResourcePropertyKind.Primitive) then
+ x.SerializeProperty (response.ResProp, item)
+ else
+ x.SerializeSingle (item)
+
+
+(*
+[]
+type SerializerStructure() =
+ class
+ inherit Serializer()
+
+ member x.ShouldExpand (property:ResourceProperty) =
+ false
+
+ member x.WriteItems () =
+ // ProcessSkipTop ?
+ // ProcessSkipToken ?
+ ()
+
+ member x.WriteItem () =
+ ()
+
+ member x.WriteProperty () =
+ ()
+
+ member x.InternalWriteItems () =
+ ()
+
+ member x.InternalWriteItem () =
+ ()
+
+ member x.InternalProperty () =
+ ()
+
end
+*)
+
+[]
module SerializerCommons =
begin
+
+ type XmlReader with
+ member x.ReadToElement() =
+ let doCont = ref true
+ let isElement = ref false
+ while !doCont do
+ match x.NodeType with
+ | XmlNodeType.None | XmlNodeType.ProcessingInstruction
+ | XmlNodeType.Comment | XmlNodeType.Whitespace
+ | XmlNodeType.XmlDeclaration ->
+ ()
+ | XmlNodeType.Text ->
+ if String.IsNullOrEmpty x.Value || x.Value.Trim().Length <> 0
+ then isElement := false; doCont := false
+ | XmlNodeType.Element ->
+ isElement := true; doCont := false
+ | _ ->
+ isElement := false; doCont := false
+ if !doCont then doCont := x.Read()
+ !isElement
+
+ type ResourceProperty
+ with
+ member x.GetValue(instance:obj) =
+ let prop = instance.GetType().GetProperty(x.Name)
+ prop.GetValue(instance, null)
+ member x.GetValueAsStr(instance:obj) =
+ let prop = instance.GetType().GetProperty(x.Name)
+ let value = prop.GetValue(instance, null)
+ if value = null
+ then null
+ else value.ToString()
+ member x.SetValue(instance:obj, value:obj) =
+ let prop = instance.GetType().GetProperty(x.Name)
+ prop.SetValue(instance, value, null)
+
+ type ResourceType
+ with
+ member x.GetKey (instance:obj) =
+ let keyValue =
+ if x.KeyProperties.Count = 1
+ then x.KeyProperties.[0].GetValueAsStr(instance)
+ else failwith "Composite keys are not supported"
+ sprintf "(%s)" keyValue
+ (*
+ member x.PathWithKey(instance:obj) =
+ let keyValue =
+ if x.KeyProperties.Count = 1
+ then x.KeyProperties.[0].GetValueAsStr(instance)
+ else failwith "Composite keys are not supported"
+ sprintf "%s(%s)" x.Name keyValue
+ *)
+
let internal create_xmlwriter(writer:TextWriter) (encoding) =
let settings = XmlWriterSettings(CheckCharacters = false,
ConformanceLevel = ConformanceLevel.Fragment,
@@ -43,4 +170,5 @@ module SerializerCommons =
let internal create_xmlreader(reader:TextReader) (encoding) =
let settings = XmlReaderSettings()
XmlReader.Create(reader, settings)
- end
\ No newline at end of file
+ end
+
diff --git a/MR3/WebApplication1/.gitignore b/MR3/Extensions/OData/tests/Castle.MonoRail.Extension.OData.Integration.Tests/.gitignore
similarity index 100%
rename from MR3/WebApplication1/.gitignore
rename to MR3/Extensions/OData/tests/Castle.MonoRail.Extension.OData.Integration.Tests/.gitignore
diff --git a/MR3/Extensions/OData/tests/Castle.MonoRail.Extension.OData.Integration.Tests/AggRootModelAtomTestCase.cs b/MR3/Extensions/OData/tests/Castle.MonoRail.Extension.OData.Integration.Tests/AggRootModelAtomTestCase.cs
new file mode 100644
index 00000000..6d9411fe
--- /dev/null
+++ b/MR3/Extensions/OData/tests/Castle.MonoRail.Extension.OData.Integration.Tests/AggRootModelAtomTestCase.cs
@@ -0,0 +1,121 @@
+namespace Castle.MonoRail.Extension.OData.Integration.Tests
+{
+ using System;
+ using System.IO;
+ using System.Net;
+ using Castle.MonoRail.Integration.Tests;
+ using FluentAssertions;
+ using NUnit.Framework;
+
+ [TestFixture, Category("Integration")]
+ public class ActionResultsIntegrationAtomTestCase : BaseServerTest
+ {
+ public ActionResultsIntegrationAtomTestCase()
+ {
+ this.WebSiteFolder = "ODataTestWebSite";
+ }
+
+ [Test]
+ public void Post_Repository_ExpectsSuccessfulCreation()
+ {
+ var req = (HttpWebRequest) WebRequest.CreateDefault(new Uri(BuildUrl("/models/RootModel/Repositories")));
+ // var req = (HttpWebRequest)WebRequest.CreateDefault(new Uri("http://localhost:2740/models/RootModel/Repositories"));
+ req.Accept = "application/atom+xml";
+ req.ContentType = "application/atom+xml";
+ req.Method = "POST";
+ var reqWriter = new StreamWriter(req.GetRequestStream());
+ // todo: write Repository in atom
+
+ reqWriter.Write(
+@"
+
+
+
+
+ repo1
+
+
+ ");
+ reqWriter.Flush();
+
+ var reply = (HttpWebResponse)req.GetResponse();
+ reply.StatusCode.Should().Be(HttpStatusCode.Created);
+ reply.ContentType.Should().Be("application/atom+xml; charset=utf-8");
+ var replyContent = new StreamReader(reply.GetResponseStream()).ReadToEnd();
+
+ Console.WriteLine(replyContent);
+ }
+
+ [Test]
+ public void Post_Branch_ExpectsSuccessfulCreation()
+ {
+ var req = (HttpWebRequest)WebRequest.CreateDefault(new Uri(BuildUrl("/models/RootModel/Repositories(1)/Branches")));
+ // var req = (HttpWebRequest)WebRequest.CreateDefault(new Uri("http://localhost:2740/models/RootModel/Repositories"));
+ req.Accept = "application/atom+xml";
+ req.ContentType = "application/atom+xml";
+ req.Method = "POST";
+ var reqWriter = new StreamWriter(req.GetRequestStream());
+ // todo: write Repository in atom
+
+ reqWriter.Write(
+@"
+
+
+ 2012-04-20T06:29:23Z
+
+
+
+
+
+ branch1
+
+
+ ");
+ reqWriter.Flush();
+
+ var reply = (HttpWebResponse)req.GetResponse();
+ reply.StatusCode.Should().Be(HttpStatusCode.Created);
+ reply.ContentType.Should().Be("application/atom+xml; charset=utf-8");
+ var replyContent = new StreamReader(reply.GetResponseStream()).ReadToEnd();
+
+ Console.WriteLine(replyContent);
+ }
+
+ [Test]
+ public void Post_Revision_ExpectsSuccessfulCreation()
+ {
+ var req = (HttpWebRequest)WebRequest.CreateDefault(new Uri(BuildUrl("/models/RootModel/Repositories(1)/Branches(100)/Revisions")));
+ // var req = (HttpWebRequest)WebRequest.CreateDefault(new Uri("http://localhost:2740/models/RootModel/Repositories"));
+ req.Accept = "application/atom+xml";
+ req.ContentType = "application/atom+xml";
+ req.Method = "POST";
+ var reqWriter = new StreamWriter(req.GetRequestStream());
+ // todo: write Repository in atom
+
+ reqWriter.Write(
+@"
+
+
+ 2012-04-20T06:29:23Z
+
+
+
+
+
+ file1
+ 123
+
+
+ ");
+ reqWriter.Flush();
+
+ var reply = (HttpWebResponse)req.GetResponse();
+ reply.StatusCode.Should().Be(HttpStatusCode.Created);
+ reply.ContentType.Should().Be("application/atom+xml; charset=utf-8");
+ var replyContent = new StreamReader(reply.GetResponseStream()).ReadToEnd();
+
+ Console.WriteLine(replyContent);
+
+ }
+ }
+}
diff --git a/MR3/Extensions/OData/tests/Castle.MonoRail.Extension.OData.Integration.Tests/AggRootModelJsonTestCase.cs b/MR3/Extensions/OData/tests/Castle.MonoRail.Extension.OData.Integration.Tests/AggRootModelJsonTestCase.cs
new file mode 100644
index 00000000..bab865c3
--- /dev/null
+++ b/MR3/Extensions/OData/tests/Castle.MonoRail.Extension.OData.Integration.Tests/AggRootModelJsonTestCase.cs
@@ -0,0 +1,144 @@
+namespace Castle.MonoRail.Extension.OData.Integration.Tests
+{
+ using System;
+ using System.IO;
+ using System.Net;
+ using Castle.MonoRail.Integration.Tests;
+ using FluentAssertions;
+ using NUnit.Framework;
+
+ [TestFixture, Category("Integration")]
+ public class ActionResultsIntegrationJsonTestCase : BaseServerTest
+ {
+ public ActionResultsIntegrationJsonTestCase()
+ {
+ this.WebSiteFolder = "ODataTestWebSite";
+ }
+
+ [Test]
+ public void Post_Repository_ExpectsSuccessfulCreation()
+ {
+ var req = (HttpWebRequest) WebRequest.CreateDefault(new Uri(BuildUrl("/models/RootModel/Repositories")));
+ // var req = (HttpWebRequest) WebRequest.CreateDefault(new Uri("http://localhost:2740/models/RootModel/Repositories"));
+
+ req.Accept = "application/json";
+ req.ContentType = "application/json";
+ req.Method = "POST";
+ var reqWriter = new StreamWriter(req.GetRequestStream());
+
+ reqWriter.Write(
+@"{
+ ""d"": {
+ ""Name"": ""Repo 2""
+ }
+}");
+ reqWriter.Flush();
+
+ var reply = (HttpWebResponse)req.GetResponse();
+ reply.StatusCode.Should().Be(HttpStatusCode.Created);
+ reply.ContentType.Should().Be("application/json; charset=utf-8");
+ var replyContent = new StreamReader(reply.GetResponseStream()).ReadToEnd();
+
+ Console.WriteLine(replyContent);
+ }
+
+ [Test]
+ public void Post_Repository_SimplerJSon_ExpectsSuccessfulCreation()
+ {
+ var req = (HttpWebRequest) WebRequest.CreateDefault(new Uri(BuildUrl("/models/RootModel/Repositories")));
+ // var req = (HttpWebRequest)WebRequest.CreateDefault(new Uri("http://localhost:2740/models/RootModel/Repositories"));
+
+ req.Accept = "application/json";
+ req.ContentType = "application/json";
+ req.Method = "POST";
+ var reqWriter = new StreamWriter(req.GetRequestStream());
+
+ reqWriter.Write(
+@"{
+
+ ""Name"": ""Repo 2""
+
+}");
+ reqWriter.Flush();
+
+ var reply = (HttpWebResponse)req.GetResponse();
+ reply.StatusCode.Should().Be(HttpStatusCode.Created);
+ reply.ContentType.Should().Be("application/json; charset=utf-8");
+ var replyContent = new StreamReader(reply.GetResponseStream()).ReadToEnd();
+
+ Console.WriteLine(replyContent);
+ }
+
+// [Test]
+// public void Post_Branch_ExpectsSuccessfulCreation()
+// {
+// var req = (HttpWebRequest)WebRequest.CreateDefault(new Uri(BuildUrl("/models/RootModel/Repositories(1)/Branches")));
+ // var req = (HttpWebRequest)WebRequest.CreateDefault(new Uri("http://localhost:2740/models/RootModel/Repositories"));
+// req.Accept = "application/atom+xml";
+// req.ContentType = "application/atom+xml";
+// req.Method = "POST";
+// var reqWriter = new StreamWriter(req.GetRequestStream());
+ // todo: write Repository in atom
+//
+// reqWriter.Write(
+//@"
+//
+//
+// 2012-04-20T06:29:23Z
+//
+//
+//
+//
+//
+// branch1
+//
+//
+// ");
+// reqWriter.Flush();
+//
+// var reply = (HttpWebResponse)req.GetResponse();
+// reply.StatusCode.Should().Be(HttpStatusCode.Created);
+// reply.ContentType.Should().Be("application/atom+xml; charset=utf-8");
+// var replyContent = new StreamReader(reply.GetResponseStream()).ReadToEnd();
+//
+// Console.WriteLine(replyContent);
+// }
+//
+// [Test]
+// public void Post_Revision_ExpectsSuccessfulCreation()
+// {
+// var req = (HttpWebRequest)WebRequest.CreateDefault(new Uri(BuildUrl("/models/RootModel/Repositories(1)/Branches(100)/Revisions")));
+ // var req = (HttpWebRequest)WebRequest.CreateDefault(new Uri("http://localhost:2740/models/RootModel/Repositories"));
+// req.Accept = "application/atom+xml";
+// req.ContentType = "application/atom+xml";
+// req.Method = "POST";
+// var reqWriter = new StreamWriter(req.GetRequestStream());
+ // todo: write Repository in atom
+//
+// reqWriter.Write(
+//@"
+//
+//
+// 2012-04-20T06:29:23Z
+//
+//
+//
+//
+//
+// file1
+// 123
+//
+//
+// ");
+// reqWriter.Flush();
+//
+// var reply = (HttpWebResponse)req.GetResponse();
+// reply.StatusCode.Should().Be(HttpStatusCode.Created);
+// reply.ContentType.Should().Be("application/atom+xml; charset=utf-8");
+// var replyContent = new StreamReader(reply.GetResponseStream()).ReadToEnd();
+//
+// Console.WriteLine(replyContent);
+//
+// }
+ }
+}
diff --git a/MR3/Extensions/OData/tests/Castle.MonoRail.Extension.OData.Integration.Tests/Castle.MonoRail.Extension.OData.Integration.Tests.csproj b/MR3/Extensions/OData/tests/Castle.MonoRail.Extension.OData.Integration.Tests/Castle.MonoRail.Extension.OData.Integration.Tests.csproj
new file mode 100644
index 00000000..c6cc94f8
--- /dev/null
+++ b/MR3/Extensions/OData/tests/Castle.MonoRail.Extension.OData.Integration.Tests/Castle.MonoRail.Extension.OData.Integration.Tests.csproj
@@ -0,0 +1,78 @@
+
+
+
+ Debug
+ AnyCPU
+ 8.0.30703
+ 2.0
+ {7AB66D00-A3FB-4B04-BA2D-4C60F0C0B6C0}
+ Library
+ Properties
+ Castle.MonoRail.Extension.OData.Integration.Tests
+ Castle.MonoRail.Extension.OData.Integration.Tests
+ v4.0
+ 512
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+ ..\..\..\..\tests\lib\CassiniDev4-lib.dll
+
+
+ ..\..\..\..\tests\lib\FluentAssertions.dll
+
+
+ ..\..\..\..\tests\lib\nunit.framework.dll
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {8C35651E-A56E-4771-95CA-E312AA318538}
+ Castle.MonoRail
+
+
+ {A9D55E13-01F2-49B5-97C7-336E44B218F4}
+ Castle.MonoRail.Integration.Tests
+
+
+ {B0D83F48-B4B4-4F6B-867F-B9D91FAE7DAD}
+ ODataTestWebSite
+
+
+
+
+
\ No newline at end of file
diff --git a/MR3/Extensions/OData/tests/Castle.MonoRail.Extension.OData.Integration.Tests/Properties/AssemblyInfo.cs b/MR3/Extensions/OData/tests/Castle.MonoRail.Extension.OData.Integration.Tests/Properties/AssemblyInfo.cs
new file mode 100644
index 00000000..b1a2cf8f
--- /dev/null
+++ b/MR3/Extensions/OData/tests/Castle.MonoRail.Extension.OData.Integration.Tests/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// 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("Castle.MonoRail.Extension.OData.Integration.Tests")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("Castle.MonoRail.Extension.OData.Integration.Tests")]
+[assembly: AssemblyCopyright("Copyright © 2012")]
+[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("4c651b0e-62a7-4301-9840-c9d514ef3b44")]
+
+// 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("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/MR3/Extensions/OData/tests/Castle.MonoRail.Extension.OData.Tests/AstLinqTranslatorTestCase.cs b/MR3/Extensions/OData/tests/Castle.MonoRail.Extension.OData.Tests/AstLinqTranslatorTestCase.cs
new file mode 100644
index 00000000..0e875902
--- /dev/null
+++ b/MR3/Extensions/OData/tests/Castle.MonoRail.Extension.OData.Tests/AstLinqTranslatorTestCase.cs
@@ -0,0 +1,377 @@
+// Copyright 2004-2012 Castle Project - http://www.castleproject.org/
+// Hamilton Verissimo de Oliveira and individual contributors as indicated.
+// See the committers.txt/contributors.txt in the distribution for a
+// full listing of individual contributors.
+//
+// This is free software; you can redistribute it and/or modify it
+// under the terms of the GNU Lesser General Public License as
+// published by the Free Software Foundation; either version 3 of
+// the License, or (at your option) any later version.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this software; if not, write to the Free
+// Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+// 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+
+namespace Castle.MonoRail.Extension.OData.Tests
+{
+ using System;
+ using System.Collections.Generic;
+ using System.ComponentModel.DataAnnotations;
+ using System.Data.Services.Providers;
+ using System.Linq;
+ using System.Linq.Expressions;
+ using FluentAssertions;
+ using NUnit.Framework;
+
+ [TestFixture]
+ public class AstLinqTranslatorTestCase
+ {
+ private StubModel _model;
+ private ResourceType _catalogRt;
+ private List _catalogs;
+
+ [SetUp]
+ public void Init()
+ {
+ _catalogs = new List
+ {
+ new Catalog2() { Id = 1, Name = "catalog 1", IsPublished = false, Owner = new User2() { Email = "email1@m.com", Name = "Mary"} },
+ new Catalog2() { Id = 2, Name = "catalog 2", IsPublished = true , Owner = new User2() { Email = "email2@m.com", Name = "John"} },
+ new Catalog2() { Id = 3, Name = "catalog 3", IsPublished = false, Owner = new User2() { Email = "email3@m.com", Name = "Jeff"} },
+ new Catalog2() { Id = 4, Name = "catalog 4", IsPublished = true , Owner = new User2() { Email = "email4@m.com", Name = "Andrew"} },
+ new Catalog2() { Id = 5, Name = "catalog 5", IsPublished = false, Owner = new User2() { Email = "email5@m.com", Name = "Mary"} },
+ };
+
+ _model = new StubModel(
+ m => m.EntitySet("catalogs", _catalogs.AsQueryable())
+ );
+
+ _catalogRt = _model.GetResourceType("Catalog2").Value;
+ }
+
+ private Expression> BuildLinqExpressionPredicate(string expression, ResourceType rt)
+ {
+ var exp = QueryExpressionParser.parse_filter(expression);
+ // Console.WriteLine(exp4.ToStringTree());
+
+ var tree = QuerySemanticAnalysis.analyze_and_convert(exp, rt);
+ //Console.WriteLine(tree.ToStringTree());
+
+ return AstLinqTranslator.build_linq_exp_predicate(typeof(T), tree);
+ }
+
+ private static IQueryable ApplyOrderByExpression(IQueryable source, string expression, ResourceType rt)
+ {
+ var exp = QueryExpressionParser.parse_orderby(expression);
+ // Console.WriteLine(exp4.ToStringTree());
+ var tree = QuerySemanticAnalysis.analyze_and_convert_orderby(exp, rt);
+ // Console.WriteLine(tree.ToStringTree());
+ return AstLinqTranslator.typed_queryable_orderby