This commit is contained in:
Doug Bunting 2016-08-08 22:01:50 -07:00
Родитель 5fa60ca38b
Коммит aa386adaf6
1430 изменённых файлов: 0 добавлений и 200007 удалений

Просмотреть файл

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<solution>
<add key="disableSourceControlIntegration" value="true" />
</solution>
<packageSources>
<add key="nuget.org" value="https://www.nuget.org/api/v2/" />
<add key="buildTools" value="https://www.myget.org/F/30de4ee06dd54956a82013fa17a3accb/" />
<add key="externalComponentDependencies" value="https://www.myget.org/F/02a8fd0d231848d2ae32cd901e273000" />
</packageSources>
</configuration>

Просмотреть файл

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Web.SkipStrongNames" version="1.0.0" />
<package id="Microsoft.Web.StyleCop" version="1.0.0" />
<package id="StyleCop" version="4.7.10.0" />
</packages>

Просмотреть файл

@ -1,392 +0,0 @@
<StyleCopSettings Version="105">
<GlobalSettings>
<StringProperty Name="MergeSettingsFiles">NoMerge</StringProperty>
</GlobalSettings>
<Parsers>
<Parser ParserId="StyleCop.CSharp.CsParser">
<ParserSettings>
<BooleanProperty Name="AnalyzeDesignerFiles">False</BooleanProperty>
</ParserSettings>
</Parser>
</Parsers>
<Analyzers>
<Analyzer AnalyzerId="StyleCop.CSharp.NamingRules">
<Rules>
<Rule Name="FieldNamesMustNotBeginWithUnderscore">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
</Rules>
<AnalyzerSettings>
<CollectionProperty Name="Hungarian">
<Value>as</Value>
<Value>db</Value>
<Value>dc</Value>
<Value>do</Value>
<Value>ef</Value>
<Value>id</Value>
<Value>if</Value>
<Value>in</Value>
<Value>is</Value>
<Value>my</Value>
<Value>no</Value>
<Value>on</Value>
<Value>sl</Value>
<Value>to</Value>
<Value>ui</Value>
<Value>vs</Value>
</CollectionProperty>
</AnalyzerSettings>
</Analyzer>
<Analyzer AnalyzerId="StyleCop.CSharp.DocumentationRules">
<Rules>
<Rule Name="FileMustHaveHeader">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="FileHeaderMustContainFileName">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="FileHeaderFileNameDocumentationMustMatchFileName">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="FileHeaderMustHaveValidCompanyText">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="ConstructorSummaryDocumentationMustBeginWithStandardText">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="DestructorSummaryDocumentationMustBeginWithStandardText">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="DocumentationHeadersMustNotContainBlankLines">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="ElementsMustBeDocumented">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="PartialElementsMustBeDocumented">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="EnumerationItemsMustBeDocumented">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="DocumentationMustContainValidXml">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="ElementDocumentationMustHaveSummary">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="PartialElementDocumentationMustHaveSummary">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="ElementDocumentationMustHaveSummaryText">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="PartialElementDocumentationMustHaveSummaryText">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="ElementDocumentationMustNotHaveDefaultSummary">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="ElementParametersMustBeDocumented">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="ElementParameterDocumentationMustMatchElementParameters">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="ElementParameterDocumentationMustDeclareParameterName">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="ElementParameterDocumentationMustHaveText">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="ElementReturnValueMustBeDocumented">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="ElementReturnValueDocumentationMustHaveText">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="VoidReturnValueMustNotBeDocumented">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="GenericTypeParametersMustBeDocumented">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="GenericTypeParametersMustBeDocumentedPartialClass">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="GenericTypeParameterDocumentationMustMatchTypeParameters">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="GenericTypeParameterDocumentationMustDeclareParameterName">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="GenericTypeParameterDocumentationMustHaveText">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="PropertySummaryDocumentationMustMatchAccessors">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="PropertySummaryDocumentationMustOmitSetAccessorWithRestrictedAccess">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="ElementDocumentationMustNotBeCopiedAndPasted">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="SingleLineCommentsMustNotUseDocumentationStyleSlashes">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="DocumentationTextMustNotBeEmpty">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="DocumentationTextMustContainWhitespace">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="DocumentationMustMeetCharacterPercentage">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="IncludedDocumentationXPathDoesNotExist">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="IncludeNodeDoesNotContainValidFileAndPath">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="InheritDocMustBeUsedWithInheritingClass">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="FileHeaderMustShowCopyright">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="FileHeaderMustHaveCopyrightText">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="FileHeaderFileNameDocumentationMustMatchTypeName">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
</Rules>
<AnalyzerSettings>
<BooleanProperty Name="IgnorePrivates">True</BooleanProperty>
<BooleanProperty Name="IgnoreInternals">True</BooleanProperty>
</AnalyzerSettings>
</Analyzer>
<Analyzer AnalyzerId="StyleCop.CSharp.ReadabilityRules">
<Rules>
<Rule Name="ParameterMustNotSpanMultipleLines">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="PrefixLocalCallsWithThis">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="ParameterMustFollowComma">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="SplitParametersMustStartOnLineAfterDeclaration">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="ParametersMustBeOnSameLineOrSeparateLines">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="UseBuiltInTypeAlias">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
</Rules>
<AnalyzerSettings />
</Analyzer>
<Analyzer AnalyzerId="Microsoft.Web.StyleCop.Rules">
<AnalyzerSettings>
<StringProperty Name="FileHeaderText"> Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.</StringProperty>
</AnalyzerSettings>
</Analyzer>
<Analyzer AnalyzerId="StyleCop.CSharp.LayoutRules">
<Rules>
<Rule Name="AllAccessorsMustBeMultiLineOrSingleLine">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="SingleLineCommentsMustNotBeFollowedByBlankLine">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="ClosingCurlyBracketMustBeFollowedByBlankLine">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="SingleLineCommentMustBePrecededByBlankLine">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="ElementsMustBeSeparatedByBlankLine">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
</Rules>
<AnalyzerSettings />
</Analyzer>
<Analyzer AnalyzerId="StyleCop.CSharp.MaintainabilityRules">
<Rules>
<Rule Name="FieldsMustBePrivate">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="DebugAssertMustProvideMessageText">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="StatementMustNotUseUnnecessaryParenthesis">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
</Rules>
<AnalyzerSettings />
</Analyzer>
<Analyzer AnalyzerId="StyleCop.CSharp.OrderingRules">
<Rules>
<Rule Name="UsingDirectivesMustBePlacedWithinNamespace">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="ElementsMustAppearInTheCorrectOrder">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="ElementsMustBeOrderedByAccess">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="ConstantsMustAppearBeforeFields">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="StaticElementsMustAppearBeforeInstanceElements">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
</Rules>
<AnalyzerSettings />
</Analyzer>
<Analyzer AnalyzerId="StyleCop.CSharp.SpacingRules">
<Rules>
<Rule Name="SingleLineCommentsMustBeginWithSingleSpace">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
</Rules>
<AnalyzerSettings />
</Analyzer>
<Analyzer AnalyzerId="Microsoft.Web.StyleCop.Rules">
<Rules />
<AnalyzerSettings>
<StringProperty Name="FileHeaderText"> Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.</StringProperty>
</AnalyzerSettings>
</Analyzer>
</Analyzers>
</StyleCopSettings>

Просмотреть файл

@ -1,119 +0,0 @@
<Project DefaultTargets="UnitTest" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="tools/WebStack.tasks.targets"/>
<UsingTask TaskName="SkipStrongNames.CheckSkipStrongNamesStatus" AssemblyFile="$(SkipStrongNamesExe)" />
<PropertyGroup>
<!-- build.cmd sets /p:Desktop=true. The CI server does not; instead, it does an additional step with /p:Configuration=CodeAnalysis. -->
<Configuration Condition=" '$(Configuration)' == '' and '$(Desktop)' == 'true' ">CodeAnalysis</Configuration>
<Configuration Condition=" '$(Configuration)' == '' ">Release</Configuration>
<CodeAnalysis Condition=" '$(CodeAnalysis)' == '' and '$(Configuration)' != 'Release' ">true</CodeAnalysis>
<StyleCopEnabled Condition=" '$(StyleCopEnabled)' == '' ">true</StyleCopEnabled>
<BuildPortable Condition=" '$(BuildPortable)' == '' ">true</BuildPortable>
<BuildInParallel Condition=" '$(BuildInParallel)' == '' And $(MSBuildNodeCount) &gt; 1 ">true</BuildInParallel>
<BuildInParallel Condition=" '$(BuildInParallel)' == '' ">false</BuildInParallel>
<TestResultsDirectory>$(MSBuildThisFileDirectory)bin\$(Configuration)\test\TestResults\</TestResultsDirectory>
<SkipStrongNamesExe>$(MSBuildThisFileDirectory)packages\Microsoft.Web.SkipStrongNames.1.0.0\tools\SkipStrongNames.exe</SkipStrongNamesExe>
<SkipStrongNamesXml>$(MSBuildThisFileDirectory)tools\SkipStrongNames.xml</SkipStrongNamesXml>
<NuGetExe>.nuget\NuGet.exe</NuGetExe>
</PropertyGroup>
<ItemGroup>
<SolutionsToBuild Include="WebApiOData.sln">
<BuildInParallel>$(BuildInParallel)</BuildInParallel>
</SolutionsToBuild>
</ItemGroup>
<Target Name="EnableSkipStrongNames" DependsOnTargets="RestoreSkipStrongNames">
<Exec Command='"$(SkipStrongNamesExe)" -e "$(SkipStrongNamesXml)"' />
<CheckSkipStrongNamesStatus AssembliesFile="$(SkipStrongNamesXml)">
<Output TaskParameter="Status" PropertyName="Status" />
</CheckSkipStrongNamesStatus>
<Message Text="SkipStrongNames: $(Status)" Importance="High" />
</Target>
<Target Name="DisableSkipStrongNames" DependsOnTargets="RestoreSkipStrongNames">
<Exec Command='"$(SkipStrongNamesExe)" -d "$(SkipStrongNamesXml)"' />
<CheckSkipStrongNamesStatus AssembliesFile="$(SkipStrongNamesXml)">
<Output TaskParameter="Status" PropertyName="Status" />
</CheckSkipStrongNamesStatus>
<Message Text="SkipStrongNames: $(Status)" Importance="High" />
</Target>
<Target Name="Integration" DependsOnTargets="Clean;Build;UnitTest" />
<Target Name="Clean">
<MSBuild
Projects="@(SolutionsToBuild)"
Targets="Clean"
Condition=" '%(SolutionsToBuild.Portable)' != 'true' or '$(BuildPortable)' == 'true' "
Properties="Configuration=$(Configuration);VisualStudioVersion=$(VisualStudioVersion)" />
<RemoveDir Directories="bin\$(Configuration)" />
</Target>
<Target Name="DownloadNuGet">
<DownloadNuGet OutputFileName="$(NuGetExe)" MinimumVersion="2.7.0" />
</Target>
<Target Name="RestoreSkipStrongNames" DependsOnTargets="DownloadNuGet">
<Exec Command='"$(NuGetExe)" restore .nuget\packages.config -PackagesDirectory packages -NonInteractive -Verbosity quiet -ConfigFile .nuget\NuGet.Config' />
</Target>
<Target Name="RestorePackages" DependsOnTargets="DownloadNuGet">
<Message Text="Restoring NuGet packages..." Importance="High" />
<Exec Command='"$(NuGetExe)" restore "%(SolutionsToBuild.Identity)" -PackagesDirectory packages -NonInteractive -Verbosity quiet -ConfigFile "$(MsBuildThisFileDirectory)\.nuget\NuGet.Config"' />
<!-- Pick the right Microsoft.Web.FxCop package to use and copy it to a standard location. -->
</Target>
<Target Name="BuildTools">
<PropertyGroup>
<FxCopProjectLocation>$(MsBuildThisFileDirectory)tools\src\Microsoft.Web.FxCop\</FxCopProjectLocation>
<CustomFxCopRulesPath>$(MsBuildThisFileDirectory)packages\CustomFxCopRules</CustomFxCopRulesPath>
</PropertyGroup>
<MsBuild
Condition=" '$(CodeAnalysis)' == 'true' "
Projects="$(FxCopProjectLocation)\Microsoft.Web.FxCop.csproj"
Properties="Configuration=Release;OutputPath=$(CustomFxCopRulesPath)" />
</Target>
<Target Name="Build" DependsOnTargets="RestorePackages;BuildTools">
<!-- we need to batch the solution files since they both build Microsoft.TestCommon -->
<Error Condition=" '$(CodeAnalysis)' == 'true' and '$(Configuration)' == 'Release' " Text="Unable to run code analysis in Release configuration. Release assemblies do not include SuppressMessage attributes (so code analysis would always fail with the errors that are normally suppressed)." />
<MakeDir Directories="bin\$(Configuration)" />
<MSBuild
Projects="@(SolutionsToBuild)"
BuildInParallel="%(SolutionsToBuild.BuildInParallel)"
Targets="Build"
Condition=" '%(SolutionsToBuild.Portable)' != 'true' or '$(BuildPortable)' == 'true' "
Properties="Configuration=$(Configuration);CodeAnalysis=$(CodeAnalysis);StyleCopEnabled=$(StyleCopEnabled);VisualStudioVersion=$(VisualStudioVersion)" />
</Target>
<Target Name="UnitTest" DependsOnTargets="CheckSkipStrongNames;Build">
<CallTarget Targets="RunTests;PrintTestRunSummary" RunEachTargetSeparately="True" />
</Target>
<Target Name="RunTests" DependsOnTargets="CheckSkipStrongNames">
<ItemGroup>
<TestDLLsXunit Include="bin\$(Configuration)\test\*.Test.dll;bin\$(Configuration)\test\*.Test.*.dll;bin\$(Configuration)\Test\NetCore\*.Test.dll" />
<XunitProject Include="tools\WebStack.xunit.targets">
<Properties>TestAssembly=%(TestDLLsXunit.FullPath);XmlPath=$(TestResultsDirectory)%(TestDLLsXunit.FileName)-XunitResults.xml</Properties>
</XunitProject>
</ItemGroup>
<!-- Re-create the test results directory so that print summary doesn't run on old test results -->
<RemoveDir Directories="$(TestResultsDirectory)" />
<MakeDir Directories="$(TestResultsDirectory)" />
<MSBuild Projects="@(XunitProject)" BuildInParallel="$(BuildInParallel)" Targets="Xunit" />
</Target>
<Target Name="CheckSkipStrongNames" DependsOnTargets="RestoreSkipStrongNames">
<CheckSkipStrongNamesStatus AssembliesFile="$(SkipStrongNamesXml)">
<Output TaskParameter="Status" PropertyName="Status" />
</CheckSkipStrongNamesStatus>
<Error Text="Unit tests will not run correctly unless SkipStrongNames is Enabled. Current status: $(Status). Run build.cmd EnableSkipStrongNames to fix this problem." Condition="'$(Status)' != 'Enabled'" />
</Target>
<Target Name="PrintTestRunSummary">
<PrintTestRunSummary TestResultsDirectory="$(TestResultsDirectory)" />
</Target>
</Project>

Просмотреть файл

@ -1,74 +0,0 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2013
VisualStudioVersion = 12.0.30501.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{A9836F9E-6DB3-4D9F-ADCA-CF42D8C8BA93}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{C40883CD-366D-4534-8B58-3EA0D13136DF}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.TestCommon", "test\Microsoft.TestCommon\Microsoft.TestCommon.csproj", "{FCCC4CB7-BAF7-4A57-9F89-E5766FE536C0}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Web.Http.OData", "src\System.Web.Http.OData\System.Web.Http.OData.csproj", "{CF73AAA7-ACE9-4C91-AFA5-5EC1DF18FEEE}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Web.Http.OData.Test", "test\System.Web.Http.OData.Test\System.Web.Http.OData.Test.csproj", "{D344485F-F8CA-4B02-AF3D-D9C6FD556CA9}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{CB34D534-9A09-4EE4-B350-C1C23AFBF5EE}"
ProjectSection(SolutionItems) = preProject
.nuget\NuGet.Config = .nuget\NuGet.Config
.nuget\packages.config = .nuget\packages.config
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Web.OData", "src\System.Web.OData\System.Web.OData.csproj", "{D23E28F1-CCD0-43E0-8C0D-36731EC91318}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Web.OData.Test", "test\System.Web.OData.Test\System.Web.OData.Test.csproj", "{66EFD03D-95B7-4C7E-83AC-1A8BD6C12612}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
CodeAnalysis|Any CPU = CodeAnalysis|Any CPU
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{FCCC4CB7-BAF7-4A57-9F89-E5766FE536C0}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU
{FCCC4CB7-BAF7-4A57-9F89-E5766FE536C0}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU
{FCCC4CB7-BAF7-4A57-9F89-E5766FE536C0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FCCC4CB7-BAF7-4A57-9F89-E5766FE536C0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FCCC4CB7-BAF7-4A57-9F89-E5766FE536C0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FCCC4CB7-BAF7-4A57-9F89-E5766FE536C0}.Release|Any CPU.Build.0 = Release|Any CPU
{CF73AAA7-ACE9-4C91-AFA5-5EC1DF18FEEE}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU
{CF73AAA7-ACE9-4C91-AFA5-5EC1DF18FEEE}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU
{CF73AAA7-ACE9-4C91-AFA5-5EC1DF18FEEE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CF73AAA7-ACE9-4C91-AFA5-5EC1DF18FEEE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CF73AAA7-ACE9-4C91-AFA5-5EC1DF18FEEE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CF73AAA7-ACE9-4C91-AFA5-5EC1DF18FEEE}.Release|Any CPU.Build.0 = Release|Any CPU
{D344485F-F8CA-4B02-AF3D-D9C6FD556CA9}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU
{D344485F-F8CA-4B02-AF3D-D9C6FD556CA9}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU
{D344485F-F8CA-4B02-AF3D-D9C6FD556CA9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D344485F-F8CA-4B02-AF3D-D9C6FD556CA9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D344485F-F8CA-4B02-AF3D-D9C6FD556CA9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D344485F-F8CA-4B02-AF3D-D9C6FD556CA9}.Release|Any CPU.Build.0 = Release|Any CPU
{D23E28F1-CCD0-43E0-8C0D-36731EC91318}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU
{D23E28F1-CCD0-43E0-8C0D-36731EC91318}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU
{D23E28F1-CCD0-43E0-8C0D-36731EC91318}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D23E28F1-CCD0-43E0-8C0D-36731EC91318}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D23E28F1-CCD0-43E0-8C0D-36731EC91318}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D23E28F1-CCD0-43E0-8C0D-36731EC91318}.Release|Any CPU.Build.0 = Release|Any CPU
{66EFD03D-95B7-4C7E-83AC-1A8BD6C12612}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU
{66EFD03D-95B7-4C7E-83AC-1A8BD6C12612}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU
{66EFD03D-95B7-4C7E-83AC-1A8BD6C12612}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{66EFD03D-95B7-4C7E-83AC-1A8BD6C12612}.Debug|Any CPU.Build.0 = Debug|Any CPU
{66EFD03D-95B7-4C7E-83AC-1A8BD6C12612}.Release|Any CPU.ActiveCfg = Release|Any CPU
{66EFD03D-95B7-4C7E-83AC-1A8BD6C12612}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{FCCC4CB7-BAF7-4A57-9F89-E5766FE536C0} = {C40883CD-366D-4534-8B58-3EA0D13136DF}
{CF73AAA7-ACE9-4C91-AFA5-5EC1DF18FEEE} = {A9836F9E-6DB3-4D9F-ADCA-CF42D8C8BA93}
{D344485F-F8CA-4B02-AF3D-D9C6FD556CA9} = {C40883CD-366D-4534-8B58-3EA0D13136DF}
{D23E28F1-CCD0-43E0-8C0D-36731EC91318} = {A9836F9E-6DB3-4D9F-ADCA-CF42D8C8BA93}
{66EFD03D-95B7-4C7E-83AC-1A8BD6C12612} = {C40883CD-366D-4534-8B58-3EA0D13136DF}
EndGlobalSection
EndGlobal

Просмотреть файл

@ -1,39 +0,0 @@
@echo off
pushd %~dp0
setlocal
if exist bin goto build
mkdir bin
:Build
REM Find the most recent 32bit MSBuild.exe on the system. Require v12.0 (installed with VS2013) or later since .NET 4.0
REM is not supported. Also handle x86 operating systems, where %ProgramFiles(x86)% is not defined. Always quote the
REM %MSBuild% value when setting the variable and never quote %MSBuild% references.
set MSBuild="%ProgramFiles(x86)%\MSBuild\12.0\Bin\MSBuild.exe"
if not exist %MSBuild% @set MSBuild="%ProgramFiles%\MSBuild\12.0\Bin\MSBuild.exe"
if "%1" == "" goto BuildDefaults
%MSBuild% WebApiOData.msbuild /m /nr:false /t:%* /p:Platform="Any CPU" /p:Desktop=true /v:M /fl /flp:LogFile=bin\msbuild.log;Verbosity=Normal
if %ERRORLEVEL% neq 0 goto BuildFail
goto BuildSuccess
:BuildDefaults
%MSBuild% WebApiOData.msbuild /m /nr:false /p:Platform="Any CPU" /p:Desktop=true /v:M /fl /flp:LogFile=bin\msbuild.log;Verbosity=Normal
if %ERRORLEVEL% neq 0 goto BuildFail
goto BuildSuccess
:BuildFail
echo.
echo *** BUILD FAILED ***
goto End
:BuildSuccess
echo.
echo **** BUILD SUCCESSFUL ***
goto end
:End
popd
endlocal

Просмотреть файл

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<repositories>
<repository path="..\src\System.Web.Http.OData\packages.config" />
<repository path="..\src\System.Web.OData\packages.config" />
<repository path="..\test\Microsoft.TestCommon\packages.config" />
<repository path="..\test\System.Web.Http.OData.Test\packages.config" />
<repository path="..\test\System.Web.OData.Test\packages.config" />
</repositories>

Просмотреть файл

@ -1,76 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<Dictionary>
<Words>
<Recognized>
<Word>Multi</Word>
<Word>Bitly</Word>
<Word>Digg</Word>
<Word>Facebook</Word>
<Word>Reddit</Word>
<Word>Captcha</Word>
<Word>Facebook</Word>
<Word>Gravatar</Word>
<Word>JSON</Word>
<Word>Lookahead</Word>
<Word>MVC</Word>
<Word>Param</Word>
<Word>Params</Word>
<Word>Pluralizer</Word>
<Word>Pragma</Word>
<Word>Pragmas</Word>
<Word>Templating</Word>
<Word>Unvalidated</Word>
<Word>Validator</Word>
<Word>Validators</Word>
<Word>Validatable</Word>
<Word>WebPage</Word>
<Word>cshtml</Word>
<Word>vbhtml</Word>
<Word>asax</Word>
<Word>Eval</Word>
<Word>Src</Word>
<Word>Charset</Word>
<Word>Coords</Word>
<Word>Rel</Word>
<Word>Dto</Word>
<Word>Tokenizer</Word>
<Word>ReDim</Word>
<Word>OAuth</Word>
<Word>OpenID</Word>
<Word>Yadis</Word>
<Word>fwlink</Word>
<Word>Edm</Word>
<Word>Deserializer</Word>
<Word>Api</Word>
<Word>ws</Word>
<Word>enc</Word>
<Word>dir</Word>
<Word>Auth</Word>
<Word>bg</Word>
<Word>Cors</Word>
<Word>Owin</Word>
<Word>Unbuffered</Word>
<Word>Rfc</Word>
<Word>Realtime</Word>
<Word>ModelName</Word>
<Word>BSON</Word>
<Word>Untyped</Word>
</Recognized>
<Compound>
<Term CompoundAlternate="WebPage">WebPage</Term>
<Term CompoundAlternate="WebPages">WebPages</Term>
<Term CompoundAlternate="TimeLine">TimeLine</Term>
<Term CompoundAlternate="OAuth">oAuth</Term>
<Term CompoundAlternate="UserName">userName</Term>
<Term CompoundAlternate="ModelName">modelName</Term>
<Term CompoundAlternate="HasId">HasId</Term>
</Compound>
</Words>
<Acronyms>
<CasingExceptions>
<Acronym>ID</Acronym>
<Acronym>Db</Acronym>
<Acronym>Dto</Acronym>
</CasingExceptions>
</Acronyms>
</Dictionary>

Просмотреть файл

@ -1,271 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Collections.ObjectModel;
using System.Diagnostics.Contracts;
using System.Linq;
namespace System.Collections.Generic
{
/// <summary>
/// Helper extension methods for fast use of collections.
/// </summary>
internal static class CollectionExtensions
{
/// <summary>
/// Return a new array with the value added to the end. Slow and best suited to long lived arrays with few writes relative to reads.
/// </summary>
public static T[] AppendAndReallocate<T>(this T[] array, T value)
{
Contract.Assert(array != null);
int originalLength = array.Length;
T[] newArray = new T[originalLength + 1];
array.CopyTo(newArray, 0);
newArray[originalLength] = value;
return newArray;
}
/// <summary>
/// Return the enumerable as an Array, copying if required. Optimized for common case where it is an Array.
/// Avoid mutating the return value.
/// </summary>
public static T[] AsArray<T>(this IEnumerable<T> values)
{
Contract.Assert(values != null);
T[] array = values as T[];
if (array == null)
{
array = values.ToArray();
}
return array;
}
/// <summary>
/// Return the enumerable as a Collection of T, copying if required. Optimized for the common case where it is
/// a Collection of T and avoiding a copy if it implements IList of T. Avoid mutating the return value.
/// </summary>
public static Collection<T> AsCollection<T>(this IEnumerable<T> enumerable)
{
Contract.Assert(enumerable != null);
Collection<T> collection = enumerable as Collection<T>;
if (collection != null)
{
return collection;
}
// Check for IList so that collection can wrap it instead of copying
IList<T> list = enumerable as IList<T>;
if (list == null)
{
list = new List<T>(enumerable);
}
return new Collection<T>(list);
}
/// <summary>
/// Return the enumerable as a IList of T, copying if required. Avoid mutating the return value.
/// </summary>
public static IList<T> AsIList<T>(this IEnumerable<T> enumerable)
{
Contract.Assert(enumerable != null);
IList<T> list = enumerable as IList<T>;
if (list != null)
{
return list;
}
return new List<T>(enumerable);
}
/// <summary>
/// Return the enumerable as a List of T, copying if required. Optimized for common case where it is an List of T
/// or a ListWrapperCollection of T. Avoid mutating the return value.
/// </summary>
public static List<T> AsList<T>(this IEnumerable<T> enumerable)
{
Contract.Assert(enumerable != null);
List<T> list = enumerable as List<T>;
if (list != null)
{
return list;
}
ListWrapperCollection<T> listWrapper = enumerable as ListWrapperCollection<T>;
if (listWrapper != null)
{
return listWrapper.ItemsList;
}
return new List<T>(enumerable);
}
/// <summary>
/// Remove values from the list starting at the index start.
/// </summary>
public static void RemoveFrom<T>(this List<T> list, int start)
{
Contract.Assert(list != null);
Contract.Assert(start >= 0 && start <= list.Count);
list.RemoveRange(start, list.Count - start);
}
/// <summary>
/// Return the only value from list, the type's default value if empty, or call the errorAction for 2 or more.
/// </summary>
public static T SingleDefaultOrError<T, TArg1>(this IList<T> list, Action<TArg1> errorAction, TArg1 errorArg1)
{
Contract.Assert(list != null);
Contract.Assert(errorAction != null);
switch (list.Count)
{
case 0:
return default(T);
case 1:
T value = list[0];
return value;
default:
errorAction(errorArg1);
return default(T);
}
}
/// <summary>
/// Returns a single value in list matching type TMatch if there is only one, null if there are none of type TMatch or calls the
/// errorAction with errorArg1 if there is more than one.
/// </summary>
public static TMatch SingleOfTypeDefaultOrError<TInput, TMatch, TArg1>(this IList<TInput> list, Action<TArg1> errorAction, TArg1 errorArg1) where TMatch : class
{
Contract.Assert(list != null);
Contract.Assert(errorAction != null);
TMatch result = null;
for (int i = 0; i < list.Count; i++)
{
TMatch typedValue = list[i] as TMatch;
if (typedValue != null)
{
if (result == null)
{
result = typedValue;
}
else
{
errorAction(errorArg1);
return null;
}
}
}
return result;
}
/// <summary>
/// Convert an ICollection to an array, removing null values. Fast path for case where there are no null values.
/// </summary>
public static T[] ToArrayWithoutNulls<T>(this ICollection<T> collection) where T : class
{
Contract.Assert(collection != null);
T[] result = new T[collection.Count];
int count = 0;
foreach (T value in collection)
{
if (value != null)
{
result[count] = value;
count++;
}
}
if (count == collection.Count)
{
return result;
}
else
{
T[] trimmedResult = new T[count];
Array.Copy(result, trimmedResult, count);
return trimmedResult;
}
}
/// <summary>
/// Convert the array to a Dictionary using the keySelector to extract keys from values and the specified comparer. Optimized for array input.
/// </summary>
public static Dictionary<TKey, TValue> ToDictionaryFast<TKey, TValue>(this TValue[] array, Func<TValue, TKey> keySelector, IEqualityComparer<TKey> comparer)
{
Contract.Assert(array != null);
Contract.Assert(keySelector != null);
Dictionary<TKey, TValue> dictionary = new Dictionary<TKey, TValue>(array.Length, comparer);
for (int i = 0; i < array.Length; i++)
{
TValue value = array[i];
dictionary.Add(keySelector(value), value);
}
return dictionary;
}
/// <summary>
/// Convert the list to a Dictionary using the keySelector to extract keys from values and the specified comparer. Optimized for IList of T input with fast path for array.
/// </summary>
public static Dictionary<TKey, TValue> ToDictionaryFast<TKey, TValue>(this IList<TValue> list, Func<TValue, TKey> keySelector, IEqualityComparer<TKey> comparer)
{
Contract.Assert(list != null);
Contract.Assert(keySelector != null);
TValue[] array = list as TValue[];
if (array != null)
{
return ToDictionaryFast(array, keySelector, comparer);
}
return ToDictionaryFastNoCheck(list, keySelector, comparer);
}
/// <summary>
/// Convert the enumerable to a Dictionary using the keySelector to extract keys from values and the specified comparer. Fast paths for array and IList of T.
/// </summary>
public static Dictionary<TKey, TValue> ToDictionaryFast<TKey, TValue>(this IEnumerable<TValue> enumerable, Func<TValue, TKey> keySelector, IEqualityComparer<TKey> comparer)
{
Contract.Assert(enumerable != null);
Contract.Assert(keySelector != null);
TValue[] array = enumerable as TValue[];
if (array != null)
{
return ToDictionaryFast(array, keySelector, comparer);
}
IList<TValue> list = enumerable as IList<TValue>;
if (list != null)
{
return ToDictionaryFastNoCheck(list, keySelector, comparer);
}
Dictionary<TKey, TValue> dictionary = new Dictionary<TKey, TValue>(comparer);
foreach (TValue value in enumerable)
{
dictionary.Add(keySelector(value), value);
}
return dictionary;
}
/// <summary>
/// Convert the list to a Dictionary using the keySelector to extract keys from values and the specified comparer. Optimized for IList of T input. No checking for other types.
/// </summary>
private static Dictionary<TKey, TValue> ToDictionaryFastNoCheck<TKey, TValue>(IList<TValue> list, Func<TValue, TKey> keySelector, IEqualityComparer<TKey> comparer)
{
Contract.Assert(list != null);
Contract.Assert(keySelector != null);
int listCount = list.Count;
Dictionary<TKey, TValue> dictionary = new Dictionary<TKey, TValue>(listCount, comparer);
for (int i = 0; i < listCount; i++)
{
TValue value = list[i];
dictionary.Add(keySelector(value), value);
}
return dictionary;
}
}
}

140
OData/src/Common/CommonWebApiResources.Designer.cs сгенерированный
Просмотреть файл

@ -1,140 +0,0 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.17929
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace System.Web.Http.Properties {
using System;
using System.Linq;
using System.Reflection;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class CommonWebApiResources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal CommonWebApiResources() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
#if NETFX_CORE
var assembly = typeof(CommonWebApiResources).GetTypeInfo().Assembly;
#else
var assembly = typeof(CommonWebApiResources).Assembly;
#endif
// Find the CommonResources.resources file's full resource name in this assembly
string commonResourcesName = assembly.GetManifestResourceNames().Where(s => s.EndsWith("CommonWebApiResources.resources", StringComparison.OrdinalIgnoreCase)).Single();
// Trim off the ".resources"
commonResourcesName = commonResourcesName.Substring(0, commonResourcesName.Length - 10);
// Load the resource manager
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager(commonResourcesName, assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// Looks up a localized string similar to Relative URI values are not supported: &apos;{0}&apos;. The URI must be absolute..
/// </summary>
internal static string ArgumentInvalidAbsoluteUri {
get {
return ResourceManager.GetString("ArgumentInvalidAbsoluteUri", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Unsupported URI scheme: &apos;{0}&apos;. The URI scheme must be either &apos;{1}&apos; or &apos;{2}&apos;..
/// </summary>
internal static string ArgumentInvalidHttpUriScheme {
get {
return ResourceManager.GetString("ArgumentInvalidHttpUriScheme", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Value must be greater than or equal to {0}..
/// </summary>
internal static string ArgumentMustBeGreaterThanOrEqualTo {
get {
return ResourceManager.GetString("ArgumentMustBeGreaterThanOrEqualTo", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Value must be less than or equal to {0}..
/// </summary>
internal static string ArgumentMustBeLessThanOrEqualTo {
get {
return ResourceManager.GetString("ArgumentMustBeLessThanOrEqualTo", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to The argument &apos;{0}&apos; is null or empty..
/// </summary>
internal static string ArgumentNullOrEmpty {
get {
return ResourceManager.GetString("ArgumentNullOrEmpty", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to URI must not contain a query component or a fragment identifier..
/// </summary>
internal static string ArgumentUriHasQueryOrFragment {
get {
return ResourceManager.GetString("ArgumentUriHasQueryOrFragment", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to The value of argument &apos;{0}&apos; ({1}) is invalid for Enum type &apos;{2}&apos;..
/// </summary>
internal static string InvalidEnumArgument {
get {
return ResourceManager.GetString("InvalidEnumArgument", resourceCulture);
}
}
}
}

Просмотреть файл

@ -1,141 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="ArgumentInvalidAbsoluteUri" xml:space="preserve">
<value>Relative URI values are not supported: '{0}'. The URI must be absolute.</value>
</data>
<data name="ArgumentInvalidHttpUriScheme" xml:space="preserve">
<value>Unsupported URI scheme: '{0}'. The URI scheme must be either '{1}' or '{2}'.</value>
</data>
<data name="ArgumentMustBeGreaterThanOrEqualTo" xml:space="preserve">
<value>Value must be greater than or equal to {0}.</value>
</data>
<data name="ArgumentMustBeLessThanOrEqualTo" xml:space="preserve">
<value>Value must be less than or equal to {0}.</value>
</data>
<data name="ArgumentNullOrEmpty" xml:space="preserve">
<value>The argument '{0}' is null or empty.</value>
</data>
<data name="ArgumentUriHasQueryOrFragment" xml:space="preserve">
<value>URI must not contain a query component or a fragment identifier.</value>
</data>
<data name="InvalidEnumArgument" xml:space="preserve">
<value>The value of argument '{0}' ({1}) is invalid for Enum type '{2}'.</value>
</data>
</root>

Просмотреть файл

@ -1,268 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Web.Http.Properties;
namespace System.Web.Http
{
/// <summary>
/// Utility class for creating and unwrapping <see cref="Exception"/> instances.
/// </summary>
internal static class Error
{
private const string HttpScheme = "http";
private const string HttpsScheme = "https";
/// <summary>
/// Formats the specified resource string using <see cref="M:CultureInfo.CurrentCulture"/>.
/// </summary>
/// <param name="format">A composite format string.</param>
/// <param name="args">An object array that contains zero or more objects to format.</param>
/// <returns>The formatted string.</returns>
internal static string Format(string format, params object[] args)
{
return String.Format(CultureInfo.CurrentCulture, format, args);
}
/// <summary>
/// Creates an <see cref="ArgumentException"/> with the provided properties.
/// </summary>
/// <param name="messageFormat">A composite format string explaining the reason for the exception.</param>
/// <param name="messageArgs">An object array that contains zero or more objects to format.</param>
/// <returns>The logged <see cref="Exception"/>.</returns>
internal static ArgumentException Argument(string messageFormat, params object[] messageArgs)
{
return new ArgumentException(Error.Format(messageFormat, messageArgs));
}
/// <summary>
/// Creates an <see cref="ArgumentException"/> with the provided properties.
/// </summary>
/// <param name="parameterName">The name of the parameter that caused the current exception.</param>
/// <param name="messageFormat">A composite format string explaining the reason for the exception.</param>
/// <param name="messageArgs">An object array that contains zero or more objects to format.</param>
/// <returns>The logged <see cref="Exception"/>.</returns>
internal static ArgumentException Argument(string parameterName, string messageFormat, params object[] messageArgs)
{
return new ArgumentException(Error.Format(messageFormat, messageArgs), parameterName);
}
/// <summary>
/// Creates an <see cref="ArgumentException"/> with a message saying that the argument must be an "http" or "https" URI.
/// </summary>
/// <param name="parameterName">The name of the parameter that caused the current exception.</param>
/// <param name="actualValue">The value of the argument that causes this exception.</param>
/// <returns>The logged <see cref="Exception"/>.</returns>
internal static ArgumentException ArgumentUriNotHttpOrHttpsScheme(string parameterName, Uri actualValue)
{
return new ArgumentException(Error.Format(CommonWebApiResources.ArgumentInvalidHttpUriScheme, actualValue, HttpScheme, HttpsScheme), parameterName);
}
/// <summary>
/// Creates an <see cref="ArgumentException"/> with a message saying that the argument must be an absolute URI.
/// </summary>
/// <param name="parameterName">The name of the parameter that caused the current exception.</param>
/// <param name="actualValue">The value of the argument that causes this exception.</param>
/// <returns>The logged <see cref="Exception"/>.</returns>
internal static ArgumentException ArgumentUriNotAbsolute(string parameterName, Uri actualValue)
{
return new ArgumentException(Error.Format(CommonWebApiResources.ArgumentInvalidAbsoluteUri, actualValue), parameterName);
}
/// <summary>
/// Creates an <see cref="ArgumentException"/> with a message saying that the argument must be an absolute URI
/// without a query or fragment identifier and then logs it with <see cref="F:TraceLevel.Error"/>.
/// </summary>
/// <param name="parameterName">The name of the parameter that caused the current exception.</param>
/// <param name="actualValue">The value of the argument that causes this exception.</param>
/// <returns>The logged <see cref="Exception"/>.</returns>
internal static ArgumentException ArgumentUriHasQueryOrFragment(string parameterName, Uri actualValue)
{
return new ArgumentException(Error.Format(CommonWebApiResources.ArgumentUriHasQueryOrFragment, actualValue), parameterName);
}
/// <summary>
/// Creates an <see cref="ArgumentNullException"/> with the provided properties.
/// </summary>
/// <returns>The logged <see cref="Exception"/>.</returns>
[SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly", Justification = "The purpose of this API is to return an error for properties")]
internal static ArgumentNullException PropertyNull()
{
return new ArgumentNullException("value");
}
/// <summary>
/// Creates an <see cref="ArgumentNullException"/> with the provided properties.
/// </summary>
/// <param name="parameterName">The name of the parameter that caused the current exception.</param>
/// <returns>The logged <see cref="Exception"/>.</returns>
internal static ArgumentNullException ArgumentNull(string parameterName)
{
return new ArgumentNullException(parameterName);
}
/// <summary>
/// Creates an <see cref="ArgumentNullException"/> with the provided properties.
/// </summary>
/// <param name="parameterName">The name of the parameter that caused the current exception.</param>
/// <param name="messageFormat">A composite format string explaining the reason for the exception.</param>
/// <param name="messageArgs">An object array that contains zero or more objects to format.</param>
/// <returns>The logged <see cref="Exception"/>.</returns>
internal static ArgumentNullException ArgumentNull(string parameterName, string messageFormat, params object[] messageArgs)
{
return new ArgumentNullException(parameterName, Error.Format(messageFormat, messageArgs));
}
/// <summary>
/// Creates an <see cref="ArgumentException"/> with a default message.
/// </summary>
/// <param name="parameterName">The name of the parameter that caused the current exception.</param>
/// <returns>The logged <see cref="Exception"/>.</returns>
internal static ArgumentException ArgumentNullOrEmpty(string parameterName)
{
return Error.Argument(parameterName, CommonWebApiResources.ArgumentNullOrEmpty, parameterName);
}
/// <summary>
/// Creates an <see cref="ArgumentOutOfRangeException"/> with the provided properties.
/// </summary>
/// <param name="parameterName">The name of the parameter that caused the current exception.</param>
/// <param name="actualValue">The value of the argument that causes this exception.</param>
/// <param name="messageFormat">A composite format string explaining the reason for the exception.</param>
/// <param name="messageArgs">An object array that contains zero or more objects to format.</param>
/// <returns>The logged <see cref="Exception"/>.</returns>
internal static ArgumentOutOfRangeException ArgumentOutOfRange(string parameterName, object actualValue, string messageFormat, params object[] messageArgs)
{
return new ArgumentOutOfRangeException(parameterName, actualValue, Error.Format(messageFormat, messageArgs));
}
/// <summary>
/// Creates an <see cref="ArgumentOutOfRangeException"/> with a message saying that the argument must be greater than or equal to <paramref name="minValue"/>.
/// </summary>
/// <param name="parameterName">The name of the parameter that caused the current exception.</param>
/// <param name="actualValue">The value of the argument that causes this exception.</param>
/// <param name="minValue">The minimum size of the argument.</param>
/// <returns>The logged <see cref="Exception"/>.</returns>
internal static ArgumentOutOfRangeException ArgumentMustBeGreaterThanOrEqualTo(string parameterName, object actualValue, object minValue)
{
return new ArgumentOutOfRangeException(parameterName, actualValue, Error.Format(CommonWebApiResources.ArgumentMustBeGreaterThanOrEqualTo, minValue));
}
/// <summary>
/// Creates an <see cref="ArgumentOutOfRangeException"/> with a message saying that the argument must be less than or equal to <paramref name="maxValue"/>.
/// </summary>
/// <param name="parameterName">The name of the parameter that caused the current exception.</param>
/// <param name="actualValue">The value of the argument that causes this exception.</param>
/// <param name="maxValue">The maximum size of the argument.</param>
/// <returns>The logged <see cref="Exception"/>.</returns>
internal static ArgumentOutOfRangeException ArgumentMustBeLessThanOrEqualTo(string parameterName, object actualValue, object maxValue)
{
return new ArgumentOutOfRangeException(parameterName, actualValue, Error.Format(CommonWebApiResources.ArgumentMustBeLessThanOrEqualTo, maxValue));
}
/// <summary>
/// Creates an <see cref="KeyNotFoundException"/> with a message saying that the key was not found.
/// </summary>
/// <returns>The logged <see cref="Exception"/>.</returns>
internal static KeyNotFoundException KeyNotFound()
{
return new KeyNotFoundException();
}
/// <summary>
/// Creates an <see cref="KeyNotFoundException"/> with a message saying that the key was not found.
/// </summary>
/// <param name="messageFormat">A composite format string explaining the reason for the exception.</param>
/// <param name="messageArgs">An object array that contains zero or more objects to format.</param>
/// <returns>The logged <see cref="Exception"/>.</returns>
internal static KeyNotFoundException KeyNotFound(string messageFormat, params object[] messageArgs)
{
return new KeyNotFoundException(Error.Format(messageFormat, messageArgs));
}
/// <summary>
/// Creates an <see cref="ObjectDisposedException"/> initialized according to guidelines.
/// </summary>
/// <param name="messageFormat">A composite format string explaining the reason for the exception.</param>
/// <param name="messageArgs">An object array that contains zero or more objects to format.</param>
/// <returns>The logged <see cref="Exception"/>.</returns>
internal static ObjectDisposedException ObjectDisposed(string messageFormat, params object[] messageArgs)
{
// Pass in null, not disposedObject.GetType().FullName as per the above guideline
return new ObjectDisposedException(null, Error.Format(messageFormat, messageArgs));
}
/// <summary>
/// Creates an <see cref="OperationCanceledException"/> initialized with the provided parameters.
/// </summary>
/// <returns>The logged <see cref="Exception"/>.</returns>
internal static OperationCanceledException OperationCanceled()
{
return new OperationCanceledException();
}
/// <summary>
/// Creates an <see cref="OperationCanceledException"/> initialized with the provided parameters.
/// </summary>
/// <param name="messageFormat">A composite format string explaining the reason for the exception.</param>
/// <param name="messageArgs">An object array that contains zero or more objects to format.</param>
/// <returns>The logged <see cref="Exception"/>.</returns>
internal static OperationCanceledException OperationCanceled(string messageFormat, params object[] messageArgs)
{
return new OperationCanceledException(Error.Format(messageFormat, messageArgs));
}
/// <summary>
/// Creates an <see cref="ArgumentException"/> for an invalid enum argument.
/// </summary>
/// <param name="parameterName">The name of the parameter that caused the current exception.</param>
/// <param name="invalidValue">The value of the argument that failed.</param>
/// <param name="enumClass">A <see cref="Type"/> that represents the enumeration class with the valid values.</param>
/// <returns>The logged <see cref="Exception"/>.</returns>
internal static ArgumentException InvalidEnumArgument(string parameterName, int invalidValue, Type enumClass)
{
#if NETFX_CORE
return new ArgumentException(Error.Format(CommonWebApiResources.InvalidEnumArgument, parameterName, invalidValue, enumClass.Name), parameterName);
#else
return new InvalidEnumArgumentException(parameterName, invalidValue, enumClass);
#endif
}
/// <summary>
/// Creates an <see cref="InvalidOperationException"/>.
/// </summary>
/// <param name="messageFormat">A composite format string explaining the reason for the exception.</param>
/// <param name="messageArgs">An object array that contains zero or more objects to format.</param>
/// <returns>The logged <see cref="Exception"/>.</returns>
internal static InvalidOperationException InvalidOperation(string messageFormat, params object[] messageArgs)
{
return new InvalidOperationException(Error.Format(messageFormat, messageArgs));
}
/// <summary>
/// Creates an <see cref="InvalidOperationException"/>.
/// </summary>
/// <param name="innerException">Inner exception</param>
/// <param name="messageFormat">A composite format string explaining the reason for the exception.</param>
/// <param name="messageArgs">An object array that contains zero or more objects to format.</param>
/// <returns>The logged <see cref="Exception"/>.</returns>
internal static InvalidOperationException InvalidOperation(Exception innerException, string messageFormat, params object[] messageArgs)
{
return new InvalidOperationException(Error.Format(messageFormat, messageArgs), innerException);
}
/// <summary>
/// Creates an <see cref="NotSupportedException"/>.
/// </summary>
/// <param name="messageFormat">A composite format string explaining the reason for the exception.</param>
/// <param name="messageArgs">An object array that contains zero or more objects to format.</param>
/// <returns>The logged <see cref="Exception"/>.</returns>
internal static NotSupportedException NotSupported(string messageFormat, params object[] messageArgs)
{
return new NotSupportedException(Error.Format(messageFormat, messageArgs));
}
}
}

Просмотреть файл

@ -1,30 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
namespace System.Collections.ObjectModel
{
/// <summary>
/// A class that inherits from Collection of T but also exposes its underlying data as List of T for performance.
/// </summary>
internal sealed class ListWrapperCollection<T> : Collection<T>
{
private readonly List<T> _items;
internal ListWrapperCollection()
: this(new List<T>())
{
}
internal ListWrapperCollection(List<T> list)
: base(list)
{
_items = list;
}
internal List<T> ItemsList
{
get { return _items; }
}
}
}

Просмотреть файл

@ -1,190 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Reflection;
#if ASPNETWEBAPI
namespace System.Web.Http.Internal
#else
namespace System.Web.WebPages
#endif
{
internal class PropertyHelper
{
private static ConcurrentDictionary<Type, PropertyHelper[]> _reflectionCache = new ConcurrentDictionary<Type, PropertyHelper[]>();
private Func<object, object> _valueGetter;
/// <summary>
/// Initializes a fast property helper. This constructor does not cache the helper.
/// </summary>
[SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors", Justification = "This is intended the Name is auto set differently per type and the type is internal")]
public PropertyHelper(PropertyInfo property)
{
Contract.Assert(property != null);
Name = property.Name;
_valueGetter = MakeFastPropertyGetter(property);
}
/// <summary>
/// Creates a single fast property setter. The result is not cached.
/// </summary>
/// <param name="propertyInfo">propertyInfo to extract the getter for.</param>
/// <returns>a fast setter.</returns>
/// <remarks>This method is more memory efficient than a dynamically compiled lambda, and about the same speed.</remarks>
public static Action<TDeclaringType, object> MakeFastPropertySetter<TDeclaringType>(PropertyInfo propertyInfo)
where TDeclaringType : class
{
Contract.Assert(propertyInfo != null);
MethodInfo setMethod = propertyInfo.GetSetMethod();
Contract.Assert(setMethod != null);
Contract.Assert(!setMethod.IsStatic);
Contract.Assert(setMethod.GetParameters().Length == 1);
Contract.Assert(!propertyInfo.ReflectedType.IsValueType);
// Instance methods in the CLR can be turned into static methods where the first parameter
// is open over "this". This parameter is always passed by reference, so we have a code
// path for value types and a code path for reference types.
Type typeInput = propertyInfo.ReflectedType;
Type typeValue = setMethod.GetParameters()[0].ParameterType;
Delegate callPropertySetterDelegate;
// Create a delegate TValue -> "TDeclaringType.Property"
var propertySetterAsAction = setMethod.CreateDelegate(typeof(Action<,>).MakeGenericType(typeInput, typeValue));
var callPropertySetterClosedGenericMethod = _callPropertySetterOpenGenericMethod.MakeGenericMethod(typeInput, typeValue);
callPropertySetterDelegate = Delegate.CreateDelegate(typeof(Action<TDeclaringType, object>), propertySetterAsAction, callPropertySetterClosedGenericMethod);
return (Action<TDeclaringType, object>)callPropertySetterDelegate;
}
public virtual string Name { get; protected set; }
public object GetValue(object instance)
{
Contract.Assert(_valueGetter != null, "Must call Initialize before using this object");
return _valueGetter(instance);
}
/// <summary>
/// Creates and caches fast property helpers that expose getters for every public get property on the underlying type.
/// </summary>
/// <param name="instance">the instance to extract property accessors for.</param>
/// <returns>a cached array of all public property getters from the underlying type of this instance.</returns>
public static PropertyHelper[] GetProperties(object instance)
{
return GetProperties(instance, CreateInstance, _reflectionCache);
}
/// <summary>
/// Creates a single fast property getter. The result is not cached.
/// </summary>
/// <param name="propertyInfo">propertyInfo to extract the getter for.</param>
/// <returns>a fast getter.</returns>
/// <remarks>This method is more memory efficient than a dynamically compiled lambda, and about the same speed.</remarks>
public static Func<object, object> MakeFastPropertyGetter(PropertyInfo propertyInfo)
{
Contract.Assert(propertyInfo != null);
MethodInfo getMethod = propertyInfo.GetGetMethod();
Contract.Assert(getMethod != null);
Contract.Assert(!getMethod.IsStatic);
Contract.Assert(getMethod.GetParameters().Length == 0);
// Instance methods in the CLR can be turned into static methods where the first parameter
// is open over "this". This parameter is always passed by reference, so we have a code
// path for value types and a code path for reference types.
Type typeInput = getMethod.ReflectedType;
Type typeOutput = getMethod.ReturnType;
Delegate callPropertyGetterDelegate;
if (typeInput.IsValueType)
{
// Create a delegate (ref TDeclaringType) -> TValue
Delegate propertyGetterAsFunc = getMethod.CreateDelegate(typeof(ByRefFunc<,>).MakeGenericType(typeInput, typeOutput));
MethodInfo callPropertyGetterClosedGenericMethod = _callPropertyGetterByReferenceOpenGenericMethod.MakeGenericMethod(typeInput, typeOutput);
callPropertyGetterDelegate = Delegate.CreateDelegate(typeof(Func<object, object>), propertyGetterAsFunc, callPropertyGetterClosedGenericMethod);
}
else
{
// Create a delegate TDeclaringType -> TValue
Delegate propertyGetterAsFunc = getMethod.CreateDelegate(typeof(Func<,>).MakeGenericType(typeInput, typeOutput));
MethodInfo callPropertyGetterClosedGenericMethod = _callPropertyGetterOpenGenericMethod.MakeGenericMethod(typeInput, typeOutput);
callPropertyGetterDelegate = Delegate.CreateDelegate(typeof(Func<object, object>), propertyGetterAsFunc, callPropertyGetterClosedGenericMethod);
}
return (Func<object, object>)callPropertyGetterDelegate;
}
private static PropertyHelper CreateInstance(PropertyInfo property)
{
return new PropertyHelper(property);
}
// Implementation of the fast getter.
private delegate TValue ByRefFunc<TDeclaringType, TValue>(ref TDeclaringType arg);
private static readonly MethodInfo _callPropertyGetterOpenGenericMethod = typeof(PropertyHelper).GetMethod("CallPropertyGetter", BindingFlags.NonPublic | BindingFlags.Static);
private static readonly MethodInfo _callPropertyGetterByReferenceOpenGenericMethod = typeof(PropertyHelper).GetMethod("CallPropertyGetterByReference", BindingFlags.NonPublic | BindingFlags.Static);
private static object CallPropertyGetter<TDeclaringType, TValue>(Func<TDeclaringType, TValue> getter, object @this)
{
return getter((TDeclaringType)@this);
}
private static object CallPropertyGetterByReference<TDeclaringType, TValue>(ByRefFunc<TDeclaringType, TValue> getter, object @this)
{
TDeclaringType unboxed = (TDeclaringType)@this;
return getter(ref unboxed);
}
// Implementation of the fast setter.
private static readonly MethodInfo _callPropertySetterOpenGenericMethod = typeof(PropertyHelper).GetMethod("CallPropertySetter", BindingFlags.NonPublic | BindingFlags.Static);
private static void CallPropertySetter<TDeclaringType, TValue>(Action<TDeclaringType, TValue> setter, object @this, object value)
{
setter((TDeclaringType)@this, (TValue)value);
}
protected static PropertyHelper[] GetProperties(object instance,
Func<PropertyInfo, PropertyHelper> createPropertyHelper,
ConcurrentDictionary<Type, PropertyHelper[]> cache)
{
// Using an array rather than IEnumerable, as this will be called on the hot path numerous times.
PropertyHelper[] helpers;
Type type = instance.GetType();
if (!cache.TryGetValue(type, out helpers))
{
// We avoid loading indexed properties using the where statement.
// Indexed properties are not useful (or valid) for grabbing properties off an anonymous object.
IEnumerable<PropertyInfo> properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where(prop => prop.GetIndexParameters().Length == 0 &&
prop.GetMethod != null);
var newHelpers = new List<PropertyHelper>();
foreach (PropertyInfo property in properties)
{
PropertyHelper propertyHelper = createPropertyHelper(property);
newHelpers.Add(propertyHelper);
}
helpers = newHelpers.ToArray();
cache.TryAdd(type, helpers);
}
return helpers;
}
}
}

Просмотреть файл

@ -1,84 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
namespace System.Threading.Tasks
{
/// <summary>
/// Helpers for safely using Task libraries.
/// </summary>
internal static class TaskHelpers
{
private static readonly Task _defaultCompleted = Task.FromResult<AsyncVoid>(default(AsyncVoid));
private static readonly Task<object> _completedTaskReturningNull = Task.FromResult<object>(null);
/// <summary>
/// Returns a canceled Task. The task is completed, IsCanceled = True, IsFaulted = False.
/// </summary>
internal static Task Canceled()
{
return CancelCache<AsyncVoid>.Canceled;
}
/// <summary>
/// Returns a canceled Task of the given type. The task is completed, IsCanceled = True, IsFaulted = False.
/// </summary>
internal static Task<TResult> Canceled<TResult>()
{
return CancelCache<TResult>.Canceled;
}
/// <summary>
/// Returns a completed task that has no result.
/// </summary>
internal static Task Completed()
{
return _defaultCompleted;
}
/// <summary>
/// Returns an error task. The task is Completed, IsCanceled = False, IsFaulted = True
/// </summary>
internal static Task FromError(Exception exception)
{
return FromError<AsyncVoid>(exception);
}
/// <summary>
/// Returns an error task of the given type. The task is Completed, IsCanceled = False, IsFaulted = True
/// </summary>
/// <typeparam name="TResult"></typeparam>
internal static Task<TResult> FromError<TResult>(Exception exception)
{
TaskCompletionSource<TResult> tcs = new TaskCompletionSource<TResult>();
tcs.SetException(exception);
return tcs.Task;
}
internal static Task<object> NullResult()
{
return _completedTaskReturningNull;
}
/// <summary>
/// Used as the T in a "conversion" of a Task into a Task{T}
/// </summary>
private struct AsyncVoid
{
}
/// <summary>
/// This class is a convenient cache for per-type cancelled tasks
/// </summary>
private static class CancelCache<TResult>
{
public static readonly Task<TResult> Canceled = GetCancelledTask();
private static Task<TResult> GetCancelledTask()
{
TaskCompletionSource<TResult> tcs = new TaskCompletionSource<TResult>();
tcs.SetCanceled();
return tcs.Task;
}
}
}
}

Просмотреть файл

@ -1,54 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Diagnostics.CodeAnalysis;
namespace System.Threading.Tasks
{
internal static class TaskHelpersExtensions
{
/// <summary>
/// Cast Task to Task of object
/// </summary>
internal static async Task<object> CastToObject(this Task task)
{
await task;
return null;
}
/// <summary>
/// Cast Task of T to Task of object
/// </summary>
internal static async Task<object> CastToObject<T>(this Task<T> task)
{
return (object)await task;
}
/// <summary>
/// Throws the first faulting exception for a task which is faulted. It preserves the original stack trace when
/// throwing the exception. Note: It is the caller's responsibility not to pass incomplete tasks to this
/// method, because it does degenerate into a call to the equivalent of .Wait() on the task when it hasn't yet
/// completed.
/// </summary>
internal static void ThrowIfFaulted(this Task task)
{
task.GetAwaiter().GetResult();
}
/// <summary>
/// Attempts to get the result value for the given task. If the task ran to completion, then
/// it will return true and set the result value; otherwise, it will return false.
/// </summary>
[SuppressMessage("Microsoft.Web.FxCop", "MW1201:DoNotCallProblematicMethodsOnTask", Justification = "The usages here are deemed safe, and provide the implementations that this rule relies upon.")]
internal static bool TryGetResult<TResult>(this Task<TResult> task, out TResult result)
{
if (task.Status == TaskStatus.RanToCompletion)
{
result = task.Result;
return true;
}
result = default(TResult);
return false;
}
}
}

Просмотреть файл

@ -1,27 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.ComponentModel;
namespace System
{
/// <summary>
/// Extension methods for <see cref="Type"/>.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
internal static class TypeExtensions
{
public static bool IsNullable(this Type type)
{
if (type.IsValueType)
{
// value types are only nullable if they are Nullable<T>
return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>);
}
else
{
// reference types are always nullable
return true;
}
}
}
}

Просмотреть файл

@ -1,32 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System;
using System.Reflection;
using System.Resources;
using System.Runtime.InteropServices;
#if !BUILD_GENERATED_VERSION
[assembly: AssemblyCompany("Microsoft Open Technologies, Inc.")]
[assembly: AssemblyCopyright("© Microsoft Open Technologies, Inc. All rights reserved.")]
#endif
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
#if !NOT_CLS_COMPLIANT
[assembly: CLSCompliant(true)]
#endif
[assembly: NeutralResourcesLanguage("en-US")]
[assembly: AssemblyMetadata("Serviceable", "True")]
// ===========================================================================
// DO NOT EDIT OR REMOVE ANYTHING BELOW THIS COMMENT.
// Version numbers are automatically generated based on regular expressions.
// ===========================================================================
#if ASPNETODATA
#if !BUILD_GENERATED_VERSION
[assembly: AssemblyVersion("5.3.1.0")] // ASPNETODATA
[assembly: AssemblyFileVersion("5.3.1.0")] // ASPNETODATA
#endif
[assembly: AssemblyProduct("Microsoft ASP.NET Web API OData")]
#endif

Просмотреть файл

@ -1,5 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Diagnostics.CodeAnalysis;
[assembly: SuppressMessage("Microsoft.Design", "CA2210:AssembliesShouldHaveValidStrongNames", Justification = "Assembly is delay-signed")]

Просмотреть файл

@ -1,5 +0,0 @@
<StyleCopSettings Version="105">
<GlobalSettings>
<StringProperty Name="MergeSettingsFiles">Parent</StringProperty>
</GlobalSettings>
</StyleCopSettings>

Просмотреть файл

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<RuleSet Name="FxCop rules for ASP.NET Web Stack" Description="This rule set contains the rules for ASP.NET Web Stack." ToolsVersion="10.0">
<RuleHintPaths>
<Path>..\packages\CustomFxCopRules</Path>
</RuleHintPaths>
<IncludeAll Action="Error" />
<Rules AnalyzerId="Microsoft.Analyzers.ManagedCodeAnalysis" RuleNamespace="Microsoft.Rules.Managed">
<Rule Id="CA1062" Action="None" />
<Rule Id="MW1200" Action="None" />
</Rules>
</RuleSet>

Просмотреть файл

@ -1,41 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.ComponentModel;
using System.Diagnostics.Contracts;
using System.Web.Http.Controllers;
using System.Web.Http.OData.Builder;
using Microsoft.Data.Edm;
namespace System.Web.Http.OData.Extensions
{
[EditorBrowsable(EditorBrowsableState.Never)]
internal static class HttpActionDescriptorExtensions
{
private const string ModelKeyPrefix = "MS_EdmModel";
internal static IEdmModel GetEdmModel(this HttpActionDescriptor actionDescriptor, Type entityClrType)
{
if (actionDescriptor == null)
{
throw Error.ArgumentNull("actionDescriptor");
}
if (entityClrType == null)
{
throw Error.ArgumentNull("entityClrType");
}
// save the EdmModel to the action descriptor
return actionDescriptor.Properties.GetOrAdd(ModelKeyPrefix + entityClrType.FullName, _ =>
{
ODataConventionModelBuilder builder =
new ODataConventionModelBuilder(actionDescriptor.Configuration, isQueryCompositionMode: true);
EntityTypeConfiguration entityTypeConfiguration = builder.AddEntity(entityClrType);
builder.AddEntitySet(entityClrType.Name, entityTypeConfiguration);
IEdmModel edmModel = builder.GetEdmModel();
Contract.Assert(edmModel != null);
return edmModel;
}) as IEdmModel;
}
}
}

Просмотреть файл

@ -1,53 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.ComponentModel;
using System.Linq;
using System.Web.Http.Filters;
using System.Web.Http.OData.Query;
namespace System.Web.Http.OData.Extensions
{
/// <summary>
/// Provides extension methods for the <see cref="HttpConfiguration"/> class.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public static class HttpConfigurationExtensions
{
/// <summary>
/// Enables query support for actions with an <see cref="IQueryable" /> or <see cref="IQueryable{T}" /> return
/// type. To avoid processing unexpected or malicious queries, use the validation settings on
/// <see cref="EnableQueryAttribute"/> to validate incoming queries. For more information, visit
/// http://go.microsoft.com/fwlink/?LinkId=279712.
/// </summary>
/// <param name="configuration">The server configuration.</param>
public static void AddODataQueryFilter(this HttpConfiguration configuration)
{
if (configuration == null)
{
throw Error.ArgumentNull("configuration");
}
configuration.Services.Add(
typeof(IFilterProvider),
new QueryFilterProvider(new EnableQueryAttribute(), skipQueryableAttribute: true));
}
/// <summary>
/// Enables query support for actions with an <see cref="IQueryable" /> or <see cref="IQueryable{T}" /> return
/// type. To avoid processing unexpected or malicious queries, use the validation settings on
/// <see cref="EnableQueryAttribute"/> to validate incoming queries. For more information, visit
/// http://go.microsoft.com/fwlink/?LinkId=279712.
/// </summary>
/// <param name="configuration">The server configuration.</param>
/// <param name="queryFilter">The action filter that executes the query.</param>
public static void AddODataQueryFilter(this HttpConfiguration configuration, IActionFilter queryFilter)
{
if (configuration == null)
{
throw Error.ArgumentNull("configuration");
}
configuration.Services.Add(typeof(IFilterProvider), new QueryFilterProvider(queryFilter));
}
}
}

Просмотреть файл

@ -1,99 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using Microsoft.Data.OData;
namespace System.Web.Http.OData.Extensions
{
/// <summary>
/// Provides extension methods for the <see cref="HttpError"/> class.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public static class HttpErrorExtensions
{
/// <summary>
/// Converts the <paramref name="httpError"/> to an <see cref="ODataError"/>.
/// </summary>
/// <param name="httpError">The <see cref="HttpError"/> instance to convert.</param>
/// <returns>The converted <see cref="ODataError"/></returns>
public static ODataError CreateODataError(this HttpError httpError)
{
if (httpError == null)
{
throw Error.ArgumentNull("httpError");
}
return new ODataError()
{
Message = httpError.GetPropertyValue<string>(HttpErrorKeys.MessageKey),
MessageLanguage = httpError.GetPropertyValue<string>(HttpErrorKeys.MessageLanguageKey),
ErrorCode = httpError.GetPropertyValue<string>(HttpErrorKeys.ErrorCodeKey),
InnerError = ToODataInnerError(httpError)
};
}
private static ODataInnerError ToODataInnerError(HttpError httpError)
{
string innerErrorMessage = httpError.GetPropertyValue<string>(HttpErrorKeys.ExceptionMessageKey);
if (innerErrorMessage == null)
{
string messageDetail = httpError.GetPropertyValue<string>(HttpErrorKeys.MessageDetailKey);
if (messageDetail == null)
{
HttpError modelStateError = httpError.GetPropertyValue<HttpError>(HttpErrorKeys.ModelStateKey);
return (modelStateError == null) ? null
: new ODataInnerError { Message = ConvertModelStateErrors(modelStateError) };
}
else
{
return new ODataInnerError() { Message = messageDetail };
}
}
else
{
ODataInnerError innerError = new ODataInnerError();
innerError.Message = innerErrorMessage;
innerError.TypeName = httpError.GetPropertyValue<string>(HttpErrorKeys.ExceptionTypeKey);
innerError.StackTrace = httpError.GetPropertyValue<string>(HttpErrorKeys.StackTraceKey);
HttpError innerExceptionError = httpError.GetPropertyValue<HttpError>(HttpErrorKeys.InnerExceptionKey);
if (innerExceptionError != null)
{
innerError.InnerError = ToODataInnerError(innerExceptionError);
}
return innerError;
}
}
// Convert the model state errors in to a string (for debugging only).
// This should be improved once ODataError allows more details.
private static string ConvertModelStateErrors(HttpError error)
{
StringBuilder builder = new StringBuilder();
foreach (KeyValuePair<string, object> modelStateError in error)
{
if (modelStateError.Value != null)
{
builder.Append(modelStateError.Key);
builder.Append(" : ");
IEnumerable<string> errorMessages = modelStateError.Value as IEnumerable<string>;
if (errorMessages != null)
{
foreach (string errorMessage in errorMessages)
{
builder.AppendLine(errorMessage);
}
}
else
{
builder.AppendLine(modelStateError.Value.ToString());
}
}
}
return builder.ToString();
}
}
}

Просмотреть файл

@ -1,94 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
using System.Net;
using System.Net.Http;
using Microsoft.Data.OData;
namespace System.Web.Http.OData.Extensions
{
/// <summary>
/// Provides extension methods for the <see cref="HttpRequestMessage"/> class.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public static class HttpRequestMessageExtensions
{
// Maintain the System.Web.Http.OData. prefix in any new properties to avoid conflicts with user properties
// and those of the v4 assembly.
private const string PropertiesKey = "System.Web.Http.OData.Properties";
/// <summary>
/// Gets the <see cref="HttpRequestMessageProperties"/> instance containing OData methods and properties
/// for given <see cref="HttpRequestMessage"/>.
/// </summary>
/// <param name="request">The request of interest.</param>
/// <returns>
/// An object through which OData methods and properties for given <paramref name="request"/> are available.
/// </returns>
public static HttpRequestMessageProperties ODataProperties(this HttpRequestMessage request)
{
if (request == null)
{
throw Error.ArgumentNull("request");
}
// Cache HttpRequestMessageProperties value to avoid lots of identical objects with no instance data.
HttpRequestMessageProperties properties;
object value;
if (request.Properties.TryGetValue(PropertiesKey, out value))
{
properties = value as HttpRequestMessageProperties;
Contract.Assert(properties != null);
}
else
{
properties = new HttpRequestMessageProperties(request);
// Avoid race conditions: Do not use Add(). Worst case here is an extra HttpRequestMessageProperties
// instance which will soon go out of scope.
request.Properties[PropertiesKey] = properties;
}
return properties;
}
/// <summary>
/// <para>Helper method that performs content negotiation and creates a <see cref="HttpResponseMessage"/>
/// representing an error with an instance of <see cref="ObjectContent{T}"/> wrapping
/// <paramref name="oDataError"/> as the content. If no formatter is found, this method returns a response with
/// status 406 NotAcceptable.</para>
///
/// <para>This method requires that <paramref name="request"/> has been associated with an instance of
/// <see cref="HttpConfiguration"/>.</para>
/// </summary>
/// <param name="request">The request of interest.</param>
/// <param name="statusCode">The status code of the created response.</param>
/// <param name="oDataError">The OData error to wrap.</param>
/// <returns>
/// An error response wrapping <paramref name="oDataError"/> with status code <paramref name="statusCode"/>.
/// </returns>
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "o",
Justification = "oDataError is spelled correctly.")]
public static HttpResponseMessage CreateErrorResponse(this HttpRequestMessage request,
HttpStatusCode statusCode, ODataError oDataError)
{
if (request.ShouldIncludeErrorDetail())
{
return request.CreateResponse(statusCode, oDataError);
}
else
{
return request.CreateResponse(
statusCode,
new ODataError()
{
ErrorCode = oDataError.ErrorCode,
Message = oDataError.Message,
MessageLanguage = oDataError.MessageLanguage
});
}
}
}
}

Просмотреть файл

@ -1,272 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Web.Http.OData.Formatter;
using System.Web.Http.OData.Routing;
using System.Web.Http.OData.Routing.Conventions;
using Microsoft.Data.Edm;
using Microsoft.Data.OData;
using Microsoft.Data.OData.Query.SemanticAst;
namespace System.Web.Http.OData.Extensions
{
/// <summary>
/// Provides properties for use with the <see cref="HttpRequestMessageExtensions.ODataProperties"/> extension
/// method.
/// </summary>
public class HttpRequestMessageProperties
{
private const string ModelKey = "MS_EdmModel";
private const string NextLinkKey = "MS_NextPageLink";
private const string PathHandlerKey = "MS_ODataPathHandler";
private const string PathKey = "MS_ODataPath";
private const string RouteNameKey = "MS_ODataRouteName";
private const string RoutingConventionsStoreKey = "MS_RoutingConventionDataStore";
private const string RoutingConventionsKey = "MS_ODataRoutingConventions";
private const string SelectExpandClauseKey = "MS_SelectExpandClause";
private const string TotalCountKey = "MS_InlineCount";
internal const string ODataServiceVersionHeader = "DataServiceVersion";
internal const string ODataMaxServiceVersionHeader = "MaxDataServiceVersion";
internal const ODataVersion DefaultODataVersion = ODataVersion.V3;
private HttpRequestMessage _request;
internal HttpRequestMessageProperties(HttpRequestMessage request)
{
Contract.Assert(request != null);
_request = request;
}
/// <summary>
/// Gets or sets the EDM model associated with the request.
/// </summary>
public IEdmModel Model
{
get
{
return GetValueOrNull<IEdmModel>(ModelKey);
}
set
{
_request.Properties[ModelKey] = value;
}
}
/// <summary>
/// Gets or sets the route name for generating OData links.
/// </summary>
public string RouteName
{
get
{
return GetValueOrNull<string>(RouteNameKey);
}
set
{
_request.Properties[RouteNameKey] = value;
}
}
/// <summary>
/// Gets or sets the OData routing conventions for controller and action selection.
/// </summary>
public IEnumerable<IODataRoutingConvention> RoutingConventions
{
get
{
return GetValueOrNull<IEnumerable<IODataRoutingConvention>>(RoutingConventionsKey);
}
set
{
_request.Properties[RoutingConventionsKey] = value;
}
}
/// <summary>
/// Gets or sets the <see cref="IODataPathHandler"/> for generating links. Getter creates a default
/// <see cref="IODataPathHandler"/> if value is currently <c>null</c>.
/// </summary>
public IODataPathHandler PathHandler
{
get
{
IODataPathHandler pathHandler = GetValueOrNull<IODataPathHandler>(PathHandlerKey);
if (pathHandler == null)
{
pathHandler = new DefaultODataPathHandler();
PathHandler = pathHandler;
}
return pathHandler;
}
set
{
_request.Properties[PathHandlerKey] = value;
}
}
/// <summary>
/// Gets or sets the OData path of the request.
/// </summary>
public Routing.ODataPath Path
{
get
{
return GetValueOrNull<Routing.ODataPath>(PathKey);
}
set
{
_request.Properties[PathKey] = value;
}
}
/// <summary>
/// Gets or sets the total count for the OData response. Getter returns <c>null</c> if no count should be sent
/// back to the client.
/// </summary>
public long? TotalCount
{
get
{
object totalCount;
if (_request.Properties.TryGetValue(TotalCountKey, out totalCount))
{
// Fairly big problem if following cast fails. Indicates something else is writing properties with
// names we've chosen. Do not silently return null because that will hide the problem.
return (long)totalCount;
}
return null;
}
set
{
if (!value.HasValue)
{
throw Error.ArgumentNull("value");
}
_request.Properties[TotalCountKey] = value;
}
}
/// <summary>
/// Gets or sets the next link for the OData response. Getter returns <c>null</c> if no next link should be
/// sent back to the client.
/// </summary>
public Uri NextLink
{
get
{
return GetValueOrNull<Uri>(NextLinkKey);
}
set
{
_request.Properties[NextLinkKey] = value;
}
}
/// <summary>
/// Gets or sets the parsed OData <see cref="SelectExpandClause"/> of the request. The
/// <see cref="ODataMediaTypeFormatter"/> will use this information (if any) while writing the response for
/// this request.
/// </summary>
public SelectExpandClause SelectExpandClause
{
get
{
return GetValueOrNull<SelectExpandClause>(SelectExpandClauseKey);
}
set
{
if (value == null)
{
throw Error.ArgumentNull("value");
}
_request.Properties[SelectExpandClauseKey] = value;
}
}
/// <summary>
/// Gets the data store used by <see cref="IODataRoutingConvention"/>s to store any custom route data. Getter
/// creates a new <c>IDictionary&lt;string, object&gt;</c> the first time it is called.
/// </summary>
public IDictionary<string, object> RoutingConventionsStore
{
get
{
IDictionary<string, object> store =
GetValueOrNull<IDictionary<string, object>>(RoutingConventionsStoreKey);
if (store == null)
{
store = new Dictionary<string, object>();
RoutingConventionsStore = store;
}
return store;
}
private set
{
_request.Properties[RoutingConventionsStoreKey] = value;
}
}
internal ODataVersion? ODataServiceVersion
{
get
{
return GetODataVersionFromHeader(_request.Headers, ODataServiceVersionHeader);
}
}
internal ODataVersion? ODataMaxServiceVersion
{
get
{
return GetODataVersionFromHeader(_request.Headers, ODataMaxServiceVersionHeader);
}
}
private static ODataVersion? GetODataVersionFromHeader(HttpHeaders headers, string headerName)
{
IEnumerable<string> values;
if (headers.TryGetValues(headerName, out values))
{
string value = values.FirstOrDefault();
if (value != null)
{
string trimmedValue = value.Trim(' ', ';');
try
{
return ODataUtils.StringToODataVersion(trimmedValue);
}
catch (ODataException)
{
// Parsing the odata version failed.
}
}
}
return null;
}
private T GetValueOrNull<T>(string propertyName) where T : class
{
object value;
if (_request.Properties.TryGetValue(propertyName, out value))
{
// Fairly big problem if following cast fails. Indicates something else is writing properties with
// names we've chosen. Do not silently return null because that will hide the problem.
return (T)value;
}
return null;
}
}
}

Просмотреть файл

@ -1,120 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Web.Http.OData.Batch;
using System.Web.Http.OData.Routing;
using System.Web.Http.OData.Routing.Conventions;
using Microsoft.Data.Edm;
namespace System.Web.Http.OData.Extensions
{
/// <summary>
/// Provides extension methods for the <see cref="HttpRouteCollection"/> class.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public static class HttpRouteCollectionExtensions
{
/// <summary>
/// Maps the specified OData route.
/// </summary>
/// <param name="routes">A collection of routes for the application.</param>
/// <param name="routeName">The name of the route to map.</param>
/// <param name="routePrefix">The prefix to add to the OData route's path template.</param>
/// <param name="model">The EDM model to use for parsing OData paths.</param>
/// <returns>The added <see cref="ODataRoute"/>.</returns>
public static ODataRoute MapODataServiceRoute(this HttpRouteCollection routes, string routeName,
string routePrefix, IEdmModel model)
{
return MapODataServiceRoute(routes, routeName, routePrefix, model, batchHandler: null);
}
/// <summary>
/// Maps the specified OData route. When the <paramref name="batchHandler"/> is provided, it will create a
/// '$batch' endpoint to handle the batch requests.
/// </summary>
/// <param name="routes">A collection of routes for the application.</param>
/// <param name="routeName">The name of the route to map.</param>
/// <param name="routePrefix">The prefix to add to the OData route's path template.</param>
/// <param name="model">The EDM model to use for parsing OData paths.</param>
/// <param name="batchHandler">The <see cref="ODataBatchHandler"/>.</param>
/// <returns>The added <see cref="ODataRoute"/>.</returns>
public static ODataRoute MapODataServiceRoute(this HttpRouteCollection routes, string routeName,
string routePrefix, IEdmModel model, ODataBatchHandler batchHandler)
{
return MapODataServiceRoute(routes, routeName, routePrefix, model, new DefaultODataPathHandler(),
ODataRoutingConventions.CreateDefault(), batchHandler);
}
/// <summary>
/// Maps the specified OData route.
/// </summary>
/// <param name="routes">A collection of routes for the application.</param>
/// <param name="routeName">The name of the route to map.</param>
/// <param name="routePrefix">The prefix to add to the OData route's path template.</param>
/// <param name="model">The EDM model to use for parsing OData paths.</param>
/// <param name="pathHandler">The <see cref="IODataPathHandler"/> to use for parsing the OData path.</param>
/// <param name="routingConventions">
/// The OData routing conventions to use for controller and action selection.
/// </param>
/// <returns>The added <see cref="ODataRoute"/>.</returns>
public static ODataRoute MapODataServiceRoute(this HttpRouteCollection routes, string routeName,
string routePrefix, IEdmModel model, IODataPathHandler pathHandler,
IEnumerable<IODataRoutingConvention> routingConventions)
{
return MapODataServiceRoute(routes, routeName, routePrefix, model, pathHandler, routingConventions,
batchHandler: null);
}
/// <summary>
/// Maps the specified OData route. When the <paramref name="batchHandler"/> is provided, it will create a
/// '$batch' endpoint to handle the batch requests.
/// </summary>
/// <param name="routes">A collection of routes for the application.</param>
/// <param name="routeName">The name of the route to map.</param>
/// <param name="routePrefix">The prefix to add to the OData route's path template.</param>
/// <param name="model">The EDM model to use for parsing OData paths.</param>
/// <param name="pathHandler">The <see cref="IODataPathHandler" /> to use for parsing the OData path.</param>
/// <param name="routingConventions">
/// The OData routing conventions to use for controller and action selection.
/// </param>
/// <param name="batchHandler">The <see cref="ODataBatchHandler"/>.</param>
/// <returns>The added <see cref="ODataRoute"/>.</returns>
[SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters",
Justification = "We want the handler to be a batch handler.")]
public static ODataRoute MapODataServiceRoute(this HttpRouteCollection routes, string routeName,
string routePrefix, IEdmModel model, IODataPathHandler pathHandler,
IEnumerable<IODataRoutingConvention> routingConventions, ODataBatchHandler batchHandler)
{
if (routes == null)
{
throw Error.ArgumentNull("routes");
}
if (!String.IsNullOrEmpty(routePrefix))
{
int prefixLastIndex = routePrefix.Length - 1;
if (routePrefix[prefixLastIndex] == '/')
{
// Remove the last trailing slash if it has one.
routePrefix = routePrefix.Substring(0, routePrefix.Length - 1);
}
}
if (batchHandler != null)
{
batchHandler.ODataRouteName = routeName;
string batchTemplate = String.IsNullOrEmpty(routePrefix) ? ODataRouteConstants.Batch
: routePrefix + '/' + ODataRouteConstants.Batch;
routes.MapHttpBatchRoute(routeName + "Batch", batchTemplate, batchHandler);
}
ODataPathRouteConstraint routeConstraint =
new ODataPathRouteConstraint(pathHandler, model, routeName, routingConventions);
ODataRoute route = new ODataRoute(routePrefix, routeConstraint);
routes.Add(routeName, route);
return route;
}
}
}

Просмотреть файл

@ -1,83 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics.Contracts;
using System.Net.Http;
using System.Web.Http.OData.Properties;
using System.Web.Http.OData.Routing;
using System.Web.Http.Routing;
namespace System.Web.Http.OData.Extensions
{
/// <summary>
/// Provides extension methods for the <see cref="UrlHelper"/> class.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public static class UrlHelperExtensions
{
/// <summary>
/// Generates an OData link using the request's OData route name and path handler and given segments.
/// </summary>
/// <param name="urlHelper">The URL helper.</param>
/// <param name="segments">The OData path segments.</param>
/// <returns>The generated OData link.</returns>
public static string CreateODataLink(this UrlHelper urlHelper, params ODataPathSegment[] segments)
{
return CreateODataLink(urlHelper, segments as IList<ODataPathSegment>);
}
/// <summary>
/// Generates an OData link using the request's OData route name and path handler and given segments.
/// </summary>
/// <param name="urlHelper">The URL helper.</param>
/// <param name="segments">The OData path segments.</param>
/// <returns>The generated OData link.</returns>
public static string CreateODataLink(this UrlHelper urlHelper, IList<ODataPathSegment> segments)
{
if (urlHelper == null)
{
throw Error.ArgumentNull("urlHelper");
}
HttpRequestMessage request = urlHelper.Request;
Contract.Assert(request != null);
string routeName = request.ODataProperties().RouteName;
if (String.IsNullOrEmpty(routeName))
{
throw Error.InvalidOperation(SRResources.RequestMustHaveODataRouteName);
}
IODataPathHandler pathHandler = request.ODataProperties().PathHandler;
return CreateODataLink(urlHelper, routeName, pathHandler, segments);
}
/// <summary>
/// Generates an OData link using the given OData route name, path handler, and segments.
/// </summary>
/// <param name="urlHelper">The URL helper.</param>
/// <param name="routeName">The name of the OData route.</param>
/// <param name="pathHandler">The path handler to use for generating the link.</param>
/// <param name="segments">The OData path segments.</param>
/// <returns>The generated OData link.</returns>
public static string CreateODataLink(this UrlHelper urlHelper, string routeName, IODataPathHandler pathHandler,
IList<ODataPathSegment> segments)
{
if (urlHelper == null)
{
throw Error.ArgumentNull("urlHelper");
}
if (pathHandler == null)
{
throw Error.ArgumentNull("pathHandler");
}
string odataPath = pathHandler.Link(new ODataPath(segments));
return urlHelper.Link(
routeName,
new HttpRouteValueDictionary() { { ODataRouteConstants.ODataPath, odataPath } });
}
}
}

Просмотреть файл

@ -1,15 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Diagnostics.CodeAnalysis;
[assembly: SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "System.Web.Http", Justification = "Follows System.Web.Http naming")]
[assembly: SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "System.Web.Http.OData.Formatter", Justification = "Follows System.Web.Http naming")]
[assembly: SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "System.Web.Http.OData.Formatter.Serialization", Justification = "Follows System.Web.Http naming")]
[assembly: SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "System.Web.Http.OData.Builder.Conventions.Attributes", Justification = "Follows System.Web.Http naming")]
[assembly: SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "System.Web.Http.OData.Results", Justification = "Follows System.Web.Http naming")]
[assembly: SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling", Scope = "type", Target = "System.Web.Http.OData.Formatter.EdmLibHelpers", Justification = "Mostly extension methods")]
[assembly: SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling", Scope = "member", Target = "System.Web.Http.OData.Formatter.EdmLibHelpers.#.cctor()", Justification = "Class coupling necessary in this class")]
[assembly: SuppressMessage("Microsoft.Web.FxCop", "MW1000:UnusedResourceUsageRule", MessageId = "172567", Justification = "Resource used by framework")]
[assembly: SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", MessageId = "inlinecount", Scope = "resource", Target = "System.Web.Http.OData.Properties.SRResources.resources", Justification = "spelled correctly")]
[assembly: SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", MessageId = "orderby", Scope = "resource", Target = "System.Web.Http.OData.Properties.SRResources.resources", Justification = "$orderby is an odata keyword")]
[assembly: SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", MessageId = "it", Scope = "resource", Target = "System.Web.Http.OData.Properties.SRResources.resources", Justification = "$it is an odata keyword")]

Просмотреть файл

@ -1,222 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using System.Web.Http.Description;
using System.Web.Http.OData.Properties;
using System.Web.Http.OData.Query;
using System.Web.Http.OData.Routing;
using Microsoft.Data.OData;
using Microsoft.Data.OData.Query;
namespace System.Web.Http.OData
{
/// <summary>
/// Provides a convenient starting point for a controller that exposes an OData entity set. This is the asynchronous version of <see cref="EntitySetController{TEntity, TKey}"/>.
/// </summary>
/// <typeparam name="TEntity">The type associated with the exposed entity set's entity type.</typeparam>
/// <typeparam name="TKey">The type associated with the entity key of the exposed entity set's entity type.</typeparam>
[CLSCompliant(false)]
[ODataNullValue]
public abstract class AsyncEntitySetController<TEntity, TKey> : ODataController where TEntity : class
{
/// <summary>
/// Gets the OData path of the current request.
/// </summary>
public ODataPath ODataPath
{
get
{
return EntitySetControllerHelpers.GetODataPath(this);
}
}
/// <summary>
/// Gets the OData query options of the current request.
/// </summary>
public ODataQueryOptions<TEntity> QueryOptions
{
get
{
return EntitySetControllerHelpers.CreateQueryOptions<TEntity>(this);
}
}
/// <summary>
/// This method should be overridden to handle GET requests that attempt to retrieve entities from the entity set. This method should asynchronously compute the
/// matching entities by applying the request's query options.
/// </summary>
/// <returns>A <see cref="Task"/> that contains the matching entities from the entity set when it completes.</returns>
[SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "Get", Justification = "Needs to be this name to follow routing conventions.")]
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Using tasks")]
public virtual Task<IEnumerable<TEntity>> Get()
{
throw EntitySetControllerHelpers.GetNotImplementedResponse(Request);
}
/// <summary>
/// Handles GET requests that attempt to retrieve an individual entity by key from the entity set.
/// </summary>
/// <param name="key">The entity key of the entity to retrieve.</param>
/// <returns>A <see cref="Task"/> that contains the response message to send back to the client when it completes.</returns>
[SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "Get", Justification = "Needs to be this name to follow routing conventions.")]
public virtual async Task<HttpResponseMessage> Get([FromODataUri] TKey key)
{
TEntity entity = await GetEntityByKeyAsync(key);
return EntitySetControllerHelpers.GetByKeyResponse<TEntity>(Request, entity);
}
/// <summary>
/// Handles POST requests that create new entities in the entity set.
/// </summary>
/// <param name="entity">The entity to insert into the entity set.</param>
/// <returns>A <see cref="Task"/> that contains the response message to send back to the client when it completes.</returns>
public virtual async Task<HttpResponseMessage> Post([FromBody] TEntity entity)
{
TEntity createdEntity = await CreateEntityAsync(entity);
return EntitySetControllerHelpers.PostResponse<TEntity, TKey>(this, createdEntity, GetKey(createdEntity));
}
/// <summary>
/// Handles PUT requests that attempt to replace a single entity in the entity set.
/// </summary>
/// <param name="key">The entity key of the entity to replace.</param>
/// <param name="update">The updated entity.</param>
/// <returns>A <see cref="Task"/> that contains the response message to send back to the client when it completes.</returns>
public virtual async Task<HttpResponseMessage> Put([FromODataUri] TKey key, [FromBody] TEntity update)
{
TEntity updatedEntity = await UpdateEntityAsync(key, update);
return EntitySetControllerHelpers.PutResponse<TEntity>(Request, updatedEntity);
}
/// <summary>
/// Handles PATCH and MERGE requests to partially update a single entity in the entity set.
/// </summary>
/// <param name="key">The entity key of the entity to update.</param>
/// <param name="patch">The patch representing the partial update.</param>
/// <returns>A <see cref="Task"/> that contains the response message to send back to the client when it completes.</returns>
[AcceptVerbs("PATCH", "MERGE")]
[SuppressMessage("Microsoft.Naming", "CA1719:ParameterNamesShouldNotMatchMemberNames", MessageId = "1#", Justification = "Patch is the action name by WebAPI convention.")]
public virtual async Task<HttpResponseMessage> Patch([FromODataUri] TKey key, Delta<TEntity> patch)
{
TEntity patchedEntity = await PatchEntityAsync(key, patch);
return EntitySetControllerHelpers.PatchResponse<TEntity>(Request, patchedEntity);
}
/// <summary>
/// This method should be overridden to handles DELETE requests for deleting existing entities from the entity set.
/// </summary>
/// <param name="key">The entity key of the entity to delete.</param>
/// <returns>A <see cref="Task"/> that completes when the entity has been successfully deleted.</returns>
public virtual Task Delete([FromODataUri] TKey key)
{
throw EntitySetControllerHelpers.DeleteEntityNotImplementedResponse(Request);
}
/// <summary>
/// This method should be overridden to handle POST and PUT requests that attempt to create a link between two entities.
/// </summary>
/// <param name="key">The key of the entity with the navigation property.</param>
/// <param name="navigationProperty">The name of the navigation property.</param>
/// <param name="link">The URI of the entity to link.</param>
/// <returns>A <see cref="Task"/> that completes when the link has been successfully created.</returns>
[AcceptVerbs("POST", "PUT")]
public virtual Task CreateLink([FromODataUri] TKey key, string navigationProperty, [FromBody] Uri link)
{
throw EntitySetControllerHelpers.CreateLinkNotImplementedResponse(Request, navigationProperty);
}
/// <summary>
/// This method should be overridden to handle DELETE requests that attempt to break a relationship between two entities.
/// </summary>
/// <param name="key">The key of the entity with the navigation property.</param>
/// <param name="navigationProperty">The name of the navigation property.</param>
/// <param name="link">The URI of the entity to remove from the navigation property.</param>
/// <returns>A <see cref="Task"/> that completes when the link has been successfully deleted.</returns>
public virtual Task DeleteLink([FromODataUri] TKey key, string navigationProperty, [FromBody] Uri link)
{
throw EntitySetControllerHelpers.DeleteLinkNotImplementedResponse(Request, navigationProperty);
}
/// <summary>
/// This method should be overridden to handle DELETE requests that attempt to break a relationship between two entities.
/// </summary>
/// <param name="key">The key of the entity with the navigation property.</param>
/// <param name="relatedKey">The key of the related entity.</param>
/// <param name="navigationProperty">The name of the navigation property.</param>
/// <returns>A <see cref="Task"/> that completes when the link has been successfully deleted.</returns>
public virtual Task DeleteLink([FromODataUri] TKey key, string relatedKey, string navigationProperty)
{
throw EntitySetControllerHelpers.DeleteLinkNotImplementedResponse(Request, navigationProperty);
}
/// <summary>
/// This method should be overridden to handle all unmapped OData requests.
/// </summary>
/// <param name="odataPath">The OData path of the request.</param>
/// <returns>A <see cref="Task"/> that contains the response message to send back to the client when it completes.</returns>
[AcceptVerbs("GET", "POST", "PUT", "PATCH", "MERGE", "DELETE")]
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "odata", Justification = "odata is spelled correctly.")]
public virtual Task<HttpResponseMessage> HandleUnmappedRequest(ODataPath odataPath)
{
throw EntitySetControllerHelpers.UnmappedRequestResponse(Request, odataPath);
}
/// <summary>
/// This method should be overridden to get the entity key of the specified entity.
/// </summary>
/// <param name="entity">The entity.</param>
/// <returns>The entity key value</returns>
protected internal virtual TKey GetKey(TEntity entity)
{
throw EntitySetControllerHelpers.GetKeyNotImplementedResponse(Request);
}
/// <summary>
/// This method should be overridden to retrieve an entity by key from the entity set.
/// </summary>
/// <param name="key">The entity key of the entity to retrieve.</param>
/// <returns>A <see cref="Task"/> that contains the retrieved entity when it completes, or <c>null</c> if an entity with the specified entity key cannot be found in the entity set.</returns>
protected internal virtual Task<TEntity> GetEntityByKeyAsync(TKey key)
{
throw EntitySetControllerHelpers.GetEntityByKeyNotImplementedResponse(Request);
}
/// <summary>
/// This method should be overridden to create a new entity in the entity set.
/// </summary>
/// <remarks>When overriding this method, the GetKey method should also be overridden so that the location header can be generated.</remarks>
/// <param name="entity">The entity to add to the entity set.</param>
/// <returns>A <see cref="Task"/> that contains the created entity when it completes.</returns>
protected internal virtual Task<TEntity> CreateEntityAsync(TEntity entity)
{
throw EntitySetControllerHelpers.CreateEntityNotImplementedResponse(Request);
}
/// <summary>
/// This method should be overridden to update an existing entity in the entity set.
/// </summary>
/// <param name="key">The entity key of the entity to update.</param>
/// <param name="update">The updated entity.</param>
/// <returns>A <see cref="Task"/> that contains the updated entity when it completes.</returns>
protected internal virtual Task<TEntity> UpdateEntityAsync(TKey key, TEntity update)
{
throw EntitySetControllerHelpers.UpdateEntityNotImplementedResponse(Request);
}
/// <summary>
/// This method should be overridden to apply a partial update to an existing entity in the entity set.
/// </summary>
/// <param name="key">The entity key of the entity to update.</param>
/// <param name="patch">The patch representing the partial update.</param>
/// <returns>A <see cref="Task"/> that contains the updated entity when it completes.</returns>
protected internal virtual Task<TEntity> PatchEntityAsync(TKey key, Delta<TEntity> patch)
{
throw EntitySetControllerHelpers.PatchEntityNotImplementedResponse(Request);
}
}
}

Просмотреть файл

@ -1,120 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
namespace System.Web.Http.OData.Batch
{
/// <summary>
/// Represents a ChangeSet request.
/// </summary>
public class ChangeSetRequestItem : ODataBatchRequestItem
{
/// <summary>
/// Initializes a new instance of the <see cref="ChangeSetRequestItem"/> class.
/// </summary>
/// <param name="requests">The request messages in the ChangeSet.</param>
public ChangeSetRequestItem(IEnumerable<HttpRequestMessage> requests)
{
if (requests == null)
{
throw Error.ArgumentNull("requests");
}
Requests = requests;
}
/// <summary>
/// Gets the request messages in the ChangeSet.
/// </summary>
public IEnumerable<HttpRequestMessage> Requests { get; private set; }
/// <summary>
/// Sends the ChangeSet request.
/// </summary>
/// <param name="invoker">The invoker.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>A <see cref="ChangeSetResponseItem"/>.</returns>
public override async Task<ODataBatchResponseItem> SendRequestAsync(HttpMessageInvoker invoker, CancellationToken cancellationToken)
{
if (invoker == null)
{
throw Error.ArgumentNull("invoker");
}
Dictionary<string, string> contentIdToLocationMapping = new Dictionary<string, string>();
List<HttpResponseMessage> responses = new List<HttpResponseMessage>();
try
{
foreach (HttpRequestMessage request in Requests)
{
HttpResponseMessage response = await SendMessageAsync(invoker, request, cancellationToken, contentIdToLocationMapping);
if (response.IsSuccessStatusCode)
{
responses.Add(response);
}
else
{
DisposeResponses(responses);
responses.Clear();
responses.Add(response);
return new ChangeSetResponseItem(responses);
}
}
}
catch
{
DisposeResponses(responses);
throw;
}
return new ChangeSetResponseItem(responses);
}
/// <summary>
/// Gets the resources registered for dispose on each request messages of the ChangeSet.
/// </summary>
/// <returns>A collection of resources registered for dispose.</returns>
public override IEnumerable<IDisposable> GetResourcesForDisposal()
{
List<IDisposable> resources = new List<IDisposable>();
foreach (HttpRequestMessage request in Requests)
{
if (request != null)
{
resources.AddRange(request.GetResourcesForDisposal());
}
}
return resources;
}
/// <inheritdoc/>
protected override void Dispose(bool disposing)
{
if (disposing)
{
foreach (HttpRequestMessage request in Requests)
{
if (request != null)
{
request.Dispose();
}
}
}
}
internal static void DisposeResponses(List<HttpResponseMessage> responses)
{
foreach (HttpResponseMessage response in responses)
{
if (response != null)
{
response.Dispose();
}
}
}
}
}

Просмотреть файл

@ -1,72 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Data.OData;
namespace System.Web.Http.OData.Batch
{
/// <summary>
/// Represents a ChangeSet response.
/// </summary>
public class ChangeSetResponseItem : ODataBatchResponseItem
{
/// <summary>
/// Initializes a new instance of the <see cref="ChangeSetResponseItem"/> class.
/// </summary>
/// <param name="responses">The response messages for the ChangeSet requests.</param>
public ChangeSetResponseItem(IEnumerable<HttpResponseMessage> responses)
{
if (responses == null)
{
throw Error.ArgumentNull("responses");
}
Responses = responses;
}
/// <summary>
/// Gets the response messages for the ChangeSet.
/// </summary>
public IEnumerable<HttpResponseMessage> Responses { get; private set; }
/// <summary>
/// Writes the responses as a ChangeSet.
/// </summary>
/// <param name="writer">The <see cref="ODataBatchWriter"/>.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
public override async Task WriteResponseAsync(ODataBatchWriter writer, CancellationToken cancellationToken)
{
if (writer == null)
{
throw Error.ArgumentNull("writer");
}
writer.WriteStartChangeset();
foreach (HttpResponseMessage responseMessage in Responses)
{
await WriteMessageAsync(writer, responseMessage, cancellationToken);
}
writer.WriteEndChangeset();
}
/// <inheritdoc/>
protected override void Dispose(bool disposing)
{
if (disposing)
{
foreach (HttpResponseMessage response in Responses)
{
if (response != null)
{
response.Dispose();
}
}
}
}
}
}

Просмотреть файл

@ -1,193 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Http.Batch;
using Microsoft.Data.OData;
namespace System.Web.Http.OData.Batch
{
/// <summary>
/// Default implementation of <see cref="ODataBatchHandler"/> for handling OData batch request.
/// </summary>
/// <remarks>
/// By default, it buffers the request content stream.
/// </remarks>
public class DefaultODataBatchHandler : ODataBatchHandler
{
/// <summary>
/// Initializes a new instance of the <see cref="DefaultODataBatchHandler"/> class.
/// </summary>
/// <param name="httpServer">The <see cref="HttpServer"/> for handling the individual batch requests.</param>
public DefaultODataBatchHandler(HttpServer httpServer)
: base(httpServer)
{
}
/// <inheritdoc/>
public override async Task<HttpResponseMessage> ProcessBatchAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
if (request == null)
{
throw Error.ArgumentNull("request");
}
ValidateRequest(request);
IList<ODataBatchRequestItem> subRequests = await ParseBatchRequestsAsync(request, cancellationToken);
try
{
IList<ODataBatchResponseItem> responses = await ExecuteRequestMessagesAsync(subRequests, cancellationToken);
return await CreateResponseMessageAsync(responses, request, cancellationToken);
}
finally
{
foreach (ODataBatchRequestItem subRequest in subRequests)
{
request.RegisterForDispose(subRequest.GetResourcesForDisposal());
request.RegisterForDispose(subRequest);
}
}
}
/// <summary>
/// Executes the OData batch requests.
/// </summary>
/// <param name="requests">The collection of OData batch requests.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>A collection of <see cref="ODataBatchResponseItem"/> for the batch requests.</returns>
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "We need to return a collection of response messages asynchronously.")]
public virtual async Task<IList<ODataBatchResponseItem>> ExecuteRequestMessagesAsync(IEnumerable<ODataBatchRequestItem> requests, CancellationToken cancellationToken)
{
if (requests == null)
{
throw Error.ArgumentNull("requests");
}
IList<ODataBatchResponseItem> responses = new List<ODataBatchResponseItem>();
try
{
foreach (ODataBatchRequestItem request in requests)
{
responses.Add(await request.SendRequestAsync(Invoker, cancellationToken));
}
}
catch
{
foreach (ODataBatchResponseItem response in responses)
{
if (response != null)
{
response.Dispose();
}
}
throw;
}
return responses;
}
/// <summary>
/// Converts the incoming OData batch request into a collection of request messages.
/// </summary>
/// <param name="request">The request containing the batch request messages.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>A collection of <see cref="ODataBatchRequestItem"/>.</returns>
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "We need to return a collection of request messages asynchronously.")]
public virtual async Task<IList<ODataBatchRequestItem>> ParseBatchRequestsAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
if (request == null)
{
throw Error.ArgumentNull("request");
}
ODataMessageReaderSettings oDataReaderSettings = new ODataMessageReaderSettings
{
DisableMessageStreamDisposal = true,
MessageQuotas = MessageQuotas,
BaseUri = EnsureTrailingSlash(GetBaseUri(request))
};
ODataMessageReader reader = await request.Content.GetODataMessageReaderAsync(oDataReaderSettings, cancellationToken);
request.RegisterForDispose(reader);
List<ODataBatchRequestItem> requests = new List<ODataBatchRequestItem>();
ODataBatchReader batchReader = reader.CreateODataBatchReader();
Guid batchId = Guid.NewGuid();
while (batchReader.Read())
{
if (batchReader.State == ODataBatchReaderState.ChangesetStart)
{
IList<HttpRequestMessage> changeSetRequests = await batchReader.ReadChangeSetRequestAsync(batchId, cancellationToken);
foreach (HttpRequestMessage changeSetRequest in changeSetRequests)
{
changeSetRequest.CopyBatchRequestProperties(request);
}
requests.Add(new ChangeSetRequestItem(changeSetRequests));
}
else if (batchReader.State == ODataBatchReaderState.Operation)
{
HttpRequestMessage operationRequest = await batchReader.ReadOperationRequestAsync(batchId, bufferContentStream: true, cancellationToken: cancellationToken);
operationRequest.CopyBatchRequestProperties(request);
requests.Add(new OperationRequestItem(operationRequest));
}
}
return requests;
}
/// <summary>
/// Creates the batch response message.
/// </summary>
/// <param name="responses">The responses for the batch requests.</param>
/// <param name="request">The original request containing all the batch requests.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>The batch response message.</returns>
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Caller is responsible for disposing the object.")]
public virtual Task<HttpResponseMessage> CreateResponseMessageAsync(
IEnumerable<ODataBatchResponseItem> responses, HttpRequestMessage request, CancellationToken cancellationToken)
{
if (request == null)
{
throw Error.ArgumentNull("request");
}
return request.CreateODataBatchResponseAsync(responses, MessageQuotas);
}
/// <summary>
/// Validates the incoming request that contains the batch request messages.
/// </summary>
/// <param name="request">The request containing the batch request messages.</param>
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Caller is responsible for disposing the object.")]
public virtual void ValidateRequest(HttpRequestMessage request)
{
if (request == null)
{
throw Error.ArgumentNull("request");
}
request.ValidateODataBatchRequest();
}
/// <summary>
/// Gets the base URI for the batched requests.
/// </summary>
/// <param name="request">The original request containing all the batch requests.</param>
/// <returns>The base URI.</returns>
public virtual Uri GetBaseUri(HttpRequestMessage request)
{
if (request == null)
{
throw Error.ArgumentNull("request");
}
return request.GetODataBatchBaseUri(ODataRouteName);
}
}
}

Просмотреть файл

@ -1,44 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.IO;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
namespace System.Web.Http.OData.Batch
{
internal class LazyStreamContent : HttpContent
{
private Func<Stream> _getStream;
private StreamContent _streamContent;
public LazyStreamContent(Func<Stream> getStream)
{
_getStream = getStream;
}
private StreamContent StreamContent
{
get
{
if (_streamContent == null)
{
_streamContent = new StreamContent(_getStream());
}
return _streamContent;
}
}
protected override Task SerializeToStreamAsync(Stream stream, TransportContext context)
{
return StreamContent.CopyToAsync(stream, context);
}
protected override bool TryComputeLength(out long length)
{
length = -1;
return false;
}
}
}

Просмотреть файл

@ -1,101 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Http.OData.Extensions;
using System.Web.Http.OData.Formatter;
using Microsoft.Data.OData;
namespace System.Web.Http.OData.Batch
{
/// <summary>
/// Encapsulates a collection of OData batch responses.
/// </summary>
public class ODataBatchContent : HttpContent
{
private ODataMessageWriterSettings _writerSettings;
/// <summary>
/// Initializes a new instance of the <see cref="ODataBatchContent"/> class.
/// </summary>
/// <param name="responses">The batch responses.</param>
public ODataBatchContent(IEnumerable<ODataBatchResponseItem> responses)
: this(responses, new ODataMessageWriterSettings())
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ODataBatchContent"/> class.
/// </summary>
/// <param name="responses">The batch responses.</param>
/// <param name="writerSettings">The <see cref="ODataMessageWriterSettings"/>.</param>
public ODataBatchContent(IEnumerable<ODataBatchResponseItem> responses, ODataMessageWriterSettings writerSettings)
{
if (responses == null)
{
throw Error.ArgumentNull("responses");
}
if (writerSettings == null)
{
throw Error.ArgumentNull("writerSettings");
}
Responses = responses;
_writerSettings = writerSettings;
Headers.ContentType = MediaTypeHeaderValue.Parse(String.Format(CultureInfo.InvariantCulture, "multipart/mixed;boundary=batchresponse_{0}", Guid.NewGuid()));
ODataVersion version = _writerSettings.Version ?? HttpRequestMessageProperties.DefaultODataVersion;
Headers.TryAddWithoutValidation(HttpRequestMessageProperties.ODataServiceVersionHeader, ODataUtils.ODataVersionToString(version));
}
/// <summary>
/// Gets the batch responses.
/// </summary>
public IEnumerable<ODataBatchResponseItem> Responses { get; private set; }
/// <inheritdoc/>
protected override void Dispose(bool disposing)
{
if (disposing)
{
foreach (ODataBatchResponseItem response in Responses)
{
if (response != null)
{
response.Dispose();
}
}
}
base.Dispose(disposing);
}
/// <inheritdoc/>
protected override async Task SerializeToStreamAsync(Stream stream, TransportContext context)
{
IODataResponseMessage responseMessage = new ODataMessageWrapper(stream, Headers);
ODataMessageWriter messageWriter = new ODataMessageWriter(responseMessage, _writerSettings);
ODataBatchWriter writer = messageWriter.CreateODataBatchWriter();
writer.WriteStartBatch();
foreach (ODataBatchResponseItem response in Responses)
{
await response.WriteResponseAsync(writer, CancellationToken.None);
}
writer.WriteEndBatch();
}
/// <inheritdoc/>
protected override bool TryComputeLength(out long length)
{
length = -1;
return false;
}
}
}

Просмотреть файл

@ -1,51 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Diagnostics.Contracts;
using System.Web.Http.Batch;
using Microsoft.Data.OData;
namespace System.Web.Http.OData.Batch
{
/// <summary>
/// Defines the abstraction for handling OData batch requests.
/// </summary>
public abstract class ODataBatchHandler : HttpBatchHandler
{
// Maxing out the received message size as we depend on the hosting layer to enforce this limit.
private ODataMessageQuotas _messageQuotas = new ODataMessageQuotas { MaxReceivedMessageSize = Int64.MaxValue };
/// <summary>
/// Initializes a new instance of the <see cref="ODataBatchHandler"/> class.
/// </summary>
/// <param name="httpServer">The <see cref="HttpServer"/> for handling the individual batch requests.</param>
protected ODataBatchHandler(HttpServer httpServer)
: base(httpServer)
{
}
/// <summary>
/// Gets the <see cref="ODataMessageQuotas"/> used for reading/writing the batch request/response.
/// </summary>
public ODataMessageQuotas MessageQuotas
{
get { return _messageQuotas; }
}
/// <summary>
/// Gets or sets the name of the OData route associated with this batch handler.
/// </summary>
public string ODataRouteName { get; set; }
internal static Uri EnsureTrailingSlash(Uri uri)
{
Contract.Assert(uri != null);
if (!uri.OriginalString.EndsWith("/", StringComparison.Ordinal))
{
return new Uri(uri.OriginalString + "/");
}
return uri;
}
}
}

Просмотреть файл

@ -1,213 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using System.Web.Http.OData.Extensions;
using System.Web.Http.OData.Formatter;
using System.Web.Http.OData.Properties;
using System.Web.Http.OData.Routing;
using System.Web.Http.Routing;
using Microsoft.Data.OData;
namespace System.Web.Http.OData.Batch
{
/// <summary>
/// Provides extension methods for the <see cref="HttpRequestMessage"/> class.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public static class ODataBatchHttpRequestMessageExtensions
{
private const string BatchIdKey = "BatchId";
private const string ChangeSetIdKey = "ChangesetId";
private const string ContentIdMappingKey = "ContentIdMapping";
private const string BatchMediaType = "multipart/mixed";
private const string Boundary = "boundary";
/// <summary>
/// Retrieves the Batch ID associated with the request.
/// </summary>
/// <param name="request">The request.</param>
/// <returns>The Batch ID associated with this request, or <c>null</c> if there isn't one.</returns>
public static Guid? GetODataBatchId(this HttpRequestMessage request)
{
if (request == null)
{
throw Error.ArgumentNull("request");
}
object batchId;
if (request.Properties.TryGetValue(BatchIdKey, out batchId))
{
return (Guid)batchId;
}
return null;
}
/// <summary>
/// Associates a given Batch ID with the request.
/// </summary>
/// <param name="request">The request.</param>
/// <param name="batchId">The Batch ID.</param>
public static void SetODataBatchId(this HttpRequestMessage request, Guid batchId)
{
if (request == null)
{
throw Error.ArgumentNull("request");
}
request.Properties[BatchIdKey] = batchId;
}
/// <summary>
/// Retrieves the ChangeSet ID associated with the request.
/// </summary>
/// <param name="request">The request.</param>
/// <returns>The ChangeSet ID associated with this request, or <c>null</c> if there isn't one.</returns>
public static Guid? GetODataChangeSetId(this HttpRequestMessage request)
{
if (request == null)
{
throw Error.ArgumentNull("request");
}
object changeSetId;
if (request.Properties.TryGetValue(ChangeSetIdKey, out changeSetId))
{
return (Guid)changeSetId;
}
return null;
}
/// <summary>
/// Associates a given ChangeSet ID with the request.
/// </summary>
/// <param name="request">The request.</param>
/// <param name="changeSetId">The ChangeSet ID.</param>
public static void SetODataChangeSetId(this HttpRequestMessage request, Guid changeSetId)
{
if (request == null)
{
throw Error.ArgumentNull("request");
}
request.Properties[ChangeSetIdKey] = changeSetId;
}
/// <summary>
/// Retrieves the Content-ID to Location mapping associated with the request.
/// </summary>
/// <param name="request">The request.</param>
/// <returns>The Content-ID to Location mapping associated with this request, or <c>null</c> if there isn't one.</returns>
public static IDictionary<string, string> GetODataContentIdMapping(this HttpRequestMessage request)
{
if (request == null)
{
throw Error.ArgumentNull("request");
}
object contentIdMapping;
if (request.Properties.TryGetValue(ContentIdMappingKey, out contentIdMapping))
{
return contentIdMapping as IDictionary<string, string>;
}
return null;
}
/// <summary>
/// Associates a given Content-ID to Location mapping with the request.
/// </summary>
/// <param name="request">The request.</param>
/// <param name="contentIdMapping">The Content-ID to Location mapping.</param>
public static void SetODataContentIdMapping(this HttpRequestMessage request, IDictionary<string, string> contentIdMapping)
{
if (request == null)
{
throw Error.ArgumentNull("request");
}
request.Properties[ContentIdMappingKey] = contentIdMapping;
}
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Caller is responsible for disposing the object.")]
internal static Task<HttpResponseMessage> CreateODataBatchResponseAsync(this HttpRequestMessage request, IEnumerable<ODataBatchResponseItem> responses, ODataMessageQuotas messageQuotas)
{
Contract.Assert(request != null);
ODataVersion odataVersion = ODataMediaTypeFormatter.GetODataResponseVersion(request);
ODataMessageWriterSettings writerSettings = new ODataMessageWriterSettings()
{
Version = odataVersion,
Indent = true,
DisableMessageStreamDisposal = true,
MessageQuotas = messageQuotas
};
HttpResponseMessage response = request.CreateResponse(HttpStatusCode.Accepted);
response.Content = new ODataBatchContent(responses, writerSettings);
return Task.FromResult(response);
}
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Caller is responsible for disposing the object.")]
internal static void ValidateODataBatchRequest(this HttpRequestMessage request)
{
Contract.Assert(request != null);
if (request.Content == null)
{
throw new HttpResponseException(request.CreateErrorResponse(
HttpStatusCode.BadRequest,
SRResources.BatchRequestMissingContent));
}
MediaTypeHeaderValue contentType = request.Content.Headers.ContentType;
if (contentType == null)
{
throw new HttpResponseException(request.CreateErrorResponse(
HttpStatusCode.BadRequest,
SRResources.BatchRequestMissingContentType));
}
if (!String.Equals(contentType.MediaType, BatchMediaType, StringComparison.OrdinalIgnoreCase))
{
throw new HttpResponseException(request.CreateErrorResponse(HttpStatusCode.BadRequest,
Error.Format(SRResources.BatchRequestInvalidMediaType, BatchMediaType)));
}
NameValueHeaderValue boundary = contentType.Parameters.FirstOrDefault(p => String.Equals(p.Name, Boundary, StringComparison.OrdinalIgnoreCase));
if (boundary == null || String.IsNullOrEmpty(boundary.Value))
{
throw new HttpResponseException(request.CreateErrorResponse(HttpStatusCode.BadRequest,
SRResources.BatchRequestMissingBoundary));
}
}
internal static Uri GetODataBatchBaseUri(this HttpRequestMessage request, string oDataRouteName)
{
Contract.Assert(request != null);
if (oDataRouteName == null)
{
// Return request's base address.
return new Uri(request.RequestUri, new Uri("/", UriKind.Relative));
}
else
{
UrlHelper helper = request.GetUrlHelper() ?? new UrlHelper(request);
string baseAddress = helper.Link(oDataRouteName, new HttpRouteValueDictionary() { { ODataRouteConstants.ODataPath, String.Empty } });
if (baseAddress == null)
{
throw new InvalidOperationException(SRResources.UnableToDetermineBaseUrl);
}
return new Uri(baseAddress);
}
}
}
}

Просмотреть файл

@ -1,189 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Http.OData.Properties;
using Microsoft.Data.OData;
namespace System.Web.Http.OData.Batch
{
/// <summary>
/// Provides extension methods for the <see cref="ODataBatchReader"/> class.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public static class ODataBatchReaderExtensions
{
/// <summary>
/// Reads a ChangeSet request.
/// </summary>
/// <param name="reader">The <see cref="ODataBatchReader"/>.</param>
/// <param name="batchId">The Batch Id.</param>
/// <returns>A collection of <see cref="HttpRequestMessage"/> in the ChangeSet.</returns>
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "We need to return a collection of request messages asynchronously.")]
public static Task<IList<HttpRequestMessage>> ReadChangeSetRequestAsync(this ODataBatchReader reader, Guid batchId)
{
return reader.ReadChangeSetRequestAsync(batchId, CancellationToken.None);
}
/// <summary>
/// Reads a ChangeSet request.
/// </summary>
/// <param name="reader">The <see cref="ODataBatchReader"/>.</param>
/// <param name="batchId">The Batch Id.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>A collection of <see cref="HttpRequestMessage"/> in the ChangeSet.</returns>
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "We need to return a collection of request messages asynchronously.")]
public static async Task<IList<HttpRequestMessage>> ReadChangeSetRequestAsync(this ODataBatchReader reader, Guid batchId, CancellationToken cancellationToken)
{
if (reader == null)
{
throw Error.ArgumentNull("reader");
}
if (reader.State != ODataBatchReaderState.ChangesetStart)
{
throw Error.InvalidOperation(
SRResources.InvalidBatchReaderState,
reader.State.ToString(),
ODataBatchReaderState.ChangesetStart.ToString());
}
Guid changeSetId = Guid.NewGuid();
List<HttpRequestMessage> requests = new List<HttpRequestMessage>();
while (reader.Read() && reader.State != ODataBatchReaderState.ChangesetEnd)
{
if (reader.State == ODataBatchReaderState.Operation)
{
requests.Add(await ReadOperationInternalAsync(reader, batchId, changeSetId, cancellationToken));
}
}
return requests;
}
/// <summary>
/// Reads an Operation request.
/// </summary>
/// <param name="reader">The <see cref="ODataBatchReader"/>.</param>
/// <param name="batchId">The Batch ID.</param>
/// <param name="bufferContentStream">if set to <c>true</c> then the request content stream will be buffered.</param>
/// <returns>A <see cref="HttpRequestMessage"/> representing the operation.</returns>
public static Task<HttpRequestMessage> ReadOperationRequestAsync(this ODataBatchReader reader, Guid batchId, bool bufferContentStream)
{
return reader.ReadOperationRequestAsync(batchId, bufferContentStream, CancellationToken.None);
}
/// <summary>
/// Reads an Operation request.
/// </summary>
/// <param name="reader">The <see cref="ODataBatchReader"/>.</param>
/// <param name="batchId">The Batch ID.</param>
/// <param name="bufferContentStream">if set to <c>true</c> then the request content stream will be buffered.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>A <see cref="HttpRequestMessage"/> representing the operation.</returns>
public static Task<HttpRequestMessage> ReadOperationRequestAsync(this ODataBatchReader reader, Guid batchId, bool bufferContentStream, CancellationToken cancellationToken)
{
if (reader == null)
{
throw Error.ArgumentNull("reader");
}
if (reader.State != ODataBatchReaderState.Operation)
{
throw Error.InvalidOperation(
SRResources.InvalidBatchReaderState,
reader.State.ToString(),
ODataBatchReaderState.Operation.ToString());
}
return ReadOperationInternalAsync(reader, batchId, changeSetId: null, cancellationToken: cancellationToken, bufferContentStream: bufferContentStream);
}
/// <summary>
/// Reads an Operation request in a ChangeSet.
/// </summary>
/// <param name="reader">The <see cref="ODataBatchReader"/>.</param>
/// <param name="batchId">The Batch ID.</param>
/// <param name="changeSetId">The ChangeSet ID.</param>
/// <param name="bufferContentStream">if set to <c>true</c> then the request content stream will be buffered.</param>
/// <returns>A <see cref="HttpRequestMessage"/> representing a ChangeSet operation</returns>
public static Task<HttpRequestMessage> ReadChangeSetOperationRequestAsync(this ODataBatchReader reader, Guid batchId, Guid changeSetId, bool bufferContentStream)
{
return reader.ReadChangeSetOperationRequestAsync(batchId, changeSetId, bufferContentStream, CancellationToken.None);
}
/// <summary>
/// Reads an Operation request in a ChangeSet.
/// </summary>
/// <param name="reader">The <see cref="ODataBatchReader"/>.</param>
/// <param name="batchId">The Batch ID.</param>
/// <param name="changeSetId">The ChangeSet ID.</param>
/// <param name="bufferContentStream">if set to <c>true</c> then the request content stream will be buffered.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>A <see cref="HttpRequestMessage"/> representing a ChangeSet operation</returns>
public static Task<HttpRequestMessage> ReadChangeSetOperationRequestAsync(
this ODataBatchReader reader, Guid batchId, Guid changeSetId, bool bufferContentStream, CancellationToken cancellationToken)
{
if (reader == null)
{
throw Error.ArgumentNull("reader");
}
if (reader.State != ODataBatchReaderState.Operation)
{
throw Error.InvalidOperation(
SRResources.InvalidBatchReaderState,
reader.State.ToString(),
ODataBatchReaderState.Operation.ToString());
}
return ReadOperationInternalAsync(reader, batchId, changeSetId, cancellationToken, bufferContentStream);
}
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Caller is responsible for disposing the object.")]
private static async Task<HttpRequestMessage> ReadOperationInternalAsync(
ODataBatchReader reader, Guid batchId, Guid? changeSetId, CancellationToken cancellationToken, bool bufferContentStream = true)
{
ODataBatchOperationRequestMessage batchRequest = reader.CreateOperationRequestMessage();
HttpRequestMessage request = new HttpRequestMessage();
request.Method = new HttpMethod(batchRequest.Method);
request.RequestUri = batchRequest.Url;
if (bufferContentStream)
{
using (Stream stream = batchRequest.GetStream())
{
MemoryStream bufferedStream = new MemoryStream();
// Passing in the default buffer size of 81920 so that we can also pass in a cancellation token
await stream.CopyToAsync(bufferedStream, bufferSize: 81920, cancellationToken: cancellationToken);
bufferedStream.Position = 0;
request.Content = new StreamContent(bufferedStream);
}
}
else
{
request.Content = new LazyStreamContent(() => batchRequest.GetStream());
}
foreach (var header in batchRequest.Headers)
{
string headerName = header.Key;
string headerValue = header.Value;
if (!request.Headers.TryAddWithoutValidation(headerName, headerValue))
{
request.Content.Headers.TryAddWithoutValidation(headerName, headerValue);
}
}
request.SetODataBatchId(batchId);
if (changeSetId != null && changeSetId.HasValue)
{
request.SetODataChangeSetId(changeSetId.Value);
}
return request;
}
}
}

Просмотреть файл

@ -1,82 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
namespace System.Web.Http.OData.Batch
{
/// <summary>
/// Represents an OData batch request.
/// </summary>
public abstract class ODataBatchRequestItem : IDisposable
{
/// <summary>
/// Sends a single OData batch request.
/// </summary>
/// <param name="invoker">The invoker.</param>
/// <param name="request">The request.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <param name="contentIdToLocationMapping">The Content-ID to Location mapping.</param>
/// <returns></returns>
public static async Task<HttpResponseMessage> SendMessageAsync(HttpMessageInvoker invoker, HttpRequestMessage request, CancellationToken cancellationToken, Dictionary<string, string> contentIdToLocationMapping)
{
if (invoker == null)
{
throw Error.ArgumentNull("invoker");
}
if (request == null)
{
throw Error.ArgumentNull("request");
}
if (contentIdToLocationMapping != null)
{
string resolvedRequestUrl = ContentIdHelpers.ResolveContentId(request.RequestUri.OriginalString, contentIdToLocationMapping);
request.RequestUri = new Uri(resolvedRequestUrl);
request.SetODataContentIdMapping(contentIdToLocationMapping);
}
HttpResponseMessage response = await invoker.SendAsync(request, cancellationToken);
ContentIdHelpers.CopyContentIdToResponse(request, response);
if (contentIdToLocationMapping != null)
{
ContentIdHelpers.AddLocationHeaderToMapping(response, contentIdToLocationMapping);
}
return response;
}
/// <inheritdoc/>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Gets the resources for disposal.
/// </summary>
/// <returns>A collection of resources for disposal.</returns>
[SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "The order of execution matters. The result can be different after calling SendMessageAsync.")]
public abstract IEnumerable<IDisposable> GetResourcesForDisposal();
/// <summary>
/// Sends the request.
/// </summary>
/// <param name="invoker">The invoker.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>A <see cref="ODataBatchResponseItem"/>.</returns>
public abstract Task<ODataBatchResponseItem> SendRequestAsync(HttpMessageInvoker invoker, CancellationToken cancellationToken);
/// <summary>
/// Releases unmanaged and - optionally - managed resources.
/// </summary>
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
protected abstract void Dispose(bool disposing);
}
}

Просмотреть файл

@ -1,91 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Data.OData;
namespace System.Web.Http.OData.Batch
{
/// <summary>
/// Represents an OData batch response.
/// </summary>
public abstract class ODataBatchResponseItem : IDisposable
{
/// <summary>
/// Writes a single OData batch response.
/// </summary>
/// <param name="writer">The <see cref="ODataBatchWriter"/>.</param>
/// <param name="response">The response message.</param>
/// <returns>A task object representing writing the given batch response using the given writer.</returns>
public static Task WriteMessageAsync(ODataBatchWriter writer, HttpResponseMessage response)
{
return WriteMessageAsync(writer, response, CancellationToken.None);
}
/// <summary>
/// Writes a single OData batch response.
/// </summary>
/// <param name="writer">The <see cref="ODataBatchWriter"/>.</param>
/// <param name="response">The response message.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>A task object representing writing the given batch response using the given writer.</returns>
public static async Task WriteMessageAsync(ODataBatchWriter writer, HttpResponseMessage response,
CancellationToken cancellationToken)
{
if (writer == null)
{
throw Error.ArgumentNull("writer");
}
if (response == null)
{
throw Error.ArgumentNull("response");
}
ODataBatchOperationResponseMessage batchResponse = writer.CreateOperationResponseMessage();
batchResponse.StatusCode = (int)response.StatusCode;
foreach (KeyValuePair<string, IEnumerable<string>> header in response.Headers)
{
batchResponse.SetHeader(header.Key, String.Join(",", header.Value));
}
if (response.Content != null)
{
foreach (KeyValuePair<string, IEnumerable<string>> header in response.Content.Headers)
{
batchResponse.SetHeader(header.Key, String.Join(",", header.Value));
}
using (Stream stream = batchResponse.GetStream())
{
cancellationToken.ThrowIfCancellationRequested();
await response.Content.CopyToAsync(stream);
}
}
}
/// <inheritdoc/>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Writes the response.
/// </summary>
/// <param name="writer">The <see cref="ODataBatchWriter"/>.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
public abstract Task WriteResponseAsync(ODataBatchWriter writer, CancellationToken cancellationToken);
/// <summary>
/// Releases unmanaged and - optionally - managed resources.
/// </summary>
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
protected abstract void Dispose(bool disposing);
}
}

Просмотреть файл

@ -1,54 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.ComponentModel;
using System.IO;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Http.OData.Formatter;
using Microsoft.Data.OData;
namespace System.Web.Http.OData.Batch
{
/// <summary>
/// Provides extension methods for the <see cref="HttpContent"/> class.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public static class ODataHttpContentExtensions
{
/// <summary>
/// Gets the <see cref="ODataMessageReader"/> for the <see cref="HttpContent"/> stream.
/// </summary>
/// <param name="content">The <see cref="HttpContent"/>.</param>
/// <param name="settings">The <see cref="ODataMessageReaderSettings"/>.</param>
/// <returns>A task object that produces an <see cref="ODataMessageReader"/> when completed.</returns>
public static Task<ODataMessageReader> GetODataMessageReaderAsync(this HttpContent content,
ODataMessageReaderSettings settings)
{
return GetODataMessageReaderAsync(content, settings, CancellationToken.None);
}
/// <summary>
/// Gets the <see cref="ODataMessageReader"/> for the <see cref="HttpContent"/> stream.
/// </summary>
/// <param name="content">The <see cref="HttpContent"/>.</param>
/// <param name="settings">The <see cref="ODataMessageReaderSettings"/>.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>A task object that produces an <see cref="ODataMessageReader"/> when completed.</returns>
public static async Task<ODataMessageReader> GetODataMessageReaderAsync(this HttpContent content,
ODataMessageReaderSettings settings, CancellationToken cancellationToken)
{
if (content == null)
{
throw Error.ArgumentNull("content");
}
cancellationToken.ThrowIfCancellationRequested();
Stream contentStream = await content.ReadAsStreamAsync();
IODataRequestMessage oDataRequestMessage = new ODataMessageWrapper(contentStream, content.Headers);
ODataMessageReader oDataMessageReader = new ODataMessageReader(oDataRequestMessage, settings);
return oDataMessageReader;
}
}
}

Просмотреть файл

@ -1,69 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
namespace System.Web.Http.OData.Batch
{
/// <summary>
/// Represents an Operation request.
/// </summary>
public class OperationRequestItem : ODataBatchRequestItem
{
/// <summary>
/// Initializes a new instance of the <see cref="OperationRequestItem"/> class.
/// </summary>
/// <param name="request">The Operation request.</param>
public OperationRequestItem(HttpRequestMessage request)
{
if (request == null)
{
throw Error.ArgumentNull("request");
}
Request = request;
}
/// <summary>
/// Gets the Operation request.
/// </summary>
public HttpRequestMessage Request { get; private set; }
/// <summary>
/// Sends the Operation request.
/// </summary>
/// <param name="invoker">The invoker.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>A <see cref="OperationResponseItem"/>.</returns>
public override async Task<ODataBatchResponseItem> SendRequestAsync(HttpMessageInvoker invoker, CancellationToken cancellationToken)
{
if (invoker == null)
{
throw Error.ArgumentNull("invoker");
}
HttpResponseMessage response = await SendMessageAsync(invoker, Request, cancellationToken, null);
return new OperationResponseItem(response);
}
/// <inheritdoc/>
protected override void Dispose(bool disposing)
{
if (disposing)
{
Request.Dispose();
}
}
/// <summary>
/// Gets the resources registered for dispose on the Operation request message.
/// </summary>
/// <returns>A collection of resources registered for dispose.</returns>
public override IEnumerable<IDisposable> GetResourcesForDisposal()
{
return Request.GetResourcesForDisposal();
}
}
}

Просмотреть файл

@ -1,58 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Data.OData;
namespace System.Web.Http.OData.Batch
{
/// <summary>
/// Represents an Operation response.
/// </summary>
public class OperationResponseItem : ODataBatchResponseItem
{
/// <summary>
/// Initializes a new instance of the <see cref="OperationResponseItem"/> class.
/// </summary>
/// <param name="response">The response messages for the Operation request.</param>
public OperationResponseItem(HttpResponseMessage response)
{
if (response == null)
{
throw Error.ArgumentNull("response");
}
Response = response;
}
/// <summary>
/// Gets the response messages for the Operation.
/// </summary>
public HttpResponseMessage Response { get; private set; }
/// <summary>
/// Writes the response as an Operation.
/// </summary>
/// <param name="writer">The <see cref="ODataBatchWriter"/>.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
public override Task WriteResponseAsync(ODataBatchWriter writer, CancellationToken cancellationToken)
{
if (writer == null)
{
throw Error.ArgumentNull("writer");
}
return WriteMessageAsync(writer, Response, cancellationToken);
}
/// <inheritdoc/>
protected override void Dispose(bool disposing)
{
if (disposing)
{
Response.Dispose();
}
}
}
}

Просмотреть файл

@ -1,232 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Http.Batch;
using Microsoft.Data.OData;
namespace System.Web.Http.OData.Batch
{
/// <summary>
/// An implementation of <see cref="ODataBatchHandler"/> that doesn't buffer the request content stream.
/// </summary>
public class UnbufferedODataBatchHandler : ODataBatchHandler
{
/// <summary>
/// Initializes a new instance of the <see cref="UnbufferedODataBatchHandler"/> class.
/// </summary>
/// <param name="httpServer">The <see cref="HttpServer"/> for handling the individual batch requests.</param>
public UnbufferedODataBatchHandler(HttpServer httpServer)
: base(httpServer)
{
}
/// <inheritdoc/>
public override async Task<HttpResponseMessage> ProcessBatchAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
if (request == null)
{
throw Error.ArgumentNull("request");
}
ValidateRequest(request);
ODataMessageReaderSettings oDataReaderSettings = new ODataMessageReaderSettings
{
DisableMessageStreamDisposal = true,
MessageQuotas = MessageQuotas,
BaseUri = EnsureTrailingSlash(GetBaseUri(request))
};
ODataMessageReader reader = await request.Content.GetODataMessageReaderAsync(oDataReaderSettings, cancellationToken);
request.RegisterForDispose(reader);
ODataBatchReader batchReader = reader.CreateODataBatchReader();
List<ODataBatchResponseItem> responses = new List<ODataBatchResponseItem>();
Guid batchId = Guid.NewGuid();
List<IDisposable> resourcesToDispose = new List<IDisposable>();
try
{
while (batchReader.Read())
{
ODataBatchResponseItem responseItem = null;
if (batchReader.State == ODataBatchReaderState.ChangesetStart)
{
responseItem = await ExecuteChangeSetAsync(batchReader, batchId, request, cancellationToken);
}
else if (batchReader.State == ODataBatchReaderState.Operation)
{
responseItem = await ExecuteOperationAsync(batchReader, batchId, request, cancellationToken);
}
if (responseItem != null)
{
responses.Add(responseItem);
}
}
}
catch
{
foreach (ODataBatchResponseItem response in responses)
{
if (response != null)
{
response.Dispose();
}
}
throw;
}
return await CreateResponseMessageAsync(responses, request, cancellationToken);
}
/// <summary>
/// Executes the operation.
/// </summary>
/// <param name="batchReader">The batch reader.</param>
/// <param name="batchId">The batch id.</param>
/// <param name="originalRequest">The original request containing all the batch requests.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>The response for the operation.</returns>
public virtual async Task<ODataBatchResponseItem> ExecuteOperationAsync(ODataBatchReader batchReader, Guid batchId, HttpRequestMessage originalRequest, CancellationToken cancellationToken)
{
if (batchReader == null)
{
throw Error.ArgumentNull("batchReader");
}
if (originalRequest == null)
{
throw Error.ArgumentNull("originalRequest");
}
cancellationToken.ThrowIfCancellationRequested();
HttpRequestMessage operationRequest = await batchReader.ReadOperationRequestAsync(batchId, bufferContentStream: false);
operationRequest.CopyBatchRequestProperties(originalRequest);
OperationRequestItem operation = new OperationRequestItem(operationRequest);
try
{
ODataBatchResponseItem response = await operation.SendRequestAsync(Invoker, cancellationToken);
return response;
}
finally
{
originalRequest.RegisterForDispose(operation.GetResourcesForDisposal());
originalRequest.RegisterForDispose(operation);
}
}
/// <summary>
/// Executes the ChangeSet.
/// </summary>
/// <param name="batchReader">The batch reader.</param>
/// <param name="batchId">The batch id.</param>
/// <param name="originalRequest">The original request containing all the batch requests.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>The response for the ChangeSet.</returns>
public virtual async Task<ODataBatchResponseItem> ExecuteChangeSetAsync(ODataBatchReader batchReader, Guid batchId, HttpRequestMessage originalRequest, CancellationToken cancellationToken)
{
if (batchReader == null)
{
throw Error.ArgumentNull("batchReader");
}
if (originalRequest == null)
{
throw Error.ArgumentNull("originalRequest");
}
Guid changeSetId = Guid.NewGuid();
List<HttpResponseMessage> changeSetResponse = new List<HttpResponseMessage>();
Dictionary<string, string> contentIdToLocationMapping = new Dictionary<string, string>();
try
{
while (batchReader.Read() && batchReader.State != ODataBatchReaderState.ChangesetEnd)
{
if (batchReader.State == ODataBatchReaderState.Operation)
{
HttpRequestMessage changeSetOperationRequest = await batchReader.ReadChangeSetOperationRequestAsync(batchId, changeSetId, bufferContentStream: false);
changeSetOperationRequest.CopyBatchRequestProperties(originalRequest);
try
{
HttpResponseMessage response = await ODataBatchRequestItem.SendMessageAsync(Invoker, changeSetOperationRequest, cancellationToken, contentIdToLocationMapping);
if (response.IsSuccessStatusCode)
{
changeSetResponse.Add(response);
}
else
{
ChangeSetRequestItem.DisposeResponses(changeSetResponse);
changeSetResponse.Clear();
changeSetResponse.Add(response);
return new ChangeSetResponseItem(changeSetResponse);
}
}
finally
{
originalRequest.RegisterForDispose(changeSetOperationRequest.GetResourcesForDisposal());
originalRequest.RegisterForDispose(changeSetOperationRequest);
}
}
}
}
catch
{
ChangeSetRequestItem.DisposeResponses(changeSetResponse);
throw;
}
return new ChangeSetResponseItem(changeSetResponse);
}
/// <summary>
/// Creates the batch response message.
/// </summary>
/// <param name="responses">The responses for the batch requests.</param>
/// <param name="request">The original request containing all the batch requests.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>The batch response message.</returns>
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Caller is responsible for disposing the object.")]
public virtual Task<HttpResponseMessage> CreateResponseMessageAsync(
IEnumerable<ODataBatchResponseItem> responses, HttpRequestMessage request, CancellationToken cancellationToken)
{
if (request == null)
{
throw Error.ArgumentNull("request");
}
return request.CreateODataBatchResponseAsync(responses, MessageQuotas);
}
/// <summary>
/// Validates the incoming request that contains the batch request messages.
/// </summary>
/// <param name="request">The request containing the batch request messages.</param>
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Caller is responsible for disposing the object.")]
public virtual void ValidateRequest(HttpRequestMessage request)
{
if (request == null)
{
throw Error.ArgumentNull("request");
}
request.ValidateODataBatchRequest();
}
/// <summary>
/// Gets the base URI for the batched requests.
/// </summary>
/// <param name="request">The original request containing all the batch requests.</param>
/// <returns>The base URI.</returns>
public virtual Uri GetBaseUri(HttpRequestMessage request)
{
if (request == null)
{
throw Error.ArgumentNull("request");
}
return request.GetODataBatchBaseUri(ODataRouteName);
}
}
}

Просмотреть файл

@ -1,303 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Web.Http.OData.Properties;
using Microsoft.Data.Edm;
namespace System.Web.Http.OData.Builder
{
/// <summary>
/// ActionConfiguration represents an OData action that you wish to expose via your service
/// <remarks>
/// ActionConfigurations are exposed via $metadata as a <FunctionImport/> element.
/// </remarks>
/// </summary>
public class ActionConfiguration : ProcedureConfiguration
{
private List<ParameterConfiguration> _parameters = new List<ParameterConfiguration>();
private BindingParameterConfiguration _bindingParameter;
private Func<EntityInstanceContext, Uri> _actionLinkFactory;
private bool _followsConventions;
/// <summary>
/// Create a new ActionConfiguration
/// </summary>
/// <param name="builder">The ODataModelBuilder to which this ActionConfiguration should be added.</param>
/// <param name="name">The name of this ActionConfiguration.</param>
internal ActionConfiguration(ODataModelBuilder builder, string name)
{
Name = name;
ModelBuilder = builder;
}
/// <summary>
/// Get the bindingParameter.
/// <remarks>Null means the Action has no bindingParameter.</remarks>
/// </summary>
public BindingParameterConfiguration BindingParameter
{
get { return _bindingParameter; }
}
/// <inheritdoc />
public override IEnumerable<ParameterConfiguration> Parameters
{
get
{
if (_bindingParameter != null)
{
yield return _bindingParameter;
}
foreach (ParameterConfiguration parameter in _parameters)
{
yield return parameter;
}
}
}
/// <inheritdoc />
public override ProcedureKind Kind
{
get { return ProcedureKind.Action; }
}
/// <inheritdoc />
public override bool IsBindable
{
get
{
return _bindingParameter != null;
}
}
/// <summary>
/// Whether this action can always be bound.
/// <example>
/// For example imagine an Watch action that can be bound to a Movie, it might not always be possible to Watch a movie,
/// in which case IsAlwaysBindable would return false.
/// </example>
/// </summary>
public override bool IsAlwaysBindable
{
get
{
if (IsBindable)
{
return _bindingParameter.AlwaysBindable;
}
return false;
}
}
/// <summary>
/// Gets a value indicating whether links provided by <see cref="GetActionLink"/> follow OData conventions.
/// </summary>
public bool FollowsConventions
{
get
{
return _followsConventions;
}
}
/// <summary>
/// Sets the return type to a single EntityType instance.
/// </summary>
/// <typeparam name="TEntityType">The type that is an EntityType</typeparam>
/// <param name="entitySetName">The name of the entity set which contains the returned entity.</param>
[SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "In keeping with rest of API")]
public ActionConfiguration ReturnsFromEntitySet<TEntityType>(string entitySetName) where TEntityType : class
{
ModelBuilder.EntitySet<TEntityType>(entitySetName);
EntitySet = ModelBuilder.EntitySets.Single(s => s.Name == entitySetName);
ReturnType = ModelBuilder.GetTypeConfigurationOrNull(typeof(TEntityType));
return this;
}
/// <summary>
/// Sets the return type to a single EntityType instance.
/// </summary>
/// <typeparam name="TEntityType">The type that is an EntityType</typeparam>
/// <param name="entitySetConfiguration">The entity set which contains the returned entity.</param>
[SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "In keeping with rest of API")]
public ActionConfiguration ReturnsFromEntitySet<TEntityType>(EntitySetConfiguration<TEntityType> entitySetConfiguration) where TEntityType : class
{
if (entitySetConfiguration == null)
{
throw Error.ArgumentNull("entitySetConfiguration");
}
EntitySet = entitySetConfiguration.EntitySet;
ReturnType = ModelBuilder.GetTypeConfigurationOrNull(typeof(TEntityType));
return this;
}
/// <summary>
/// Sets the return type to a collection of entities.
/// </summary>
/// <typeparam name="TElementEntityType">The entity type.</typeparam>
/// <param name="entitySetName">The name of the entity set which contains the returned entities.</param>
[SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "In keeping with rest of API")]
public ActionConfiguration ReturnsCollectionFromEntitySet<TElementEntityType>(string entitySetName) where TElementEntityType : class
{
Type clrCollectionType = typeof(IEnumerable<TElementEntityType>);
ModelBuilder.EntitySet<TElementEntityType>(entitySetName);
EntitySet = ModelBuilder.EntitySets.Single(s => s.Name == entitySetName);
IEdmTypeConfiguration elementType = ModelBuilder.GetTypeConfigurationOrNull(typeof(TElementEntityType));
ReturnType = new CollectionTypeConfiguration(elementType, clrCollectionType);
return this;
}
/// <summary>
/// Sets the return type to a collection of entities.
/// </summary>
/// <typeparam name="TElementEntityType">The entity type.</typeparam>
/// <param name="entitySetConfiguration">The entity set which contains the returned entities.</param>
[SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "In keeping with rest of API")]
public ActionConfiguration ReturnsCollectionFromEntitySet<TElementEntityType>(
EntitySetConfiguration<TElementEntityType> entitySetConfiguration) where TElementEntityType : class
{
if (entitySetConfiguration == null)
{
throw Error.ArgumentNull("entitySetConfiguration");
}
Type clrCollectionType = typeof(IEnumerable<TElementEntityType>);
EntitySet = entitySetConfiguration.EntitySet;
IEdmTypeConfiguration elementType = ModelBuilder.GetTypeConfigurationOrNull(typeof(TElementEntityType));
ReturnType = new CollectionTypeConfiguration(elementType, clrCollectionType);
return this;
}
/// <summary>
/// Established the return type of the Action.
/// <remarks>Used when the return type is a single Primitive or ComplexType.</remarks>
/// </summary>
[SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "In keeping with rest of API")]
public ActionConfiguration Returns<TReturnType>()
{
Type returnType = typeof(TReturnType);
IEdmTypeConfiguration configuration = ModelBuilder.GetTypeConfigurationOrNull(returnType);
if (configuration is EntityTypeConfiguration)
{
throw Error.InvalidOperation(SRResources.ReturnEntityWithoutEntitySet, configuration.FullName);
}
if (configuration == null)
{
ModelBuilder.AddComplexType(returnType);
configuration = ModelBuilder.GetTypeConfigurationOrNull(typeof(TReturnType));
}
ReturnType = configuration;
return this;
}
/// <summary>
/// Establishes the return type of the Action
/// <remarks>Used when the return type is a collection of either Primitive or ComplexTypes.</remarks>
/// </summary>
[SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "In keeping with rest of API")]
public ActionConfiguration ReturnsCollection<TReturnElementType>()
{
// TODO: I don't like this temporary solution that says the CLR type of the collection is IEnumerable<T>.
// It basically has no meaning. That said the CLR type is meaningful for IEdmTypeConfiguration
// because I still think it is useful for IEdmPrimitiveTypes too.
// You can imagine the override of this that takes a delegate using the correct CLR type for the return type.
Type clrCollectionType = typeof(IEnumerable<TReturnElementType>);
Type clrElementType = typeof(TReturnElementType);
IEdmTypeConfiguration edmElementType = ModelBuilder.GetTypeConfigurationOrNull(clrElementType);
if (edmElementType is EntityTypeConfiguration)
{
throw Error.InvalidOperation(SRResources.ReturnEntityCollectionWithoutEntitySet, edmElementType.FullName);
}
if (edmElementType == null)
{
ModelBuilder.AddComplexType(clrElementType);
edmElementType = ModelBuilder.GetTypeConfigurationOrNull(clrElementType);
}
ReturnType = new CollectionTypeConfiguration(edmElementType, clrCollectionType);
return this;
}
/// <summary>
/// Specifies the bindingParameter name, type and whether it is alwaysBindable, use only if the Action "isBindable".
/// </summary>
public ActionConfiguration SetBindingParameter(string name, IEdmTypeConfiguration bindingParameterType, bool alwaysBindable)
{
_bindingParameter = new BindingParameterConfiguration(name, bindingParameterType, alwaysBindable);
return this;
}
/// <summary>
/// Adds a new non-binding parameter.
/// </summary>
public ParameterConfiguration AddParameter(string name, IEdmTypeConfiguration parameterType)
{
ParameterConfiguration parameter = new NonbindingParameterConfiguration(name, parameterType);
_parameters.Add(parameter);
return parameter;
}
/// <summary>
/// Adds a new non-binding parameter
/// </summary>
[SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "In keeping with rest of API")]
public ParameterConfiguration Parameter<TParameter>(string name)
{
Type type = typeof(TParameter);
IEdmTypeConfiguration parameterType = ModelBuilder.GetTypeConfigurationOrNull(type);
if (parameterType == null)
{
ModelBuilder.AddComplexType(type);
parameterType = ModelBuilder.GetTypeConfigurationOrNull(type);
}
return AddParameter(name, parameterType);
}
/// <summary>
/// Adds a new non-binding collection parameter
/// </summary>
[SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "In keeping with rest of API")]
public ParameterConfiguration CollectionParameter<TElementType>(string name)
{
Type elementType = typeof(TElementType);
IEdmTypeConfiguration elementTypeConfiguration = ModelBuilder.GetTypeConfigurationOrNull(elementType);
if (elementTypeConfiguration == null)
{
ModelBuilder.AddComplexType(elementType);
elementTypeConfiguration = ModelBuilder.GetTypeConfigurationOrNull(elementType);
}
CollectionTypeConfiguration parameterType = new CollectionTypeConfiguration(elementTypeConfiguration, typeof(IEnumerable<>).MakeGenericType(elementType));
return AddParameter(name, parameterType);
}
/// <summary>
/// Register a factory that creates actions links.
/// </summary>
public ActionConfiguration HasActionLink(Func<EntityInstanceContext, Uri> actionLinkFactory, bool followsConventions)
{
if (!IsBindable || BindingParameter.TypeConfiguration.Kind != EdmTypeKind.Entity)
{
throw Error.InvalidOperation(SRResources.HasActionLinkRequiresBindToEntity, Name);
}
_actionLinkFactory = actionLinkFactory;
_followsConventions = followsConventions;
return this;
}
/// <summary>
/// Retrieves the currently registered action link factory.
/// </summary>
[SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "Consistent with EF Has/Get pattern")]
public Func<EntityInstanceContext, Uri> GetActionLink()
{
return _actionLinkFactory;
}
}
}

Просмотреть файл

@ -1,76 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
namespace System.Web.Http.OData.Builder
{
/// <summary>
/// ActionLinkBuilder can be used to annotate an Action.
/// This is how formatters create links to invoke bound actions.
/// </summary>
public class ActionLinkBuilder
{
private Func<EntityInstanceContext, Uri> _actionLinkFactory;
/// <summary>
/// Create a new ActionLinkBuilder based on an actionLinkFactory.
/// <remarks>
/// If the action is not available the actionLinkFactory delegate should return NULL.
/// </remarks>
/// </summary>
/// <param name="actionLinkFactory">The actionLinkFactory this ActionLinkBuilder should use when building links.</param>
/// <param name="followsConventions">
/// A value indicating whether the action link factory generates links that follow OData conventions.
/// </param>
public ActionLinkBuilder(Func<EntityInstanceContext, Uri> actionLinkFactory, bool followsConventions)
{
if (actionLinkFactory == null)
{
throw Error.ArgumentNull("actionLinkFactory");
}
_actionLinkFactory = actionLinkFactory;
FollowsConventions = followsConventions;
}
/// <summary>
/// Gets a boolean indicating whether the link factory follows OData conventions or not.
/// </summary>
public bool FollowsConventions { get; private set; }
/// <summary>
/// Builds the action link for the given entity.
/// </summary>
/// <param name="context">An instance context wrapping the entity instance.</param>
/// <returns>The generated action link.</returns>
public virtual Uri BuildActionLink(EntityInstanceContext context)
{
return _actionLinkFactory(context);
}
/// <summary>
/// Creates an action link factory that builds an action link, but only when appropriate based on the expensiveAvailabilityCheck, and whether expensive checks should be made,
/// which is deduced by looking at the EntityInstanceContext.SkipExpensiveActionAvailabilityChecks property.
/// </summary>
/// <param name="baseFactory">The action link factory that actually builds links if all checks pass.</param>
/// <param name="expensiveAvailabilityCheck">The availability check function that is expensive but when called returns whether the action is available.</param>
/// <returns>The new action link factory.</returns>
public static Func<EntityInstanceContext, Uri> CreateActionLinkFactory(Func<EntityInstanceContext, Uri> baseFactory, Func<EntityInstanceContext, bool> expensiveAvailabilityCheck)
{
return (EntityInstanceContext ctx) =>
{
if (ctx.SkipExpensiveAvailabilityChecks)
{
// OData says that if it is too expensive to check availability you should advertize actions
return baseFactory(ctx);
}
else if (expensiveAvailabilityCheck(ctx))
{
return baseFactory(ctx);
}
else
{
return null;
}
};
}
}
}

Просмотреть файл

@ -1,69 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Linq;
using Microsoft.Data.Edm;
namespace System.Web.Http.OData.Builder
{
/// <summary>
/// This class builds a cache that allows for efficient look up of bindable procedure by EntityType.
/// </summary>
internal class BindableProcedureFinder
{
private Dictionary<IEdmEntityType, List<IEdmFunctionImport>> _map = new Dictionary<IEdmEntityType, List<IEdmFunctionImport>>();
/// <summary>
/// Constructs a concurrent cache for looking up bindable procedures for any EntityType in the provided model.
/// </summary>
public BindableProcedureFinder(IEdmModel model)
{
var query =
from ec in model.EntityContainers()
from fi in ec.FunctionImports()
where fi.IsBindable && fi.Parameters.First().Type.TypeKind() == EdmTypeKind.Entity
group fi by fi.Parameters.First().Type.Definition into fgroup
select new { EntityType = fgroup.Key as IEdmEntityType, BindableFunctions = fgroup.ToList() };
foreach (var match in query)
{
_map[match.EntityType] = match.BindableFunctions;
}
}
/// <summary>
/// Finds procedures that can be invoked on the given entity type. This would include all the procedures that are bound
/// to the given type and its base types.
/// </summary>
/// <param name="entityType">The EDM entity type.</param>
/// <returns>A collection of procedures bound to the entity type.</returns>
public virtual IEnumerable<IEdmFunctionImport> FindProcedures(IEdmEntityType entityType)
{
return GetTypeHierarchy(entityType).SelectMany(e => FindDeclaredProcedures(e));
}
private IEnumerable<IEdmEntityType> GetTypeHierarchy(IEdmEntityType entityType)
{
IEdmEntityType current = entityType;
while (current != null)
{
yield return current;
current = current.BaseEntityType();
}
}
private IEnumerable<IEdmFunctionImport> FindDeclaredProcedures(IEdmEntityType entityType)
{
List<IEdmFunctionImport> results = null;
if (_map.TryGetValue(entityType, out results))
{
return results;
}
else
{
return Enumerable.Empty<IEdmFunctionImport>();
}
}
}
}

Просмотреть файл

@ -1,62 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Web.Http.OData.Properties;
using Microsoft.Data.Edm;
namespace System.Web.Http.OData.Builder
{
/// <summary>
/// Represents a BindingParameter.
/// <remarks>
/// Actions/Functions can have at most one BindingParameter.
/// This parameter has similar semantics to the 'this' keyword in C# extensions methods.
/// <example>
/// For example given a url that identifies a Movie, if there is an action that has a bindingParameter that is a Movie,
/// you can bind the Action to the url.
///
/// i.e. if ~/Movies(1) identifies a Movie, and there exists a Checkout action that has a Movie BindingParameter,
/// you can invoke that Action at this url ~/Movies(1)/Checkout
/// </example>
/// The BindingParameter type must either be an EntityType or a Collection of EntityTypes.
/// </remarks>
/// </summary>
public class BindingParameterConfiguration : ParameterConfiguration
{
/// <summary>
/// The default parameter name for an action's binding parameter.
/// </summary>
public const string DefaultBindingParameterName = "bindingParameter";
private bool _alwaysBindable;
/// <summary>
/// Create a BindingParameterConfiguration
/// </summary>
/// <param name="name">The name of the Binding Parameter</param>
/// <param name="parameterType">The type of the Binding Parameter</param>
/// <param name="alwaysBindable">Whether the action can always be bound to instances of the binding parameter.</param>
public BindingParameterConfiguration(string name, IEdmTypeConfiguration parameterType, bool alwaysBindable)
: base(name, parameterType)
{
EdmTypeKind kind = parameterType.Kind;
if (kind == EdmTypeKind.Collection)
{
kind = (parameterType as CollectionTypeConfiguration).ElementType.Kind;
}
if (kind != EdmTypeKind.Entity)
{
throw Error.Argument("parameterType", SRResources.InvalidBindingParameterType, parameterType.FullName);
}
_alwaysBindable = alwaysBindable;
}
/// <summary>
/// Indicates whether the BindingParameter is always bindable or not.
/// For example some actions are always available some are only available at certain times or in certain states.
/// </summary>
public bool AlwaysBindable
{
get { return _alwaysBindable; }
}
}
}

Просмотреть файл

@ -1,65 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Reflection;
using System.Web.Http.OData.Properties;
namespace System.Web.Http.OData.Builder
{
/// <summary>
/// CollectionPropertyConfiguration represents a CollectionProperty on either an EntityType or ComplexType.
/// </summary>
public class CollectionPropertyConfiguration : StructuralPropertyConfiguration
{
private Type _elementType;
/// <summary>
/// Constructs a CollectionPropertyConfiguration using the <paramref name="property">property</paramref> provided.
/// </summary>
public CollectionPropertyConfiguration(PropertyInfo property, StructuralTypeConfiguration declaringType)
: base(property, declaringType)
{
if (!property.PropertyType.IsCollection(out _elementType))
{
throw Error.Argument("property", SRResources.CollectionPropertiesMustReturnIEnumerable, property.Name, property.DeclaringType.FullName);
}
}
/// <inheritdoc />
public override PropertyKind Kind
{
get { return PropertyKind.Collection; }
}
/// <inheritdoc />
public override Type RelatedClrType
{
get { return ElementType; }
}
/// <summary>
/// Returns the type of Elements in the Collection
/// </summary>
public Type ElementType
{
get { return _elementType; }
}
/// <summary>
/// Sets the CollectionProperty to optional (i.e. nullable).
/// </summary>
public CollectionPropertyConfiguration IsOptional()
{
OptionalProperty = true;
return this;
}
/// <summary>
/// Sets the CollectionProperty to required (i.e. non-nullable).
/// </summary>
public CollectionPropertyConfiguration IsRequired()
{
OptionalProperty = false;
return this;
}
}
}

Просмотреть файл

@ -1,99 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Globalization;
using Microsoft.Data.Edm;
namespace System.Web.Http.OData.Builder
{
/// <summary>
/// Represents a Collection of some named type.
/// <example>
/// Collection(Namespace.Customer) or Collection(Namespace.Address).
/// </example>
/// </summary>
public class CollectionTypeConfiguration : IEdmTypeConfiguration
{
private IEdmTypeConfiguration _elementType;
private Type _clrType;
/// <summary>
/// Constructs a collection that contains elements of the specified ElementType
/// and that is represented in CLR using the specified clrType.
/// </summary>
/// <param name="elementType">The EdmTypeConfiguration of the elements in the collection</param>
/// <param name="clrType">The type of this collection when manifested in CLR.</param>
public CollectionTypeConfiguration(IEdmTypeConfiguration elementType, Type clrType)
{
if (elementType == null)
{
throw Error.ArgumentNull("elementType");
}
if (clrType == null)
{
throw Error.ArgumentNull("clrType");
}
_elementType = elementType;
_clrType = clrType;
}
/// <summary>
/// Gets the <see cref="IEdmTypeConfiguration" /> of elements in this collection.
/// </summary>
public IEdmTypeConfiguration ElementType
{
get { return _elementType; }
}
/// <summary>
/// Gets the CLR type associated with this collection type.
/// </summary>
public Type ClrType
{
get { return _clrType; }
}
/// <summary>
/// Gets the fullname (including namespace) of this collection type.
/// </summary>
public string FullName
{
get
{
// There is no need to include the Namespace when it comes from the Edm Namespace.
return Name;
}
}
/// <summary>
/// Gets the namespace of this collection type.
/// </summary>
public string Namespace
{
get { return "Edm"; }
}
/// <summary>
/// Gets the name of this collection type.
/// </summary>
public string Name
{
get { return String.Format(CultureInfo.InvariantCulture, "Collection({0})", ElementType.FullName); }
}
/// <summary>
/// Gets the kind of the <see cref="IEdmType" />. In this case, it is <see cref="EdmTypeKind.Collection" />.
/// </summary>
public EdmTypeKind Kind
{
get { return EdmTypeKind.Collection; }
}
/// <summary>
/// Gets the <see cref="ODataModelBuilder"/> used to create this configuration.
/// </summary>
public ODataModelBuilder ModelBuilder
{
get { return _elementType.ModelBuilder; }
}
}
}

Просмотреть файл

@ -1,54 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Reflection;
namespace System.Web.Http.OData.Builder
{
/// <summary>
/// Represents the configuration for a complex property of a structural type (an entity type or a complex type).
/// </summary>
public class ComplexPropertyConfiguration : StructuralPropertyConfiguration
{
/// <summary>
/// Instantiates a new instance of the <see cref="ComplexPropertyConfiguration"/> class.
/// </summary>
/// <param name="property">The property of the configuration.</param>
/// <param name="declaringType">The declaring type of the property.</param>
public ComplexPropertyConfiguration(PropertyInfo property, StructuralTypeConfiguration declaringType)
: base(property, declaringType)
{
}
/// <inheritdoc />
public override PropertyKind Kind
{
get { return PropertyKind.Complex; }
}
/// <inheritdoc />
public override Type RelatedClrType
{
get { return PropertyInfo.PropertyType; }
}
/// <summary>
/// Marks the current complex property as optional.
/// </summary>
/// <returns>Returns itself so that multiple calls can be chained.</returns>
public ComplexPropertyConfiguration IsOptional()
{
OptionalProperty = true;
return this;
}
/// <summary>
/// Marks the current complex property as required.
/// </summary>
/// <returns>Returns itself so that multiple calls can be chained.</returns>
public ComplexPropertyConfiguration IsRequired()
{
OptionalProperty = false;
return this;
}
}
}

Просмотреть файл

@ -1,39 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using Microsoft.Data.Edm;
namespace System.Web.Http.OData.Builder
{
/// <summary>
/// Allows configuration to be performed for a complex type in a model. A ComplexTypeConfiguration can be obtained by using the method <see cref="ODataModelBuilder.ComplexType"/>.
/// </summary>
public class ComplexTypeConfiguration : StructuralTypeConfiguration
{
/// <summary>
/// Initializes a new instance of the <see cref="ComplexTypeConfiguration"/> class.
/// </summary>
/// <remarks>The default constructor is intended for use by unit testing only.</remarks>
public ComplexTypeConfiguration()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ComplexTypeConfiguration"/> class.
/// <param name="modelBuilder">The <see cref="ODataModelBuilder"/> being used.</param>
/// <param name="clrType">The backing CLR type for this entity type.</param>
/// </summary>
public ComplexTypeConfiguration(ODataModelBuilder modelBuilder, Type clrType)
: base(modelBuilder, clrType)
{
}
/// <inheritdoc />
public override EdmTypeKind Kind
{
get
{
return EdmTypeKind.Complex;
}
}
}
}

Просмотреть файл

@ -1,22 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using Microsoft.Data.Edm;
namespace System.Web.Http.OData.Builder
{
/// <summary>
/// Represents an <see cref="IEdmComplexType"/> that can be built using <see cref="ODataModelBuilder"/>.
/// </summary>
public class ComplexTypeConfiguration<TComplexType> : StructuralTypeConfiguration<TComplexType> where TComplexType : class
{
internal ComplexTypeConfiguration(ComplexTypeConfiguration configuration)
: base(configuration)
{
}
internal ComplexTypeConfiguration(ODataModelBuilder modelBuilder)
: base(new ComplexTypeConfiguration(modelBuilder, typeof(TComplexType)))
{
}
}
}

Просмотреть файл

@ -1,19 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
namespace System.Web.Http.OData.Builder.Conventions
{
/// <summary>
/// <see cref="EntityTypeConvention"/> to figure out if an entity is abstract or not.
/// <remarks>This convention configures all entity types backed by an abstract CLR type as abstract entities.</remarks>
/// </summary>
internal class AbstractEntityTypeDiscoveryConvention : EntityTypeConvention
{
public override void Apply(EntityTypeConfiguration entity, ODataModelBuilder model)
{
if (entity.IsAbstract == null)
{
entity.IsAbstract = entity.ClrType.IsAbstract;
}
}
}
}

Просмотреть файл

@ -1,26 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Web.Http.OData.Routing;
using Microsoft.Data.Edm;
namespace System.Web.Http.OData.Builder.Conventions
{
/// <summary>
/// The ActionLinkGenerationConvention calls action.HasActionLink(..) for all actions that bind to a single entity if they have not previously been configured.
/// The convention uses the <see cref="ODataRouteConstants"/>.InvokeBoundAction route to build a link that invokes the action.
/// </summary>
internal class ActionLinkGenerationConvention : IProcedureConvention
{
public void Apply(ProcedureConfiguration configuration, ODataModelBuilder model)
{
ActionConfiguration action = configuration as ActionConfiguration;
// You only need to create links for bindable actions that bind to a single entity.
if (action != null && action.IsBindable && action.BindingParameter.TypeConfiguration.Kind == EdmTypeKind.Entity && action.GetActionLink() == null)
{
string bindingParamterType = action.BindingParameter.TypeConfiguration.FullName;
action.HasActionLink(entityContext => entityContext.GenerateActionLink(bindingParamterType, action.Name), followsConventions: true);
}
}
}
}

Просмотреть файл

@ -1,75 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Linq;
using System.Web.Http.OData.Properties;
namespace System.Web.Http.OData.Builder.Conventions
{
/// <summary>
/// <see cref="IEntitySetConvention"/> to configure the EDM association sets for the given entity set.
/// <remarks>This convention adds an association set for each EDM navigation property defined in this type, its base types and all its derived types.
/// The target entity set chosen is the default entity set for the navigation property's target entity type.
/// The default entity set for an entity type is the entity set that contains entries of that entity type. If more than one entity sets match, the default entity set is none.
/// If no entity sets match the default entity set is the default entity set of the base type.</remarks>
/// </summary>
internal class AssociationSetDiscoveryConvention : IEntitySetConvention
{
public void Apply(EntitySetConfiguration configuration, ODataModelBuilder model)
{
foreach (EntityTypeConfiguration entity in model.ThisAndBaseAndDerivedTypes(configuration.EntityType))
{
foreach (NavigationPropertyConfiguration navigationProperty in entity.NavigationProperties)
{
EntitySetConfiguration targetEntitySet = GetTargetEntitySet(navigationProperty, model);
if (targetEntitySet != null)
{
configuration.AddBinding(navigationProperty, targetEntitySet);
}
}
}
}
// Get the default target entity set for this navigation property.
internal static EntitySetConfiguration GetTargetEntitySet(NavigationPropertyConfiguration navigationProperty, ODataModelBuilder model)
{
EntityTypeConfiguration targetEntityType =
model
.StructuralTypes
.OfType<EntityTypeConfiguration>()
.Where(e => e.ClrType == navigationProperty.RelatedClrType).SingleOrDefault();
if (targetEntityType == null)
{
throw Error.InvalidOperation(SRResources.TargetEntityTypeMissing, navigationProperty.Name, navigationProperty.PropertyInfo.ReflectedType.FullName);
}
return GetDefaultEntitySet(targetEntityType, model);
}
private static EntitySetConfiguration GetDefaultEntitySet(EntityTypeConfiguration targetEntityType, ODataModelBuilder model)
{
if (targetEntityType == null)
{
return null;
}
IEnumerable<EntitySetConfiguration> matchingEntitySets = model.EntitySets.Where(e => e.EntityType == targetEntityType);
if (matchingEntitySets.Count() > 1)
{
// no default entity set if more than one entity set match.
return null;
}
else if (matchingEntitySets.Count() == 1)
{
return matchingEntitySets.Single();
}
else
{
// default entity set is the same as the default entity set for the base type.
return GetDefaultEntitySet(targetEntityType.BaseType, model);
}
}
}
}

Просмотреть файл

@ -1,72 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Linq;
using System.Reflection;
using System.Web.Http.OData.Properties;
namespace System.Web.Http.OData.Builder.Conventions.Attributes
{
/// <summary>
/// Base class for all attribute based conventions.
/// </summary>
internal abstract class AttributeConvention : IConvention
{
/// <summary>
/// Initializes a new instance of the <see cref="AttributeConvention"/> class.
/// </summary>
/// <param name="attributeFilter">A function to test whether this convention applies to an attribute or not.</param>
/// <param name="allowMultiple"><see langword="true"/> if the convention allows multiple attribues; otherwise, <see langword="false"/>.</param>
protected AttributeConvention(Func<Attribute, bool> attributeFilter, bool allowMultiple)
{
if (attributeFilter == null)
{
throw Error.ArgumentNull("attributeFilter");
}
AllowMultiple = allowMultiple;
AttributeFilter = attributeFilter;
}
/// <summary>
/// Gets the filter that finds the attributes that this convention applies to.
/// </summary>
public Func<Attribute, bool> AttributeFilter { get; private set; }
/// <summary>
/// Gets whether this convention allows multiple instances of the attribute.
/// </summary>
public bool AllowMultiple { get; private set; }
/// <summary>
/// Returns the attributes on <paramref name="member"/> that this convention applies to.
/// </summary>
/// <param name="member"></param>
/// <returns></returns>
public Attribute[] GetAttributes(MemberInfo member)
{
if (member == null)
{
throw Error.ArgumentNull("member");
}
Attribute[] attributes =
member
.GetCustomAttributes(inherit: true)
.OfType<Attribute>()
.Where(AttributeFilter)
.ToArray();
if (!AllowMultiple && attributes.Length > 1)
{
throw Error.Argument(
"member",
SRResources.MultipleAttributesFound,
member.Name,
member.ReflectedType.Name,
attributes.First().GetType().Name);
}
return attributes;
}
}
}

Просмотреть файл

@ -1,77 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
namespace System.Web.Http.OData.Builder.Conventions.Attributes
{
/// <summary>
/// Base class for all attribute based <see cref="IEdmPropertyConvention"/>'s.
/// </summary>
/// <typeparam name="TPropertyConfiguration">The type of the property this configuration applies to.</typeparam>
internal abstract class AttributeEdmPropertyConvention<TPropertyConfiguration> : AttributeConvention, IEdmPropertyConvention<TPropertyConfiguration>
where TPropertyConfiguration : PropertyConfiguration
{
/// <summary>
/// Initializes a new instance of the <see cref="AttributeEdmPropertyConvention{TPropertyConfiguration}"/> class.
/// </summary>
/// <param name="attributeFilter">A function to test whether this convention applies to an attribute or not.</param>
/// <param name="allowMultiple"><see langword="true"/> if the convention allows multiple attributes; otherwise, <see langword="false"/>.</param>
protected AttributeEdmPropertyConvention(Func<Attribute, bool> attributeFilter, bool allowMultiple)
: base(attributeFilter, allowMultiple)
{
}
/// <summary>
/// Applies the convention.
/// </summary>
/// <param name="edmProperty">The property being configured.</param>
/// <param name="structuralTypeConfiguration">The type being configured.</param>
public void Apply(PropertyConfiguration edmProperty, StructuralTypeConfiguration structuralTypeConfiguration)
{
if (edmProperty == null)
{
throw Error.ArgumentNull("edmProperty");
}
if (structuralTypeConfiguration == null)
{
throw Error.ArgumentNull("structuralTypeConfiguration");
}
TPropertyConfiguration property = edmProperty as TPropertyConfiguration;
if (property != null)
{
Apply(property, structuralTypeConfiguration);
}
}
/// <summary>
/// Applies the convention.
/// </summary>
/// <param name="edmProperty">The property being configured.</param>
/// <param name="structuralTypeConfiguration">The type being configured.</param>
public void Apply(TPropertyConfiguration edmProperty, StructuralTypeConfiguration structuralTypeConfiguration)
{
if (edmProperty == null)
{
throw Error.ArgumentNull("edmProperty");
}
if (structuralTypeConfiguration == null)
{
throw Error.ArgumentNull("structuralTypeConfiguration");
}
foreach (Attribute attribute in GetAttributes(edmProperty.PropertyInfo))
{
Apply(edmProperty, structuralTypeConfiguration, attribute);
}
}
/// <summary>
/// Applies the convention.
/// </summary>
/// <param name="edmProperty">The property being configured.</param>
/// <param name="structuralTypeConfiguration">The type being configured.</param>
/// <param name="attribute">The attribute to be used during configuration.</param>
public abstract void Apply(TPropertyConfiguration edmProperty, StructuralTypeConfiguration structuralTypeConfiguration, Attribute attribute);
}
}

Просмотреть файл

@ -1,62 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
namespace System.Web.Http.OData.Builder.Conventions.Attributes
{
/// <summary>
/// Base class for all <see cref="IEdmTypeConvention"/>'s based on a attribute on the type.
/// </summary>
/// <typeparam name="TEdmTypeConfiguration">The kind of Edm type that this convention must be applied to.</typeparam>
internal abstract class AttributeEdmTypeConvention<TEdmTypeConfiguration> : AttributeConvention, IEdmTypeConvention
where TEdmTypeConfiguration : StructuralTypeConfiguration
{
/// <summary>
/// Initializes a new instance of the <see cref="AttributeEdmTypeConvention{TEdmTypeConfiguration}"/> class.
/// </summary>
/// <param name="attributeFilter">A function to test whether this convention applies to an attribute or not.</param>
/// <param name="allowMultiple"><see langword="true"/> if the convention allows multiple attributes; otherwise, <see langword="false"/>.</param>
protected AttributeEdmTypeConvention(Func<Attribute, bool> attributeFilter, bool allowMultiple)
: base(attributeFilter, allowMultiple)
{
}
/// <summary>
/// Applies the convention.
/// </summary>
/// <param name="edmTypeConfiguration">The edm type to apply the convention to.</param>
/// <param name="model">The model that this edm type belongs to.</param>
public void Apply(IEdmTypeConfiguration edmTypeConfiguration, ODataModelBuilder model)
{
TEdmTypeConfiguration type = edmTypeConfiguration as TEdmTypeConfiguration;
if (type != null)
{
Apply(type, model);
}
}
/// <summary>
/// Applies the convention.
/// </summary>
/// <param name="edmTypeConfiguration">The edm type to apply the convention to.</param>
/// <param name="model">The model that this edm type belongs to.</param>
public void Apply(TEdmTypeConfiguration edmTypeConfiguration, ODataModelBuilder model)
{
if (edmTypeConfiguration == null)
{
throw Error.ArgumentNull("edmTypeConfiguration");
}
foreach (Attribute attribute in GetAttributes(edmTypeConfiguration.ClrType))
{
Apply(edmTypeConfiguration, model, attribute);
}
}
/// <summary>
/// Applies the convention.
/// </summary>
/// <param name="edmTypeConfiguration">The edm type to apply the convention to.</param>
/// <param name="model">The model that this edm type belongs to.</param>
/// <param name="attribute">The attribute found on this edm type.</param>
public abstract void Apply(TEdmTypeConfiguration edmTypeConfiguration, ODataModelBuilder model, Attribute attribute);
}
}

Просмотреть файл

@ -1,45 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
namespace System.Web.Http.OData.Builder.Conventions.Attributes
{
/// <summary>
/// Configures classes that have the <see cref="DataContractAttribute"/> to follow DataContract serialization/deserialization rules.
/// </summary>
internal class DataContractAttributeEdmTypeConvention : AttributeEdmTypeConvention<StructuralTypeConfiguration>
{
public DataContractAttributeEdmTypeConvention()
: base(attribute => attribute.GetType() == typeof(DataContractAttribute), allowMultiple: false)
{
}
/// <summary>
/// Removes properties that do not have the <see cref="DataMemberAttribute"/> attribute from the edm type.
/// </summary>
/// <param name="edmTypeConfiguration">The edm type to configure.</param>
/// <param name="model">The edm model that this type belongs to.</param>
/// <param name="attribute">The <see cref="Attribute"/> found on this type.</param>
public override void Apply(StructuralTypeConfiguration edmTypeConfiguration, ODataModelBuilder model, Attribute attribute)
{
if (edmTypeConfiguration == null)
{
throw Error.ArgumentNull("edmTypeConfiguration");
}
IEnumerable<PropertyConfiguration> allProperties = edmTypeConfiguration.Properties.ToArray();
foreach (PropertyConfiguration property in allProperties)
{
if (!property.PropertyInfo.GetCustomAttributes(typeof(DataMemberAttribute), inherit: true).Any())
{
if (!property.AddedExplicitly)
{
edmTypeConfiguration.RemoveProperty(property.PropertyInfo);
}
}
}
}
}
}

Просмотреть файл

@ -1,57 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Linq;
using System.Runtime.Serialization;
using Microsoft.Data.Edm;
namespace System.Web.Http.OData.Builder.Conventions.Attributes
{
/// <summary>
/// Configures properties that have <see cref="DataMemberAttribute"/> as optional or required on their edm type.
/// </summary>
internal class DataMemberAttributeEdmPropertyConvention : AttributeEdmPropertyConvention<PropertyConfiguration>
{
public DataMemberAttributeEdmPropertyConvention()
: base(attribute => attribute.GetType() == typeof(DataMemberAttribute), allowMultiple: false)
{
}
public override void Apply(PropertyConfiguration edmProperty, StructuralTypeConfiguration structuralTypeConfiguration, Attribute attribute)
{
if (structuralTypeConfiguration == null)
{
throw Error.ArgumentNull("structuralTypeConfiguration");
}
if (edmProperty == null)
{
throw Error.ArgumentNull("edmProperty");
}
bool isTypeDataContract = structuralTypeConfiguration.ClrType.GetCustomAttributes(typeof(DataContractAttribute), inherit: true).Any();
DataMemberAttribute dataMember = attribute as DataMemberAttribute;
if (isTypeDataContract && dataMember != null && !edmProperty.AddedExplicitly)
{
StructuralPropertyConfiguration structuralProperty = edmProperty as StructuralPropertyConfiguration;
if (structuralProperty != null)
{
structuralProperty.OptionalProperty = !dataMember.IsRequired;
}
NavigationPropertyConfiguration navigationProperty = edmProperty as NavigationPropertyConfiguration;
if (navigationProperty != null && navigationProperty.Multiplicity != EdmMultiplicity.Many)
{
if (dataMember.IsRequired)
{
navigationProperty.Required();
}
else
{
navigationProperty.Optional();
}
}
}
}
}
}

Просмотреть файл

@ -1,44 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.ComponentModel.DataAnnotations.Schema;
namespace System.Web.Http.OData.Builder.Conventions.Attributes
{
/// <summary>
/// Marks properties that have <see cref="DatabaseGeneratedAttribute"/> as non-optional on their EDM type.
/// </summary>
internal class DatabaseGeneratedAttributeEdmPropertyConvention :
AttributeEdmPropertyConvention<PropertyConfiguration>
{
public DatabaseGeneratedAttributeEdmPropertyConvention()
: base(attribute => attribute.GetType() == typeof(DatabaseGeneratedAttribute), allowMultiple: false)
{
}
/// <summary>
/// Marks the property with StoreGeneratedPattern on the EDM type.
/// </summary>
/// <param name="edmProperty">The EDM property.</param>
/// <param name="structuralTypeConfiguration">The EDM type being configured.</param>
/// <param name="attribute">The <see cref="Attribute"/> found.</param>
public override void Apply(PropertyConfiguration edmProperty,
StructuralTypeConfiguration structuralTypeConfiguration, Attribute attribute)
{
if (edmProperty == null)
{
throw Error.ArgumentNull("edmProperty");
}
EntityTypeConfiguration entityType = structuralTypeConfiguration as EntityTypeConfiguration;
PrimitivePropertyConfiguration primitiveProperty = edmProperty as PrimitivePropertyConfiguration;
DatabaseGeneratedAttribute databaseGeneratedAttribute = attribute as DatabaseGeneratedAttribute;
if (entityType != null && primitiveProperty != null && databaseGeneratedAttribute != null)
{
if (!edmProperty.AddedExplicitly)
{
primitiveProperty.StoreGeneratedPattern = databaseGeneratedAttribute.DatabaseGeneratedOption;
}
}
}
}
}

Просмотреть файл

@ -1,53 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Linq;
using System.Runtime.Serialization;
namespace System.Web.Http.OData.Builder.Conventions.Attributes
{
/// <summary>
/// Removes properties that have <see cref="IgnoreDataMemberAttribute"/> from their edm type.
/// </summary>
internal class IgnoreDataMemberAttributeEdmPropertyConvention : AttributeEdmPropertyConvention<PropertyConfiguration>
{
public IgnoreDataMemberAttributeEdmPropertyConvention()
: base(attribute => attribute.GetType() == typeof(IgnoreDataMemberAttribute), allowMultiple: false)
{
}
/// <summary>
/// Removes the property from the edm type.
/// </summary>
/// <param name="edmProperty">The property being removed.</param>
/// <param name="structuralTypeConfiguration">The edm type from which the property is being removed.</param>
/// <param name="attribute">The <see cref="Attribute"/> found on this type.</param>
public override void Apply(PropertyConfiguration edmProperty, StructuralTypeConfiguration structuralTypeConfiguration, Attribute attribute)
{
if (structuralTypeConfiguration == null)
{
throw Error.ArgumentNull("structuralTypeConfiguration");
}
if (edmProperty == null)
{
throw Error.ArgumentNull("edmProperty");
}
if (!edmProperty.AddedExplicitly)
{
bool isTypeDataContract = structuralTypeConfiguration.ClrType.GetCustomAttributes(typeof(DataContractAttribute), inherit: true).Any();
bool isPropertyDataMember = edmProperty.PropertyInfo.GetCustomAttributes(typeof(DataMemberAttribute), inherit: true).Any();
if (isTypeDataContract && isPropertyDataMember)
{
// both Datamember and IgnoreDataMember. DataMember wins as this a DataContract
return;
}
else
{
structuralTypeConfiguration.RemoveProperty(edmProperty.PropertyInfo);
}
}
}
}
}

Просмотреть файл

@ -1,38 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.ComponentModel.DataAnnotations;
using Microsoft.Data.Edm;
namespace System.Web.Http.OData.Builder.Conventions.Attributes
{
/// <summary>
/// Configures properties that have the <see cref="KeyAttribute"/> as keys in the <see cref="IEdmEntityType"/>.
/// </summary>
internal class KeyAttributeEdmPropertyConvention : AttributeEdmPropertyConvention<PrimitivePropertyConfiguration>
{
public KeyAttributeEdmPropertyConvention()
: base(attribute => attribute.GetType() == typeof(KeyAttribute), allowMultiple: false)
{
}
/// <summary>
/// Configures the property as a key on the edm type.
/// </summary>
/// <param name="edmProperty">The key property.</param>
/// <param name="structuralTypeConfiguration">The edm type being configured.</param>
/// <param name="attribute">The <see cref="Attribute"/> found on the property.</param>
public override void Apply(PrimitivePropertyConfiguration edmProperty, StructuralTypeConfiguration structuralTypeConfiguration, Attribute attribute)
{
if (edmProperty == null)
{
throw Error.ArgumentNull("edmProperty");
}
EntityTypeConfiguration entity = structuralTypeConfiguration as EntityTypeConfiguration;
if (entity != null)
{
entity.HasKey(edmProperty.PropertyInfo);
}
}
}
}

Просмотреть файл

@ -1,43 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using Microsoft.Data.Edm;
namespace System.Web.Http.OData.Builder.Conventions.Attributes
{
/// <summary>
/// Ignores properties with the NotMappedAttribute from <see cref="IEdmStructuredType"/>.
/// </summary>
internal class NotMappedAttributeConvention : AttributeEdmPropertyConvention<PropertyConfiguration>
{
// .net 4.5 NotMappedAttribute has the same name.
private const string EntityFrameworkNotMappedAttributeTypeName = "System.ComponentModel.DataAnnotations.Schema.NotMappedAttribute";
private static Func<Attribute, bool> _filter = attribute =>
{
return attribute.GetType().FullName.Equals(EntityFrameworkNotMappedAttributeTypeName, StringComparison.Ordinal);
};
public NotMappedAttributeConvention()
: base(_filter, allowMultiple: false)
{
}
public override void Apply(PropertyConfiguration edmProperty, StructuralTypeConfiguration structuralTypeConfiguration, Attribute attribute)
{
if (edmProperty == null)
{
throw Error.ArgumentNull("edmProperty");
}
if (structuralTypeConfiguration == null)
{
throw Error.ArgumentNull("structuralTypeConfiguration");
}
if (!edmProperty.AddedExplicitly)
{
structuralTypeConfiguration.RemoveProperty(edmProperty.PropertyInfo);
}
}
}
}

Просмотреть файл

@ -1,47 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.ComponentModel.DataAnnotations;
using Microsoft.Data.Edm;
namespace System.Web.Http.OData.Builder.Conventions.Attributes
{
/// <summary>
/// Marks properties that have <see cref="RequiredAttribute"/> as non-optional on their edm type.
/// </summary>
internal class RequiredAttributeEdmPropertyConvention : AttributeEdmPropertyConvention<PropertyConfiguration>
{
public RequiredAttributeEdmPropertyConvention()
: base(attribute => attribute.GetType() == typeof(RequiredAttribute), allowMultiple: false)
{
}
/// <summary>
/// Marks the property non-optional on the edm type.
/// </summary>
/// <param name="edmProperty">The edm property.</param>
/// <param name="structuralTypeConfiguration">The edm type being configured.</param>
/// <param name="attribute">The <see cref="Attribute"/> found.</param>
public override void Apply(PropertyConfiguration edmProperty, StructuralTypeConfiguration structuralTypeConfiguration, Attribute attribute)
{
if (edmProperty == null)
{
throw Error.ArgumentNull("edmProperty");
}
if (!edmProperty.AddedExplicitly)
{
StructuralPropertyConfiguration structuralProperty = edmProperty as StructuralPropertyConfiguration;
if (structuralProperty != null)
{
structuralProperty.OptionalProperty = false;
}
NavigationPropertyConfiguration navigationProperty = edmProperty as NavigationPropertyConfiguration;
if (navigationProperty != null && navigationProperty.Multiplicity != EdmMultiplicity.Many)
{
navigationProperty.Required();
}
}
}
}
}

Просмотреть файл

@ -1,176 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Web.Http.OData.Formatter;
using System.Web.Http.OData.Formatter.Serialization;
using System.Web.Http.OData.Properties;
using Microsoft.Data.Edm;
using Microsoft.Data.OData;
using Microsoft.Data.OData.Query;
namespace System.Web.Http.OData.Builder.Conventions
{
internal static class ConventionsHelpers
{
public static string GetEntityKeyValue(EntityInstanceContext entityContext)
{
Contract.Assert(entityContext != null);
Contract.Assert(entityContext.EntityType != null);
Contract.Assert(entityContext.EdmObject != null);
IEnumerable<IEdmProperty> keys = entityContext.EntityType.Key();
// TODO: BUG 453795: reflection cleanup
if (keys.Count() == 1)
{
return GetUriRepresentationForKeyValue(keys.First(), entityContext);
}
else
{
IEnumerable<string> keyValues =
keys.Select(key => String.Format(
CultureInfo.InvariantCulture, "{0}={1}", key.Name, GetUriRepresentationForKeyValue(key, entityContext)));
return String.Join(",", keyValues);
}
}
// Get properties of this entity type that are not already declared in the base entity type and are not already ignored.
public static IEnumerable<PropertyInfo> GetProperties(EntityTypeConfiguration entity, bool includeReadOnly)
{
IEnumerable<PropertyInfo> allProperties = GetAllProperties(entity as StructuralTypeConfiguration, includeReadOnly);
if (entity.BaseType != null)
{
IEnumerable<PropertyInfo> baseTypeProperties = GetAllProperties(entity.BaseType as StructuralTypeConfiguration, includeReadOnly);
return allProperties.Except(baseTypeProperties, PropertyEqualityComparer.Instance);
}
else
{
return allProperties;
}
}
// Get all properties of this type (that are not already ignored).
public static IEnumerable<PropertyInfo> GetAllProperties(StructuralTypeConfiguration type, bool includeReadOnly)
{
if (type == null)
{
throw Error.ArgumentNull("type");
}
return type
.ClrType
.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.Where(p => p.IsValidStructuralProperty() && !type.IgnoredProperties().Any(p1 => p1.Name == p.Name))
.Where(p => includeReadOnly || (p.GetSetMethod() != null || p.PropertyType.IsCollection()))
.ToArray();
}
public static bool IsValidStructuralProperty(this PropertyInfo propertyInfo)
{
if (propertyInfo == null)
{
throw Error.ArgumentNull("propertyInfo");
}
// ignore any indexer properties.
if (propertyInfo.GetIndexParameters().Any())
{
return false;
}
if (propertyInfo.CanRead)
{
// non-public getters are not valid properties
MethodInfo publicGetter = propertyInfo.GetGetMethod();
if (publicGetter != null && propertyInfo.PropertyType.IsValidStructuralPropertyType())
{
return true;
}
}
return false;
}
// Gets the ignored properties from this type and the base types.
public static IEnumerable<PropertyInfo> IgnoredProperties(this StructuralTypeConfiguration structuralType)
{
if (structuralType == null)
{
return Enumerable.Empty<PropertyInfo>();
}
EntityTypeConfiguration entityType = structuralType as EntityTypeConfiguration;
if (entityType != null)
{
return entityType.IgnoredProperties.Concat(entityType.BaseType.IgnoredProperties());
}
else
{
return structuralType.IgnoredProperties;
}
}
public static bool IsValidStructuralPropertyType(this Type type)
{
if (type == null)
{
throw Error.ArgumentNull("type");
}
Type elementType;
return !(type.IsGenericTypeDefinition
|| type.IsPointer
|| type == typeof(object)
|| (type.IsCollection(out elementType) && elementType == typeof(object)));
}
// gets the primitive odata uri representation.
public static string GetUriRepresentationForValue(object value)
{
Contract.Assert(value != null);
Contract.Assert(EdmLibHelpers.GetEdmPrimitiveTypeOrNull(value.GetType()) != null);
value = ODataPrimitiveSerializer.ConvertUnsupportedPrimitives(value);
return ODataUriUtils.ConvertToUriLiteral(value, ODataVersion.V3);
}
private static string GetUriRepresentationForKeyValue(IEdmProperty key, EntityInstanceContext entityInstanceContext)
{
Contract.Assert(key != null);
Contract.Assert(entityInstanceContext != null);
object value = entityInstanceContext.GetPropertyValue(key.Name);
if (value == null)
{
IEdmTypeReference edmType = entityInstanceContext.EdmObject.GetEdmType();
throw Error.InvalidOperation(SRResources.KeyValueCannotBeNull, key.Name, edmType.Definition);
}
return GetUriRepresentationForValue(value);
}
private class PropertyEqualityComparer : IEqualityComparer<PropertyInfo>
{
public static PropertyEqualityComparer Instance = new PropertyEqualityComparer();
public bool Equals(PropertyInfo x, PropertyInfo y)
{
Contract.Assert(x != null);
Contract.Assert(y != null);
return x.Name == y.Name;
}
public int GetHashCode(PropertyInfo obj)
{
Contract.Assert(obj != null);
return obj.Name.GetHashCode();
}
}
}
}

Просмотреть файл

@ -1,53 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Linq;
using System.Web.Http.OData.Formatter;
namespace System.Web.Http.OData.Builder.Conventions
{
/// <summary>
/// <see cref="EntityTypeConvention"/> for figuring out the entity keys.
/// <remarks>This convention configures properties that are named 'ID' (case-insensitive) or {EntityName}+ID (case-insensitive) as the key.</remarks>
/// </summary>
internal class EntityKeyConvention : EntityTypeConvention
{
/// <summary>
/// Figures out the key properties and marks them as Keys in the EDM model.
/// </summary>
/// <param name="entity">The entity type being configured.</param>
/// <param name="model">The <see cref="ODataModelBuilder"/>.</param>
public override void Apply(EntityTypeConfiguration entity, ODataModelBuilder model)
{
if (entity == null)
{
throw Error.ArgumentNull("entity");
}
// Try to figure out keys only if there is no base type.
if (entity.BaseType == null)
{
PropertyConfiguration key = GetKeyProperty(entity);
if (key != null)
{
entity.HasKey(key.PropertyInfo);
}
}
}
private static PropertyConfiguration GetKeyProperty(EntityTypeConfiguration entityType)
{
IEnumerable<PropertyConfiguration> keys =
entityType.Properties
.Where(p => (p.Name.Equals(entityType.Name + "Id", StringComparison.OrdinalIgnoreCase) || p.Name.Equals("Id", StringComparison.OrdinalIgnoreCase))
&& EdmLibHelpers.GetEdmPrimitiveTypeOrNull(p.PropertyInfo.PropertyType) != null);
if (keys.Count() == 1)
{
return keys.Single();
}
return null;
}
}
}

Просмотреть файл

@ -1,31 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
namespace System.Web.Http.OData.Builder.Conventions
{
/// <summary>
/// An <see cref="EntityTypeConvention"/> is used to configure an <see cref="EntityTypeConfiguration"/> in the
/// <see cref="ODataConventionModelBuilder"/>.
/// </summary>
internal abstract class EntityTypeConvention : IEdmTypeConvention
{
protected EntityTypeConvention()
{
}
public void Apply(IEdmTypeConfiguration edmTypeConfiguration, ODataModelBuilder model)
{
EntityTypeConfiguration entity = edmTypeConfiguration as EntityTypeConfiguration;
if (entity != null)
{
Apply(entity, model);
}
}
/// <summary>
/// Applies the convention.
/// </summary>
/// <param name="entity">The <see cref="EntityTypeConfiguration"/> to apply the convention on.</param>
/// <param name="model">The <see cref="ODataModelBuilder"/> instance.</param>
public abstract void Apply(EntityTypeConfiguration entity, ODataModelBuilder model);
}
}

Просмотреть файл

@ -1,11 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Diagnostics.CodeAnalysis;
namespace System.Web.Http.OData.Builder.Conventions
{
[SuppressMessage("Microsoft.Design", "CA1040:AvoidEmptyInterfaces", Justification = "Marker interface acceptable here for derivation")]
internal interface IConvention
{
}
}

Просмотреть файл

@ -1,17 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
namespace System.Web.Http.OData.Builder.Conventions
{
/// <summary>
/// Convention to process properties of <see cref="StructuralTypeConfiguration"/>.
/// </summary>
internal interface IEdmPropertyConvention : IConvention
{
/// <summary>
/// Applies the convention.
/// </summary>
/// <param name="edmProperty">The property the convention is applied on.</param>
/// <param name="structuralTypeConfiguration">The <see cref="StructuralTypeConfiguration"/> the edmProperty belongs to.</param>
void Apply(PropertyConfiguration edmProperty, StructuralTypeConfiguration structuralTypeConfiguration);
}
}

Просмотреть файл

@ -1,18 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
namespace System.Web.Http.OData.Builder.Conventions
{
/// <summary>
/// Convention to process properties of <see cref="StructuralTypeConfiguration"/>.
/// </summary>
/// <typeparam name="TPropertyConfiguration"></typeparam>
internal interface IEdmPropertyConvention<TPropertyConfiguration> : IEdmPropertyConvention where TPropertyConfiguration : PropertyConfiguration
{
/// <summary>
/// Applies the convention.
/// </summary>
/// <param name="edmProperty">The property the convention is applied on.</param>
/// <param name="structuralTypeConfiguration">The <see cref="StructuralTypeConfiguration"/> the edmProperty belongs to.</param>
void Apply(TPropertyConfiguration edmProperty, StructuralTypeConfiguration structuralTypeConfiguration);
}
}

Просмотреть файл

@ -1,9 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
namespace System.Web.Http.OData.Builder.Conventions
{
internal interface IEdmTypeConvention : IConvention
{
void Apply(IEdmTypeConfiguration edmTypeConfiguration, ODataModelBuilder model);
}
}

Просмотреть файл

@ -1,9 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
namespace System.Web.Http.OData.Builder.Conventions
{
internal interface IEntitySetConvention : IConvention
{
void Apply(EntitySetConfiguration configuration, ODataModelBuilder model);
}
}

Просмотреть файл

@ -1,12 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
namespace System.Web.Http.OData.Builder.Conventions
{
/// <summary>
/// Convention to apply to <see cref="ProcedureConfiguration"/> instances in the model
/// </summary>
internal interface IProcedureConvention : IConvention
{
void Apply(ProcedureConfiguration configuration, ODataModelBuilder model);
}
}

Просмотреть файл

@ -1,47 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
namespace System.Web.Http.OData.Builder.Conventions
{
internal class NavigationLinksGenerationConvention : IEntitySetConvention
{
public void Apply(EntitySetConfiguration configuration, ODataModelBuilder model)
{
if (configuration == null)
{
throw Error.ArgumentNull("configuration");
}
// generate links without cast for declared and inherited navigation properties
foreach (EntityTypeConfiguration entity in configuration.EntityType.ThisAndBaseTypes())
{
foreach (NavigationPropertyConfiguration property in entity.NavigationProperties)
{
if (configuration.GetNavigationPropertyLink(property) == null)
{
configuration.HasNavigationPropertyLink(
property,
new NavigationLinkBuilder(
(entityContext, navigationProperty) =>
entityContext.GenerateNavigationPropertyLink(navigationProperty, includeCast: false), followsConventions: true));
}
}
}
// generate links with cast for navigation properties in derived types.
foreach (EntityTypeConfiguration entity in model.DerivedTypes(configuration.EntityType))
{
foreach (NavigationPropertyConfiguration property in entity.NavigationProperties)
{
if (configuration.GetNavigationPropertyLink(property) == null)
{
configuration.HasNavigationPropertyLink(
property,
new NavigationLinkBuilder(
(entityContext, navigationProperty) =>
entityContext.GenerateNavigationPropertyLink(navigationProperty, includeCast: true), followsConventions: true));
}
}
}
}
}
}

Просмотреть файл

@ -1,50 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Linq;
using System.Web.Http.OData.Extensions;
using System.Web.Http.OData.Routing;
namespace System.Web.Http.OData.Builder.Conventions
{
internal class SelfLinksGenerationConvention : IEntitySetConvention
{
public void Apply(EntitySetConfiguration configuration, ODataModelBuilder model)
{
if (configuration == null)
{
throw Error.ArgumentNull("configuration");
}
// Configure the self link for the feed
if (configuration.GetFeedSelfLink() == null)
{
configuration.HasFeedSelfLink(entitySetContext =>
{
string selfLink = entitySetContext.Url.CreateODataLink(new EntitySetPathSegment(entitySetContext.EntitySet));
if (selfLink == null)
{
return null;
}
return new Uri(selfLink);
});
}
// We only need to configure the IdLink by convention, ReadLink and EditLink both delegate to IdLink
if (configuration.GetIdLink() == null)
{
bool derivedTypesDefineNavigationProperty = model.DerivedTypes(configuration.EntityType).Any(e => e.NavigationProperties.Any());
// generate links with cast if any of the derived types define a navigation property
if (derivedTypesDefineNavigationProperty)
{
configuration.HasIdLink(new SelfLinkBuilder<string>((entityContext) => entityContext.GenerateSelfLink(includeCast: true), followsConventions: true));
}
else
{
configuration.HasIdLink(new SelfLinkBuilder<string>((entityContext) => entityContext.GenerateSelfLink(includeCast: false), followsConventions: true));
}
}
}
}
}

Просмотреть файл

@ -1,336 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Web.Http.OData.Formatter;
using System.Web.Http.OData.Properties;
using Microsoft.Data.Edm;
using Microsoft.Data.Edm.Annotations;
using Microsoft.Data.Edm.Csdl;
using Microsoft.Data.Edm.Expressions;
using Microsoft.Data.Edm.Library;
using Microsoft.Data.Edm.Library.Expressions;
using Microsoft.Data.OData;
namespace System.Web.Http.OData.Builder
{
internal static class EdmModelHelperMethods
{
public static IEdmModel BuildEdmModel(ODataModelBuilder builder)
{
if (builder == null)
{
throw Error.ArgumentNull("builder");
}
EdmModel model = new EdmModel();
EdmEntityContainer container = new EdmEntityContainer(builder.Namespace, builder.ContainerName);
// add types and sets, building an index on the way.
Dictionary<Type, IEdmStructuredType> edmTypeMap = model.AddTypes(builder.StructuralTypes);
Dictionary<string, EdmEntitySet> edmEntitySetMap = model.AddEntitySets(builder, container, edmTypeMap);
// add procedures
model.AddProcedures(builder.Procedures, container, edmTypeMap, edmEntitySetMap);
// finish up
model.AddElement(container);
model.SetIsDefaultEntityContainer(container, isDefaultContainer: true);
// build the map from IEdmEntityType to IEdmFunctionImport
model.SetAnnotationValue<BindableProcedureFinder>(model, new BindableProcedureFinder(model));
// set the data service version annotations.
model.SetDataServiceVersion(builder.DataServiceVersion);
model.SetMaxDataServiceVersion(builder.MaxDataServiceVersion);
return model;
}
private static void AddTypes(this EdmModel model, Dictionary<Type, IEdmStructuredType> structuredTypes)
{
Contract.Assert(model != null);
Contract.Assert(structuredTypes != null);
foreach (IEdmStructuredType type in structuredTypes.Values)
{
model.AddType(type);
}
}
private static Dictionary<string, EdmEntitySet> AddEntitySets(this EdmModel model, ODataModelBuilder builder, EdmEntityContainer container, Dictionary<Type, IEdmStructuredType> edmTypeMap)
{
IEnumerable<EntitySetConfiguration> configurations = builder.EntitySets;
// build the entitysets and their annotations
IEnumerable<Tuple<EdmEntitySet, EntitySetConfiguration>> entitySets = AddEntitySets(configurations, container, edmTypeMap);
var entitySetAndAnnotations = entitySets.Select(e => new
{
EntitySet = e.Item1,
Configuration = e.Item2,
Annotations = new
{
LinkBuilder = new EntitySetLinkBuilderAnnotation(e.Item2),
Url = new EntitySetUrlAnnotation { Url = e.Item2.GetUrl() }
}
}).ToArray();
// index the entitySets by name
Dictionary<string, EdmEntitySet> edmEntitySetMap = entitySetAndAnnotations.ToDictionary(e => e.EntitySet.Name, e => e.EntitySet);
// apply the annotations
foreach (var iter in entitySetAndAnnotations)
{
EdmEntitySet entitySet = iter.EntitySet;
model.SetAnnotationValue<EntitySetUrlAnnotation>(entitySet, iter.Annotations.Url);
model.SetEntitySetLinkBuilder(entitySet, iter.Annotations.LinkBuilder);
AddNavigationBindings(iter.Configuration, iter.EntitySet, iter.Annotations.LinkBuilder, builder, edmTypeMap, edmEntitySetMap);
}
return edmEntitySetMap;
}
private static void AddNavigationBindings(EntitySetConfiguration configuration, EdmEntitySet entitySet, EntitySetLinkBuilderAnnotation linkBuilder, ODataModelBuilder builder,
Dictionary<Type, IEdmStructuredType> edmTypeMap, Dictionary<string, EdmEntitySet> edmEntitySetMap)
{
foreach (EntityTypeConfiguration entity in builder.ThisAndBaseAndDerivedTypes(configuration.EntityType))
{
foreach (NavigationPropertyConfiguration navigation in entity.NavigationProperties)
{
NavigationPropertyBindingConfiguration binding = configuration.FindBinding(navigation);
if (binding != null)
{
EdmEntityType edmEntityType = edmTypeMap[entity.ClrType] as EdmEntityType;
IEdmNavigationProperty edmNavigationProperty = edmEntityType.NavigationProperties().Single(np => np.Name == navigation.Name);
entitySet.AddNavigationTarget(edmNavigationProperty, edmEntitySetMap[binding.EntitySet.Name]);
NavigationLinkBuilder linkBuilderFunc = configuration.GetNavigationPropertyLink(navigation);
if (linkBuilderFunc != null)
{
linkBuilder.AddNavigationPropertyLinkBuilder(edmNavigationProperty, linkBuilderFunc);
}
}
}
}
}
private static void AddProcedures(this IEdmModel model, IEnumerable<ProcedureConfiguration> configurations, EdmEntityContainer container, Dictionary<Type, IEdmStructuredType> edmTypeMap, Dictionary<string, EdmEntitySet> edmEntitySetMap)
{
foreach (ProcedureConfiguration procedure in configurations)
{
switch (procedure.Kind)
{
case ProcedureKind.Action:
ActionConfiguration action = procedure as ActionConfiguration;
IEdmTypeReference returnReference = GetEdmTypeReference(edmTypeMap, action.ReturnType, nullable: true);
IEdmExpression expression = GetEdmEntitySetExpression(edmEntitySetMap, action);
EdmFunctionImport functionImport = new EdmFunctionImport(container, action.Name, returnReference, expression, action.IsSideEffecting, action.IsComposable, action.IsBindable);
if (action.IsBindable)
{
model.SetIsAlwaysBindable(functionImport, action.IsAlwaysBindable);
if (action.BindingParameter.TypeConfiguration.Kind == EdmTypeKind.Entity)
{
Func<EntityInstanceContext, Uri> actionFactory = action.GetActionLink();
if (actionFactory != null)
{
model.SetActionLinkBuilder(functionImport, new ActionLinkBuilder(actionFactory, action.FollowsConventions));
}
}
}
foreach (ParameterConfiguration parameter in action.Parameters)
{
// TODO: http://aspnetwebstack.codeplex.com/workitem/417
bool isParameterOptional = EdmLibHelpers.IsNullable(parameter.TypeConfiguration.ClrType);
IEdmTypeReference parameterTypeReference = GetEdmTypeReference(edmTypeMap, parameter.TypeConfiguration, nullable: isParameterOptional);
EdmFunctionParameter functionParameter = new EdmFunctionParameter(functionImport, parameter.Name, parameterTypeReference, EdmFunctionParameterMode.In);
functionImport.AddParameter(functionParameter);
}
container.AddElement(functionImport);
break;
case ProcedureKind.Function:
Contract.Assert(false, "Functions are not supported.");
break;
case ProcedureKind.ServiceOperation:
Contract.Assert(false, "ServiceOperations are not supported.");
break;
}
}
}
private static Dictionary<Type, IEdmStructuredType> AddTypes(this EdmModel model, IEnumerable<StructuralTypeConfiguration> types)
{
StructuralTypeConfiguration[] configTypes = types.ToArray();
// build types
EdmTypeMap edmTypeMap = EdmTypeBuilder.GetTypesAndProperties(configTypes);
Dictionary<Type, IEdmStructuredType> edmTypes = edmTypeMap.EdmTypes;
// Add an annotate types
model.AddTypes(edmTypes);
model.AddClrTypeAnnotations(edmTypes);
// Add direct value annotation
model.AddDirectValueAnnotations(edmTypeMap.DirectValueAnnotations);
return edmTypes;
}
private static void AddType(this EdmModel model, IEdmStructuredType type)
{
if (type.TypeKind == EdmTypeKind.Complex)
{
model.AddElement(type as IEdmComplexType);
}
else if (type.TypeKind == EdmTypeKind.Entity)
{
model.AddElement(type as IEdmEntityType);
}
else
{
Contract.Assert(false, "Only ComplexTypes and EntityTypes are supported.");
}
}
private static EdmEntitySet AddEntitySet(this EdmEntityContainer container, EntitySetConfiguration entitySet, IDictionary<Type, IEdmStructuredType> edmTypeMap)
{
return container.AddEntitySet(entitySet.Name, (IEdmEntityType)edmTypeMap[entitySet.EntityType.ClrType]);
}
private static IEnumerable<Tuple<EdmEntitySet, EntitySetConfiguration>> AddEntitySets(IEnumerable<EntitySetConfiguration> entitySets, EdmEntityContainer container, Dictionary<Type, IEdmStructuredType> edmTypeMap)
{
return entitySets.Select(es => Tuple.Create(container.AddEntitySet(es, edmTypeMap), es));
}
private static void AddClrTypeAnnotations(this EdmModel model, Dictionary<Type, IEdmStructuredType> edmTypes)
{
foreach (KeyValuePair<Type, IEdmStructuredType> map in edmTypes)
{
// pre-populate the model with clr-type annotations so that we dont have to scan
// all loaded assemblies to find the clr type for an edm type that we build.
IEdmStructuredType edmType = map.Value;
Type clrType = map.Key;
model.SetAnnotationValue<ClrTypeAnnotation>(edmType, new ClrTypeAnnotation(clrType));
}
}
private static void AddDirectValueAnnotations(this EdmModel model,
IEnumerable<IEdmDirectValueAnnotationBinding> directValueAnnotations)
{
foreach (IEdmDirectValueAnnotationBinding annotation in directValueAnnotations)
{
model.SetAnnotationValue(annotation.Element, annotation.NamespaceUri, annotation.Name, annotation.Value);
}
}
private static IEdmExpression GetEdmEntitySetExpression(Dictionary<string, EdmEntitySet> entitySets, ActionConfiguration action)
{
if (action.EntitySet != null)
{
if (entitySets.ContainsKey(action.EntitySet.Name))
{
EdmEntitySet entitySet = entitySets[action.EntitySet.Name];
return new EdmEntitySetReferenceExpression(entitySet);
}
else
{
throw Error.InvalidOperation(SRResources.EntitySetNotFoundForName, action.EntitySet.Name);
}
}
return null;
}
private static IEdmTypeReference GetEdmTypeReference(Dictionary<Type, IEdmStructuredType> availableTypes, IEdmTypeConfiguration configuration, bool nullable)
{
Contract.Assert(availableTypes != null);
if (configuration == null)
{
return null;
}
EdmTypeKind kind = configuration.Kind;
if (kind == EdmTypeKind.Collection)
{
CollectionTypeConfiguration collectionType = configuration as CollectionTypeConfiguration;
EdmCollectionType edmCollectionType = new EdmCollectionType(GetEdmTypeReference(availableTypes, collectionType.ElementType, false));
return new EdmCollectionTypeReference(edmCollectionType, nullable);
}
else if (availableTypes.ContainsKey(configuration.ClrType))
{
IEdmStructuredType structuralType = availableTypes[configuration.ClrType];
if (kind == EdmTypeKind.Complex)
{
return new EdmComplexTypeReference(structuralType as IEdmComplexType, nullable);
}
else if (kind == EdmTypeKind.Entity)
{
return new EdmEntityTypeReference(structuralType as IEdmEntityType, nullable);
}
else
{
throw Error.InvalidOperation(SRResources.UnsupportedEdmTypeKind, kind.ToString());
}
}
else if (configuration.Kind == EdmTypeKind.Primitive)
{
PrimitiveTypeConfiguration primitiveTypeConfiguration = configuration as PrimitiveTypeConfiguration;
return new EdmPrimitiveTypeReference(primitiveTypeConfiguration.EdmPrimitiveType, nullable);
}
else
{
throw Error.InvalidOperation(SRResources.NoMatchingIEdmTypeFound, configuration.FullName);
}
}
internal static string GetEntitySetUrl(this IEdmModel model, IEdmEntitySet entitySet)
{
if (model == null)
{
throw Error.ArgumentNull("model");
}
if (entitySet == null)
{
throw Error.ArgumentNull("entitySet");
}
EntitySetUrlAnnotation annotation = model.GetAnnotationValue<EntitySetUrlAnnotation>(entitySet);
if (annotation == null)
{
return entitySet.Name;
}
else
{
return annotation.Url;
}
}
internal static IEnumerable<IEdmFunctionImport> GetAvailableProcedures(this IEdmModel model, IEdmEntityType entityType)
{
if (model == null)
{
throw Error.ArgumentNull("model");
}
if (entityType == null)
{
throw Error.ArgumentNull("entityType");
}
BindableProcedureFinder annotation = model.GetAnnotationValue<BindableProcedureFinder>(model);
if (annotation == null)
{
annotation = new BindableProcedureFinder(model);
model.SetAnnotationValue(model, annotation);
}
return annotation.FindProcedures(entityType);
}
}
}

Просмотреть файл

@ -1,229 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Web.Http.OData.Formatter;
using System.Web.Http.OData.Properties;
using Microsoft.Data.Edm;
using Microsoft.Data.Edm.Annotations;
using Microsoft.Data.Edm.Library;
namespace System.Web.Http.OData.Builder
{
/// <summary>
/// <see cref="EdmTypeBuilder"/> builds <see cref="IEdmType"/>'s from <see cref=" StructuralTypeConfiguration"/>'s.
/// </summary>
internal class EdmTypeBuilder
{
private readonly List<StructuralTypeConfiguration> _configurations;
private readonly Dictionary<Type, IEdmStructuredType> _types = new Dictionary<Type, IEdmStructuredType>();
private readonly List<IEdmDirectValueAnnotationBinding> _directValueAnnotations = new List<IEdmDirectValueAnnotationBinding>();
internal EdmTypeBuilder(IEnumerable<StructuralTypeConfiguration> configurations)
{
_configurations = configurations.ToList();
}
private Dictionary<Type, IEdmStructuredType> GetEdmTypes()
{
// Reset
_types.Clear();
_directValueAnnotations.Clear();
// Create headers to allow CreateEdmTypeBody to blindly references other things.
foreach (StructuralTypeConfiguration config in _configurations)
{
CreateEdmTypeHeader(config);
}
foreach (StructuralTypeConfiguration config in _configurations)
{
CreateEdmTypeBody(config);
}
return _types;
}
private void CreateEdmTypeHeader(StructuralTypeConfiguration config)
{
if (!_types.ContainsKey(config.ClrType))
{
if (config.Kind == EdmTypeKind.Complex)
{
_types.Add(config.ClrType, new EdmComplexType(config.Namespace, config.Name));
}
else
{
EntityTypeConfiguration entity = config as EntityTypeConfiguration;
Contract.Assert(entity != null);
IEdmEntityType baseType = null;
if (entity.BaseType != null)
{
CreateEdmTypeHeader(entity.BaseType);
baseType = _types[entity.BaseType.ClrType] as IEdmEntityType;
Contract.Assert(baseType != null);
}
_types.Add(config.ClrType, new EdmEntityType(config.Namespace, config.Name, baseType, entity.IsAbstract ?? false, isOpen: false));
}
}
}
private void CreateEdmTypeBody(StructuralTypeConfiguration config)
{
IEdmType edmType = _types[config.ClrType];
if (edmType.TypeKind == EdmTypeKind.Complex)
{
CreateComplexTypeBody(edmType as EdmComplexType, config as ComplexTypeConfiguration);
}
else
{
if (edmType.TypeKind == EdmTypeKind.Entity)
{
CreateEntityTypeBody(edmType as EdmEntityType, config as EntityTypeConfiguration);
}
}
}
private void CreateStructuralTypeBody(EdmStructuredType type, StructuralTypeConfiguration config)
{
foreach (PropertyConfiguration property in config.Properties)
{
switch (property.Kind)
{
case PropertyKind.Primitive:
PrimitivePropertyConfiguration primitiveProperty = property as PrimitivePropertyConfiguration;
EdmPrimitiveTypeKind typeKind = GetTypeKind(primitiveProperty.PropertyInfo.PropertyType);
IEdmTypeReference primitiveTypeReference = EdmCoreModel.Instance.GetPrimitive(
typeKind,
primitiveProperty.OptionalProperty);
var primitiveProp = new EdmStructuralProperty(
type,
primitiveProperty.PropertyInfo.Name,
primitiveTypeReference);
type.AddProperty(primitiveProp);
// Set Annotation StoreGeneratedPattern
if (config.Kind == EdmTypeKind.Entity
&& primitiveProperty.StoreGeneratedPattern != DatabaseGeneratedOption.None)
{
_directValueAnnotations.Add(
new StoreGeneratedPatternAnnotation(primitiveProp, primitiveProperty.StoreGeneratedPattern));
}
break;
case PropertyKind.Complex:
ComplexPropertyConfiguration complexProperty = property as ComplexPropertyConfiguration;
IEdmComplexType complexType = _types[complexProperty.RelatedClrType] as IEdmComplexType;
type.AddStructuralProperty(
complexProperty.PropertyInfo.Name,
new EdmComplexTypeReference(complexType, complexProperty.OptionalProperty));
break;
case PropertyKind.Collection:
CollectionPropertyConfiguration collectionProperty = property as CollectionPropertyConfiguration;
IEdmTypeReference elementTypeReference = null;
if (_types.ContainsKey(collectionProperty.ElementType))
{
IEdmComplexType elementType = _types[collectionProperty.ElementType] as IEdmComplexType;
elementTypeReference = new EdmComplexTypeReference(elementType, false);
}
else
{
elementTypeReference = EdmLibHelpers.GetEdmPrimitiveTypeReferenceOrNull(collectionProperty.ElementType);
}
type.AddStructuralProperty(
collectionProperty.PropertyInfo.Name,
new EdmCollectionTypeReference(
new EdmCollectionType(elementTypeReference),
collectionProperty.OptionalProperty));
break;
default:
break;
}
}
}
private void CreateComplexTypeBody(EdmComplexType type, ComplexTypeConfiguration config)
{
CreateStructuralTypeBody(type, config);
}
private void CreateEntityTypeBody(EdmEntityType type, EntityTypeConfiguration config)
{
CreateStructuralTypeBody(type, config);
IEdmStructuralProperty[] keys = config.Keys.Select(p => type.DeclaredProperties.OfType<IEdmStructuralProperty>().First(dp => dp.Name == p.PropertyInfo.Name)).ToArray();
type.AddKeys(keys);
foreach (NavigationPropertyConfiguration navProp in config.NavigationProperties)
{
EdmNavigationPropertyInfo info = new EdmNavigationPropertyInfo();
info.Name = navProp.Name;
info.TargetMultiplicity = navProp.Multiplicity;
info.Target = _types[navProp.RelatedClrType] as IEdmEntityType;
//TODO: If target end has a multiplity of 1 this assumes the source end is 0..1.
// I think a better default multiplicity is *
type.AddUnidirectionalNavigation(info);
}
}
/// <summary>
/// Builds <see cref="IEdmType"/>'s from <paramref name="configurations"/>
/// </summary>
/// <param name="configurations">A collection of <see cref="StructuralTypeConfiguration"/>'s</param>
/// <returns>The built dictionary of <see cref="StructuralTypeConfiguration"/>'s indexed by their backing CLR type</returns>
public static Dictionary<Type, IEdmStructuredType> GetTypes(IEnumerable<StructuralTypeConfiguration> configurations)
{
if (configurations == null)
{
throw Error.ArgumentNull("configurations");
}
EdmTypeBuilder builder = new EdmTypeBuilder(configurations);
return builder.GetEdmTypes();
}
/// <summary>
/// Builds <see cref="IEdmType"/> and <see cref="IEdmProperty"/>'s from <paramref name="configurations"/>
/// </summary>
/// <param name="configurations">A collection of <see cref="StructuralTypeConfiguration"/>'s</param>
/// <returns>The built dictionary of <see cref="IEdmStructuredType"/>'s indexed by their backing CLR type</returns>
public static EdmTypeMap GetTypesAndProperties(IEnumerable<StructuralTypeConfiguration> configurations)
{
if (configurations == null)
{
throw Error.ArgumentNull("configurations");
}
EdmTypeBuilder builder = new EdmTypeBuilder(configurations);
return new EdmTypeMap(builder.GetEdmTypes(), builder._directValueAnnotations);
}
/// <summary>
/// Gets the <see cref="EdmPrimitiveTypeKind"/> that maps to the <see cref="Type"/>
/// </summary>
/// <param name="clrType">The clr type</param>
/// <returns>The corresponding Edm primitive kind.</returns>
public static EdmPrimitiveTypeKind GetTypeKind(Type clrType)
{
IEdmPrimitiveType primitiveType = EdmLibHelpers.GetEdmPrimitiveTypeOrNull(clrType);
if (primitiveType == null)
{
throw Error.Argument("clrType", SRResources.MustBePrimitiveType, clrType.FullName);
}
return primitiveType.PrimitiveKind;
}
}
}

Просмотреть файл

@ -1,109 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
namespace System.Web.Http.OData.Builder
{
internal static class EdmTypeConfigurationExtensions
{
// returns all the properties declared in the base types of this type.
public static IEnumerable<PropertyConfiguration> DerivedProperties(this EntityTypeConfiguration entity)
{
if (entity == null)
{
throw Error.ArgumentNull("entity");
}
EntityTypeConfiguration baseType = entity.BaseType;
while (baseType != null)
{
foreach (PropertyConfiguration property in baseType.Properties)
{
yield return property;
}
baseType = baseType.BaseType;
}
}
// returns the keys declared or inherited for this entity
public static IEnumerable<PropertyConfiguration> Keys(this EntityTypeConfiguration entity)
{
Contract.Assert(entity != null);
return entity.BaseType == null ? entity.Keys : Keys(entity.BaseType);
}
// Returns the base types, this type.
public static IEnumerable<EntityTypeConfiguration> ThisAndBaseTypes(this EntityTypeConfiguration entity)
{
Contract.Assert(entity != null);
return entity.BaseTypes().Concat(new[] { entity });
}
// Returns the base types, this type and all the derived types of this type.
public static IEnumerable<EntityTypeConfiguration> ThisAndBaseAndDerivedTypes(this ODataModelBuilder modelBuilder, EntityTypeConfiguration entity)
{
Contract.Assert(modelBuilder != null);
Contract.Assert(entity != null);
return entity.BaseTypes()
.Concat(new[] { entity })
.Concat(modelBuilder.DerivedTypes(entity));
}
// Returns the base types for this type.
public static IEnumerable<EntityTypeConfiguration> BaseTypes(this EntityTypeConfiguration entity)
{
Contract.Assert(entity != null);
entity = entity.BaseType;
while (entity != null)
{
yield return entity;
entity = entity.BaseType;
}
}
// Returns all the derived types of this type.
public static IEnumerable<EntityTypeConfiguration> DerivedTypes(this ODataModelBuilder modelBuilder, EntityTypeConfiguration entity)
{
if (modelBuilder == null)
{
throw Error.ArgumentNull("modelBuilder");
}
if (entity == null)
{
throw Error.ArgumentNull("entity");
}
IEnumerable<EntityTypeConfiguration> derivedEntities = modelBuilder.StructuralTypes.OfType<EntityTypeConfiguration>().Where(e => e.BaseType == entity);
foreach (EntityTypeConfiguration derivedEntity in derivedEntities)
{
yield return derivedEntity;
foreach (EntityTypeConfiguration derivedDerivedEntity in modelBuilder.DerivedTypes(derivedEntity))
{
yield return derivedDerivedEntity;
}
}
}
public static bool IsAssignableFrom(this EntityTypeConfiguration baseEntity, EntityTypeConfiguration entity)
{
while (entity != null)
{
if (baseEntity == entity)
{
return true;
}
entity = entity.BaseType;
}
return false;
}
}
}

Просмотреть файл

@ -1,23 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using Microsoft.Data.Edm;
using Microsoft.Data.Edm.Annotations;
namespace System.Web.Http.OData.Builder
{
internal class EdmTypeMap
{
public EdmTypeMap(
Dictionary<Type, IEdmStructuredType> edmTypes,
IEnumerable<IEdmDirectValueAnnotationBinding> directValueAnnotations)
{
EdmTypes = edmTypes;
DirectValueAnnotations = directValueAnnotations;
}
public Dictionary<Type, IEdmStructuredType> EdmTypes { get; private set; }
public IEnumerable<IEdmDirectValueAnnotationBinding> DirectValueAnnotations { get; private set; }
}
}

Просмотреть файл

@ -1,46 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Diagnostics.Contracts;
namespace System.Web.Http.OData.Builder
{
/// <summary>
/// EntityCollectionConfiguration represents a Collection of Entities.
/// This class can be used to configure things that get bound to entities, like Actions bound to a collection.
/// </summary>
/// <typeparam name="TEntityType">The EntityType that is the ElementType of the EntityCollection</typeparam>
public class EntityCollectionConfiguration<TEntityType> : CollectionTypeConfiguration
{
internal EntityCollectionConfiguration(EntityTypeConfiguration elementType)
: base(elementType, typeof(IEnumerable<TEntityType>))
{
}
/// <summary>
/// Creates a new Action that binds to Collection(EntityType).
/// </summary>
/// <param name="name">The name of the Action</param>
/// <returns>An ActionConfiguration to allow further configuration of the Action.</returns>
public ActionConfiguration Action(string name)
{
Contract.Assert(ModelBuilder != null);
ActionConfiguration configuration = ModelBuilder.Action(name);
configuration.SetBindingParameter(BindingParameterConfiguration.DefaultBindingParameterName, this, alwaysBindable: true);
return configuration;
}
/// <summary>
/// Creates a new Action that sometimes binds to Collection(EntityType).
/// </summary>
/// <param name="name">The name of the Action</param>
/// <returns>An ActionConfiguration to allow further configuration of the Action.</returns>
public ActionConfiguration TransientAction(string name)
{
Contract.Assert(ModelBuilder != null);
ActionConfiguration configuration = ModelBuilder.Action(name);
configuration.SetBindingParameter(BindingParameterConfiguration.DefaultBindingParameterName, this, alwaysBindable: false);
return configuration;
}
}
}

Просмотреть файл

@ -1,419 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Web.Http.OData.Properties;
namespace System.Web.Http.OData.Builder
{
/// <summary>
/// Allows configuration to be performed for a entity set in a model.
/// A <see cref="EntitySetConfiguration"/> can be obtained by using the method <see cref="ODataModelBuilder.EntitySet"/>.
/// </summary>
public class EntitySetConfiguration
{
private readonly ODataModelBuilder _modelBuilder;
private readonly Dictionary<NavigationPropertyConfiguration, NavigationPropertyBindingConfiguration> _entitySetBindings;
private string _url;
private Func<FeedContext, Uri> _feedSelfLinkFactory;
private SelfLinkBuilder<Uri> _editLinkBuilder;
private SelfLinkBuilder<Uri> _readLinkBuilder;
private SelfLinkBuilder<string> _idLinkBuilder;
private readonly Dictionary<NavigationPropertyConfiguration, NavigationLinkBuilder> _navigationPropertyLinkBuilders;
/// <summary>
/// Initializes a new instance of the <see cref="EntitySetConfiguration"/> class.
/// </summary>
/// <remarks>The default constructor is intended for use by unit testing only.</remarks>
public EntitySetConfiguration()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="EntitySetConfiguration"/> class.
/// <param name="modelBuilder">The <see cref="ODataModelBuilder"/>.</param>
/// <param name="entityType">The CLR <see cref="Type"/> of the entity type contained in this entity set.</param>
/// <param name="name">The name of the entity set.</param>
/// </summary>
public EntitySetConfiguration(ODataModelBuilder modelBuilder, Type entityType, string name)
: this(modelBuilder, new EntityTypeConfiguration(modelBuilder, entityType), name)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="EntitySetConfiguration"/> class.
/// </summary>
/// <param name="modelBuilder">The <see cref="ODataModelBuilder"/>.</param>
/// <param name="entityType">The entity type contained in this entity set.</param>
/// <param name="name">The name of the entity set.</param>
[SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors", Justification = "The property being set, ClrType, is on a different object.")]
public EntitySetConfiguration(ODataModelBuilder modelBuilder, EntityTypeConfiguration entityType, string name)
{
if (modelBuilder == null)
{
throw Error.ArgumentNull("modelBuilder");
}
if (entityType == null)
{
throw Error.ArgumentNull("entityType");
}
if (name == null)
{
throw Error.ArgumentNull("name");
}
_modelBuilder = modelBuilder;
Name = name;
EntityType = entityType;
ClrType = entityType.ClrType;
_url = Name;
_editLinkBuilder = null;
_readLinkBuilder = null;
_navigationPropertyLinkBuilders = new Dictionary<NavigationPropertyConfiguration, NavigationLinkBuilder>();
_entitySetBindings = new Dictionary<NavigationPropertyConfiguration, NavigationPropertyBindingConfiguration>();
}
/// <summary>
/// Gets the navigation targets of this entity set.
/// </summary>
public virtual IEnumerable<NavigationPropertyBindingConfiguration> Bindings
{
get
{
return _entitySetBindings.Values;
}
}
/// <summary>
/// Gets the entity type contained in this entity set.
/// </summary>
public virtual EntityTypeConfiguration EntityType { get; private set; }
/// <summary>
/// Gets the backing clr type for the entity type contained in this entity set.
/// </summary>
public virtual Type ClrType { get; private set; }
/// <summary>
/// Gets the name of this entity set.
/// </summary>
public virtual string Name { get; private set; }
/// <summary>
/// Configures the entity set URL.
/// </summary>
/// <param name="url">The entity set URL.</param>
/// <returns>Returns itself so that multiple calls can be chained.</returns>
[SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "0#", Justification = "This Url property is not required to be a valid Uri")]
public virtual EntitySetConfiguration HasUrl(string url)
{
_url = url;
return this;
}
/// <summary>
/// Adds a self link to the feed.
/// </summary>
/// <param name="feedSelfLinkFactory">The builder used to generate the link URL.</param>
/// <returns>The entity set configuration currently being configured.</returns>
public virtual EntitySetConfiguration HasFeedSelfLink(Func<FeedContext, Uri> feedSelfLinkFactory)
{
_feedSelfLinkFactory = feedSelfLinkFactory;
return this;
}
/// <summary>
/// Configures the edit link for the entities from this entity set.
/// </summary>
/// <param name="editLinkBuilder">The builder used to generate the edit link.</param>
/// <returns>Returns itself so that multiple calls can be chained.</returns>
public virtual EntitySetConfiguration HasEditLink(SelfLinkBuilder<Uri> editLinkBuilder)
{
if (editLinkBuilder == null)
{
throw Error.ArgumentNull("editLinkBuilder");
}
_editLinkBuilder = editLinkBuilder;
return this;
}
/// <summary>
/// Configures the read link for the entities from this entity set.
/// </summary>
/// <param name="readLinkBuilder">The builder used to generate the read link.</param>
/// <returns>Returns itself so that multiple calls can be chained.</returns>
public virtual EntitySetConfiguration HasReadLink(SelfLinkBuilder<Uri> readLinkBuilder)
{
if (readLinkBuilder == null)
{
throw Error.ArgumentNull("readLinkBuilder");
}
_readLinkBuilder = readLinkBuilder;
return this;
}
/// <summary>
/// Configures the ID for the entities from this entity set.
/// </summary>
/// <param name="idLinkBuilder">The builder used to generate the ID.</param>
/// <returns>Returns itself so that multiple calls can be chained.</returns>
public virtual EntitySetConfiguration HasIdLink(SelfLinkBuilder<string> idLinkBuilder)
{
if (idLinkBuilder == null)
{
throw Error.ArgumentNull("idLinkBuilder");
}
_idLinkBuilder = idLinkBuilder;
return this;
}
/// <summary>
/// Configures the navigation link for the given navigation property for entities from this entity set.
/// </summary>
/// <param name="navigationProperty">The navigation property for which the navigation link is being generated.</param>
/// <param name="navigationLinkBuilder">The builder used to generate the navigation link.</param>
/// <returns>Returns itself so that multiple calls can be chained.</returns>
public virtual EntitySetConfiguration HasNavigationPropertyLink(NavigationPropertyConfiguration navigationProperty, NavigationLinkBuilder navigationLinkBuilder)
{
if (navigationProperty == null)
{
throw Error.ArgumentNull("navigationProperty");
}
if (navigationLinkBuilder == null)
{
throw Error.ArgumentNull("navigationLinkBuilder");
}
EntityTypeConfiguration declaringEntityType = navigationProperty.DeclaringEntityType;
if (!(declaringEntityType.IsAssignableFrom(EntityType) || EntityType.IsAssignableFrom(declaringEntityType)))
{
throw Error.Argument("navigationProperty", SRResources.NavigationPropertyNotInHierarchy, declaringEntityType.FullName, EntityType.FullName, Name);
}
_navigationPropertyLinkBuilders[navigationProperty] = navigationLinkBuilder;
return this;
}
/// <summary>
/// Configures the navigation link for the given navigation properties for entities from this entity set.
/// </summary>
/// <param name="navigationProperties">The navigation properties for which the navigation link is being generated.</param>
/// <param name="navigationLinkBuilder">The builder used to generate the navigation link.</param>
/// <returns>Returns itself so that multiple calls can be chained.</returns>
public virtual EntitySetConfiguration HasNavigationPropertiesLink(IEnumerable<NavigationPropertyConfiguration> navigationProperties, NavigationLinkBuilder navigationLinkBuilder)
{
if (navigationProperties == null)
{
throw Error.ArgumentNull("navigationProperties");
}
if (navigationLinkBuilder == null)
{
throw Error.ArgumentNull("navigationLinkBuilder");
}
foreach (NavigationPropertyConfiguration navigationProperty in navigationProperties)
{
HasNavigationPropertyLink(navigationProperty, navigationLinkBuilder);
}
return this;
}
/// <summary>
/// Binds the given navigation property to the target entity set.
/// </summary>
/// <param name="navigationConfiguration">The navigation property.</param>
/// <param name="targetEntitySet">The target entity set.</param>
/// <returns>The <see cref="NavigationPropertyBindingConfiguration"/> so that it can be further configured.</returns>
public virtual NavigationPropertyBindingConfiguration AddBinding(NavigationPropertyConfiguration navigationConfiguration, EntitySetConfiguration targetEntitySet)
{
if (navigationConfiguration == null)
{
throw Error.ArgumentNull("navigationConfiguration");
}
if (targetEntitySet == null)
{
throw Error.ArgumentNull("targetEntitySet");
}
EntityTypeConfiguration declaringEntityType = navigationConfiguration.DeclaringEntityType;
if (!(declaringEntityType.IsAssignableFrom(EntityType) || EntityType.IsAssignableFrom(declaringEntityType)))
{
throw Error.Argument("navigationConfiguration", SRResources.NavigationPropertyNotInHierarchy, declaringEntityType.FullName, EntityType.FullName, Name);
}
NavigationPropertyBindingConfiguration navigationPropertyBinding = null;
if (_entitySetBindings.ContainsKey(navigationConfiguration))
{
navigationPropertyBinding = _entitySetBindings[navigationConfiguration];
if (navigationPropertyBinding.EntitySet != targetEntitySet)
{
throw Error.NotSupported(SRResources.RebindingNotSupported);
}
}
else
{
navigationPropertyBinding = new NavigationPropertyBindingConfiguration(navigationConfiguration, targetEntitySet);
_entitySetBindings[navigationConfiguration] = navigationPropertyBinding;
}
return navigationPropertyBinding;
}
/// <summary>
/// Removes the binding for the given navigation property.
/// </summary>
/// <param name="navigationConfiguration">The navigation property</param>
public virtual void RemoveBinding(NavigationPropertyConfiguration navigationConfiguration)
{
if (_entitySetBindings.ContainsKey(navigationConfiguration))
{
_entitySetBindings.Remove(navigationConfiguration);
}
}
/// <summary>
/// Finds the binding for the given navigation property and tries to create it if it doesnot exist.
/// </summary>
/// <param name="navigationConfiguration">The navigation property.</param>
/// <returns>The <see cref="NavigationPropertyBindingConfiguration"/> so that it can be further configured.</returns>
public virtual NavigationPropertyBindingConfiguration FindBinding(NavigationPropertyConfiguration navigationConfiguration)
{
return FindBinding(navigationConfiguration, autoCreate: true);
}
/// <summary>
/// Finds the binding for the given navigation property.
/// </summary>
/// <param name="autoCreate">Tells whether the binding should be auto created if it does not exist.</param>
/// <param name="navigationConfiguration">The navigation property.</param>
/// <returns>The <see cref="NavigationPropertyBindingConfiguration"/> so that it can be further configured.</returns>
public virtual NavigationPropertyBindingConfiguration FindBinding(NavigationPropertyConfiguration navigationConfiguration, bool autoCreate)
{
if (navigationConfiguration == null)
{
throw Error.ArgumentNull("navigationConfiguration");
}
if (_entitySetBindings.ContainsKey(navigationConfiguration))
{
return _entitySetBindings[navigationConfiguration];
}
if (!autoCreate)
{
return null;
}
Type entityType = navigationConfiguration.RelatedClrType;
EntitySetConfiguration[] matchingSets = _modelBuilder.EntitySets.Where(es => es.EntityType.ClrType == entityType).ToArray();
if (matchingSets.Count() == 1)
{
return AddBinding(navigationConfiguration, matchingSets[0]);
}
else if (!matchingSets.Any())
{
return null;
}
else
{
throw Error.NotSupported(
SRResources.CannotAutoCreateMultipleCandidates,
navigationConfiguration.Name,
navigationConfiguration.DeclaringEntityType.FullName,
Name,
String.Join(", ", matchingSets.Select(entitySet => entitySet.Name)));
}
}
/// <summary>
/// Gets the entity set URL.
/// </summary>
/// <returns>The entity set URL.</returns>
[SuppressMessage("Microsoft.Design", "CA1055:UriReturnValuesShouldNotBeStrings", Justification = "This Url property is not required to be a valid Uri")]
[SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "Consistent with EF Has/Get pattern")]
public virtual string GetUrl()
{
return _url;
}
/// <summary>
/// Gets the builder used to generate self links for feeds for this entity set.
/// </summary>
/// <returns>The link builder.</returns>
[SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "Consistent with EF Has/Get pattern")]
public virtual Func<FeedContext, Uri> GetFeedSelfLink()
{
return _feedSelfLinkFactory;
}
/// <summary>
/// Gets the builder used to generate edit links for entries from this entity set.
/// </summary>
/// <returns>The link builder.</returns>
[SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "Consistent with EF Has/Get pattern")]
public virtual SelfLinkBuilder<Uri> GetEditLink()
{
return _editLinkBuilder;
}
/// <summary>
/// Gets the builder used to generate read links for entries from this entity set.
/// </summary>
/// <returns>The link builder.</returns>
[SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "Consistent with EF Has/Get pattern")]
public virtual SelfLinkBuilder<Uri> GetReadLink()
{
return _readLinkBuilder;
}
/// <summary>
/// Gets the builder used to generate ID for entries from this entity set.
/// </summary>
/// <returns>The builder.</returns>
[SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "Consistent with EF Has/Get pattern")]
public virtual SelfLinkBuilder<string> GetIdLink()
{
return _idLinkBuilder;
}
/// <summary>
/// Gets the builder used to generate navigation link for the given navigation property for entries from this entity set.
/// </summary>
/// <param name="navigationProperty">The navigation property.</param>
/// <returns>The link builder.</returns>
public virtual NavigationLinkBuilder GetNavigationPropertyLink(NavigationPropertyConfiguration navigationProperty)
{
if (navigationProperty == null)
{
throw Error.ArgumentNull("navigationProperty");
}
NavigationLinkBuilder navigationPropertyLinkBuilder;
_navigationPropertyLinkBuilders.TryGetValue(navigationProperty, out navigationPropertyLinkBuilder);
return navigationPropertyLinkBuilder;
}
/// <summary>
/// Gets the <see cref="NavigationPropertyBindingConfiguration"/> for the navigation property with the given name.
/// </summary>
/// <param name="propertyName">The name of the navigation property.</param>
/// <returns>The <see cref="NavigationPropertyBindingConfiguration" />.</returns>
public virtual NavigationPropertyBindingConfiguration FindBinding(string propertyName)
{
return Bindings.Single(b => b.NavigationProperty.Name == propertyName);
}
}
}

Просмотреть файл

@ -1,566 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq.Expressions;
using Microsoft.Data.Edm;
namespace System.Web.Http.OData.Builder
{
/// <summary>
/// Represents an <see cref="IEdmEntitySet"/> that can be built using <see cref="ODataModelBuilder"/>.
/// <typeparam name="TEntityType">The element type of the entity set.</typeparam>
/// </summary>
public class EntitySetConfiguration<TEntityType> where TEntityType : class
{
private EntitySetConfiguration _configuration;
private EntityTypeConfiguration<TEntityType> _entityType;
private ODataModelBuilder _modelBuilder;
internal EntitySetConfiguration(ODataModelBuilder modelBuilder, string name)
: this(modelBuilder, new EntitySetConfiguration(modelBuilder, typeof(TEntityType), name))
{
}
internal EntitySetConfiguration(ODataModelBuilder modelBuilder, EntitySetConfiguration configuration)
{
if (modelBuilder == null)
{
throw Error.ArgumentNull("modelBuilder");
}
if (configuration == null)
{
throw Error.ArgumentNull("configuration");
}
_configuration = configuration;
_modelBuilder = modelBuilder;
_entityType = new EntityTypeConfiguration<TEntityType>(modelBuilder, _configuration.EntityType);
}
internal EntitySetConfiguration EntitySet
{
get { return _configuration; }
}
/// <summary>
/// Gets the entity type contained in this entity set.
/// </summary>
public EntityTypeConfiguration<TEntityType> EntityType
{
get
{
return _entityType;
}
}
/// <summary>
/// Configures a many relationship from this entity type and binds the corresponding navigation property to the given entity set.
/// </summary>
/// <typeparam name="TTargetType">The target entity set type.</typeparam>
/// <typeparam name="TDerivedEntityType">The target entity type.</typeparam>
/// <param name="navigationExpression">A lambda expression representing the navigation property for the relationship.
/// For example, in C# <c>t => t.MyProperty</c> and in Visual Basic .NET <c>Function(t) t.MyProperty</c>.</param>
/// <param name="entitySetName">The target entity set name for the binding. It will be created if it does not exist.</param>
/// <returns>A configuration object that can be used to further configure the relationship further.</returns>
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Nested generic appropriate here")]
public NavigationPropertyBindingConfiguration HasManyBinding<TTargetType, TDerivedEntityType>(
Expression<Func<TDerivedEntityType, IEnumerable<TTargetType>>> navigationExpression, string entitySetName)
where TTargetType : class
where TDerivedEntityType : class, TEntityType
{
if (navigationExpression == null)
{
throw Error.ArgumentNull("navigationExpression");
}
EntityTypeConfiguration<TDerivedEntityType> derivedEntityType =
_modelBuilder.Entity<TDerivedEntityType>().DerivesFrom<TEntityType>();
return _configuration.AddBinding(derivedEntityType.HasMany(navigationExpression), _modelBuilder.EntitySet<TTargetType>(entitySetName)._configuration);
}
/// <summary>
/// Configures a many relationship from this entity type and binds the corresponding navigation property to the given entity set.
/// </summary>
/// <typeparam name="TTargetType">The target entity set type.</typeparam>
/// <param name="navigationExpression">A lambda expression representing the navigation property for the relationship.
/// For example, in C# <c>t => t.MyProperty</c> and in Visual Basic .NET <c>Function(t) t.MyProperty</c>.</param>
/// <param name="entitySetName">The target entity set name for the binding. It will be created if it does not exist.</param>
/// <returns>A configuration object that can be used to further configure the relationship.</returns>
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Nested generic appropriate here")]
public NavigationPropertyBindingConfiguration HasManyBinding<TTargetType>(
Expression<Func<TEntityType, IEnumerable<TTargetType>>> navigationExpression, string entitySetName)
where TTargetType : class
{
if (navigationExpression == null)
{
throw Error.ArgumentNull("navigationExpression");
}
return _configuration.AddBinding(EntityType.HasMany(navigationExpression), _modelBuilder.EntitySet<TTargetType>(entitySetName)._configuration);
}
/// <summary>
/// Configures a many relationship from this entity type and binds the corresponding navigation property to the given entity set.
/// </summary>
/// <typeparam name="TTargetType">The target entity set type.</typeparam>
/// <param name="navigationExpression">A lambda expression representing the navigation property for the relationship.
/// For example, in C# <c>t => t.MyProperty</c> and in Visual Basic .NET <c>Function(t) t.MyProperty</c>.</param>
/// <param name="targetSet">The target entity set for the binding.</param>
/// <returns>A configuration object that can be used to further configure the relationship further.</returns>
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Nested generic appropriate here")]
public NavigationPropertyBindingConfiguration HasManyBinding<TTargetType>(
Expression<Func<TEntityType, IEnumerable<TTargetType>>> navigationExpression,
EntitySetConfiguration<TTargetType> targetSet) where TTargetType : class
{
if (navigationExpression == null)
{
throw Error.ArgumentNull("navigationExpression");
}
if (targetSet == null)
{
throw Error.ArgumentNull("targetSet");
}
return _configuration.AddBinding(EntityType.HasMany(navigationExpression), targetSet._configuration);
}
/// <summary>
/// Configures a many relationship from this entity type and binds the corresponding navigation property to the given entity set.
/// </summary>
/// <typeparam name="TTargetType">The target entity set type.</typeparam>
/// <typeparam name="TDerivedEntityType">The target entity type.</typeparam>
/// <param name="navigationExpression">A lambda expression representing the navigation property for the relationship.
/// For example, in C# <c>t => t.MyProperty</c> and in Visual Basic .NET <c>Function(t) t.MyProperty</c>.</param>
/// <param name="targetSet">The target entity set for the binding.</param>
/// <returns>A configuration object that can be used to further configure the relationship further.</returns>
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Nested generic appropriate here")]
public NavigationPropertyBindingConfiguration HasManyBinding<TTargetType, TDerivedEntityType>(
Expression<Func<TDerivedEntityType, IEnumerable<TTargetType>>> navigationExpression,
EntitySetConfiguration<TTargetType> targetSet)
where TTargetType : class
where TDerivedEntityType : class, TEntityType
{
if (navigationExpression == null)
{
throw Error.ArgumentNull("navigationExpression");
}
if (targetSet == null)
{
throw Error.ArgumentNull("targetSet");
}
EntityTypeConfiguration<TDerivedEntityType> derivedEntityType =
_modelBuilder.Entity<TDerivedEntityType>().DerivesFrom<TEntityType>();
return _configuration.AddBinding(derivedEntityType.HasMany(navigationExpression), targetSet._configuration);
}
/// <summary>
/// Configures a required relationship from this entity type and binds the corresponding navigation property to the given entity set.
/// </summary>
/// <typeparam name="TTargetType">The target entity set type.</typeparam>
/// <param name="navigationExpression">A lambda expression representing the navigation property for the relationship.
/// For example, in C# <c>t => t.MyProperty</c> and in Visual Basic .NET <c>Function(t) t.MyProperty</c>.</param>
/// <param name="entitySetName">The target entity set name for the binding.</param>
/// <returns>A configuration object that can be used to further configure the relationship further.</returns>
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Nested generic appropriate here")]
public NavigationPropertyBindingConfiguration HasRequiredBinding<TTargetType>(
Expression<Func<TEntityType, TTargetType>> navigationExpression, string entitySetName)
where TTargetType : class
{
if (navigationExpression == null)
{
throw Error.ArgumentNull("navigationExpression");
}
return _configuration.AddBinding(EntityType.HasRequired(navigationExpression), _modelBuilder.EntitySet<TTargetType>(entitySetName)._configuration);
}
/// <summary>
/// Configures a required relationship from this entity type and binds the corresponding navigation property to the given entity set.
/// </summary>
/// <typeparam name="TTargetType">The target entity set type.</typeparam>
/// <typeparam name="TDerivedEntityType">The target entity type.</typeparam>
/// <param name="navigationExpression">A lambda expression representing the navigation property for the relationship.
/// For example, in C# <c>t => t.MyProperty</c> and in Visual Basic .NET <c>Function(t) t.MyProperty</c>.</param>
/// <param name="entitySetName">The target entity set name for the binding.</param>
/// <returns>A configuration object that can be used to further configure the relationship further.</returns>
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Nested generic appropriate here")]
public NavigationPropertyBindingConfiguration HasRequiredBinding<TTargetType, TDerivedEntityType>(
Expression<Func<TDerivedEntityType, TTargetType>> navigationExpression, string entitySetName)
where TTargetType : class
where TDerivedEntityType : class, TEntityType
{
if (navigationExpression == null)
{
throw Error.ArgumentNull("navigationExpression");
}
EntityTypeConfiguration<TDerivedEntityType> derivedEntityType =
_modelBuilder.Entity<TDerivedEntityType>().DerivesFrom<TEntityType>();
return _configuration.AddBinding(derivedEntityType.HasRequired(navigationExpression), _modelBuilder.EntitySet<TTargetType>(entitySetName)._configuration);
}
/// <summary>
/// Configures a required relationship from this entity type and binds the corresponding navigation property to the given entity set.
/// </summary>
/// <typeparam name="TTargetType">The target entity set type.</typeparam>
/// <param name="navigationExpression">A lambda expression representing the navigation property for the relationship.
/// For example, in C# <c>t => t.MyProperty</c> and in Visual Basic .NET <c>Function(t) t.MyProperty</c>.</param>
/// <param name="targetSet">The target entity set for the binding.</param>
/// <returns>A configuration object that can be used to further configure the relationship further.</returns>
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Nested generic appropriate here")]
public NavigationPropertyBindingConfiguration HasRequiredBinding<TTargetType>(
Expression<Func<TEntityType, TTargetType>> navigationExpression,
EntitySetConfiguration<TTargetType> targetSet) where TTargetType : class
{
if (navigationExpression == null)
{
throw Error.ArgumentNull("navigationExpression");
}
if (targetSet == null)
{
throw Error.ArgumentNull("targetSet");
}
return _configuration.AddBinding(EntityType.HasRequired(navigationExpression), targetSet._configuration);
}
/// <summary>
/// Configures a required relationship from this entity type and binds the corresponding navigation property to the given entity set.
/// </summary>
/// <typeparam name="TTargetType">The target entity set type.</typeparam>
/// <typeparam name="TDerivedEntityType">The target entity type.</typeparam>
/// <param name="navigationExpression">A lambda expression representing the navigation property for the relationship.
/// For example, in C# <c>t => t.MyProperty</c> and in Visual Basic .NET <c>Function(t) t.MyProperty</c>.</param>
/// <param name="targetSet">The target entity set for the binding.</param>
/// <returns>A configuration object that can be used to further configure the relationship further.</returns>
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Nested generic appropriate here")]
public NavigationPropertyBindingConfiguration HasRequiredBinding<TTargetType, TDerivedEntityType>(
Expression<Func<TDerivedEntityType, TTargetType>> navigationExpression,
EntitySetConfiguration<TTargetType> targetSet)
where TTargetType : class
where TDerivedEntityType : class, TEntityType
{
if (navigationExpression == null)
{
throw Error.ArgumentNull("navigationExpression");
}
if (targetSet == null)
{
throw Error.ArgumentNull("targetSet");
}
EntityTypeConfiguration<TDerivedEntityType> derivedEntityType =
_modelBuilder.Entity<TDerivedEntityType>().DerivesFrom<TEntityType>();
return _configuration.AddBinding(derivedEntityType.HasRequired(navigationExpression), targetSet._configuration);
}
/// <summary>
/// Configures an optional relationship from this entity type and binds the corresponding navigation property to the given entity set.
/// </summary>
/// <typeparam name="TTargetType">The target entity set type.</typeparam>
/// <param name="navigationExpression">A lambda expression representing the navigation property for the relationship.
/// For example, in C# <c>t => t.MyProperty</c> and in Visual Basic .NET <c>Function(t) t.MyProperty</c>.</param>
/// <param name="entitySetName">The target entity set name for the binding.</param>
/// <returns>A configuration object that can be used to further configure the relationship further.</returns>
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Nested generic appropriate here")]
public NavigationPropertyBindingConfiguration HasOptionalBinding<TTargetType>(
Expression<Func<TEntityType, TTargetType>> navigationExpression, string entitySetName)
where TTargetType : class
{
if (navigationExpression == null)
{
throw Error.ArgumentNull("navigationExpression");
}
return _configuration.AddBinding(EntityType.HasOptional(navigationExpression), _modelBuilder.EntitySet<TTargetType>(entitySetName)._configuration);
}
/// <summary>
/// Configures an optional relationship from this entity type and binds the corresponding navigation property to the given entity set.
/// </summary>
/// <typeparam name="TTargetType">The target entity set type.</typeparam>
/// <typeparam name="TDerivedEntityType">The target entity type.</typeparam>
/// <param name="navigationExpression">A lambda expression representing the navigation property for the relationship.
/// For example, in C# <c>t => t.MyProperty</c> and in Visual Basic .NET <c>Function(t) t.MyProperty</c>.</param>
/// <param name="entitySetName">The target entity set name for the binding. It will be created if it does not exist.</param>
/// <returns>A configuration object that can be used to further configure the relationship further.</returns>
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Nested generic appropriate here")]
public NavigationPropertyBindingConfiguration HasOptionalBinding<TTargetType, TDerivedEntityType>(
Expression<Func<TDerivedEntityType, TTargetType>> navigationExpression, string entitySetName)
where TTargetType : class
where TDerivedEntityType : class, TEntityType
{
if (navigationExpression == null)
{
throw Error.ArgumentNull("navigationExpression");
}
EntityTypeConfiguration<TDerivedEntityType> derivedEntityType =
_modelBuilder.Entity<TDerivedEntityType>().DerivesFrom<TEntityType>();
return _configuration.AddBinding(derivedEntityType.HasOptional(navigationExpression), _modelBuilder.EntitySet<TTargetType>(entitySetName)._configuration);
}
/// <summary>
/// Configures an optional relationship from this entity type and binds the corresponding navigation property to the given entity set.
/// </summary>
/// <typeparam name="TTargetType">The target entity set type.</typeparam>
/// <param name="navigationExpression">A lambda expression representing the navigation property for the relationship.
/// For example, in C# <c>t => t.MyProperty</c> and in Visual Basic .NET <c>Function(t) t.MyProperty</c>.</param>
/// <param name="targetSet">The target entity set for the binding.</param>
/// <returns>A configuration object that can be used to further configure the relationship further.</returns>
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Nested generic appropriate here")]
public NavigationPropertyBindingConfiguration HasOptionalBinding<TTargetType>(
Expression<Func<TEntityType, TTargetType>> navigationExpression,
EntitySetConfiguration<TTargetType> targetSet) where TTargetType : class
{
if (navigationExpression == null)
{
throw Error.ArgumentNull("navigationExpression");
}
if (targetSet == null)
{
throw Error.ArgumentNull("targetSet");
}
return _configuration.AddBinding(EntityType.HasOptional(navigationExpression), targetSet._configuration);
}
/// <summary>
/// Configures an optional relationship from this entity type and binds the corresponding navigation property to the given entity set.
/// </summary>
/// <typeparam name="TTargetType">The target entity set type.</typeparam>
/// <typeparam name="TDerivedEntityType">The target entity type.</typeparam>
/// <param name="navigationExpression">A lambda expression representing the navigation property for the relationship.
/// For example, in C# <c>t => t.MyProperty</c> and in Visual Basic .NET <c>Function(t) t.MyProperty</c>.</param>
/// <param name="targetSet">The target entity set for the binding.</param>
/// <returns>A configuration object that can be used to further configure the relationship further.</returns>
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Nested generic appropriate here")]
public NavigationPropertyBindingConfiguration HasOptionalBinding<TTargetType, TDerivedEntityType>(
Expression<Func<TDerivedEntityType, TTargetType>> navigationExpression,
EntitySetConfiguration<TTargetType> targetSet)
where TTargetType : class
where TDerivedEntityType : class, TEntityType
{
if (navigationExpression == null)
{
throw Error.ArgumentNull("navigationExpression");
}
if (targetSet == null)
{
throw Error.ArgumentNull("targetSet");
}
EntityTypeConfiguration<TDerivedEntityType> derivedEntityType =
_modelBuilder.Entity<TDerivedEntityType>().DerivesFrom<TEntityType>();
return _configuration.AddBinding(derivedEntityType.HasOptional(navigationExpression), targetSet._configuration);
}
/// <summary>
/// Adds a self link to the feed.
/// </summary>
/// <param name="feedSelfLinkFactory">The builder used to generate the link URL.</param>
public void HasFeedSelfLink(Func<FeedContext, string> feedSelfLinkFactory)
{
if (feedSelfLinkFactory == null)
{
throw Error.ArgumentNull("feedSelfLinkFactory");
}
_configuration.HasFeedSelfLink(feedContext => new Uri(feedSelfLinkFactory(feedContext)));
}
/// <summary>
/// Adds a self link to the feed.
/// </summary>
/// <param name="feedSelfLinkFactory">The builder used to generate the link URL.</param>
public void HasFeedSelfLink(Func<FeedContext, Uri> feedSelfLinkFactory)
{
if (feedSelfLinkFactory == null)
{
throw Error.ArgumentNull("feedSelfLinkFactory");
}
_configuration.HasFeedSelfLink(feedSelfLinkFactory);
}
/// <summary>
/// Configures the edit link for the entities from this entity set.
/// </summary>
/// <param name="editLinkFactory">The factory used to generate the edit link.</param>
/// <param name="followsConventions"><see langword="true"/> if the factory follows OData edit link conventions; otherwise, <see langword="false"/>.</param>
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Nested generic appropriate here")]
public void HasEditLink(Func<EntityInstanceContext<TEntityType>, string> editLinkFactory, bool followsConventions)
{
if (editLinkFactory == null)
{
throw Error.ArgumentNull("editLinkFactory");
}
HasEditLink(entityInstanceContext => new Uri(editLinkFactory(entityInstanceContext)), followsConventions);
}
/// <summary>
/// Configures the edit link for the entities from this entity set.
/// </summary>
/// <param name="editLinkFactory">The factory used to generate the edit link.</param>
/// <param name="followsConventions"><see langword="true"/> if the factory follows OData edit link conventions; otherwise, <see langword="false"/>.</param>
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Nested generic appropriate here")]
public void HasEditLink(Func<EntityInstanceContext<TEntityType>, Uri> editLinkFactory, bool followsConventions)
{
if (editLinkFactory == null)
{
throw Error.ArgumentNull("editLinkFactory");
}
_configuration.HasEditLink(new SelfLinkBuilder<Uri>((entity) => editLinkFactory(UpCastEntityInstanceContext(entity)), followsConventions));
}
/// <summary>
/// Configures the read link for the entities from this entity set.
/// </summary>
/// <param name="readLinkFactory">The factory used to generate the read link.</param>
/// <param name="followsConventions"><see langword="true"/> if the factory follows OData read link conventions; otherwise, <see langword="false"/>.</param>
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Nested generic appropriate here")]
public void HasReadLink(Func<EntityInstanceContext<TEntityType>, string> readLinkFactory, bool followsConventions)
{
if (readLinkFactory == null)
{
throw Error.ArgumentNull("readLinkFactory");
}
HasReadLink(entityInstanceContext => new Uri(readLinkFactory(entityInstanceContext)), followsConventions);
}
/// <summary>
/// Configures the read link for the entities from this entity set.
/// </summary>
/// <param name="readLinkFactory">The factory used to generate the read link.</param>
/// <param name="followsConventions"><see langword="true"/> if the factory follows OData read link conventions; otherwise, <see langword="false"/>.</param>
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Nested generic appropriate here")]
public void HasReadLink(Func<EntityInstanceContext<TEntityType>, Uri> readLinkFactory, bool followsConventions)
{
if (readLinkFactory == null)
{
throw Error.ArgumentNull("readLinkFactory");
}
_configuration.HasReadLink(new SelfLinkBuilder<Uri>((entity) => readLinkFactory(UpCastEntityInstanceContext(entity)), followsConventions));
}
/// <summary>
/// Configures the ID link for the entities from this entity set.
/// </summary>
/// <param name="idLinkFactory">The factory used to generate the ID link.</param>
/// <param name="followsConventions"><see langword="true"/> if the factory follows OData ID link conventions; otherwise, <see langword="false"/>.</param>
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Nested generic appropriate here")]
public void HasIdLink(Func<EntityInstanceContext<TEntityType>, string> idLinkFactory, bool followsConventions)
{
if (idLinkFactory == null)
{
throw Error.ArgumentNull("idLinkFactory");
}
_configuration.HasIdLink(new SelfLinkBuilder<string>((entity) => idLinkFactory(UpCastEntityInstanceContext(entity)), followsConventions));
}
/// <summary>
/// Configures the navigation link for the given navigation property for entities from this entity set.
/// </summary>
/// <param name="navigationProperty">The navigation property for which the navigation link is being generated.</param>
/// <param name="navigationLinkFactory">The factory used to generate the navigation link.</param>
/// <param name="followsConventions"><see langword="true"/> if the factory follows OData navigation link conventions; otherwise, <see langword="false"/>.</param>
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Nested generic appropriate here")]
public void HasNavigationPropertyLink(NavigationPropertyConfiguration navigationProperty, Func<EntityInstanceContext<TEntityType>, IEdmNavigationProperty, Uri> navigationLinkFactory, bool followsConventions)
{
if (navigationProperty == null)
{
throw Error.ArgumentNull("navigationProperty");
}
if (navigationLinkFactory == null)
{
throw Error.ArgumentNull("navigationLinkFactory");
}
_configuration.HasNavigationPropertyLink(navigationProperty, new NavigationLinkBuilder((entity, property) => navigationLinkFactory(UpCastEntityInstanceContext(entity), property), followsConventions));
}
/// <summary>
/// Configures the navigation link for the given navigation properties for entities from this entity set.
/// </summary>
/// <param name="navigationProperties">The navigation properties for which the navigation link is being generated.</param>
/// <param name="navigationLinkFactory">The factory used to generate the navigation link.</param>
/// <param name="followsConventions"><see langword="true"/> if the factory follows OData navigation link conventions; otherwise, <see langword="false"/>.</param>
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Nested generic appropriate here")]
public void HasNavigationPropertiesLink(IEnumerable<NavigationPropertyConfiguration> navigationProperties, Func<EntityInstanceContext<TEntityType>, IEdmNavigationProperty, Uri> navigationLinkFactory, bool followsConventions)
{
if (navigationProperties == null)
{
throw Error.ArgumentNull("navigationProperties");
}
if (navigationLinkFactory == null)
{
throw Error.ArgumentNull("navigationLinkFactory");
}
_configuration.HasNavigationPropertiesLink(navigationProperties, new NavigationLinkBuilder((entity, property) => navigationLinkFactory(UpCastEntityInstanceContext(entity), property), followsConventions));
}
/// <summary>
/// Finds the <see cref="NavigationPropertyBindingConfiguration"/> for the navigation property with the given name.
/// </summary>
/// <param name="propertyName">The name of the navigation property.</param>
/// <returns>The binding, if found; otherwise, <see langword="null"/>.</returns>
public NavigationPropertyBindingConfiguration FindBinding(string propertyName)
{
return _configuration.FindBinding(propertyName);
}
/// <summary>
/// Finds the <see cref="NavigationPropertyBindingConfiguration"/> for the given navigation property and creates it if it does not exist.
/// </summary>
/// <param name="navigationConfiguration">The navigation property.</param>
/// <returns>The binding if found else the created binding.</returns>
public NavigationPropertyBindingConfiguration FindBinding(NavigationPropertyConfiguration navigationConfiguration)
{
return _configuration.FindBinding(navigationConfiguration, autoCreate: true);
}
/// <summary>
/// Finds the <see cref="NavigationPropertyBindingConfiguration"/> for the given navigation property.
/// </summary>
/// <param name="navigationConfiguration">The navigation property.</param>
/// <param name="autoCreate">Represents a value specifying if the binding should be created if it is not found.</param>
/// <returns>The binding if found.</returns>
public NavigationPropertyBindingConfiguration FindBinding(NavigationPropertyConfiguration navigationConfiguration, bool autoCreate)
{
return _configuration.FindBinding(navigationConfiguration, autoCreate);
}
private static EntityInstanceContext<TEntityType> UpCastEntityInstanceContext(EntityInstanceContext context)
{
return new EntityInstanceContext<TEntityType>
{
SerializerContext = context.SerializerContext,
EdmObject = context.EdmObject,
EntityType = context.EntityType
};
}
}
}

Просмотреть файл

@ -1,293 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Web.Http.OData.Formatter;
using System.Web.Http.OData.Formatter.Serialization;
using System.Web.Http.OData.Properties;
using Microsoft.Data.Edm;
namespace System.Web.Http.OData.Builder
{
/// <summary>
/// <see cref="EntitySetLinkBuilderAnnotation" /> is a class used to annotate an <see cref="IEdmEntitySet" /> inside an <see cref="IEdmModel" />
/// with information about how to build links related to that entity set.
/// </summary>
public class EntitySetLinkBuilderAnnotation
{
private readonly Func<FeedContext, Uri> _feedSelfLinkBuilder;
private readonly SelfLinkBuilder<string> _idLinkBuilder;
private readonly SelfLinkBuilder<Uri> _editLinkBuilder;
private readonly SelfLinkBuilder<Uri> _readLinkBuilder;
private readonly Dictionary<IEdmNavigationProperty, NavigationLinkBuilder> _navigationPropertyLinkBuilderLookup = new Dictionary<IEdmNavigationProperty, NavigationLinkBuilder>();
private readonly string _entitySetName;
/// <summary>
/// Initializes a new instance of the <see cref="EntitySetLinkBuilderAnnotation" /> class.
/// </summary>
/// <remarks>The default constructor is intended for use by unit testing only.</remarks>
public EntitySetLinkBuilderAnnotation()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="EntitySetLinkBuilderAnnotation"/> class.
/// </summary>
/// <param name="entitySet">The entity set for which the link builder is being constructed.</param>
/// <param name="model">The EDM model that this entity set belongs to.</param>
/// <remarks>This constructor creates a link builder that generates URL's that follow OData conventions for the given entity set.</remarks>
public EntitySetLinkBuilderAnnotation(IEdmEntitySet entitySet, IEdmModel model)
{
if (entitySet == null)
{
throw Error.ArgumentNull("entitySet");
}
if (model == null)
{
throw Error.ArgumentNull("model");
}
IEdmEntityType elementType = entitySet.ElementType;
IEnumerable<IEdmEntityType> derivedTypes = model.FindAllDerivedTypes(elementType).Cast<IEdmEntityType>();
bool derivedTypesDefineNavigationProperty = false;
// Add navigation link builders for all navigation properties of entity.
foreach (IEdmNavigationProperty navigationProperty in elementType.NavigationProperties())
{
Func<EntityInstanceContext, IEdmNavigationProperty, Uri> navigationLinkFactory =
(entityInstanceContext, navProperty) => entityInstanceContext.GenerateNavigationPropertyLink(navProperty, includeCast: false);
AddNavigationPropertyLinkBuilder(navigationProperty, new NavigationLinkBuilder(navigationLinkFactory, followsConventions: true));
}
// Add navigation link builders for all navigation properties in derived types.
foreach (IEdmEntityType derivedEntityType in derivedTypes)
{
foreach (IEdmNavigationProperty navigationProperty in derivedEntityType.DeclaredNavigationProperties())
{
derivedTypesDefineNavigationProperty = true;
Func<EntityInstanceContext, IEdmNavigationProperty, Uri> navigationLinkFactory =
(entityInstanceContext, navProperty) => entityInstanceContext.GenerateNavigationPropertyLink(navProperty, includeCast: true);
AddNavigationPropertyLinkBuilder(navigationProperty, new NavigationLinkBuilder(navigationLinkFactory, followsConventions: true));
}
}
_entitySetName = entitySet.Name;
_feedSelfLinkBuilder = (feedContext) => feedContext.GenerateFeedSelfLink();
Func<EntityInstanceContext, string> selfLinkFactory =
(entityInstanceContext) => entityInstanceContext.GenerateSelfLink(includeCast: derivedTypesDefineNavigationProperty);
_idLinkBuilder = new SelfLinkBuilder<string>(selfLinkFactory, followsConventions: true);
}
/// <summary>
/// Constructs an instance of an <see cref="EntitySetLinkBuilderAnnotation" />.
/// <remarks>Every parameter must be non-null.</remarks>
/// </summary>
public EntitySetLinkBuilderAnnotation(
IEdmEntitySet entitySet,
Func<FeedContext, Uri> feedSelfLinkBuilder,
SelfLinkBuilder<string> idLinkBuilder,
SelfLinkBuilder<Uri> editLinkBuilder,
SelfLinkBuilder<Uri> readLinkBuilder)
{
if (entitySet == null)
{
throw Error.ArgumentNull("entitySet");
}
_entitySetName = entitySet.Name;
_feedSelfLinkBuilder = feedSelfLinkBuilder;
_idLinkBuilder = idLinkBuilder;
_editLinkBuilder = editLinkBuilder;
_readLinkBuilder = readLinkBuilder;
}
/// <summary>
/// Constructs an instance of an <see cref="EntitySetLinkBuilderAnnotation" /> from an <see cref="EntitySetConfiguration" />.
/// </summary>
public EntitySetLinkBuilderAnnotation(EntitySetConfiguration entitySet)
{
if (entitySet == null)
{
throw Error.ArgumentNull("entitySet");
}
_entitySetName = entitySet.Name;
_feedSelfLinkBuilder = entitySet.GetFeedSelfLink();
_idLinkBuilder = entitySet.GetIdLink();
_editLinkBuilder = entitySet.GetEditLink();
_readLinkBuilder = entitySet.GetReadLink();
}
/// <summary>
/// Register a link builder for a <see cref="IEdmNavigationProperty" /> that navigates from Entities in this EntitySet.
/// </summary>
public void AddNavigationPropertyLinkBuilder(IEdmNavigationProperty navigationProperty, NavigationLinkBuilder linkBuilder)
{
_navigationPropertyLinkBuilderLookup[navigationProperty] = linkBuilder;
}
/// <summary>
/// Build a self-link URI given a <see cref="FeedContext" />.
/// </summary>
public virtual Uri BuildFeedSelfLink(FeedContext context)
{
if (context == null)
{
throw Error.ArgumentNull("context");
}
if (_feedSelfLinkBuilder == null)
{
return null;
}
return _feedSelfLinkBuilder(context);
}
/// <summary>
/// Constructs the <see cref="EntitySelfLinks" /> for a particular <see cref="EntityInstanceContext" /> and <see cref="ODataMetadataLevel" />.
/// </summary>
public virtual EntitySelfLinks BuildEntitySelfLinks(EntityInstanceContext instanceContext, ODataMetadataLevel metadataLevel)
{
EntitySelfLinks selfLinks = new EntitySelfLinks();
selfLinks.IdLink = BuildIdLink(instanceContext, metadataLevel);
selfLinks.EditLink = BuildEditLink(instanceContext, metadataLevel, selfLinks.IdLink);
selfLinks.ReadLink = BuildReadLink(instanceContext, metadataLevel, selfLinks.EditLink);
return selfLinks;
}
/// <summary>
/// Constructs the IdLink for a particular <see cref="EntityInstanceContext" /> and <see cref="ODataMetadataLevel" />.
/// </summary>
public virtual string BuildIdLink(EntityInstanceContext instanceContext, ODataMetadataLevel metadataLevel)
{
if (instanceContext == null)
{
throw Error.ArgumentNull("instanceContext");
}
if (_idLinkBuilder == null)
{
throw Error.InvalidOperation(SRResources.NoIdLinkFactoryFound, _entitySetName);
}
if (IsDefaultOrFull(metadataLevel) || (IsMinimal(metadataLevel) && !_idLinkBuilder.FollowsConventions))
{
return _idLinkBuilder.Factory(instanceContext);
}
else
{
// client can infer it and didn't ask for it.
return null;
}
}
/// <summary>
/// Constructs the EditLink URL for a particular <see cref="EntityInstanceContext" /> and <see cref="ODataMetadataLevel" />.
/// </summary>
public virtual Uri BuildEditLink(EntityInstanceContext instanceContext, ODataMetadataLevel metadataLevel, string idLink)
{
if (instanceContext == null)
{
throw Error.ArgumentNull("instanceContext");
}
if (_editLinkBuilder == null)
{
// edit link is the same as id link. emit only in default metadata mode.
if (metadataLevel == ODataMetadataLevel.Default)
{
return new Uri(idLink);
}
}
else if (IsDefaultOrFull(metadataLevel) ||
(IsMinimal(metadataLevel) && !_editLinkBuilder.FollowsConventions))
{
// edit link is the not the same as id link. Generate if the client asked for it (full metadata modes) or
// if the client cannot infer it (not follow conventions).
return _editLinkBuilder.Factory(instanceContext);
}
// client can infer it and didn't ask for it.
return null;
}
/// <summary>
/// Constructs a ReadLink URL for a particular <see cref="EntityInstanceContext" /> and <see cref="ODataMetadataLevel" />.
/// </summary>
public virtual Uri BuildReadLink(EntityInstanceContext instanceContext, ODataMetadataLevel metadataLevel, Uri editLink)
{
if (instanceContext == null)
{
throw Error.ArgumentNull("instanceContext");
}
if (_readLinkBuilder == null)
{
// read link is the same as edit link. emit only in default metadata mode.
if (metadataLevel == ODataMetadataLevel.Default)
{
return editLink;
}
}
else if (IsDefaultOrFull(metadataLevel) ||
(IsMinimal(metadataLevel) && !_readLinkBuilder.FollowsConventions))
{
// read link is not the same as edit link. Generate if the client asked for it (full metadata modes) or
// if the client cannot infer it (not follow conventions).
return _readLinkBuilder.Factory(instanceContext);
}
// client can infer it and didn't ask for it.
return null;
}
/// <summary>
/// Constructs a NavigationLink for a particular <see cref="EntityInstanceContext" />, <see cref="IEdmNavigationProperty" /> and <see cref="ODataMetadataLevel" />.
/// </summary>
public virtual Uri BuildNavigationLink(EntityInstanceContext instanceContext, IEdmNavigationProperty navigationProperty, ODataMetadataLevel metadataLevel)
{
if (instanceContext == null)
{
throw Error.ArgumentNull("instanceContext");
}
if (navigationProperty == null)
{
throw Error.ArgumentNull("navigationProperty");
}
NavigationLinkBuilder navigationLinkBuilder;
if (!_navigationPropertyLinkBuilderLookup.TryGetValue(navigationProperty, out navigationLinkBuilder))
{
throw Error.Argument("navigationProperty", SRResources.NoNavigationLinkFactoryFound, navigationProperty.Name, navigationProperty.DeclaringEntityType(), _entitySetName);
}
Contract.Assert(navigationLinkBuilder != null);
if (IsDefaultOrFull(metadataLevel) ||
(IsMinimal(metadataLevel) && !navigationLinkBuilder.FollowsConventions))
{
return navigationLinkBuilder.Factory(instanceContext, navigationProperty);
}
else
{
// client can infer it and didn't ask for it.
return null;
}
}
private static bool IsDefaultOrFull(ODataMetadataLevel level)
{
return level == ODataMetadataLevel.Default || level == ODataMetadataLevel.FullMetadata;
}
private static bool IsMinimal(ODataMetadataLevel level)
{
return level == ODataMetadataLevel.MinimalMetadata;
}
}
}

Просмотреть файл

@ -1,9 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
namespace System.Web.Http.OData.Builder
{
internal class EntitySetUrlAnnotation
{
public string Url { get; set; }
}
}

Просмотреть файл

@ -1,322 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Web.Http.OData.Properties;
using Microsoft.Data.Edm;
namespace System.Web.Http.OData.Builder
{
// TODO: add support for FK properties
// CUT: support for bi-directional properties
/// <summary>
/// Represents an <see cref="IEdmEntityType"/> that can be built using <see cref="ODataModelBuilder"/>.
/// </summary>
public class EntityTypeConfiguration : StructuralTypeConfiguration
{
private List<PrimitivePropertyConfiguration> _keys = new List<PrimitivePropertyConfiguration>();
private EntityTypeConfiguration _baseType;
private bool _baseTypeConfigured;
/// <summary>
/// Initializes a new instance of the <see cref="EntityTypeConfiguration"/> class.
/// </summary>
/// <remarks>The default constructor is intended for use by unit testing only.</remarks>
public EntityTypeConfiguration()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="EntityTypeConfiguration"/> class.
/// </summary>
/// <param name="modelBuilder">The <see cref="ODataModelBuilder"/> being used.</param>
/// <param name="clrType">The backing CLR type for this entity type.</param>
public EntityTypeConfiguration(ODataModelBuilder modelBuilder, Type clrType)
: base(modelBuilder, clrType)
{
}
/// <summary>
/// Gets the <see cref="EdmTypeKind"/> of this <see cref="IEdmTypeConfiguration"/>
/// </summary>
public override EdmTypeKind Kind
{
get { return EdmTypeKind.Entity; }
}
/// <summary>
/// Gets the collection of <see cref="NavigationPropertyConfiguration"/> of this entity type.
/// </summary>
public virtual IEnumerable<NavigationPropertyConfiguration> NavigationProperties
{
get
{
return ExplicitProperties.Values.OfType<NavigationPropertyConfiguration>();
}
}
/// <summary>
/// Gets the collection of keys for this entity type.
/// </summary>
public virtual IEnumerable<PrimitivePropertyConfiguration> Keys
{
get
{
return _keys;
}
}
/// <summary>
/// Gets or sets a value indicating whether this type is abstract.
/// </summary>
public virtual bool? IsAbstract { get; set; }
/// <summary>
/// Gets or sets the base type of this entity type.
/// </summary>
public virtual EntityTypeConfiguration BaseType
{
get
{
return _baseType;
}
set
{
DerivesFrom(value);
}
}
/// <summary>
/// Gets a value that represents whether the base type is explicitly configured or inferred.
/// </summary>
public virtual bool BaseTypeConfigured
{
get
{
return _baseTypeConfigured;
}
}
/// <summary>
/// Marks this entity type as abstract.
/// </summary>
/// <returns>Returns itself so that multiple calls can be chained.</returns>
public virtual EntityTypeConfiguration Abstract()
{
IsAbstract = true;
return this;
}
/// <summary>
/// Configures the key property(s) for this entity type.
/// </summary>
/// <param name="keyProperty">The property to be added to the key properties of this entity type.</param>
/// <returns>Returns itself so that multiple calls can be chained.</returns>
public virtual EntityTypeConfiguration HasKey(PropertyInfo keyProperty)
{
if (BaseType != null)
{
throw Error.InvalidOperation(SRResources.CannotDefineKeysOnDerivedTypes, FullName, BaseType.FullName);
}
PrimitivePropertyConfiguration propertyConfig = AddProperty(keyProperty);
// keys are always required
propertyConfig.IsRequired();
if (!_keys.Contains(propertyConfig))
{
_keys.Add(propertyConfig);
}
return this;
}
/// <summary>
/// Removes the property from the entity keys collection.
/// </summary>
/// <param name="keyProperty">The key to be removed.</param>
/// <remarks>This method just disable the property to be not a key anymore. It does not remove the property all together.
/// To remove the property completely, use the method <see cref="RemoveProperty"/></remarks>
public virtual void RemoveKey(PrimitivePropertyConfiguration keyProperty)
{
if (keyProperty == null)
{
throw Error.ArgumentNull("keyProperty");
}
_keys.Remove(keyProperty);
}
/// <summary>
/// Sets the base type of this entity type to <c>null</c> meaning that this entity type
/// does not derive from anything.
/// </summary>
/// <returns>Returns itself so that multiple calls can be chained.</returns>
public virtual EntityTypeConfiguration DerivesFromNothing()
{
_baseType = null;
_baseTypeConfigured = true;
return this;
}
/// <summary>
/// Sets the base type of this entity type.
/// </summary>
/// <param name="baseType">The base entity type.</param>
/// <returns>Returns itself so that multiple calls can be chained.</returns>
public virtual EntityTypeConfiguration DerivesFrom(EntityTypeConfiguration baseType)
{
if (baseType == null)
{
throw Error.ArgumentNull("baseType");
}
_baseType = baseType;
_baseTypeConfigured = true;
if (!baseType.ClrType.IsAssignableFrom(ClrType) || baseType.ClrType == ClrType)
{
throw Error.Argument("baseType", SRResources.TypeDoesNotInheritFromBaseType, ClrType.FullName, baseType.ClrType.FullName);
}
if (Keys.Any())
{
throw Error.InvalidOperation(SRResources.CannotDefineKeysOnDerivedTypes, FullName, baseType.FullName);
}
foreach (PropertyConfiguration property in Properties)
{
ValidatePropertyNotAlreadyDefinedInBaseTypes(property.PropertyInfo);
}
foreach (PropertyConfiguration property in this.DerivedProperties())
{
ValidatePropertyNotAlreadyDefinedInDerivedTypes(property.PropertyInfo);
}
return this;
}
/// <summary>
/// Adds a new EDM primitive property to this entity type.
/// </summary>
/// <param name="propertyInfo">The backing CLR property.</param>
/// <returns>Returns the <see cref="PrimitivePropertyConfiguration"/> of the added property.</returns>
public override PrimitivePropertyConfiguration AddProperty(PropertyInfo propertyInfo)
{
ValidatePropertyNotAlreadyDefinedInBaseTypes(propertyInfo);
ValidatePropertyNotAlreadyDefinedInDerivedTypes(propertyInfo);
return base.AddProperty(propertyInfo);
}
/// <summary>
/// Adds a new EDM complex property to this entity type.
/// </summary>
/// <param name="propertyInfo">The backing CLR property.</param>
/// <returns>Returns the <see cref="ComplexPropertyConfiguration"/> of the added property.</returns>
public override ComplexPropertyConfiguration AddComplexProperty(PropertyInfo propertyInfo)
{
ValidatePropertyNotAlreadyDefinedInBaseTypes(propertyInfo);
ValidatePropertyNotAlreadyDefinedInDerivedTypes(propertyInfo);
return base.AddComplexProperty(propertyInfo);
}
/// <summary>
/// Adds a new EDM collection property to this entity type.
/// </summary>
/// <param name="propertyInfo">The backing CLR property.</param>
/// <returns>Returns the <see cref="CollectionPropertyConfiguration"/> of the added property.</returns>
public override CollectionPropertyConfiguration AddCollectionProperty(PropertyInfo propertyInfo)
{
ValidatePropertyNotAlreadyDefinedInBaseTypes(propertyInfo);
ValidatePropertyNotAlreadyDefinedInDerivedTypes(propertyInfo);
return base.AddCollectionProperty(propertyInfo);
}
/// <summary>
/// Adds a new EDM navigation property to this entity type.
/// </summary>
/// <param name="navigationProperty">The backing CLR property.</param>
/// <param name="multiplicity">The <see cref="EdmMultiplicity"/> of the navigation property.</param>
/// <returns>Returns the <see cref="NavigationPropertyConfiguration"/> of the added property.</returns>
public virtual NavigationPropertyConfiguration AddNavigationProperty(PropertyInfo navigationProperty, EdmMultiplicity multiplicity)
{
if (navigationProperty == null)
{
throw Error.ArgumentNull("navigationProperty");
}
if (!navigationProperty.ReflectedType.IsAssignableFrom(ClrType))
{
throw Error.Argument("navigationProperty", SRResources.PropertyDoesNotBelongToType, navigationProperty.Name, ClrType.FullName);
}
ValidatePropertyNotAlreadyDefinedInBaseTypes(navigationProperty);
ValidatePropertyNotAlreadyDefinedInDerivedTypes(navigationProperty);
PropertyConfiguration propertyConfig;
NavigationPropertyConfiguration navigationPropertyConfig;
if (ExplicitProperties.ContainsKey(navigationProperty))
{
propertyConfig = ExplicitProperties[navigationProperty];
if (propertyConfig.Kind != PropertyKind.Navigation)
{
throw Error.Argument("navigationProperty", SRResources.MustBeNavigationProperty, navigationProperty.Name, ClrType.FullName);
}
navigationPropertyConfig = propertyConfig as NavigationPropertyConfiguration;
if (navigationPropertyConfig.Multiplicity != multiplicity)
{
throw Error.Argument("navigationProperty", SRResources.MustHaveMatchingMultiplicity, navigationProperty.Name, multiplicity);
}
}
else
{
navigationPropertyConfig = new NavigationPropertyConfiguration(navigationProperty, multiplicity, this);
ExplicitProperties[navigationProperty] = navigationPropertyConfig;
// make sure the related type is configured
ModelBuilder.AddEntity(navigationPropertyConfig.RelatedClrType);
}
return navigationPropertyConfig;
}
/// <summary>
/// Removes the property from the entity.
/// </summary>
/// <param name="propertyInfo">The <see cref="PropertyInfo"/> of the property to be removed.</param>
public override void RemoveProperty(PropertyInfo propertyInfo)
{
base.RemoveProperty(propertyInfo);
_keys.RemoveAll(p => p.PropertyInfo == propertyInfo);
}
private void ValidatePropertyNotAlreadyDefinedInBaseTypes(PropertyInfo propertyInfo)
{
PropertyConfiguration baseProperty = this.DerivedProperties().Where(p => p.Name == propertyInfo.Name).FirstOrDefault();
if (baseProperty != null)
{
throw Error.Argument("propertyInfo", SRResources.CannotRedefineBaseTypeProperty, propertyInfo.Name, baseProperty.PropertyInfo.ReflectedType.FullName);
}
}
private void ValidatePropertyNotAlreadyDefinedInDerivedTypes(PropertyInfo propertyInfo)
{
foreach (EntityTypeConfiguration derivedEntity in ModelBuilder.DerivedTypes(this))
{
PropertyConfiguration propertyInDerivedType = derivedEntity.Properties.Where(p => p.Name == propertyInfo.Name).FirstOrDefault();
if (propertyInDerivedType != null)
{
throw Error.Argument("propertyInfo", SRResources.PropertyAlreadyDefinedInDerivedType, propertyInfo.Name, FullName, derivedEntity.FullName);
}
}
}
}
}

Просмотреть файл

@ -1,195 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
using System.Linq.Expressions;
using System.Reflection;
using Microsoft.Data.Edm;
namespace System.Web.Http.OData.Builder
{
/// <summary>
/// Represents an <see cref="IEdmEntityType"/> that can be built using <see cref="ODataModelBuilder"/>.
/// </summary>
/// <typeparam name="TEntityType">The backing CLR type for this <see cref="IEdmEntityType"/>.</typeparam>
public class EntityTypeConfiguration<TEntityType> : StructuralTypeConfiguration<TEntityType> where TEntityType : class
{
private EntityTypeConfiguration _configuration;
private EntityCollectionConfiguration<TEntityType> _collection;
private ODataModelBuilder _modelBuilder;
/// <summary>
/// Initializes a new instance of <see cref="EntityTypeConfiguration"/>.
/// </summary>
/// <param name="modelBuilder">The <see cref="ODataModelBuilder"/> being used.</param>
internal EntityTypeConfiguration(ODataModelBuilder modelBuilder)
: this(modelBuilder, new EntityTypeConfiguration(modelBuilder, typeof(TEntityType)))
{
}
internal EntityTypeConfiguration(ODataModelBuilder modelBuilder, EntityTypeConfiguration configuration)
: base(configuration)
{
Contract.Assert(modelBuilder != null);
Contract.Assert(configuration != null);
_modelBuilder = modelBuilder;
_configuration = configuration;
_collection = new EntityCollectionConfiguration<TEntityType>(configuration);
}
/// <summary>
/// Gets the base type of this entity type.
/// </summary>
public EntityTypeConfiguration BaseType
{
get
{
return _configuration.BaseType;
}
}
/// <summary>
/// Gets the collection of <see cref="NavigationPropertyConfiguration"/> of this entity type.
/// </summary>
public IEnumerable<NavigationPropertyConfiguration> NavigationProperties
{
get { return _configuration.NavigationProperties; }
}
/// <summary>
/// Used to access a Collection of Entities throw which you can configure
/// actions that are bindable to EntityCollections.
/// </summary>
public EntityCollectionConfiguration<TEntityType> Collection
{
get { return _collection; }
}
/// <summary>
/// Marks this entity type as abstract.
/// </summary>
/// <returns>Returns itself so that multiple calls can be chained.</returns>
public EntityTypeConfiguration<TEntityType> Abstract()
{
_configuration.IsAbstract = true;
return this;
}
/// <summary>
/// Sets the base type of this entity type to <c>null</c> meaning that this entity type
/// does not derive from anything.
/// </summary>
/// <returns>Returns itself so that multiple calls can be chained.</returns>
public EntityTypeConfiguration<TEntityType> DerivesFromNothing()
{
_configuration.DerivesFromNothing();
return this;
}
/// <summary>
/// Sets the base type of this entity type.
/// </summary>
/// <typeparam name="TBaseType">The base entity type.</typeparam>
/// <returns>Returns itself so that multiple calls can be chained.</returns>
[SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "typeof(TBaseType) is used and getting it as a generic argument is cleaner")]
public EntityTypeConfiguration<TEntityType> DerivesFrom<TBaseType>() where TBaseType : class
{
EntityTypeConfiguration<TBaseType> baseEntityType = _modelBuilder.Entity<TBaseType>();
_configuration.DerivesFrom(baseEntityType._configuration);
return this;
}
/// <summary>
/// Configures the key property(s) for this entity type.
/// </summary>
/// <typeparam name="TKey">The type of key.</typeparam>
/// <param name="keyDefinitionExpression">A lambda expression representing the property to be used as the primary key. For example, in C# t => t.Id and in Visual Basic .Net Function(t) t.Id.</param>
/// <returns>Returns itself so that multiple calls can be chained.</returns>
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Nested generic appropriate here")]
[SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "Explicit Expression generic type is more clear")]
public EntityTypeConfiguration<TEntityType> HasKey<TKey>(Expression<Func<TEntityType, TKey>> keyDefinitionExpression)
{
ICollection<PropertyInfo> properties = PropertySelectorVisitor.GetSelectedProperties(keyDefinitionExpression);
foreach (PropertyInfo property in properties)
{
_configuration.HasKey(property);
}
return this;
}
/// <summary>
/// Configures a many relationship from this entity type.
/// </summary>
/// <typeparam name="TTargetEntity">The type of the entity at the other end of the relationship.</typeparam>
/// <param name="navigationPropertyExpression">A lambda expression representing the navigation property for the relationship. For example, in C# <c>t => t.MyProperty</c> and in Visual Basic .NET <c>Function(t) t.MyProperty</c>.</param>
/// <returns>A configuration object that can be used to further configure the relationship.</returns>
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Nested generic appropriate here")]
[SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "Explicit Expression generic type is more clear")]
public NavigationPropertyConfiguration HasMany<TTargetEntity>(Expression<Func<TEntityType, IEnumerable<TTargetEntity>>> navigationPropertyExpression) where TTargetEntity : class
{
return GetOrCreateNavigationProperty(navigationPropertyExpression, EdmMultiplicity.Many);
}
/// <summary>
/// Configures an optional relationship from this entity type.
/// </summary>
/// <typeparam name="TTargetEntity">The type of the entity at the other end of the relationship.</typeparam>
/// <param name="navigationPropertyExpression">A lambda expression representing the navigation property for the relationship. For example, in C# <c>t => t.MyProperty</c> and in Visual Basic .NET <c>Function(t) t.MyProperty</c>.</param>
/// <returns>A configuration object that can be used to further configure the relationship.</returns>
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Nested generic appropriate here")]
[SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "Explicit Expression generic type is more clear")]
public NavigationPropertyConfiguration HasOptional<TTargetEntity>(Expression<Func<TEntityType, TTargetEntity>> navigationPropertyExpression) where TTargetEntity : class
{
return GetOrCreateNavigationProperty(navigationPropertyExpression, EdmMultiplicity.ZeroOrOne);
}
/// <summary>
/// Configures a required relationship from this entity type.
/// </summary>
/// <typeparam name="TTargetEntity">The type of the entity at the other end of the relationship.</typeparam>
/// <param name="navigationPropertyExpression">A lambda expression representing the navigation property for the relationship. For example, in C# <c>t => t.MyProperty</c> and in Visual Basic .NET <c>Function(t) t.MyProperty</c>.</param>
/// <returns>A configuration object that can be used to further configure the relationship.</returns>
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Nested generic appropriate here")]
[SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "Explicit Expression generic type is more clear")]
public NavigationPropertyConfiguration HasRequired<TTargetEntity>(Expression<Func<TEntityType, TTargetEntity>> navigationPropertyExpression) where TTargetEntity : class
{
return GetOrCreateNavigationProperty(navigationPropertyExpression, EdmMultiplicity.One);
}
/// <summary>
/// Create an Action that binds to this EntityType.
/// </summary>
/// <param name="name">The name of the action.</param>
/// <returns>The ActionConfiguration to allow further configuration of the new Action.</returns>
public ActionConfiguration Action(string name)
{
Contract.Assert(_configuration != null && _configuration.ModelBuilder != null);
ActionConfiguration action = _configuration.ModelBuilder.Action(name);
action.SetBindingParameter(BindingParameterConfiguration.DefaultBindingParameterName, _configuration, alwaysBindable: true);
return action;
}
/// <summary>
/// Create an Action that sometimes binds to this EntityType
/// </summary>
/// <param name="name">The name of the action.</param>
/// <returns>The ActionConfiguration to allow further configuration of the new 'transient' Action.</returns>
public ActionConfiguration TransientAction(string name)
{
Contract.Assert(_configuration != null && _configuration.ModelBuilder != null);
ActionConfiguration action = _configuration.ModelBuilder.Action(name);
action.SetBindingParameter(BindingParameterConfiguration.DefaultBindingParameterName, _configuration, alwaysBindable: false);
return action;
}
internal NavigationPropertyConfiguration GetOrCreateNavigationProperty(Expression navigationPropertyExpression, EdmMultiplicity multiplicity)
{
PropertyInfo navigationProperty = PropertySelectorVisitor.GetSelectedProperty(navigationPropertyExpression);
return _configuration.AddNavigationProperty(navigationProperty, multiplicity);
}
}
}

Просмотреть файл

@ -1,45 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Diagnostics.CodeAnalysis;
using Microsoft.Data.Edm;
namespace System.Web.Http.OData.Builder
{
/// <summary>
/// Represents an EdmType
/// </summary>
public interface IEdmTypeConfiguration
{
/// <summary>
/// The CLR type associated with the EdmType.
/// </summary>
Type ClrType { get; }
/// <summary>
/// The fullname (including namespace) of the EdmType.
/// </summary>
string FullName { get; }
/// <summary>
/// The namespace of the EdmType.
/// </summary>
[SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "Namespace", Justification = "Namespace matches the EF naming scheme")]
string Namespace { get; }
/// <summary>
/// The name of the EdmType.
/// </summary>
string Name { get; }
/// <summary>
/// The kind of the EdmType.
/// Examples include EntityType, ComplexType, PrimitiveType, CollectionType.
/// </summary>
EdmTypeKind Kind { get; }
/// <summary>
/// The ODataModelBuilder used to create this IEdmType.
/// </summary>
ODataModelBuilder ModelBuilder { get; }
}
}

Просмотреть файл

@ -1,156 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Web.Http.OData.Builder.Conventions;
using System.Web.Http.OData.Extensions;
using System.Web.Http.OData.Properties;
using System.Web.Http.OData.Routing;
using Microsoft.Data.Edm;
namespace System.Web.Http.OData.Builder
{
/// <summary>
/// Contains helper methods for generating OData links that follow OData URL conventions.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public static class LinkGenerationHelpers
{
/// <summary>
/// Generates a self link following the OData URL conventions for the entity represented by <paramref name="entityContext"/>.
/// </summary>
/// <param name="entityContext">The <see cref="EntityInstanceContext"/> representing the entity for which the self link needs to be generated.</param>
/// <param name="includeCast">Represents whether the generated link should have a cast segment representing a type cast.</param>
/// <returns>The self link following the OData URL conventions.</returns>
public static string GenerateSelfLink(this EntityInstanceContext entityContext, bool includeCast)
{
if (entityContext == null)
{
throw Error.ArgumentNull("entityContext");
}
if (entityContext.Url == null)
{
throw Error.Argument("entityContext", SRResources.UrlHelperNull, typeof(EntityInstanceContext).Name);
}
List<ODataPathSegment> idLinkPathSegments = new List<ODataPathSegment>();
idLinkPathSegments.Add(new EntitySetPathSegment(entityContext.EntitySet));
idLinkPathSegments.Add(new KeyValuePathSegment(ConventionsHelpers.GetEntityKeyValue(entityContext)));
if (includeCast)
{
idLinkPathSegments.Add(new CastPathSegment(entityContext.EntityType));
}
string idLink = entityContext.Url.CreateODataLink(idLinkPathSegments);
if (idLink == null)
{
return null;
}
return idLink;
}
/// <summary>
/// Generates a navigation link following the OData URL conventions for the entity represented by <paramref name="entityContext"/> and the given
/// navigation property.
/// </summary>
/// <param name="entityContext">The <see cref="EntityInstanceContext"/> representing the entity for which the navigation link needs to be generated.</param>
/// <param name="navigationProperty">The EDM navigation property.</param>
/// <param name="includeCast">Represents whether the generated link should have a cast segment representing a type cast.</param>
/// <returns>The navigation link following the OData URL conventions.</returns>
public static Uri GenerateNavigationPropertyLink(this EntityInstanceContext entityContext, IEdmNavigationProperty navigationProperty, bool includeCast)
{
if (entityContext == null)
{
throw Error.ArgumentNull("entityContext");
}
if (entityContext.Url == null)
{
throw Error.Argument("entityContext", SRResources.UrlHelperNull, typeof(EntityInstanceContext).Name);
}
List<ODataPathSegment> navigationPathSegments = new List<ODataPathSegment>();
navigationPathSegments.Add(new EntitySetPathSegment(entityContext.EntitySet));
navigationPathSegments.Add(new KeyValuePathSegment(ConventionsHelpers.GetEntityKeyValue(entityContext)));
if (includeCast)
{
navigationPathSegments.Add(new CastPathSegment(entityContext.EntityType));
}
navigationPathSegments.Add(new NavigationPathSegment(navigationProperty));
string link = entityContext.Url.CreateODataLink(navigationPathSegments);
if (link == null)
{
return null;
}
return new Uri(link);
}
/// <summary>
/// Generates a feed self link following the OData URL conventions for the feed represented by <paramref name="feedContext"/>.
/// </summary>
/// <param name="feedContext">The <see cref="FeedContext"/> representing the feed for which the self link needs to be generated.</param>
/// <returns>The generated feed self link following the OData URL conventions.</returns>
public static Uri GenerateFeedSelfLink(this FeedContext feedContext)
{
if (feedContext == null)
{
throw Error.ArgumentNull("feedContext");
}
string selfLink = feedContext.Url.CreateODataLink(new EntitySetPathSegment(feedContext.EntitySet));
return selfLink == null ? null : new Uri(selfLink);
}
/// <summary>
/// Generates an action link following the OData URL conventions for the action <paramref name="action"/> and bound to the entity
/// represented by <paramref name="entityContext"/>.
/// </summary>
/// <param name="entityContext">The <see cref="EntityInstanceContext"/> representing the entity for which the action link needs to be generated.</param>
/// <param name="action">The action for which the action link needs to be generated.</param>
/// <returns>The generated action link following OData URL conventions.</returns>
public static Uri GenerateActionLink(this EntityInstanceContext entityContext, IEdmFunctionBase action)
{
if (entityContext == null)
{
throw Error.ArgumentNull("entityContext");
}
if (action == null)
{
throw Error.ArgumentNull("action");
}
IEdmFunctionParameter bindingParameter = action.Parameters.FirstOrDefault();
if (bindingParameter == null || !bindingParameter.Type.IsEntity())
{
throw Error.Argument("action", SRResources.ActionNotBoundToEntity, action.Name);
}
return GenerateActionLink(entityContext, bindingParameter.Type.FullName(), action.Name);
}
internal static Uri GenerateActionLink(this EntityInstanceContext entityContext, string bindingParameterType, string actionName)
{
List<ODataPathSegment> actionPathSegments = new List<ODataPathSegment>();
actionPathSegments.Add(new EntitySetPathSegment(entityContext.EntitySet));
actionPathSegments.Add(new KeyValuePathSegment(ConventionsHelpers.GetEntityKeyValue(entityContext)));
// generate link with cast if the entityset type doesn't match the entity type the action is bound to.
if (entityContext.EntitySet.ElementType.FullName() != bindingParameterType)
{
actionPathSegments.Add(new CastPathSegment(bindingParameterType));
}
actionPathSegments.Add(new ActionPathSegment(actionName));
string actionLink = entityContext.Url.CreateODataLink(actionPathSegments);
return actionLink == null ? null : new Uri(actionLink);
}
}
}

Просмотреть файл

@ -1,38 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using Microsoft.Data.Edm;
namespace System.Web.Http.OData.Builder
{
/// <summary>
/// Encapsulates a navigation link factory and whether the link factory follows conventions or not.
/// </summary>
public class NavigationLinkBuilder
{
/// <summary>
/// Initializes a new instance of the <see cref="NavigationLinkBuilder"/> class.
/// </summary>
/// <param name="navigationLinkFactory">The navigation link factory for creating navigation links.</param>
/// <param name="followsConventions">Represents whether this factory follows OData conventions or not.</param>
public NavigationLinkBuilder(Func<EntityInstanceContext, IEdmNavigationProperty, Uri> navigationLinkFactory, bool followsConventions)
{
if (navigationLinkFactory == null)
{
throw Error.ArgumentNull("navigationLinkFactory");
}
Factory = navigationLinkFactory;
FollowsConventions = followsConventions;
}
/// <summary>
/// Gets the navigation link factory for creating navigation links.
/// </summary>
public Func<EntityInstanceContext, IEdmNavigationProperty, Uri> Factory { get; private set; }
/// <summary>
/// Gets a value representing whether this factory follows OData conventions or not.
/// </summary>
public bool FollowsConventions { get; private set; }
}
}

Просмотреть файл

@ -1,34 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
namespace System.Web.Http.OData.Builder
{
/// <summary>
/// Used to configure the binding for a navigation property for an entity set.
/// This configuration functionality is exposed by the model builder Fluent API, see <see
/// cref="ODataModelBuilder"/>.
/// </summary>
public class NavigationPropertyBindingConfiguration
{
/// <summary>
/// Initializes a new instance of the <see cref="NavigationPropertyBindingConfiguration"/> class.
/// </summary>
/// <param name="navigationProperty">The navigation property for the binding.</param>
/// <param name="entitySet">The target entity set of the binding.</param>
public NavigationPropertyBindingConfiguration(NavigationPropertyConfiguration navigationProperty,
EntitySetConfiguration entitySet)
{
NavigationProperty = navigationProperty;
EntitySet = entitySet;
}
/// <summary>
/// Gets the navigation property of the binding.
/// </summary>
public NavigationPropertyConfiguration NavigationProperty { get; private set; }
/// <summary>
/// Gets the target entity set of the binding.
/// </summary>
public EntitySetConfiguration EntitySet { get; private set; }
}
}

Просмотреть файл

@ -1,106 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Reflection;
using System.Web.Http.OData.Properties;
using Microsoft.Data.Edm;
namespace System.Web.Http.OData.Builder
{
/// <summary>
/// Represents the configuration for a navigation property of an entity type.
/// </summary>
/// <remarks>This configuration functionality is exposed by the model builder Fluent API, see <see cref="ODataModelBuilder"/>.</remarks>
public class NavigationPropertyConfiguration : PropertyConfiguration
{
private readonly Type _relatedType = null;
/// <summary>
/// Initializes a new instance of the <see cref="NavigationPropertyConfiguration"/> class.
/// </summary>
/// <param name="property">The backing CLR property.</param>
/// <param name="multiplicity">The <see cref="EdmMultiplicity"/>.</param>
/// <param name="declaringType">The declaring entity type.</param>
public NavigationPropertyConfiguration(PropertyInfo property, EdmMultiplicity multiplicity, EntityTypeConfiguration declaringType)
: base(property, declaringType)
{
if (property == null)
{
throw Error.ArgumentNull("property");
}
Multiplicity = multiplicity;
_relatedType = property.PropertyType;
if (multiplicity == EdmMultiplicity.Many)
{
Type elementType;
if (!_relatedType.IsCollection(out elementType))
{
throw Error.Argument("property", SRResources.ManyToManyNavigationPropertyMustReturnCollection, property.Name, property.ReflectedType.Name);
}
_relatedType = elementType;
}
}
/// <summary>
/// Gets the declaring entity type.
/// </summary>
public EntityTypeConfiguration DeclaringEntityType
{
get
{
return DeclaringType as EntityTypeConfiguration;
}
}
/// <summary>
/// Gets the <see cref="EdmMultiplicity"/> of this navigation property.
/// </summary>
public EdmMultiplicity Multiplicity { get; private set; }
/// <summary>
/// Gets the backing CLR type of this property type.
/// </summary>
public override Type RelatedClrType
{
get { return _relatedType; }
}
/// <summary>
/// Gets the <see cref="PropertyKind"/> of this property.
/// </summary>
public override PropertyKind Kind
{
get { return PropertyKind.Navigation; }
}
/// <summary>
/// Marks the navigation property as optional.
/// </summary>
public NavigationPropertyConfiguration Optional()
{
if (Multiplicity == EdmMultiplicity.Many)
{
throw Error.InvalidOperation(SRResources.ManyNavigationPropertiesCannotBeChanged, Name);
}
Multiplicity = EdmMultiplicity.ZeroOrOne;
return this;
}
/// <summary>
/// Marks the navigation property as required.
/// </summary>
public NavigationPropertyConfiguration Required()
{
if (Multiplicity == EdmMultiplicity.Many)
{
throw Error.InvalidOperation(SRResources.ManyNavigationPropertiesCannotBeChanged, Name);
}
Multiplicity = EdmMultiplicity.One;
return this;
}
}
}

Просмотреть файл

@ -1,39 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Web.Http.OData.Properties;
using Microsoft.Data.Edm;
namespace System.Web.Http.OData.Builder
{
/// <summary>
/// Represents a non-binding procedure parameter.
/// <remarks>
/// Non binding parameters are provided in the POST body for Actions
/// Non binding parameters are provided in 3 ways for Functions
/// - ~/.../Function(p1=value)
/// - ~/.../Function(p1=@x)?@x=value
/// - ~/.../Function?p1=value (only allowed if the Function is the last url path segment).
/// </remarks>
/// </summary>
public class NonbindingParameterConfiguration : ParameterConfiguration
{
/// <summary>
/// Initializes a new instance of the <see cref="NonbindingParameterConfiguration"/> class.
/// </summary>
/// <param name="name">The name of the parameter.</param>
/// <param name="parameterType">The EDM type of the parameter.</param>
public NonbindingParameterConfiguration(string name, IEdmTypeConfiguration parameterType)
: base(name, parameterType)
{
EdmTypeKind kind = parameterType.Kind;
if (kind == EdmTypeKind.Collection)
{
kind = (parameterType as CollectionTypeConfiguration).ElementType.Kind;
}
if (kind == EdmTypeKind.Entity)
{
throw Error.Argument("parameterType", SRResources.InvalidParameterType, parameterType.FullName);
}
}
}
}

Просмотреть файл

@ -1,723 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Reflection;
using System.Web.Http.Dispatcher;
using System.Web.Http.OData.Builder.Conventions;
using System.Web.Http.OData.Builder.Conventions.Attributes;
using System.Web.Http.OData.Formatter;
using System.Web.Http.OData.Properties;
using Microsoft.Data.Edm;
namespace System.Web.Http.OData.Builder
{
/// <summary>
/// <see cref="ODataConventionModelBuilder"/> is used to automatically map CLR classes to an EDM model based on a set of <see cref="IConvention"/>.
/// </summary>
public class ODataConventionModelBuilder : ODataModelBuilder
{
private static readonly List<IConvention> _conventions = new List<IConvention>
{
// type and property conventions (ordering is important here).
new AbstractEntityTypeDiscoveryConvention(),
new DataContractAttributeEdmTypeConvention(),
new NotMappedAttributeConvention(), // NotMappedAttributeConvention has to run before EntityKeyConvention
new DataMemberAttributeEdmPropertyConvention(),
new RequiredAttributeEdmPropertyConvention(),
new EntityKeyConvention(),
new KeyAttributeEdmPropertyConvention(),
new IgnoreDataMemberAttributeEdmPropertyConvention(),
new DatabaseGeneratedAttributeEdmPropertyConvention(),
// IEntitySetConvention's
new SelfLinksGenerationConvention(),
new NavigationLinksGenerationConvention(),
new AssociationSetDiscoveryConvention(),
// IEdmFunctionImportConventions's
new ActionLinkGenerationConvention(),
};
// These hashset's keep track of edmtypes/entitysets for which conventions
// have been applied or being applied so that we don't run a convention twice on the
// same type/set.
private HashSet<StructuralTypeConfiguration> _mappedTypes;
private HashSet<EntitySetConfiguration> _configuredEntitySets;
private HashSet<Type> _ignoredTypes;
private IEnumerable<StructuralTypeConfiguration> _explicitlyAddedTypes;
private bool _isModelBeingBuilt;
private bool _isQueryCompositionMode;
// build the mapping between type and its derived types to be used later.
private Lazy<IDictionary<Type, List<Type>>> _allTypesWithDerivedTypeMapping;
/// <summary>
/// Initializes a new instance of the <see cref="ODataConventionModelBuilder"/> class.
/// </summary>
public ODataConventionModelBuilder()
{
Initialize(new DefaultAssembliesResolver(), isQueryCompositionMode: false);
}
/// <summary>
/// Initializes a new <see cref="ODataConventionModelBuilder"/>.
/// </summary>
/// <param name="configuration">The <see cref="HttpConfiguration"/> to use.</param>
public ODataConventionModelBuilder(HttpConfiguration configuration)
: this(configuration, isQueryCompositionMode: false)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ODataConventionModelBuilder"/> class.
/// </summary>
/// <param name="configuration">The <see cref="HttpConfiguration"/> to use.</param>
/// <param name="isQueryCompositionMode">If the model is being built for only querying.</param>
/// <remarks>The model built if <paramref name="isQueryCompositionMode"/> is <see langword="true"/> has more relaxed
/// inference rules and also treats all types as entity types. This constructor is intended for use by unit testing only.</remarks>
public ODataConventionModelBuilder(HttpConfiguration configuration, bool isQueryCompositionMode)
{
if (configuration == null)
{
throw Error.ArgumentNull("configuration");
}
Initialize(configuration.Services.GetAssembliesResolver(), isQueryCompositionMode);
}
/// <summary>
/// This action is invoked after the <see cref="ODataConventionModelBuilder"/> has run all the conventions, but before the configuration is locked
/// down and used to build the <see cref="IEdmModel"/>.
/// </summary>
/// <remarks>Use this action to modify the <see cref="ODataModelBuilder"/> configuration that has been inferred by convention.</remarks>
public Action<ODataConventionModelBuilder> OnModelCreating { get; set; }
internal void Initialize(IAssembliesResolver assembliesResolver, bool isQueryCompositionMode)
{
_isQueryCompositionMode = isQueryCompositionMode;
_configuredEntitySets = new HashSet<EntitySetConfiguration>();
_mappedTypes = new HashSet<StructuralTypeConfiguration>();
_ignoredTypes = new HashSet<Type>();
_allTypesWithDerivedTypeMapping = new Lazy<IDictionary<Type, List<Type>>>(
() => BuildDerivedTypesMapping(assembliesResolver),
isThreadSafe: false);
}
/// <summary>
/// Excludes a type from the model. This is used to remove types from the model that were added by convention during initial model discovery.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns>The same <c ref="ODataConventionModelBuilder"/> so that multiple calls can be chained.</returns>
[SuppressMessage("Microsoft.Design", "CA1004: GenericMethodsShouldProvideTypeParameter", Justification = "easier to call the generic version than using typeof().")]
public ODataConventionModelBuilder Ignore<T>()
{
_ignoredTypes.Add(typeof(T));
return this;
}
/// <summary>
/// Excludes a type or types from the model. This is used to remove types from the model that were added by convention during initial model discovery.
/// </summary>
/// <param name="types">The types to be excluded from the model.</param>
/// <returns>The same <c ref="ODataConventionModelBuilder"/> so that multiple calls can be chained.</returns>
public ODataConventionModelBuilder Ignore(params Type[] types)
{
foreach (Type type in types)
{
_ignoredTypes.Add(type);
}
return this;
}
/// <inheritdoc />
public override EntityTypeConfiguration AddEntity(Type type)
{
EntityTypeConfiguration entityTypeConfiguration = base.AddEntity(type);
if (_isModelBeingBuilt)
{
MapType(entityTypeConfiguration);
}
return entityTypeConfiguration;
}
/// <inheritdoc />
public override ComplexTypeConfiguration AddComplexType(Type type)
{
ComplexTypeConfiguration complexTypeConfiguration = base.AddComplexType(type);
if (_isModelBeingBuilt)
{
MapType(complexTypeConfiguration);
}
return complexTypeConfiguration;
}
/// <inheritdoc />
public override EntitySetConfiguration AddEntitySet(string name, EntityTypeConfiguration entityType)
{
EntitySetConfiguration entitySetConfiguration = base.AddEntitySet(name, entityType);
if (_isModelBeingBuilt)
{
ApplyEntitySetConventions(entitySetConfiguration);
}
return entitySetConfiguration;
}
/// <inheritdoc />
public override IEdmModel GetEdmModel()
{
if (_isModelBeingBuilt)
{
throw Error.NotSupported(SRResources.GetEdmModelCalledMoreThanOnce);
}
// before we begin, get the set of types the user had added explicitly.
_explicitlyAddedTypes = new List<StructuralTypeConfiguration>(StructuralTypes);
_isModelBeingBuilt = true;
MapTypes();
DiscoverInheritanceRelationships();
// Don't RediscoverComplexTypes() and treat everything as an entity type if buidling a model for EnableQueryAttribute.
if (!_isQueryCompositionMode)
{
RediscoverComplexTypes();
}
// prune unreachable types
PruneUnreachableTypes();
// Apply entity set conventions.
IEnumerable<EntitySetConfiguration> explictlyConfiguredEntitySets = new List<EntitySetConfiguration>(EntitySets);
foreach (EntitySetConfiguration entitySet in explictlyConfiguredEntitySets)
{
ApplyEntitySetConventions(entitySet);
}
foreach (ProcedureConfiguration procedure in Procedures)
{
ApplyProcedureConventions(procedure);
}
if (OnModelCreating != null)
{
OnModelCreating(this);
}
return base.GetEdmModel();
}
internal bool IsIgnoredType(Type type)
{
Contract.Requires(type != null);
return _ignoredTypes.Contains(type);
}
// patch up the base type for all entities that don't have any yet.
internal void DiscoverInheritanceRelationships()
{
Dictionary<Type, EntityTypeConfiguration> entityMap = StructuralTypes.OfType<EntityTypeConfiguration>().ToDictionary(e => e.ClrType);
foreach (EntityTypeConfiguration entity in StructuralTypes.OfType<EntityTypeConfiguration>().Where(e => !e.BaseTypeConfigured).ToArray())
{
Type baseClrType = entity.ClrType.BaseType;
while (baseClrType != null)
{
// see if we there is an entity that we know mapping to this clr types base type.
EntityTypeConfiguration baseEntityType;
if (entityMap.TryGetValue(baseClrType, out baseEntityType))
{
RemoveBaseTypeProperties(entity, baseEntityType);
// disable derived type key check if we are building a model for query composition.
if (_isQueryCompositionMode)
{
// modifying the collection in the iterator, hence the ToArray().
foreach (PrimitivePropertyConfiguration keyProperty in entity.Keys.ToArray())
{
entity.RemoveKey(keyProperty);
}
}
entity.DerivesFrom(baseEntityType);
break;
}
baseClrType = baseClrType.BaseType;
}
}
}
internal void MapDerivedTypes(EntityTypeConfiguration entity)
{
HashSet<Type> visitedEntities = new HashSet<Type>();
Queue<EntityTypeConfiguration> entitiesToBeVisited = new Queue<EntityTypeConfiguration>();
entitiesToBeVisited.Enqueue(entity);
// populate all the derived types
while (entitiesToBeVisited.Count != 0)
{
EntityTypeConfiguration baseEntity = entitiesToBeVisited.Dequeue();
visitedEntities.Add(baseEntity.ClrType);
List<Type> derivedTypes;
if (_allTypesWithDerivedTypeMapping.Value.TryGetValue(baseEntity.ClrType, out derivedTypes))
{
foreach (Type derivedType in derivedTypes)
{
if (!visitedEntities.Contains(derivedType) && !IsIgnoredType(derivedType))
{
EntityTypeConfiguration derivedEntity = AddEntity(derivedType);
entitiesToBeVisited.Enqueue(derivedEntity);
}
}
}
}
}
// remove base type properties from the derived types.
internal void RemoveBaseTypeProperties(EntityTypeConfiguration derivedEntity, EntityTypeConfiguration baseEntity)
{
IEnumerable<EntityTypeConfiguration> typesToLift = new[] { derivedEntity }.Concat(this.DerivedTypes(derivedEntity));
foreach (PropertyConfiguration property in baseEntity.Properties.Concat(baseEntity.DerivedProperties()))
{
foreach (EntityTypeConfiguration entity in typesToLift)
{
PropertyConfiguration derivedPropertyToRemove = entity.Properties.Where(p => p.Name == property.Name).SingleOrDefault();
if (derivedPropertyToRemove != null)
{
entity.RemoveProperty(derivedPropertyToRemove.PropertyInfo);
}
}
}
foreach (PropertyInfo ignoredProperty in baseEntity.IgnoredProperties())
{
foreach (EntityTypeConfiguration entity in typesToLift)
{
PropertyConfiguration derivedPropertyToRemove = entity.Properties.Where(p => p.Name == ignoredProperty.Name).SingleOrDefault();
if (derivedPropertyToRemove != null)
{
entity.RemoveProperty(derivedPropertyToRemove.PropertyInfo);
}
}
}
}
private void RediscoverComplexTypes()
{
Contract.Assert(_explicitlyAddedTypes != null);
IEnumerable<EntityTypeConfiguration> misconfiguredEntityTypes = StructuralTypes
.Except(_explicitlyAddedTypes)
.OfType<EntityTypeConfiguration>()
.Where(entity => !entity.Keys().Any())
.ToArray();
ReconfigureEntityTypesAsComplexType(misconfiguredEntityTypes);
}
private void ReconfigureEntityTypesAsComplexType(IEnumerable<EntityTypeConfiguration> misconfiguredEntityTypes)
{
IEnumerable<EntityTypeConfiguration> actualEntityTypes = StructuralTypes
.Except(misconfiguredEntityTypes)
.OfType<EntityTypeConfiguration>()
.ToArray();
foreach (EntityTypeConfiguration misconfiguredEntityType in misconfiguredEntityTypes)
{
RemoveStructuralType(misconfiguredEntityType.ClrType);
// this is a wrongly inferred type. so just ignore any pending configuration from it.
AddComplexType(misconfiguredEntityType.ClrType);
foreach (EntityTypeConfiguration entityToBePatched in actualEntityTypes)
{
NavigationPropertyConfiguration[] propertiesToBeRemoved = entityToBePatched
.NavigationProperties
.Where(navigationProperty => navigationProperty.RelatedClrType == misconfiguredEntityType.ClrType)
.ToArray();
foreach (NavigationPropertyConfiguration propertyToBeRemoved in propertiesToBeRemoved)
{
entityToBePatched.RemoveProperty(propertyToBeRemoved.PropertyInfo);
if (propertyToBeRemoved.Multiplicity == EdmMultiplicity.Many)
{
entityToBePatched.AddCollectionProperty(propertyToBeRemoved.PropertyInfo);
}
else
{
entityToBePatched.AddComplexProperty(propertyToBeRemoved.PropertyInfo);
}
}
}
}
}
private void MapTypes()
{
foreach (StructuralTypeConfiguration edmType in _explicitlyAddedTypes)
{
MapType(edmType);
}
}
private void MapType(StructuralTypeConfiguration edmType)
{
if (!_mappedTypes.Contains(edmType))
{
_mappedTypes.Add(edmType);
EntityTypeConfiguration entity = edmType as EntityTypeConfiguration;
if (entity != null)
{
MapEntityType(entity);
}
else
{
MapComplexType(edmType as ComplexTypeConfiguration);
}
ApplyTypeAndPropertyConventions(edmType);
}
}
private void MapEntityType(EntityTypeConfiguration entity)
{
IEnumerable<PropertyInfo> properties = ConventionsHelpers.GetProperties(entity, includeReadOnly: _isQueryCompositionMode);
foreach (PropertyInfo property in properties)
{
bool isCollection;
StructuralTypeConfiguration mappedType;
PropertyKind propertyKind = GetPropertyType(property, out isCollection, out mappedType);
if (propertyKind == PropertyKind.Primitive || propertyKind == PropertyKind.Complex)
{
MapStructuralProperty(entity, property, propertyKind, isCollection);
}
else
{
// don't add this property if the user has already added it.
if (!entity.NavigationProperties.Where(p => p.Name == property.Name).Any())
{
NavigationPropertyConfiguration addedNavigationProperty;
if (!isCollection)
{
addedNavigationProperty = entity.AddNavigationProperty(property, EdmMultiplicity.ZeroOrOne);
}
else
{
addedNavigationProperty = entity.AddNavigationProperty(property, EdmMultiplicity.Many);
}
addedNavigationProperty.AddedExplicitly = false;
}
}
}
MapDerivedTypes(entity);
}
private void MapComplexType(ComplexTypeConfiguration complexType)
{
IEnumerable<PropertyInfo> properties = ConventionsHelpers.GetAllProperties(complexType, includeReadOnly: _isQueryCompositionMode);
foreach (PropertyInfo property in properties)
{
bool isCollection;
StructuralTypeConfiguration mappedType;
PropertyKind propertyKind = GetPropertyType(property, out isCollection, out mappedType);
if (propertyKind == PropertyKind.Primitive || propertyKind == PropertyKind.Complex)
{
MapStructuralProperty(complexType, property, propertyKind, isCollection);
}
else
{
// navigation property in a complex type ?
if (mappedType == null)
{
// the user told nothing about this type and this is the first time we are seeing this type.
// complex types cannot contain entities. So, treat it as complex property.
MapStructuralProperty(complexType, property, PropertyKind.Complex, isCollection);
}
else if (_explicitlyAddedTypes.Contains(mappedType))
{
// user told us that this is an entity type.
throw Error.InvalidOperation(SRResources.ComplexTypeRefersToEntityType, complexType.ClrType.FullName, mappedType.ClrType.FullName, property.Name);
}
else
{
// we tried to be over-smart earlier and made the bad choice. so patch up now.
EntityTypeConfiguration mappedTypeAsEntity = mappedType as EntityTypeConfiguration;
Contract.Assert(mappedTypeAsEntity != null);
ReconfigureEntityTypesAsComplexType(new EntityTypeConfiguration[] { mappedTypeAsEntity });
MapStructuralProperty(complexType, property, PropertyKind.Complex, isCollection);
}
}
}
}
private void MapStructuralProperty(StructuralTypeConfiguration type, PropertyInfo property, PropertyKind propertyKind, bool isCollection)
{
Contract.Assert(type != null);
Contract.Assert(property != null);
Contract.Assert(propertyKind == PropertyKind.Complex || propertyKind == PropertyKind.Primitive);
bool addedExplicitly = false;
if (type.Properties.Where(p => p.Name == property.Name).Any())
{
addedExplicitly = true;
}
PropertyConfiguration addedEdmProperty;
if (!isCollection)
{
if (propertyKind == PropertyKind.Primitive)
{
addedEdmProperty = type.AddProperty(property);
}
else
{
addedEdmProperty = type.AddComplexProperty(property);
}
}
else
{
if (_isQueryCompositionMode)
{
Contract.Assert(propertyKind != PropertyKind.Complex, "we don't create complex types in query composition mode.");
}
addedEdmProperty = type.AddCollectionProperty(property);
}
addedEdmProperty.AddedExplicitly = addedExplicitly;
}
// figures out the type of the property (primitive, complex, navigation) and the corresponding edm type if we have seen this type
// earlier or the user told us about it.
private PropertyKind GetPropertyType(PropertyInfo property, out bool isCollection, out StructuralTypeConfiguration mappedType)
{
Contract.Assert(property != null);
if (EdmLibHelpers.GetEdmPrimitiveTypeOrNull(property.PropertyType) != null)
{
isCollection = false;
mappedType = null;
return PropertyKind.Primitive;
}
else
{
mappedType = GetStructuralTypeOrNull(property.PropertyType);
if (mappedType != null)
{
isCollection = false;
if (mappedType is ComplexTypeConfiguration)
{
return PropertyKind.Complex;
}
else
{
return PropertyKind.Navigation;
}
}
else
{
Type elementType;
if (property.PropertyType.IsCollection(out elementType))
{
isCollection = true;
// Collection properties - can be a collection of primitives, complex or entities.
if (EdmLibHelpers.GetEdmPrimitiveTypeOrNull(elementType) != null)
{
return PropertyKind.Primitive;
}
else
{
mappedType = GetStructuralTypeOrNull(elementType);
if (mappedType != null && mappedType is ComplexTypeConfiguration)
{
return PropertyKind.Complex;
}
else
{
// if we know nothing about this type we assume it to be an entity
// and patch up later
return PropertyKind.Navigation;
}
}
}
else
{
// if we know nothing about this type we assume it to be an entity
// and patch up later
isCollection = false;
return PropertyKind.Navigation;
}
}
}
}
// the convention model builder MapTypes() method might have went through deep object graphs and added a bunch of types
// only to realise after applying the conventions that the user has ignored some of the properties. So, prune the unreachable stuff.
private void PruneUnreachableTypes()
{
Contract.Assert(_explicitlyAddedTypes != null);
// Do a BFS starting with the types the user has explicitly added to find out the unreachable nodes.
Queue<StructuralTypeConfiguration> reachableTypes = new Queue<StructuralTypeConfiguration>(_explicitlyAddedTypes);
HashSet<StructuralTypeConfiguration> visitedTypes = new HashSet<StructuralTypeConfiguration>();
while (reachableTypes.Count != 0)
{
StructuralTypeConfiguration currentType = reachableTypes.Dequeue();
// go visit other end of each of this node's edges.
foreach (PropertyConfiguration property in currentType.Properties.Where(property => property.Kind != PropertyKind.Primitive))
{
if (property.Kind == PropertyKind.Collection)
{
// if the elementType is primitive we don't need to do anything.
CollectionPropertyConfiguration colProperty = property as CollectionPropertyConfiguration;
if (EdmLibHelpers.GetEdmPrimitiveTypeOrNull(colProperty.ElementType) != null)
{
continue;
}
}
StructuralTypeConfiguration propertyType = GetStructuralTypeOrNull(property.RelatedClrType);
Contract.Assert(propertyType != null, "we should already have seen this type");
if (!visitedTypes.Contains(propertyType))
{
reachableTypes.Enqueue(propertyType);
}
}
// all derived types and the base type are also reachable
EntityTypeConfiguration currentEntityType = currentType as EntityTypeConfiguration;
if (currentEntityType != null)
{
if (currentEntityType.BaseType != null && !visitedTypes.Contains(currentEntityType.BaseType))
{
reachableTypes.Enqueue(currentEntityType.BaseType);
}
foreach (EntityTypeConfiguration derivedType in this.DerivedTypes(currentEntityType))
{
if (!visitedTypes.Contains(derivedType))
{
reachableTypes.Enqueue(derivedType);
}
}
}
visitedTypes.Add(currentType);
}
StructuralTypeConfiguration[] allConfiguredTypes = StructuralTypes.ToArray();
foreach (StructuralTypeConfiguration type in allConfiguredTypes)
{
if (!visitedTypes.Contains(type))
{
// we don't have to fix up any properties because this type is unreachable and cannot be a property of any reachable type.
RemoveStructuralType(type.ClrType);
}
}
}
private void ApplyTypeAndPropertyConventions(StructuralTypeConfiguration edmTypeConfiguration)
{
foreach (IConvention convention in _conventions)
{
IEdmTypeConvention typeConvention = convention as IEdmTypeConvention;
if (typeConvention != null)
{
typeConvention.Apply(edmTypeConfiguration, this);
}
IEdmPropertyConvention propertyConvention = convention as IEdmPropertyConvention;
if (propertyConvention != null)
{
ApplyPropertyConvention(propertyConvention, edmTypeConfiguration);
}
}
}
private void ApplyEntitySetConventions(EntitySetConfiguration entitySetConfiguration)
{
if (!_configuredEntitySets.Contains(entitySetConfiguration))
{
_configuredEntitySets.Add(entitySetConfiguration);
foreach (IEntitySetConvention convention in _conventions.OfType<IEntitySetConvention>())
{
if (convention != null)
{
convention.Apply(entitySetConfiguration, this);
}
}
}
}
private void ApplyProcedureConventions(ProcedureConfiguration procedure)
{
foreach (IProcedureConvention convention in _conventions.OfType<IProcedureConvention>())
{
convention.Apply(procedure, this);
}
}
private StructuralTypeConfiguration GetStructuralTypeOrNull(Type clrType)
{
return StructuralTypes.Where(edmType => edmType.ClrType == clrType).SingleOrDefault();
}
private static void ApplyPropertyConvention(IEdmPropertyConvention propertyConvention, StructuralTypeConfiguration edmTypeConfiguration)
{
Contract.Assert(propertyConvention != null);
Contract.Assert(edmTypeConfiguration != null);
foreach (PropertyConfiguration property in edmTypeConfiguration.Properties.ToArray())
{
propertyConvention.Apply(property, edmTypeConfiguration);
}
}
private static Dictionary<Type, List<Type>> BuildDerivedTypesMapping(IAssembliesResolver assemblyResolver)
{
IEnumerable<Type> allTypes = TypeHelper.GetLoadedTypes(assemblyResolver).Where(t => t.IsVisible && t.IsClass && t != typeof(object));
Dictionary<Type, List<Type>> allTypeMapping = allTypes.ToDictionary(k => k, k => new List<Type>());
foreach (Type type in allTypes)
{
List<Type> derivedTypes;
if (type.BaseType != null && allTypeMapping.TryGetValue(type.BaseType, out derivedTypes))
{
derivedTypes.Add(type);
}
}
return allTypeMapping;
}
}
}

Просмотреть файл

@ -1,386 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Web.Http.OData.Formatter;
using System.Web.Http.OData.Properties;
using Microsoft.Data.Edm;
namespace System.Web.Http.OData.Builder
{
/// <summary>
/// <see cref="ODataModelBuilder"/> is used to map CLR classes to an EDM model.
/// </summary>
// TODO: Feature 443884: add support for starting from an original model
public class ODataModelBuilder
{
private static readonly Version _defaultDataServiceVersion = new Version(3, 0);
private static readonly Version _defaultMaxDataServiceVersionn = new Version(3, 0);
private Dictionary<Type, StructuralTypeConfiguration> _structuralTypes = new Dictionary<Type, StructuralTypeConfiguration>();
private Dictionary<string, EntitySetConfiguration> _entitySets = new Dictionary<string, EntitySetConfiguration>();
private Dictionary<Type, PrimitiveTypeConfiguration> _primitiveTypes = new Dictionary<Type, PrimitiveTypeConfiguration>();
private List<ProcedureConfiguration> _procedures = new List<ProcedureConfiguration>();
private Version _dataServiceVersion;
private Version _maxDataServiceVersion;
/// <summary>
/// Initializes a new instance of the <see cref="ODataModelBuilder"/> class.
/// </summary>
public ODataModelBuilder()
{
Namespace = "Default";
ContainerName = "Container";
DataServiceVersion = _defaultDataServiceVersion;
MaxDataServiceVersion = _defaultMaxDataServiceVersionn;
}
/// <summary>
/// Gets or sets the namespace that will be used for the resulting model
/// </summary>
public string Namespace { get; set; }
/// <summary>
/// Gets or sets the name of the container that will hold all the EntitySets, Actions and Functions
/// </summary>
public string ContainerName { get; set; }
/// <summary>
/// Gets or sets the data service version of the model. The default value is 3.0.
/// </summary>
public Version DataServiceVersion
{
get
{
return _dataServiceVersion;
}
set
{
if (value == null)
{
throw Error.PropertyNull();
}
_dataServiceVersion = value;
}
}
/// <summary>
/// Gets or sets the maximum data service version of the model. The default value is 3.0.
/// </summary>
public Version MaxDataServiceVersion
{
get
{
return _maxDataServiceVersion;
}
set
{
if (value == null)
{
throw Error.PropertyNull();
}
_maxDataServiceVersion = value;
}
}
/// <summary>
/// Gets the collection of EDM entity sets in the model to be built.
/// </summary>
public virtual IEnumerable<EntitySetConfiguration> EntitySets
{
get { return _entitySets.Values; }
}
/// <summary>
/// Gets the collection of EDM types in the model to be built.
/// </summary>
public virtual IEnumerable<StructuralTypeConfiguration> StructuralTypes
{
get { return _structuralTypes.Values; }
}
/// <summary>
/// Gets the collection of Procedures (i.e. Actions, Functions and ServiceOperations) in the model to be built
/// </summary>
public virtual IEnumerable<ProcedureConfiguration> Procedures
{
get { return _procedures; }
}
/// <summary>
/// Registers an entity type as part of the model and returns an object that can be used to configure the entity.
/// This method can be called multiple times for the same entity to perform multiple lines of configuration.
/// </summary>
/// <typeparam name="TEntityType">The type to be registered or configured.</typeparam>
/// <returns>The configuration object for the specified entity type.</returns>
public EntityTypeConfiguration<TEntityType> Entity<TEntityType>() where TEntityType : class
{
return new EntityTypeConfiguration<TEntityType>(this, AddEntity(typeof(TEntityType)));
}
/// <summary>
/// Registers a type as a complex type in the model and returns an object that can be used to configure the complex type.
/// This method can be called multiple times for the same type to perform multiple lines of configuration.
/// </summary>
/// <typeparam name="TComplexType">The type to be registered or configured.</typeparam>
/// <returns>The configuration object for the specified complex type.</returns>
public ComplexTypeConfiguration<TComplexType> ComplexType<TComplexType>() where TComplexType : class
{
return new ComplexTypeConfiguration<TComplexType>(AddComplexType(typeof(TComplexType)));
}
/// <summary>
/// Registers an entity set as a part of the model and returns an object that can be used to configure the entity set.
/// This method can be called multiple times for the same type to perform multiple lines of configuration.
/// </summary>
/// <typeparam name="TEntityType">The entity type of the entity set.</typeparam>
/// <param name="name">The name of the entity set.</param>
/// <returns>The configuration object for the specified entity set.</returns>
public EntitySetConfiguration<TEntityType> EntitySet<TEntityType>(string name) where TEntityType : class
{
EntityTypeConfiguration entity = AddEntity(typeof(TEntityType));
return new EntitySetConfiguration<TEntityType>(this, AddEntitySet(name, entity));
}
/// <summary>
/// Adds a non-bindable action to the builder.
/// </summary>
/// <param name="name">The name of the action.</param>
/// <returns>The configuration object for the specified action.</returns>
public virtual ActionConfiguration Action(string name)
{
ActionConfiguration action = new ActionConfiguration(this, name);
_procedures.Add(action);
return action;
}
/// <summary>
/// Registers an entity type as part of the model and returns an object that can be used to configure the entity.
/// This method can be called multiple times for the same entity to perform multiple lines of configuration.
/// </summary>
/// <param name="type">The type to be registered or configured.</param>
/// <returns>The configuration object for the specified entity type.</returns>
public virtual EntityTypeConfiguration AddEntity(Type type)
{
if (type == null)
{
throw Error.ArgumentNull("type");
}
if (!_structuralTypes.ContainsKey(type))
{
EntityTypeConfiguration entityTypeConfig = new EntityTypeConfiguration(this, type);
_structuralTypes.Add(type, entityTypeConfig);
return entityTypeConfig;
}
else
{
EntityTypeConfiguration config = _structuralTypes[type] as EntityTypeConfiguration;
if (config == null || config.ClrType != type)
{
throw Error.Argument("type", SRResources.TypeCannotBeEntityWasComplex, type.FullName);
}
return config;
}
}
/// <summary>
/// Registers an complex type as part of the model and returns an object that can be used to configure the entity.
/// This method can be called multiple times for the same entity to perform multiple lines of configuration.
/// </summary>
/// <param name="type">The type to be registered or configured.</param>
/// <returns>The configuration object for the specified complex type.</returns>
public virtual ComplexTypeConfiguration AddComplexType(Type type)
{
if (type == null)
{
throw Error.ArgumentNull("type");
}
if (!_structuralTypes.ContainsKey(type))
{
ComplexTypeConfiguration complexTypeConfig = new ComplexTypeConfiguration(this, type);
_structuralTypes.Add(type, complexTypeConfig);
return complexTypeConfig;
}
else
{
ComplexTypeConfiguration complexTypeConfig = _structuralTypes[type] as ComplexTypeConfiguration;
if (complexTypeConfig == null || complexTypeConfig.ClrType != type)
{
throw Error.Argument("type", SRResources.TypeCannotBeComplexWasEntity, type.FullName);
}
return complexTypeConfig;
}
}
/// <summary>
/// Adds a procedure to the model.
/// </summary>
public virtual void AddProcedure(ProcedureConfiguration procedure)
{
_procedures.Add(procedure);
}
/// <summary>
/// Registers an entity set as a part of the model and returns an object that can be used to configure the entity set.
/// This method can be called multiple times for the same type to perform multiple lines of configuration.
/// </summary>
/// <param name="name">The name of the entity set.</param>
/// <param name="entityType">The type to be registered or configured.</param>
/// <returns>The configuration object for the specified entity set.</returns>
public virtual EntitySetConfiguration AddEntitySet(string name, EntityTypeConfiguration entityType)
{
if (String.IsNullOrWhiteSpace(name))
{
throw Error.ArgumentNullOrEmpty("name");
}
if (entityType == null)
{
throw Error.ArgumentNull("entityType");
}
if (name.Contains("."))
{
throw Error.NotSupported(SRResources.InvalidEntitySetName, name);
}
EntitySetConfiguration entitySet = null;
if (_entitySets.ContainsKey(name))
{
entitySet = _entitySets[name] as EntitySetConfiguration;
if (entitySet.EntityType != entityType)
{
throw Error.Argument("entityType", SRResources.EntitySetAlreadyConfiguredDifferentEntityType, entitySet.Name, entitySet.EntityType.Name);
}
}
else
{
entitySet = new EntitySetConfiguration(this, entityType, name);
_entitySets[name] = entitySet;
}
return entitySet;
}
/// <summary>
/// Removes the type from the model.
/// </summary>
/// <param name="type">The type to be removed</param>
/// <returns><see>true</see> if the type is present in the model and <see>false</see> otherwise.</returns>
public virtual bool RemoveStructuralType(Type type)
{
if (type == null)
{
throw Error.ArgumentNull("type");
}
return _structuralTypes.Remove(type);
}
/// <summary>
/// Removes the entity set from the model.
/// </summary>
/// <param name="name">The name of the entity set to be removed</param>
/// <returns><see>true</see> if the entity set is present in the model and <see>false</see> otherwise.</returns>
public virtual bool RemoveEntitySet(string name)
{
if (name == null)
{
throw Error.ArgumentNull("name");
}
return _entitySets.Remove(name);
}
/// <summary>
/// Remove the procedure from the model
/// <remarks>
/// If there is more than one procedure with the name specified this method will not work.
/// You need to use the other RemoveProcedure(..) overload instead.
/// </remarks>
/// </summary>
/// <param name="name">The name of the procedure to be removed</param>
/// <returns><see>true</see> if the procedure is present in the model and <see>false</see> otherwise.</returns>
public virtual bool RemoveProcedure(string name)
{
if (name == null)
{
throw Error.ArgumentNull("name");
}
ProcedureConfiguration[] toRemove = _procedures.Where(p => p.Name == name).ToArray();
int count = toRemove.Count();
if (count == 1)
{
return RemoveProcedure(toRemove[0]);
}
else if (count == 0)
{
// For consistency with RemoveStructuralType().
// uses same semantics as Dictionary.Remove(key).
return false;
}
else
{
throw Error.InvalidOperation(SRResources.MoreThanOneProcedureFound, name);
}
}
/// <summary>
/// Remove the procedure from the model
/// </summary>
/// <param name="procedure">The procedure to be removed</param>
/// <returns><see>true</see> if the procedure is present in the model and <see>false</see> otherwise.</returns>
public virtual bool RemoveProcedure(ProcedureConfiguration procedure)
{
if (procedure == null)
{
throw Error.ArgumentNull("procedure");
}
return _procedures.Remove(procedure);
}
/// <summary>
/// Attempts to find either a pre-configured structural type or a primitive type that matches the T.
/// If no matches are found NULL is returned.
/// </summary>
public IEdmTypeConfiguration GetTypeConfigurationOrNull(Type type)
{
if (_primitiveTypes.ContainsKey(type))
{
return _primitiveTypes[type];
}
else
{
IEdmPrimitiveType edmType = EdmLibHelpers.GetEdmPrimitiveTypeOrNull(type);
PrimitiveTypeConfiguration primitiveType = null;
if (edmType != null)
{
primitiveType = new PrimitiveTypeConfiguration(this, edmType, type);
_primitiveTypes[type] = primitiveType;
return primitiveType;
}
else if (_structuralTypes.ContainsKey(type))
{
return _structuralTypes[type];
}
}
return null;
}
/// <summary>
/// Creates a <see cref="IEdmModel"/> based on the configuration performed using this builder.
/// </summary>
/// <returns>The model that was built.</returns>
[SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "Property is not appropriate, method does work")]
public virtual IEdmModel GetEdmModel()
{
return EdmModelHelperMethods.BuildEdmModel(this);
}
}
}

Просмотреть файл

@ -1,39 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
namespace System.Web.Http.OData.Builder
{
/// <summary>
/// Represents a parameter to a Procedure
/// </summary>
public abstract class ParameterConfiguration
{
/// <summary>
/// Initializes a new instance of the <see cref="ParameterConfiguration"/> class.
/// </summary>
/// <param name="name">The name of the parameter.</param>
/// <param name="parameterType">The EDM type of the paramter.</param>
protected ParameterConfiguration(string name, IEdmTypeConfiguration parameterType)
{
if (name == null)
{
throw Error.ArgumentNull("name");
}
if (parameterType == null)
{
throw Error.ArgumentNull("bindingParameterType");
}
Name = name;
TypeConfiguration = parameterType;
}
/// <summary>
/// The name of the parameter
/// </summary>
public string Name { get; protected set; }
/// <summary>
/// The type of the parameter
/// </summary>
public IEdmTypeConfiguration TypeConfiguration { get; protected set; }
}
}

Просмотреть файл

@ -1,76 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.ComponentModel.DataAnnotations.Schema;
using System.Reflection;
namespace System.Web.Http.OData.Builder
{
/// <summary>
/// Used to configure a primitive property of an entity type or complex type.
/// This configuration functionality is exposed by the model builder Fluent API, see <see cref="ODataModelBuilder"/>.
/// </summary>
public class PrimitivePropertyConfiguration : StructuralPropertyConfiguration
{
/// <summary>
/// Initializes a new instance of the <see cref="PrimitivePropertyConfiguration"/> class.
/// </summary>
/// <param name="property">The name of the property.</param>
/// <param name="declaringType">The declaring EDM type of the property.</param>
public PrimitivePropertyConfiguration(PropertyInfo property, StructuralTypeConfiguration declaringType)
: base(property, declaringType)
{
}
/// <summary>
/// Gets the type of this property.
/// </summary>
public override PropertyKind Kind
{
get { return PropertyKind.Primitive; }
}
/// <summary>
/// Gets the backing CLR type of this property type.
/// </summary>
public override Type RelatedClrType
{
get { return PropertyInfo.PropertyType; }
}
/// <summary>
/// Gets or sets a value indicating which StoreGeneratedPattern is this property.
/// </summary>
public DatabaseGeneratedOption StoreGeneratedPattern { get; set; }
/// <summary>
/// Configures the property to be optional.
/// </summary>
/// <returns>Returns itself so that multiple calls can be chained.</returns>
public PrimitivePropertyConfiguration IsOptional()
{
OptionalProperty = true;
return this;
}
/// <summary>
/// Configures the property to be required.
/// </summary>
/// <returns>Returns itself so that multiple calls can be chained.</returns>
public PrimitivePropertyConfiguration IsRequired()
{
OptionalProperty = false;
return this;
}
/// <summary>
/// Configures the property to have the given <paramref name="databaseGeneratedOption"/>.
/// </summary>
/// <param name="databaseGeneratedOption">Target DatabaseGeneratedOption.</param>
/// <returns>Returns itself so that multiple calls can be chained.</returns>
public PrimitivePropertyConfiguration HasStoreGeneratedPattern(DatabaseGeneratedOption databaseGeneratedOption)
{
StoreGeneratedPattern = databaseGeneratedOption;
return this;
}
}
}

Просмотреть файл

@ -1,95 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using Microsoft.Data.Edm;
namespace System.Web.Http.OData.Builder
{
/// <summary>
/// Represents a PrimitiveType
/// </summary>
public class PrimitiveTypeConfiguration : IEdmTypeConfiguration
{
private Type _clrType;
private IEdmPrimitiveType _edmType;
private ODataModelBuilder _builder;
/// <summary>
/// This constructor is public only for unit testing purposes.
/// To get a PrimitiveTypeConfiguration use ODataModelBuilder.GetTypeConfigurationOrNull(Type)
/// </summary>
public PrimitiveTypeConfiguration(ODataModelBuilder builder, IEdmPrimitiveType edmType, Type clrType)
{
if (builder == null)
{
throw Error.ArgumentNull("builder");
}
if (edmType == null)
{
throw Error.ArgumentNull("edmType");
}
if (clrType == null)
{
throw Error.ArgumentNull("clrType");
}
_builder = builder;
_clrType = clrType;
_edmType = edmType;
}
/// <summary>
/// Gets the backing CLR type of this EDM type.
/// </summary>
public Type ClrType
{
get { return _clrType; }
}
/// <summary>
/// Gets the full name of this EDM type.
/// </summary>
public string FullName
{
get { return _edmType.FullName(); }
}
/// <summary>
/// Gets the namespace of this EDM type.
/// </summary>
public string Namespace
{
get { return _edmType.Namespace; }
}
/// <summary>
/// Gets the name of this EDM type.
/// </summary>
public string Name
{
get { return _edmType.Name; }
}
/// <summary>
/// Gets the <see cref="EdmTypeKind"/> of this EDM type.
/// </summary>
public EdmTypeKind Kind
{
get { return EdmTypeKind.Primitive; }
}
/// <summary>
/// Gets the <see cref="ODataModelBuilder"/> used to create this configuration.
/// </summary>
public ODataModelBuilder ModelBuilder
{
get { return _builder; }
}
/// <summary>
/// Returns the IEdmPrimitiveType associated with this PrimitiveTypeConfiguration
/// </summary>
public IEdmPrimitiveType EdmPrimitiveType
{
get { return _edmType; }
}
}
}

Просмотреть файл

@ -1,105 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
namespace System.Web.Http.OData.Builder
{
/// <summary>
/// Represents a Procedure that is exposed in the model
/// </summary>
public abstract class ProcedureConfiguration
{
/// <summary>
/// The Name of the procedure
/// </summary>
public string Name { get; protected set; }
/// <summary>
/// The parameters the procedure takes
/// </summary>
public abstract IEnumerable<ParameterConfiguration> Parameters { get; }
/// <summary>
/// The type returned when the procedure is invoked.
/// </summary>
public IEdmTypeConfiguration ReturnType { get; set; }
/// <summary>
/// The EntitySet that entities are returned from.
/// </summary>
public EntitySetConfiguration EntitySet { get; set; }
/// <summary>
/// The Kind of procedure, which can be either Action, Function or ServiceOperation
/// </summary>
public abstract ProcedureKind Kind { get; }
/// <summary>
/// The qualified name of the procedure when used in OData urls.
/// Qualification is required to distinguish the procedure from other possible single part identifiers.
/// </summary>
public string ContainerQualifiedName
{
get { return ModelBuilder.ContainerName + "." + Name; }
}
/// <summary>
/// The FullyQualifiedName is the ContainerQualifiedName further qualified using the Namespace.
/// Typically this is not required, because most services have at most one container with the same name.
/// </summary>
public string FullyQualifiedName
{
get { return ModelBuilder.Namespace + "." + ContainerQualifiedName; }
}
/// <summary>
/// The FullName is the ContainerQualifiedName.
/// </summary>
public string FullName
{
get { return ContainerQualifiedName; }
}
/// <summary>
/// Can the procedure be composed upon.
///
/// For example can a URL that invokes the procedure be used as the base url for
/// a request that invokes the procedure and does something else with the results
/// </summary>
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", Justification = "Copies existing spelling used in EdmLib.")]
public virtual bool IsComposable
{
get { return false; }
}
/// <summary>
/// Can the procedure be bound to a url representing the BindingParameter.
/// </summary>
public virtual bool IsBindable
{
get { return false; }
}
/// <summary>
/// If the procedure IsBindable is it Always bindable.
/// </summary>
public virtual bool IsAlwaysBindable
{
get { return IsBindable; }
}
/// <summary>
/// Does the procedure have side-effects.
/// </summary>
public virtual bool IsSideEffecting
{
get { return true; }
}
/// <summary>
/// Gets or sets the <see cref="ODataModelBuilder"/> used to create this configuration.
/// </summary>
protected ODataModelBuilder ModelBuilder { get; set; }
}
}

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше