Merge remote-tracking branch 'orchard/dev' into features/dev/6986

# Conflicts:
#	src/Orchard.Web/Modules/Orchard.MediaProcessing/Orchard.MediaProcessing.csproj
#	src/Orchard.Web/Modules/Orchard.MediaProcessing/packages.config
This commit is contained in:
Benedek Farkas 2024-11-14 18:44:42 +01:00
commit cbb9a4ddba
2507 changed files with 284034 additions and 139841 deletions
.deployment
.github/workflows
.gitignoreCREDITS.txtCalculateBindingRedirects.ps1ClickToBuild.cmdDeploymentUtility.psm1Orchard.projREADME.mdSECURITY.mddeploy.cmddeploy.ps1
lib
src
.editorconfigGulpfile.js
Libraries/NHibernate/NHibernate.Linq
NuGet.config
Orchard.Azure.Tests
Orchard.Core.Tests
Orchard.Profile
Orchard.Specs

View File

@ -1,3 +1,34 @@
; .deployment is actually an INI file and parsed by the following file
; https://raw.githubusercontent.com/projectkudu/kudu/master/Kudu.Core/Infrastructure/IniFile.cs
; Document of deployment with custom script
; https://github.com/projectkudu/kudu/wiki/Customizing-deployments#deploying-with-custom-script
; Document of configurable settings https://github.com/projectkudu/kudu/wiki/Configurable-settings
; Runtime settings cannot be overridden in .deployment e.g. WEBSITE_NODE_DEFAULT_VERSION
; More info https://github.com/projectkudu/kudu/wiki/Configurable-settings#runtime-settings
# Define default node version in WEBSITE_NODE_DEFAULT_VERSION APP Setting
# Find all Node.js versions from your AppService Kudu api/diagnostics/runtime
# More info https://codesanook-reactjs-server-side-rendering.scm.azurewebsites.net/api/diagnostics/runtime
; You can define a custom environment variable as
; CUSTOM_VARIABLE = my custom variable value
; and read in a deploy.ps1 script as
; $Env:CUSTOM_VARIABLE
; https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_powershell_exe?view=powershell-5.1#examples
[config]
command = deploy.cmd
COMMAND = PowerShell -NoProfile -NoLogo -ExecutionPolicy Unrestricted -Command "& "$(Join-Path -Path $(Get-Location) -ChildPath deploy.ps1)" 2>&1 | Write-Output"
; Set additional environment variables
; Timeout in seconds
; Set to one hour
SCM_COMMAND_IDLE_TIMEOUT = 3600
; Variables for MSBuild
MSBUILD_PATH = D:\Program Files (x86)\MSBuild-16.4\MSBuild\Current\Bin\MSBuild.exe
SOLUTION_PATH = src/Orchard.sln
; For Azure deployment, we use custom MS Build at root of the project.
PROJECT_PATH = Orchard.proj

View File

@ -0,0 +1,30 @@
name: Build Crowdin Translation Packages
on:
workflow_dispatch:
schedule:
- cron: "0 6 * * *"
jobs:
build-crowdin-translation-packages:
defaults:
run:
shell: pwsh
runs-on: ubuntu-latest
steps:
- name: Orchard CMS
uses: andrii-bodnar/crowdin-request-action@aac9a865d62b37060b0ce530db5ac5cfca02dd2c # 0.0.2
with:
route: POST /projects/{projectId}/translations/builds
projectId: 46524
env:
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_API_TOKEN }}
- name: Orchard CMS Gallery
uses: andrii-bodnar/crowdin-request-action@aac9a865d62b37060b0ce530db5ac5cfca02dd2c # 0.0.2
with:
route: POST /projects/{projectId}/translations/builds
projectId: 63766
env:
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_API_TOKEN }}

70
.github/workflows/compile.yml vendored Normal file
View File

@ -0,0 +1,70 @@
name: Compile
# Compiles the solution and runs unit tests.
on:
workflow_dispatch:
pull_request:
push:
branches:
- dev
- 1.10.x
jobs:
compile-dotnet:
name: Compile .NET solution
defaults:
run:
shell: pwsh
runs-on: windows-latest
steps:
- name: Clone repository
uses: actions/checkout@v4.1.1
- name: Restore NuGet packages
run: nuget restore src/Orchard.sln
- name: Add msbuild to PATH
uses: microsoft/setup-msbuild@v2
- name: Compile
run: msbuild Orchard.proj /m /v:minimal /t:Compile /p:MvcBuildViews=true /p:TreatWarningsAsErrors=true -WarnAsError
- name: Test
run: msbuild Orchard.proj /m /v:minimal /t:Test
compile-node:
name: Compile client-side assets
defaults:
run:
shell: pwsh
runs-on: windows-latest
steps:
- name: Clone repository
uses: actions/checkout@v4.1.1
- name: Setup NodeJS
uses: actions/setup-node@v4.0.2
with:
node-version: '7'
- name: Setup NPM packages
working-directory: ./src
run: |
npm install --loglevel warn
# Install gulp globally to be able to run the rebuild task, using the same version as in the project.
$gulpVersion = (Get-Content Package.json -Raw | ConvertFrom-Json).devDependencies.gulp
Start-Process npm -NoNewWindow -Wait -ArgumentList "install gulp@$gulpVersion -g --loglevel warn"
- name: Rebuild client-side assets
working-directory: ./src
run: |
gulp rebuild
git add . # To make line ending changes "disappear".
$gitStatus = (git status --porcelain)
if ($gitStatus)
{
throw ("Client-side assets are not up-to-date. Please run 'gulp rebuild' and commit the changes.`n" +
[System.String]::Join([System.Environment]::NewLine, $gitStatus))
}

39
.github/workflows/specflow.yml vendored Normal file
View File

@ -0,0 +1,39 @@
name: SpecFlow tests
# Compiles the solution and runs unit tests, as well the SpecFlow tests on the main development branches.
on:
workflow_dispatch:
schedule:
- cron: '0 0 * * *' # Every day at midnight.
jobs:
compile:
name: SpecFlow tests
defaults:
run:
shell: pwsh
runs-on: windows-latest
strategy:
matrix:
branch: [dev, 1.10.x]
steps:
- name: Clone repository
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
ref: ${{ matrix.branch }}
- name: Restore NuGet packages
run: nuget restore src/Orchard.sln
- name: Add msbuild to PATH
uses: microsoft/setup-msbuild@6fb02220983dee41ce7ae257b6f4d8f9bf5ed4ce # v2.0.0
- name: Compile
run: msbuild Orchard.proj /m /v:minimal /t:Compile /p:MvcBuildViews=true /p:TreatWarningsAsErrors=true -WarnAsError
- name: Test
run: msbuild Orchard.proj /m /v:minimal /t:Test
- name: Spec
run: msbuild Orchard.proj /m /v:minimal /t:Spec

9
.gitignore vendored
View File

@ -1,5 +1,7 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
**/.vs/**
src/Rebracer.xml
# User-specific files
*.suo
@ -68,6 +70,9 @@ ipch/
_ReSharper*/
*.[Rr]e[Ss]harper
# CodeRush is a .NET coding add-in
.cr*/
# TeamCity is a build add-in
_TeamCity*
@ -172,7 +177,7 @@ profiling/
src/Orchard.Web/Modules-temp/*
src/Backup/*
src/packages/*
src/node_modules
src/**/node_modules
src/UpgradeLog.*
*.itrace.csdef
*.build.csdef
@ -187,5 +192,3 @@ src/Orchard.Azure/Orchard.Azure.CloudService/Staging/
#enable all /lib artifacts
!lib/**/*.*
*/.vs/*
src/Rebracer.xml

View File

@ -187,15 +187,15 @@ License: Apache Software Foundation License 2.0
Lucene.net
-----
Website: http://incubator.apache.org/projects/lucene.net.html
Website: https://lucenenet.apache.org/
Copyright: Copyright (c) 2009 Apache Software Foundation
License: Apache Software Foundation License 2.0
MarkdownSharp
Markdig
-----
Website: http://code.google.com/p/markdownsharp/
Copyright: Copyright (c) 2009-2011 Jeff Atwood
License: MIT
Website: https://github.com/lunet-io/markdig
Copyright: Copyright (c) 2018-2019, Alexandre Mutel
License: BSD 2-Clause
Mono Class Library
-----
@ -293,4 +293,4 @@ YUI
-----
Website: http://developer.yahoo.com/yui/
Copyright: Copyright (c) 2010, Yahoo! Inc.
License: New BSD
License: New BSD

View File

@ -0,0 +1,34 @@
[Reflection.Assembly]::LoadWithPartialName("System.Xml") | Out-Null
[Reflection.Assembly]::LoadWithPartialName("System.Xml.Linq") | Out-Null
[System.Xml.Linq.XNamespace]$ns1 = "urn:schemas-microsoft-com:asm.v1"
$currentPath = (Get-Item -Path ".\").FullName
$orchardWebConfigFullPath = $currentPath+"\src\Orchard.Web\Web.Config"
[XML] $orchardWebConfig = Get-Content ($orchardWebConfigFullPath)
$configFiles = Get-ChildItem -Path ($currentPath +"\src\") -Filter web.config -Recurse -ErrorAction SilentlyContinue -Force
foreach ($configFile in $configFiles) {
$configFullPath = $configFile.FullName
Write-Host "Processing $configFullPath ..."
if ($configFullPath.ToLower().EndsWith("\orchard.web\web.config")) { #skip orchard.web config files
continue
}
[XML] $projectWebConfig = Get-Content ($configFullPath)
$elements = $projectWebConfig.configuration.runtime.assemblyBinding.dependentAssembly
foreach ($element in $elements){
Write-Host "Checking" $element.assemblyIdentity.name
$hasBinding = $orchardWebConfig.configuration.runtime.assemblyBinding.dependentAssembly.assemblyIdentity.Where({ $_.name -eq $element.assemblyIdentity.name -and $_.publicKeyToken -eq $element.assemblyIdentity.publicKeyToken -and $_.culture -eq $element.assemblyIdentity.culture }, 'First').Count -gt 0
if (-not $hasBinding){
# add the node in $webConfig
Write-Host "Adding" $element.assemblyIdentity.name
$newNode = $orchardWebConfig.ImportNode($element, $true);
$orchardWebConfig.configuration.runtime.assemblyBinding.AppendChild($newNode)
Write-Host "Added " $element.assemblyIdentity.name
} else {
Write-Host "Skipped" $element.assemblyIdentity.name
}
}
Write-Host "Processed $configFullPath ..."
}
$orchardWebConfig.Save($orchardWebConfigFullPath)

View File

@ -1,22 +1,20 @@
FOR %%b in (
"%VS140COMNTOOLS%..\..\VC\vcvarsall.bat"
"%ProgramFiles(x86)%\Microsoft Visual Studio 14.0\VC\vcvarsall.bat"
"%ProgramFiles%\Microsoft Visual Studio 14.0\VC\vcvarsall.bat"
@echo off
"%VS120COMNTOOLS%..\..\VC\vcvarsall.bat"
"%ProgramFiles(x86)%\Microsoft Visual Studio 12.0\VC\vcvarsall.bat"
"%ProgramFiles%\Microsoft Visual Studio 12.0\VC\vcvarsall.bat"
REM Necessary for the InstallDir variable to work inside the MsBuild-finding loop below.
SETLOCAL ENABLEDELAYEDEXPANSION
"%VS110COMNTOOLS%..\..\VC\vcvarsall.bat"
"%ProgramFiles(x86)%\Microsoft Visual Studio 11.0\VC\vcvarsall.bat"
"%ProgramFiles%\Microsoft Visual Studio 11.0\VC\vcvarsall.bat"
) do (
if exist %%b (
call %%b x86
goto build
)
for /f "usebackq tokens=1* delims=: " %%i in (`lib\vswhere\vswhere -latest -version "[16.0,18.0)" -requires Microsoft.Component.MSBuild`) do (
if /i "%%i"=="installationPath" (
set InstallDir=%%j
echo !InstallDir!
if exist "!InstallDir!\MSBuild\Current\Bin\MSBuild.exe" (
echo "Using MSBuild from !InstallDir!"
set msbuild="!InstallDir!\MSBuild\Current\Bin\MSBuild.exe"
goto build
)
)
)
echo "Unable to detect suitable environment. Build may not succeed."
:build
@ -31,6 +29,8 @@ IF "%solution%" == "" SET solution=src\Orchard.sln
lib\nuget\nuget.exe restore %solution%
msbuild /t:%target% %project% /p:Solution=%solution%
%msbuild% /t:%target% %project% /p:Solution=%solution% /m
:end
pause

83
DeploymentUtility.psm1 Normal file
View File

@ -0,0 +1,83 @@
Set-StrictMode -Version Latest
# Continue a build process even though there is a warning wrote to std err.
# We will check exit code in Invoke-ExternalCommand to design whether it fail or not
$ErrorActionPreference = "Continue"
function Add-NpmToPathVariable {
$path = "$env:Appdata\npm"
$escapedPath = [Regex]::Escape($path)
# Remove existing npm path safe to add npm path again
$paths = $env:Path -split ';' | Where-Object {
$_ -notmatch "^$escapedPath\\?$"
}
# Update a path variable to this session
$env:Path = ($paths + $path) -join ";" # array + element item
}
function Invoke-ExternalCommand {
param (
[Parameter(Mandatory = $true)] [scriptblock] $ScriptBlock
)
# Displays an error message and continue executing if there is a standard error.
# This is because there are some external command tools write warning message to standard error.
# Use Write-Output also fix "Window title cannot be longer than 1023 characters" issue
# https://github.com/projectkudu/kudu/issues/2635
& $ScriptBlock 2>&1 | Write-Output
# If last exit code is not 0, throw an exception to stop a script
if ($LastExitCode) {
throw "Failed with exit code = $LastExitCode and command = $($ScriptBlock.ToString())"
}
}
function Write-EnviromentValue {
param (
[Parameter(Mandatory = $true)] [String[]] $EnvironmentName
)
"----------------- Begin of environment variables ---------------------------------"
Get-Item -Path Env:* | Where-Object {
$EnvironmentName -contains $_.Name
} | Format-Table Name, Value -Wrap
"----------------- End of environment variables ---------------------------------"
}
function Install-Yarn {
"Verify if yarn installed"
if (Get-Command -Name yarn -ErrorAction Ignore) {
"Updating yarn as a global tool to the latest version"
# https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/cmd#parameters
# issue https://github.com/projectkudu/kudu/issues/2635
Invoke-ExternalCommand -ScriptBlock { npm update yarn -g }
}
else {
"Installing yarn as a global tool"
Invoke-ExternalCommand -ScriptBlock { npm install yarn -g }
Add-NpmToPathVariable
}
}
function Install-KuduSync {
"Verify if kudusync installed"
if (Get-Command -Name kudusync -ErrorAction Ignore) {
"Updating kudusync as a global tool to the latest version"
# https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/cmd#parameters
# issue https://github.com/projectkudu/kudu/issues/2635
Invoke-ExternalCommand -ScriptBlock { npm update kudusync -g }
}
else {
"Installing kudusync as a global tool"
Invoke-ExternalCommand -ScriptBlock { npm install kudusync -g }
Add-NpmToPathVariable
}
}
Export-ModuleMember -Function Invoke-ExternalCommand
Export-ModuleMember -Function Write-EnviromentValue
Export-ModuleMember -Function Install-Yarn
Export-ModuleMember -Function Install-KuduSync

View File

@ -13,9 +13,10 @@
<SourceArtifactFolder>$(ArtifactsFolder)\Source</SourceArtifactFolder>
<MsDeployArtifactFolder>$(ArtifactsFolder)\MsDeploy</MsDeployArtifactFolder>
<GalleryArtifactFolder>$(ArtifactsFolder)\Gallery</GalleryArtifactFolder>
<ModulesSrcFolder>$(MSBuildProjectDirectory)\src\Orchard.Web\Modules</ModulesSrcFolder>
<OrchardCoreSrcFolder>$(MSBuildProjectDirectory)\src\Orchard.Web\Core</OrchardCoreSrcFolder>
<ThemesSrcFolder>$(MSBuildProjectDirectory)\src\Orchard.Web\Themes</ThemesSrcFolder>
<OrchardWebFolder>$(SrcFolder)\Orchard.Web</OrchardWebFolder>
<ModulesSrcFolder>$(OrchardWebFolder)\Modules</ModulesSrcFolder>
<OrchardCoreSrcFolder>$(OrchardWebFolder)\Core</OrchardCoreSrcFolder>
<ThemesSrcFolder>$(OrchardWebFolder)\Themes</ThemesSrcFolder>
<CompileFolder>$(BuildFolder)\Compile</CompileFolder>
<WebSitesFolder>$(CompileFolder)\_PublishedWebsites</WebSitesFolder>
@ -112,15 +113,25 @@
</Target>
<Target Name="Compile">
<CallTarget Targets="DevCompile"/>
<!-- Compile to "OutputFolder" -->
<MSBuild
Projects="$(Solution)"
Targets="Build"
Properties="Configuration=$(Configuration);OutputPath=$(CompileFolder)" />
</Target>
<Target Name="DevCompile">
<!-- To make sure that Roslyn tools are available, since it's included with Orchard.Web, which is not referenced by
other projects, so it will be built towards the end. -->
<MSBuild
Projects="$(OrchardWebFolder)\Orchard.Web.csproj"
Targets="CopyRoslynCompilerFilesToOutputDirectory" />
<!-- Compile to "regular" output folder for devs using VS locally -->
<MSBuild
Projects="$(Solution)"
Targets="Build"/>
Targets="Build"
Properties="Configuration=$(Configuration);MvcBuildViews=$(MvcBuildViews)" />
</Target>
<Target Name="CompileMsBuildTasks">
@ -150,12 +161,13 @@
<NUnit Assemblies="@(TestAssemblies)" ToolPath="@(NUnitPackageToolsFolder)" WorkingDirectory="$(CompileFolder)" OutputXmlFile="$(BuildFolder)\Orchard.Tests.xml" ExcludeCategory="longrunning" />
</Target>
<Target Name="Spec" DependsOnTargets="Package-Stage">
<CreateItem Include="$(CompileFolder)\*.Specs.dll">
<Target Name="Spec" DependsOnTargets="Compile">
<!-- Running SpecFlow tests -->
<CreateItem Include="$(CompileFolder)\*.Specs.*dll">
<Output TaskParameter="Include" ItemName="SpecAssemblies" />
</CreateItem>
<NUnit Assemblies="@(SpecAssemblies)" ToolPath="$(NUnitPackageToolsFolder)" />
<NUnit Assemblies="@(SpecAssemblies)" ToolPath="@(NUnitPackageToolsFolder)" WorkingDirectory="$(CompileFolder)" OutputXmlFile="$(BuildFolder)\Orchard.Specs.xml" />
</Target>
<!-- Packaging (Stage) -->
@ -367,11 +379,6 @@
</ItemGroup>
<Copy SourceFiles="@(Profiling-Web)" DestinationFolder="$(ProfilingFolder)\%(RecursiveDir)"/>
<XmlUpdate XmlFileName="$(ProfilingFolder)\web.config"
XPath="/configuration/system.web/trust/@level"
Value="Full" />
</Target>
<Target Name="Profiling-Setup">
@ -379,7 +386,7 @@
<Output TaskParameter="Lines" ItemName="ProfilingSetupCommands"/>
</ReadLinesFromFile>
<Exec Command="$(ProfilingFolder)\bin\Orchard.exe %(ProfilingSetupCommands.Identity)" WorkingDirectory="$(ProfilingFolder)"/>
<Exec Command="&quot;$(ProfilingFolder)\bin\Orchard.exe&quot; %(ProfilingSetupCommands.Identity)" WorkingDirectory="$(ProfilingFolder)"/>
</Target>
<!-- Gallery Packaging -->

View File

@ -6,54 +6,47 @@ Orchard is a free, open source, community-focused Content Management System buil
You can try it for free on [DotNest.com](https://dotnest.com) or on Microsoft Azure by clicking on this button.
[![Deploy to Azure](https://azuredeploy.net/deploybutton.png)](https://portal.azure.com/#create/OutercurveFoundation.OrchardCMS.1.0.4)
[![Deploy to Azure](https://azuredeploy.net/deploybutton.png)](https://portal.azure.com/#create/OutercurveFoundation.OrchardCMS)
## About The Orchard Project
#### Please visit our website at http://orchardproject.net for the most current information about this project.
#### Please visit our website at https://orchardproject.net for the most current information about this project.
Orchard is a free, open source, community-focused **Content Management System** built on the ASP.NET MVC platform.
Orchard is built on a modern architecture that puts extensibility up-front, as its number one concern. All components in Orchard can be replaced or extended. Content is built from easily composable building blocks. Modules extend the system in a very decoupled fashion, where a commenting module for example can as easily apply to pages, blog posts, photos or products. A rich UI composition system completes the picture and ensures that you can get the exact presentation that you need for your content.
Orchard is delivered under the [.NET Foundation](http://www.dotnetfoundation.org/orchard). It is licensed under a [New BSD license](http://www.opensource.org/licenses/bsd-license.php), which is approved by the OSI.
Orchard is delivered under the [.NET Foundation](https://www.dotnetfoundation.org/projects?searchquery=Orchard&type=project). It is licensed under a [New BSD license](https://www.opensource.org/licenses/bsd-license.php), which is approved by the OSI.
Our mission is to empower our users and foster a dedicated and diverse community that builds the CMS that we all want to use.
## Project Status
Orchard is currently in version **1.10** and **1.9.3**:
- *1.10* is the latest major version which introduces more impactful changes and new features as well. If you're new to Orchard, you should start with this version.
- *1.9.3* contains further bugfixes in addition to *1.9.2* and these versions are based on the feature set of Orchard *1.9*.
Orchard is currently in version **[1.10.3](https://github.com/OrchardCMS/Orchard/releases/tag/1.10.3)**: It contains bugfixes and the more impactful changes and new features added in the latest major version (*1.10*).
We invite participation by the developer community in shaping the projects direction, so that we can publicly validate our designs and development approach.
All our releases are available on our [Releases](https://github.com/OrchardCMS/Orchard/releases) page, and it's easy to [Install Orchard using the Web Platform Installer](http://docs.orchardproject.net/Documentation/Installing-Orchard) as well. We encourage interested developers to check out the source code on the Orchard GitHub site and get involved with the project.
All our releases are available on our [Releases](https://github.com/OrchardCMS/Orchard/releases) page, and we encourage interested developers to check out the source code on the Orchard GitHub site and get involved with the project.
* [Download the latest release](https://github.com/OrchardCMS/Orchard/releases)
* [Feature roadmap](http://docs.orchardproject.net/Documentation/feature-roadmap)
* [Docs and designs/specs](http://www.orchardproject.net/docs)
* [About us](http://www.orchardproject.net/about)
* [Contact us](mailto:ofeedbk@microsoft.com)
* [Feature roadmap](https://docs.orchardproject.net/en/latest/Documentation/Feature-roadmap/)
* [Docs and designs/specs](https://docs.orchardproject.net)
## How To Get Involved
We hope that by engaging with the community we will continue to shape Orchard into a valuable set of tools and applications. The Orchard team is committed to open community participation and accepts code contributions. We encourage community participation at all levels from general project feedback to bug fixes and patches.
There are many ways you can [contribute to Orchard](http://orchardproject.net/contribution):
There are many ways you can contribute to Orchard:
* [Check out the code](https://github.com/OrchardCMS/Orchard)
* [Write documentation](https://github.com/OrchardCMS/OrchardDoc)
* [Find and file a bug](https://github.com/OrchardCMS/Orchard/issues)
* [Propose a feature idea](http://orchard.uservoice.com)
* [Ask and answer questions in our forums](http://www.orchardproject.net/discussions) and [on Stack Overflow](http://stackoverflow.com/questions/tagged/orchardcms)
* [Propose a feature idea](https://github.com/OrchardCMS/Orchard/issues/new)
* [Ask and answer questions on Stack Overflow](https://stackoverflow.com/questions/tagged/orchardcms)
* [Participate in our gitter.im chatroom](https://gitter.im/OrchardCMS/Orchard)
* [Participate in forum discussions](http://orchard.codeplex.com/discussions)
* [Submit a pull request](http://docs.orchardproject.net/Documentation/Contributing-patches)
* [Translate Orchard](http://orchardproject.net/localization)
* [Contribute modules and themes to our gallery](http://gallery.orchardproject.net/)
* [Send us feedback](mailto:ofeedbk@microsoft.com)
* [Submit a pull request](https://docs.orchardproject.net/en/latest/Documentation/Contributing-patches/)
* [Translate Orchard](https://crowdin.com/project/orchard-cms)
* [Contribute modules and themes to our gallery](https://gallery.orchardproject.net/)
## The Future Of Orchard CMS: Orchard 2
## The Future Of Orchard CMS: Orchard Core
As the underlying frameworks (.NET, ASP.NET and ASP.NET MVC) are constantly evolving, Orchard of course keeps track of the changes and improvements of these: Orchard 2 is the next generation of Orchard releases that is based on [ASP.NET Core](http://www.asp.net/core). Just like the current Orchard project, it's fully [open-source and is publicly available on GitHub](https://github.com/OrchardCMS/Orchard2). Orchard 2 (as a framework) is being built from scratch: it's still in development and does not share any of its code base (at least directly) with the current versions of Orchard.
As the underlying frameworks (.NET, ASP.NET and ASP.NET MVC) are constantly evolving, Orchard of course keeps track of the changes and improvements of these: Orchard Core is the next generation of Orchard releases that is based on [ASP.NET Core](https://www.asp.net/core). Just like the current Orchard project, it's fully [open-source and is publicly available on GitHub](https://github.com/OrchardCMS/OrchardCore). Orchard Core (as a framework) is being built from scratch: it's still in development and does not share any of its code base (at least directly) with the current versions (1.x) of Orchard.

21
SECURITY.md Normal file
View File

@ -0,0 +1,21 @@
# Security Policy
## Supported Versions
| Version | Supported |
| ------- | ------------------ |
| 1.0 | :x: |
| 1.1 | :x: |
| 1.2 | :x: |
| 1.3 | :x: |
| 1.4 | :x: |
| 1.5 | :x: |
| 1.6 | :x: |
| 1.7 | :x: |
| 1.8 | :x: |
| 1.9 | :x: |
| 1.10 | :white_check_mark: |
## Reporting a Vulnerability
Send an email to sebros@microsoft.com

View File

@ -72,7 +72,7 @@ call :ExecuteCmd nuget restore "%DEPLOYMENT_SOURCE%\src\Orchard.sln"
IF !ERRORLEVEL! NEQ 0 goto error
:: 2. Build to the temporary path
call :ExecuteCmd "%MSBUILD_PATH%" "%DEPLOYMENT_SOURCE%\Orchard.proj" /t:Precompiled /v:m
call :ExecuteCmd "%MSBUILD_PATH%" "%DEPLOYMENT_SOURCE%\Orchard.proj" /t:Precompiled /v:m /m
IF !ERRORLEVEL! NEQ 0 goto error
:: 3. KuduSync

119
deploy.ps1 Normal file
View File

@ -0,0 +1,119 @@
# https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/set-strictmode?view=powershell-7
Set-StrictMode -Version Latest
# https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_preference_variables?view=powershell-7#erroractionpreference
$ErrorActionPreference = "Continue" # Just explicit set it
Import-Module -Name ./DeploymentUtility -Force
"Verify if Node.js installed"
if (-not (Get-Command -Name node -ErrorAction Ignore)) {
throw (
"Missing Node.js executable, please install Node.js." +
"If already installed, make sure it can be reached from the current environment."
)
}
$ARTIFACTS = "$PSScriptRoot/../artifacts"
# Set deployment source folder
if (-not $Env:DEPLOYMENT_SOURCE) {
'Set $DEPLOYMENT_SOURCE variable from the current directory'
$Env:DEPLOYMENT_SOURCE = $PSScriptRoot
}
if (-not $Env:DEPLOYMENT_TARGET) {
'Set $DEPLOYMENT_TARGET variable'
$Env:DEPLOYMENT_TARGET = "$ARTIFACTS/wwwroot"
}
if (-not $Env:NEXT_MANIFEST_PATH) {
'Set $NEXT_MANIFEST_PATH variable'
$Env:NEXT_MANIFEST_PATH = "$ARTIFACTS/manifest"
if (-not $Env:PREVIOUS_MANIFEST_PATH) {
'Set $PREVIOUS_MANIFEST_PATH variable'
$Env:PREVIOUS_MANIFEST_PATH = "$ARTIFACTS/manifest"
}
}
# Log environment variables
$environmentNameToWriteValue = @(
"DEPLOYMENT_SOURCE"
"DEPLOYMENT_TARGET"
"NEXT_MANIFEST_PATH"
"PREVIOUS_MANIFEST_PATH"
"WEBSITE_NODE_DEFAULT_VERSION"
"WEBSITE_NPM_DEFAULT_VERSION"
"SCM_REPOSITORY_PATH"
"SOLUTION_PATH"
"PROJECT_PATH"
"MSBUILD_PATH"
"Path"
)
Write-EnviromentValue -EnvironmentName $environmentNameToWriteValue
################ Build Node.js project with yarn if there is yarn.lock file ################
$nodeProjectsDir = Get-ChildItem -Path . -Recurse -Filter "yarn.lock" |
Select-Object -ExpandProperty DirectoryName -Unique |
Where-Object { $_ -NotMatch "node_modules" }
"Node projects directory:"
$nodeProjectsDir
Install-Yarn
$nodeProjectsDir | Foreach-Object {
$projectDir = $_
Push-Location -Path $projectDir
"Current Node project directory is $(Get-Location)"
"Installing npm packages with yarn"
Invoke-ExternalCommand -ScriptBlock { yarn install }
"Building Node.js project with yarn"
Invoke-ExternalCommand -ScriptBlock { yarn build }
Pop-Location
}
###########################################################################################
# Build .NET project
"Restore NuGet packages"
# REF https://docs.microsoft.com/en-us/nuget/reference/cli-reference/cli-ref-restore#options
$msBuildDir = Split-Path -Path $Env:MSBUILD_PATH -Parent
Invoke-ExternalCommand -ScriptBlock { ./lib/nuget/nuget.exe restore "$Env:SOLUTION_PATH" -MSBuildPath "$msBuildDir" }
"Build .NET project to the pre-compiled directory"
$preCompiledDir = "$Env:DEPLOYMENT_SOURCE/build/Precompiled"
"Build .NET project to the temp directory"
"Building the project with MSBuild to '$preCompiledDir'"
Invoke-ExternalCommand -ScriptBlock {
cmd /c "$Env:MSBUILD_PATH" `
"$Env:PROJECT_PATH" `
/t:Precompiled `
/p:PreCompiledDir=$preCompiledDir `
/verbosity:minimal `
/maxcpucount `
/nologo `
$Env:SCM_BUILD_ARGS
# Set SCM_BUILD_ARGS as App Service Configuration to any string you want to append to the MSBuild command line.
}
Install-KuduSync
"Syncing a build output to a deployment folder"
Invoke-ExternalCommand -ScriptBlock {
cmd /c kudusync `
-f "$preCompiledDir" `
-t "$Env:DEPLOYMENT_TARGET" `
-n "$Env:NEXT_MANIFEST_PATH" `
-p "$Env:PREVIOUS_MANIFEST_PATH" `
-i ".git;.hg;.deployment;deploy.cmd;deploy.ps1;node_modules;"
}
if ($Env:POST_DEPLOYMENT_ACTION) {
"Post deployment stub"
Invoke-ExternalCommand -ScriptBlock { $Env:POST_DEPLOYMENT_ACTION }
}
"Deployment successfully"

View File

@ -1,4 +0,0 @@
This library has been modified to be compatible with NH 3.3.
The file DetachedCriteriaAdapter.cs in this folder is used instead of the original onw from soure code.
Source code can be found at http://sourceforge.net/projects/nhcontrib/files/NHibernate.Linq/1.0/NHibernate.Linq-1.0.0.GA-src.zip

Binary file not shown.

BIN
lib/vswhere/vswhere.exe Normal file

Binary file not shown.

11
src/.editorconfig Normal file
View File

@ -0,0 +1,11 @@
root = true
[*]
end_of_line = crlf
indent_style = space
indent_size = 4
trim_trailing_whitespace = true
[*.cs]
csharp_new_line_before_open_brace = none
dotnet_sort_system_directives_first = true

View File

@ -10,8 +10,10 @@ var fs = require("fs"),
plumber = require("gulp-plumber"),
sourcemaps = require("gulp-sourcemaps"),
less = require("gulp-less"),
cssnano = require("gulp-cssnano"),
sass = require("gulp-sass"),
postcss = require("gulp-postcss"),
autoprefixer = require("autoprefixer"),
cssnano = require("cssnano"),
typescript = require("gulp-typescript"),
uglify = require("gulp-uglify"),
rename = require("gulp-rename"),
@ -95,10 +97,16 @@ function resolveAssetGroupPaths(assetGroup, assetManifestPath) {
assetGroup.manifestPath = assetManifestPath;
assetGroup.basePath = path.dirname(assetManifestPath);
assetGroup.inputPaths = assetGroup.inputs.map(function (inputPath) {
return path.resolve(path.join(assetGroup.basePath, inputPath));
var excludeFile = false;
if (inputPath.startsWith('!')) {
inputPath = inputPath.slice(1);
excludeFile = true;
}
var newPath = path.resolve(path.join(assetGroup.basePath, inputPath));
return (excludeFile ? '!' : '') + newPath;
});
assetGroup.watchPaths = [];
if (!!assetGroup.watch) {
if (assetGroup.watch) {
assetGroup.watchPaths = assetGroup.watch.map(function (watchPath) {
return path.resolve(path.join(assetGroup.basePath, watchPath));
});
@ -111,12 +119,6 @@ function resolveAssetGroupPaths(assetGroup, assetManifestPath) {
function createAssetGroupTask(assetGroup, doRebuild) {
var outputExt = path.extname(assetGroup.output).toLowerCase();
var doConcat = path.basename(assetGroup.outputFileName, outputExt) !== "@";
if (doConcat && !doRebuild) {
// Force a rebuild of this asset group is the asset manifest file itself is newer than the output.
var assetManifestStats = fs.statSync(assetGroup.manifestPath);
var outputStats = fs.existsSync(assetGroup.outputPath) ? fs.statSync(assetGroup.outputPath) : null;
doRebuild = !outputStats || assetManifestStats.mtime > outputStats.mtime;
}
switch (outputExt) {
case ".css":
return buildCssPipeline(assetGroup, doConcat, doRebuild);
@ -136,51 +138,65 @@ function buildCssPipeline(assetGroup, doConcat, doRebuild) {
throw "Input file '" + inputPath + "' is not of a valid type for output file '" + assetGroup.outputPath + "'.";
});
var generateSourceMaps = assetGroup.hasOwnProperty("generateSourceMaps") ? assetGroup.generateSourceMaps : true;
var containsLessOrScss = assetGroup.inputPaths.some(function (inputPath) {
var ext = path.extname(inputPath).toLowerCase();
return ext === ".less" || ext === ".scss";
});
// Source maps are useless if neither concatenating nor transforming.
if ((!doConcat || assetGroup.inputPaths.length < 2) && !assetGroup.inputPaths.some(function (inputPath) { return path.extname(inputPath).toLowerCase() === ".less"; }))
if ((!doConcat || assetGroup.inputPaths.length < 2) && !containsLessOrScss)
generateSourceMaps = false;
var minifiedStream = gulp.src(assetGroup.inputPaths) // Minified output, source mapping completely disabled.
.pipe(gulpif(!doRebuild,
gulpif(doConcat,
newer(assetGroup.outputPath),
newer({
dest: assetGroup.outputDir,
ext: ".css"
}))))
newer({
dest: doConcat ? assetGroup.outputPath : assetGroup.outputDir,
ext: doConcat ? null : ".css",
extra: assetGroup.manifestPath // Force a rebuild of this asset group is the asset manifest file itself is newer than the output(s).
})
))
.pipe(plumber())
.pipe(gulpif("*.less", less()))
.pipe(gulpif("*.scss", sass({
precision: 10
precision: 10
})))
.pipe(gulpif(doConcat, concat(assetGroup.outputFileName)))
.pipe(cssnano({
autoprefixer: { browsers: ["last 2 versions"] },
discardComments: { removeAll: true },
discardUnused: false,
mergeIdents: false,
reduceIdents: false,
zindex: false
}))
.pipe(rename({
suffix: ".min"
}))
.pipe(postcss([
autoprefixer({ browsers: ["last 2 versions"] }),
cssnano({
discardComments: { removeAll: true },
discardUnused: false,
mergeIdents: false,
reduceIdents: false,
zindex: false
})
]))
.pipe(eol())
.pipe(rename(function (path) {
if (assetGroup.flatten)
path.dirname = "";
if (assetGroup.separateMinified)
path.dirname += "/min";
else
path.basename += ".min";
}))
.pipe(gulp.dest(assetGroup.outputDir));
var devStream = gulp.src(assetGroup.inputPaths) // Non-minified output, with source mapping
var devStream = gulp.src(assetGroup.inputPaths) // Non-minified output, with source mapping.
.pipe(gulpif(!doRebuild,
gulpif(doConcat,
newer(assetGroup.outputPath),
newer({
dest: assetGroup.outputDir,
ext: ".css"
}))))
newer({
dest: doConcat ? assetGroup.outputPath : assetGroup.outputDir,
ext: doConcat ? null : ".css",
extra: assetGroup.manifestPath // Force a rebuild of this asset group is the asset manifest file itself is newer than the output(s).
})
))
.pipe(plumber())
.pipe(gulpif(generateSourceMaps, sourcemaps.init()))
.pipe(gulpif("*.less", less()))
.pipe(gulpif("*.scss", sass({
precision: 10
precision: 10
})))
.pipe(gulpif(doConcat, concat(assetGroup.outputFileName)))
.pipe(postcss([
autoprefixer({ browsers: ["last 2 versions"] })
]))
.pipe(header(
"/*\n" +
"** NOTE: This file is generated by Gulp and should not be edited directly!\n" +
@ -188,6 +204,10 @@ function buildCssPipeline(assetGroup, doConcat, doRebuild) {
"*/\n\n"))
.pipe(gulpif(generateSourceMaps, sourcemaps.write()))
.pipe(eol())
.pipe(rename(function (path) {
if (assetGroup.flatten)
path.dirname = "";
}))
.pipe(gulp.dest(assetGroup.outputDir));
return merge([minifiedStream, devStream]);
}
@ -199,37 +219,44 @@ function buildJsPipeline(assetGroup, doConcat, doRebuild) {
throw "Input file '" + inputPath + "' is not of a valid type for output file '" + assetGroup.outputPath + "'.";
});
var generateSourceMaps = assetGroup.hasOwnProperty("generateSourceMaps") ? assetGroup.generateSourceMaps : true;
// Source maps are useless if neither concatenating nor transforming.
// Source maps are useless if neither concatenating nor transpiling.
if ((!doConcat || assetGroup.inputPaths.length < 2) && !assetGroup.inputPaths.some(function (inputPath) { return path.extname(inputPath).toLowerCase() === ".ts"; }))
generateSourceMaps = false;
var typeScriptOptions = { allowJs: true, noImplicitAny: true, noEmitOnError: true, module: 'amd' };
if (assetGroup.typeScriptOptions)
typeScriptOptions = Object.assign(typeScriptOptions, assetGroup.typeScriptOptions); // Merge override options from asset group if any.
if (doConcat)
typeScriptOptions.outFile = assetGroup.outputFileName;
return gulp.src(assetGroup.inputPaths)
.pipe(gulpif(!doRebuild,
gulpif(doConcat,
newer(assetGroup.outputPath),
newer({
dest: assetGroup.outputDir,
ext: ".js"
}))))
newer({
dest: doConcat ? assetGroup.outputPath : assetGroup.outputDir,
ext: doConcat ? null : ".js",
extra: assetGroup.manifestPath // Force a rebuild of this asset group is the asset manifest file itself is newer than the output(s).
})
))
.pipe(plumber())
.pipe(gulpif(generateSourceMaps, sourcemaps.init()))
.pipe(gulpif("*.ts", typescript({
declaration: false,
noImplicitAny: true,
noEmitOnError: true,
sortOutput: true,
}).js))
.pipe(gulpif(doConcat, concat(assetGroup.outputFileName)))
.pipe(typescript(typeScriptOptions))
.pipe(header(
"/*\n" +
"** NOTE: This file is generated by Gulp and should not be edited directly!\n" +
"** Any changes made directly to this file will be overwritten next time its asset group is processed by Gulp.\n" +
"*/\n\n"))
.pipe(gulpif(generateSourceMaps, sourcemaps.write()))
.pipe(eol())
.pipe(rename(function (path) {
if (assetGroup.flatten)
path.dirname = "";
}))
.pipe(gulp.dest(assetGroup.outputDir))
.pipe(uglify())
.pipe(rename({
suffix: ".min"
}))
.pipe(eol())
.pipe(rename(function (path) {
if (assetGroup.separateMinified)
path.dirname += "/min";
else
path.basename += ".min";
}))
.pipe(gulp.dest(assetGroup.outputDir));
}

View File

@ -0,0 +1,27 @@
using System;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Security;
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:2.0.50727.4016
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
[assembly: CLSCompliantAttribute(true)]
[assembly: AssemblyTitleAttribute("NHibernate.Linq")]
[assembly: AssemblyDescriptionAttribute("A linq provider for NHibernate")]
[assembly: AssemblyCompanyAttribute("NHForge.org")]
[assembly: AssemblyProductAttribute("NHibernate.Linq")]
[assembly: AssemblyCopyrightAttribute("Licensed under LGPL.")]
[assembly: AssemblyVersionAttribute("1.0.0.4000")]
[assembly: AssemblyInformationalVersionAttribute("1.0.0.4000")]
[assembly: AssemblyFileVersionAttribute("1.0.0.4000")]
[assembly: AssemblyDelaySignAttribute(false)]
[assembly: AllowPartiallyTrustedCallersAttribute()]

View File

@ -0,0 +1,35 @@
using System.Collections;
using System.Collections.Generic;
namespace NHibernate.Linq
{
/// <summary>
/// Wraps an ICriteria object providing results when necessary.
/// </summary>
/// <typeparam name="T"></typeparam>
public class CriteriaResultReader<T> : IEnumerable<T>, IEnumerable
{
private readonly ICriteria _criteria;
public CriteriaResultReader(ICriteria criteria)
{
_criteria = criteria;
}
private IList List()
{
return _criteria.List();
}
public IEnumerator<T> GetEnumerator()
{
foreach (var item in List())
yield return (T)item;
}
IEnumerator IEnumerable.GetEnumerator()
{
return List().GetEnumerator();
}
}
}

View File

@ -0,0 +1,21 @@
using NHibernate.Type;
namespace NHibernate.Linq.Expressions
{
public class CollectionAccessExpression : PropertyAccessExpression
{
private readonly EntityExpression _elementExpression;
public EntityExpression ElementExpression
{
get { return _elementExpression; }
}
public CollectionAccessExpression(string name, System.Type type, IType nhibernateType,
EntityExpression expression, EntityExpression elementExpression)
: base(name, type, nhibernateType, expression, NHibernateExpressionType.CollectionAccess)
{
_elementExpression = elementExpression;
}
}
}

View File

@ -0,0 +1,63 @@
using System.Linq.Expressions;
using NHibernate.Metadata;
namespace NHibernate.Linq.Expressions
{
public class EntityExpression : NHibernateExpression
{
private readonly string _alias;
private readonly string _associationPath;
private readonly IClassMetadata _metaData;
private readonly Expression _expression;
public string Alias
{
get { return _alias; }
}
public string AssociationPath
{
get { return _associationPath; }
}
public IClassMetadata MetaData
{
get { return _metaData; }
}
public Expression Expression
{
get { return _expression; }
}
public EntityExpression(string associationPath, string alias, System.Type type, IClassMetadata metaData, Expression expression)
: base(IsRoot(expression) ? NHibernateExpressionType.RootEntity : NHibernateExpressionType.Entity, type)
{
_associationPath = associationPath;
_alias = alias;
_metaData = metaData;
_expression = expression;
}
private static bool IsRoot(Expression expr)
{
if (expr == null) return true;
if (!(expr is EntityExpression)) return true;
return false;
}
public override string ToString()
{
return Alias;
}
public virtual string GetAliasedIdentifierPropertyName()
{
if ((NHibernateExpressionType)this.NodeType == NHibernateExpressionType.RootEntity)
{
return this.MetaData.IdentifierPropertyName;
}
return string.Format("{0}.{1}", this.Alias, this.MetaData.IdentifierPropertyName);
}
}
}

View File

@ -0,0 +1,21 @@
using System;
using System.Linq.Expressions;
namespace NHibernate.Linq.Expressions
{
public abstract class NHibernateExpression : Expression
{
public NHibernateExpression(NHibernateExpressionType nodeType, System.Type type)
: base() {
_nodeType = nodeType;
_type = type;
}
private readonly NHibernateExpressionType _nodeType;
public override ExpressionType NodeType { get { return (ExpressionType)_nodeType; } }
private readonly System.Type _type;
public override System.Type Type { get { return _type; } }
}
}

View File

@ -0,0 +1,15 @@
namespace NHibernate.Linq.Expressions
{
/// <summary>
/// Extended node types for custom expressions
/// </summary>
public enum NHibernateExpressionType
{
QuerySource = 1000, //make sure these don't overlap with ExpressionType
RootEntity,
Entity,
PropertyAccess,
CollectionAccess
}
}

View File

@ -0,0 +1,48 @@
using System;
using NHibernate.Type;
namespace NHibernate.Linq.Expressions
{
public class PropertyAccessExpression : NHibernateExpression
{
private readonly string _name;
private readonly EntityExpression _expression;
private readonly IType _nhibernateType;
public string Name
{
get { return _name; }
}
public EntityExpression Expression
{
get { return _expression; }
}
public IType NHibernateType
{
get { return _nhibernateType; }
}
public PropertyAccessExpression(string name, System.Type type, IType nhibernateType, EntityExpression expression)
: this(name, type, nhibernateType, expression, NHibernateExpressionType.PropertyAccess) { }
protected PropertyAccessExpression(string name, System.Type type, IType nhibernateType, EntityExpression expression, NHibernateExpressionType nodeType)
: base(nodeType, type)
{
if (String.IsNullOrEmpty(name)) throw new ArgumentNullException("name");
if (type == null) throw new ArgumentNullException("type");
if (nhibernateType == null) throw new ArgumentNullException("nhibernateType");
if (expression == null) throw new ArgumentNullException("expression");
_name = name;
_expression = expression;
_nhibernateType = nhibernateType;
}
public override string ToString()
{
return this.Expression.ToString() + "." + this.Name;
}
}
}

View File

@ -0,0 +1,46 @@
using System;
using System.Linq;
namespace NHibernate.Linq.Expressions
{
public class QuerySourceExpression : NHibernateExpression
{
private readonly string _alias;
private readonly IQueryable _query;
private readonly System.Type _elementType;
public string Alias
{
get { return _alias; }
}
public IQueryable Query
{
get { return _query; }
}
public System.Type ElementType
{
get { return _elementType ?? Query.ElementType; }
}
public QuerySourceExpression(string alias, IQueryable query)
: this(alias, query, null) { }
public QuerySourceExpression(string alias, IQueryable query, System.Type elementType)
: base(NHibernateExpressionType.QuerySource, query.GetType())
{
_alias = alias;
_query = query;
_elementType = elementType;
}
public override string ToString()
{
if (!String.IsNullOrEmpty(Alias))
return Alias;
return base.ToString();
}
}
}

View File

@ -0,0 +1,88 @@
using System;
using System.Collections.Generic;
using NHibernate.Criterion;
using NHibernate.Linq.Util;
using NHibernate.SqlCommand;
using NHibernate.Type;
namespace NHibernate.Linq.Expressions
{
public class SqlAggregateFunctionProjection : AggregateProjection
{
public SqlAggregateFunctionProjection(string functionName, string propertyName)
: this(functionName, propertyName, null)
{
}
public SqlAggregateFunctionProjection(string functionName, string propertyName, System.Type returnType)
: this(functionName, propertyName, returnType, null)
{
}
public SqlAggregateFunctionProjection(string functionName, string propertyName, System.Type returnType, object[] paramValues)
: this(functionName, propertyName, 0, returnType, paramValues)
{
}
public SqlAggregateFunctionProjection(string functionName, string propertyName, int propertyPosition, System.Type returnType,
object[] paramValues)
: base(functionName, propertyName)
{
ReturnType = returnType;
ParameterValues = paramValues;
PropertyPosition = propertyPosition;
}
public System.Type ReturnType { get; private set; }
public Object[] ParameterValues { get; private set; }
public int PropertyPosition { get; private set; }
public override IType[] GetTypes(ICriteria criteria, ICriteriaQuery criteriaQuery)
{
if (ReturnType != null)
{
return new[] { TypeFactory.HeuristicType(ReturnType.Name) };
}
return base.GetTypes(criteria, criteriaQuery);
}
public override SqlString ToSqlString(ICriteria criteria, int loc, ICriteriaQuery criteriaQuery)
{
if (ParameterValues != null && ParameterValues.Length > 0)
{
var sql = new SqlStringBuilder();
sql.Add(aggregate).Add("(");
bool hasProperty = false;
bool hasParameter = false;
for (int i = 0; i < ParameterValues.Length; i++)
{
if (PropertyPosition == i)
{
if (i > 0) sql.Add(", ");
sql.Add(criteriaQuery.GetColumn(criteria, propertyName)).Add(", ");
hasProperty = true;
}
else if (i > 0)
{
sql.Add(", ");
}
sql.Add(LinqUtil.SqlEncode(ParameterValues[i]));
hasParameter = true;
}
if (!hasProperty)
{
if (hasParameter) sql.Add(", ");
sql.Add(criteriaQuery.GetColumn(criteria, propertyName));
}
return sql.Add(") as y").Add(loc.ToString()).Add("_").ToSqlString();
}
// if ParameterValues were not specified, we defer to the base functionality
return base.ToSqlString(criteria, loc, criteriaQuery);
}
}
}

View File

@ -0,0 +1,37 @@
using System;
namespace NHibernate.Linq.Expressions
{
/// <summary>
/// Associates a method with a corresponding SQL function.
/// </summary>
[AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = false)]
public class SqlFunctionAttribute : Attribute
{
/// <summary>
/// Initializes a new instance of the <see cref="T:NHibernate.Linq.Expressions.SqlFunctionAttribute"/> class.
/// </summary>
public SqlFunctionAttribute()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="T:NHibernate.Linq.Expressions.SqlFunctionAttribute"/> class.
/// </summary>
/// <param name="owner">The name of the schema that owns the SQL function.</param>
public SqlFunctionAttribute(string owner)
{
Owner = owner;
}
/// <summary>
/// Gets or sets the name of the schema that owns the SQL function.
/// </summary>
public string Owner { get; set; }
/// <summary>
/// Gets or sets the position of the function parameter that accepts the property name.
/// </summary>
public int PropertyPosition { get; set; }
}
}

View File

@ -0,0 +1,170 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using NHibernate.Criterion;
using NHibernate.Engine;
using NHibernate.SqlCommand;
using NHibernate.Type;
namespace NHibernate.Linq.Expressions
{
public class SqlFunctionExpression : ICriterion
{
private String op;
public SqlFunctionExpression(String functionName, System.Type returnType, ICriterion innerCriterion)
: this(functionName, returnType, null, null, innerCriterion, 0, null)
{
}
public SqlFunctionExpression(String functionName, System.Type returnType, Object[] paramValues,
System.Type[] paramTypes, ICriterion innerCriterion)
: this(functionName, returnType, paramValues, paramTypes, innerCriterion, 0, null)
{
}
public SqlFunctionExpression(String functionName, System.Type returnType, Object[] paramValues,
System.Type[] paramTypes, ICriterion innerCriterion, int propertyPosition)
: this(functionName, returnType, paramValues, paramTypes, innerCriterion, propertyPosition, null)
{
}
public SqlFunctionExpression(String functionName, System.Type returnType, Object[] paramValues,
System.Type[] paramTypes, ICriterion innerCriterion, int propertyPosition,
SqlFunctionExpression rightFunction)
{
FunctionName = functionName;
ReturnType = returnType;
ParameterValues = paramValues;
ParameterTypes = paramTypes;
InnerCriterion = innerCriterion;
PropertyPosition = propertyPosition;
RightFunction = rightFunction;
}
private SqlFunctionExpression RightFunction { get; set; }
public ICriterion InnerCriterion { get; set; }
public String FunctionName { get; private set; }
public System.Type ReturnType { get; private set; }
public Object[] ParameterValues { get; private set; }
public System.Type[] ParameterTypes { get; private set; }
public int PropertyPosition { get; private set; }
protected virtual string Op
{
get
{
if (String.IsNullOrEmpty(op))
{
op = InnerCriterion.GetType().GetProperty("Op", BindingFlags.NonPublic | BindingFlags.Instance)
.GetValue(InnerCriterion, null) as String;
}
return op;
}
}
#region ICriterion Members
public virtual TypedValue[] GetTypedValues(ICriteria criteria, ICriteriaQuery criteriaQuery)
{
var values = new List<TypedValue>();
if (ParameterValues != null)
{
for (int i = 0; i < ParameterValues.Length; i++)
{
values.Add(new TypedValue(TypeFactory.HeuristicType(ParameterTypes[i].Name), ParameterValues[i]));
}
}
if (ReturnType != null && InnerCriterion is SimpleExpression)
{
var simple = InnerCriterion as SimpleExpression;
values.Add(new TypedValue(TypeFactory.HeuristicType(ReturnType.Name), simple.Value));
}
if (RightFunction != null)
{
values.AddRange(RightFunction.GetTypedValues(criteria, criteriaQuery));
}
return values.ToArray();
}
public virtual SqlString ToSqlString(ICriteria criteria, ICriteriaQuery criteriaQuery)
{
var sql = new SqlStringBuilder();
string leftPropertyName = null;
string rightPropertyName = null;
if (InnerCriterion is SimpleExpression)
{
leftPropertyName = ((SimpleExpression)InnerCriterion).PropertyName;
}
else if (InnerCriterion is PropertyExpression)
{
System.Type type = typeof(PropertyExpression);
leftPropertyName =
type.GetField("_lhsPropertyName", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(InnerCriterion) as
String;
rightPropertyName =
type.GetField("_rhsPropertyName", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(InnerCriterion) as
String;
}
AddParameters(leftPropertyName, sql, criteria, criteriaQuery);
sql.Add(" ").Add(Op).Add(" ");
if (RightFunction != null)
{
RightFunction.AddParameters(rightPropertyName, sql, criteria, criteriaQuery);
}
else
{
sql.AddParameter();
}
return sql.ToSqlString();
}
public IProjection[] GetProjections()
{
return null;
}
#endregion
private void AddParameters(String propertyName, SqlStringBuilder sql, ICriteria criteria, ICriteriaQuery criteriaQuery)
{
bool hasProperty = false;
bool hasParameter = false;
sql.Add(FunctionName).Add("(");
if (ParameterValues != null && ParameterValues.Length > 0)
{
for (int i = 0; i < ParameterValues.Length; i++)
{
if (PropertyPosition == i)
{
if (i > 0) sql.Add(", ");
sql.Add(criteriaQuery.GetColumn(criteria, propertyName)).Add(", ");
hasProperty = true;
}
else if (i > 0)
{
sql.Add(", ");
}
sql.AddParameter();
hasParameter = true;
}
}
if (!hasProperty)
{
if (hasParameter) sql.Add(", ");
sql.Add(criteriaQuery.GetColumn(criteria, propertyName));
}
sql.Add(")");
}
}
}

View File

@ -0,0 +1,9 @@
namespace NHibernate.Linq
{
/// <summary>
/// Marker interface used to conditionally include database provider specific methods.
/// </summary>
public interface IDbMethods
{
}
}

View File

@ -0,0 +1,14 @@
using System.Linq;
namespace NHibernate.Linq
{
public interface INHibernateQueryable
{
QueryOptions QueryOptions { get; }
}
public interface INHibernateQueryable<T> : INHibernateQueryable, IOrderedQueryable<T>
{
IQueryable<T> Expand(string path);
}
}

View File

@ -0,0 +1,46 @@
<?xml version="1.0" ?>
<project
name="NHibernate.Linq"
default="build"
xmlns="http://nant.sf.net/release/0.85-rc3/nant.xsd"
>
<property name="root.dir" value="../.." />
<include buildfile="${root.dir}/common-project.xml" />
<target name="init" depends="common.init">
<property name="assembly.description" value="A linq provider for NHibernate" />
<property name="assembly.allow-partially-trusted-callers" value="true" />
<assemblyfileset id="project.references" basedir="${bin.dir}" defaultexcludes="false">
<include name="System.dll" />
<include name="System.Core.dll" />
<include name="System.Linq.Dynamic.dll"/>
<include name="System.Xml.dll" />
<include name="Iesi.Collections.dll" />
<include name="NHibernate.dll" />
<include name="log4net.dll" />
<include name="System.Data.Services.dll" />
<include name="Antlr3.Runtime.dll" />
</assemblyfileset>
<resourcefileset id="project.resources" prefix="NHibernate.Linq" dynamicprefix="true">
<include name="*.xsd" />
<include name="**/*.xml" />
<exclude name="bin/**/*.xml" />
</resourcefileset>
<fileset id="project.sources">
<include name="**/*.cs" />
</fileset>
</target>
<target name="generate-assemblyinfo" depends="init common.generate-assemblyinfo" />
<target name="build" description="Build NHibernate.Linq"
depends="generate-assemblyinfo common.compile-dll">
</target>
</project>

View File

@ -0,0 +1,139 @@
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>9.0.30729</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{2CF9A83A-BC40-4485-A763-BFF59B2EBDAD}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>NHibernate.Linq</RootNamespace>
<AssemblyName>NHibernate.Linq</AssemblyName>
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<StartupObject>
</StartupObject>
<SignAssembly>false</SignAssembly>
<AssemblyOriginatorKeyFile>..\NH.Linq.snk</AssemblyOriginatorKeyFile>
<FileUpgradeFlags>
</FileUpgradeFlags>
<UpgradeBackupLocation>
</UpgradeBackupLocation>
<OldToolsVersion>3.5</OldToolsVersion>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>none</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE;USING_NET_35_SP1</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<Reference Include="Antlr3.Runtime, Version=3.5.0.2, Culture=neutral, PublicKeyToken=eb42632606e9261f, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\Antlr3.Runtime.3.5.1\lib\net40-client\Antlr3.Runtime.dll</HintPath>
</Reference>
<Reference Include="Iesi.Collections, Version=4.0.0.4000, Culture=neutral, PublicKeyToken=aa95f207798dfdb4, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\Iesi.Collections.4.0.4\lib\net461\Iesi.Collections.dll</HintPath>
</Reference>
<Reference Include="Microsoft.CSharp" />
<Reference Include="NHibernate, Version=5.3.0.0, Culture=neutral, PublicKeyToken=aa95f207798dfdb4, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\NHibernate.5.3.10\lib\net461\NHibernate.dll</HintPath>
</Reference>
<Reference Include="Remotion.Linq, Version=2.2.0.0, Culture=neutral, PublicKeyToken=fee00910d6e5f53b, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\Remotion.Linq.2.2.0\lib\net45\Remotion.Linq.dll</HintPath>
</Reference>
<Reference Include="Remotion.Linq.EagerFetching, Version=2.2.0.0, Culture=neutral, PublicKeyToken=fee00910d6e5f53b, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\Remotion.Linq.EagerFetching.2.2.0\lib\net45\Remotion.Linq.EagerFetching.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Configuration" />
<Reference Include="System.Core">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
<Reference Include="System.Data" />
<Reference Include="System.Data.Services">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
<Reference Include="System.ServiceModel" />
<Reference Include="System.Transactions" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Expressions\CollectionAccessExpression.cs" />
<Compile Include="Expressions\NHibernateExpression.cs" />
<Compile Include="Expressions\QuerySourceExpression.cs" />
<Compile Include="INHibernateQueryable.cs" />
<Compile Include="QueryOptions.cs" />
<Compile Include="Transform\TypeSafeConstructorMemberInitResultTransformer.cs" />
<Compile Include="Util\SessionFactoryUtil.cs" />
<Compile Include="Visitors\BinaryBooleanReducer.cs" />
<Compile Include="Visitors\BinaryCriterionType.cs" />
<Compile Include="Visitors\BinaryCriterionVisitor.cs" />
<Compile Include="Visitors\BinaryCriterionDelegates.cs" />
<Compile Include="Visitors\AssociationVisitor.cs" />
<Compile Include="CriteriaResultReader.cs" />
<Compile Include="Visitors\BinaryExpressionOrderer.cs" />
<Compile Include="Visitors\CollectionAliasVisitor.cs" />
<Compile Include="Visitors\RootVisitor.cs" />
<Compile Include="Visitors\EntityExpressionVisitor.cs" />
<Compile Include="Visitors\Evaluator.cs" />
<Compile Include="Expressions\EntityExpression.cs" />
<Compile Include="Expressions\NHibernateExpressionType.cs" />
<Compile Include="Expressions\PropertyAccessExpression.cs" />
<Compile Include="Visitors\InheritanceVisitor.cs" />
<Compile Include="Visitors\MemberNameVisitor.cs" />
<Compile Include="Visitors\ImmediateResultsVisitor.cs" />
<Compile Include="Visitors\NHibernateExpressionVisitor.cs" />
<Compile Include="NHibernateQueryProvider.cs" />
<Compile Include="Query.cs" />
<Compile Include="QueryProvider.cs" />
<Compile Include="Visitors\PropertyToMethodVisitor.cs" />
<Compile Include="Visitors\SelectManyVisitor.cs" />
<Compile Include="Transform\LinqJoinResultsTransformer.cs" />
<Compile Include="Util\DetachedCriteriaAdapter.cs" />
<Compile Include="Util\CriteriaUtil.cs" />
<Compile Include="Expressions\SqlFunctionAttribute.cs" />
<Compile Include="Expressions\SqlFunctionExpression.cs" />
<Compile Include="Expressions\SqlAggregateFunctionProjection.cs" />
<Compile Include="Visitors\GroupingArgumentsVisitor.cs" />
<Compile Include="Transform\LinqGroupingResultTransformer.cs" />
<Compile Include="NHibernateExtensions.cs" />
<Compile Include="IDbMethods.cs" />
<Compile Include="Visitors\ExpressionVisitor.cs" />
<Compile Include="Util\LinqUtil.cs" />
<Compile Include="NHibernateContext.cs" />
<Compile Include="Visitors\NHibernateQueryTranslator.cs" />
<Compile Include="Util\QueryUtil.cs" />
<Compile Include="Visitors\SelectArgumentsVisitor.cs" />
<Compile Include="SqlClient\SqlClientExtensions.cs" />
<Compile Include="Util\TypeSystem.cs" />
<Compile Include="Visitors\WhereArgumentsVisitor.cs" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<Folder Include="Properties\" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View File

@ -0,0 +1,432 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data.Services;
using System.Linq;
using System.Reflection;
using NHibernate.Metadata;
namespace NHibernate.Linq
{
/// <summary>
/// Wraps an <see cref="T:NHibernate.ISession"/> object to provide base functionality
/// for custom, database-specific context classes.
/// </summary>
public abstract class NHibernateContext : IDisposable, ICloneable, IUpdatable, IExpandProvider
{
/// <summary>
/// Provides access to database provider specific methods.
/// </summary>
public readonly IDbMethods Methods;
private ISession session;
/// <summary>
/// Initializes a new instance of the <see cref="NHibernateContext"/> class.
/// </summary>
public NHibernateContext()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="T:NHibernate.Linq.NHibernateContext"/> class.
/// </summary>
/// <param name="session">An initialized <see cref="T:NHibernate.ISession"/> object.</param>
public NHibernateContext(ISession session)
{
this.session = session;
}
/// <summary>
/// Gets a reference to the <see cref="T:NHibernate.ISession"/> associated with this object.
/// </summary>
public virtual ISession Session
{
get
{
if (session == null)
{
// Attempt to get the Session
session = ProvideSession();
}
return session;
}
}
/// <summary>
/// Allows for empty construction but provides an interface for an interface to have the derived
/// classes provide a session object late in the cycle.
/// </summary>
/// <returns>The Required <see cref="T:NHibernate.ISession"/> object.</returns>
protected virtual ISession ProvideSession()
{
// Should not be called as supplying the session in the constructor
throw new NotImplementedException("If NHibernateContext is constructed with the empty constructor, inheritor is required to override ProvideSession to supply Session.");
}
#region ICloneable Members
/// <summary>
/// Creates a new object that is a copy of the current instance.
/// </summary>
/// <returns></returns>
public virtual object Clone()
{
if (session == null)
{
throw new ArgumentNullException("session");
}
return Activator.CreateInstance(GetType(), session);
}
#endregion
#region IDisposable Members
/// <summary>
/// Disposes the wrapped <see cref="T:NHibernate.ISession"/> object.
/// </summary>
public virtual void Dispose()
{
if (session != null)
{
session.Dispose();
session = null;
}
}
#endregion
#region IUpdatable Members
List<object> _updateCache = null;
/// <summary>
/// Gets the update cache.
/// </summary>
/// <value>The update cache.</value>
List<object> UpdateCache
{
get
{
if (_updateCache == null)
{
_updateCache = new List<object>();
}
return _updateCache;
}
}
/// <summary>
/// Adds the reference to collection.
/// </summary>
/// <param name="targetResource">The target resource.</param>
/// <param name="propertyName">Name of the property.</param>
/// <param name="resourceToBeAdded">The resource to be added.</param>
void IUpdatable.AddReferenceToCollection(object targetResource, string propertyName, object resourceToBeAdded)
{
IClassMetadata metadata = session.SessionFactory.GetClassMetadata(targetResource.GetType().FullName);
if (metadata == null)
{
throw new DataServiceException("Type not recognized as a valid type for this Context");
}
// Get the property to use to add the resource to
object collection = metadata.GetPropertyValue(targetResource, propertyName);
// Try with IList implementation first (its faster)
if (collection is IList)
{
((IList)collection).Add(resourceToBeAdded);
}
else // Try with Reflection's Add()
{
MethodInfo addMethod = collection.GetType().GetMethod("Add", BindingFlags.Public | BindingFlags.Instance);
if (addMethod == null)
{
throw new DataServiceException(string.Concat("Could not determine the collection type of the ", propertyName, " property."));
}
addMethod.Invoke(collection, new object[] { resourceToBeAdded });
}
}
/// <summary>
/// Clears the changes.
/// </summary>
void IUpdatable.ClearChanges()
{
UpdateCache.Clear();
session.Clear();
}
/// <summary>
/// Creates the resource.
/// </summary>
/// <param name="containerName">Name of the container.</param>
/// <param name="fullTypeName">Full name of the type.</param>
/// <returns>Newly created Resource</returns>
object IUpdatable.CreateResource(string containerName, string fullTypeName)
{
// Get the metadata
IClassMetadata metadata = session.SessionFactory.GetClassMetadata(fullTypeName);
object newResource = metadata.Instantiate(null);
// We can't save it to the session as it may not be valid yet
// This happens if the key is a non-initancable key (e.g. Northwind.Customers)
// So we save them to a local cache. Only when SaveAll happens will be push them to the Session
UpdateCache.Add(newResource);
// Returns the new resource
return newResource;
}
/// <summary>
/// Deletes the resource.
/// </summary>
/// <param name="targetResource">The target resource.</param>
void IUpdatable.DeleteResource(object targetResource)
{
// Push it to the Session to support deletion
if (UpdateCache.Contains(targetResource))
{
UpdateCache.Remove(targetResource);
session.Save(targetResource);
}
// Mark it as deleted
if (session.Contains(targetResource)) session.Delete(targetResource);
}
/// <summary>
/// Gets the resource.
/// </summary>
/// <param name="query">The query.</param>
/// <param name="fullTypeName">Full name of the type.</param>
/// <returns></returns>
object IUpdatable.GetResource(System.Linq.IQueryable query, string fullTypeName)
{
// Get the first result
IEnumerable results = (IEnumerable)query;
object returnValue = null;
foreach (object result in results)
{
if (returnValue != null) break;
returnValue = result;
}
// Check the Typename if needed
if (fullTypeName != null)
{
if (fullTypeName != returnValue.GetType().FullName)
{
throw new DataServiceException("Incorrect Type Returned");
}
}
// Return the resource
return returnValue;
}
/// <summary>
/// Gets the value.
/// </summary>
/// <param name="targetResource">The target resource.</param>
/// <param name="propertyName">Name of the property.</param>
/// <returns></returns>
object IUpdatable.GetValue(object targetResource, string propertyName)
{
IClassMetadata metadata = session.SessionFactory.GetClassMetadata(targetResource.GetType().FullName);
if (metadata == null)
{
throw new DataServiceException("Type not recognized as a valid type for this Context");
}
// If
if (metadata.IdentifierPropertyName == propertyName)
{
return metadata.GetIdentifier(targetResource);
}
else
{
return metadata.GetPropertyValue(targetResource, propertyName);
}
}
/// <summary>
/// Removes the reference from collection.
/// </summary>
/// <param name="targetResource">The target resource.</param>
/// <param name="propertyName">Name of the property.</param>
/// <param name="resourceToBeRemoved">The resource to be removed.</param>
void IUpdatable.RemoveReferenceFromCollection(object targetResource, string propertyName, object resourceToBeRemoved)
{
IClassMetadata metadata = session.SessionFactory.GetClassMetadata(targetResource.GetType().FullName);
if (metadata == null)
{
throw new DataServiceException("Type not recognized as a valid type for this Context");
}
// Get the property to use to remove the resource to
object collection = metadata.GetPropertyValue(targetResource, propertyName);
// Try with IList implementation first (its faster)
if (collection is IList)
{
((IList)collection).Remove(resourceToBeRemoved);
}
else // Try with Reflection's Add()
{
MethodInfo removeMethod = collection.GetType().GetMethod("Remove", BindingFlags.Public | BindingFlags.Instance);
if (removeMethod == null)
{
throw new DataServiceException(string.Concat("Could not determine the collection type of the ", propertyName, " property."));
}
removeMethod.Invoke(collection, new object[] { resourceToBeRemoved });
}
}
/// <summary>
/// Replaces the resource.
/// </summary>
/// <param name="resource">The resource to reset.</param>
/// <returns></returns>
object IUpdatable.ResetResource(object resource)
{
IUpdatable update = this;
// Create a new resource of the same type
// but only make a local copy as we're only using it to set the default fields
// Get the metadata
IClassMetadata metadata = session.SessionFactory.GetClassMetadata(resource.GetType().ToString());
object tempCopy = metadata.Instantiate(null);
// Copy the default non-keys
foreach (string propName in metadata.PropertyNames)
{
object value = metadata.GetPropertyValue(tempCopy, propName);
update.SetValue(resource, propName, value);
}
//Return the new resource
return resource;
}
/// <summary>
/// Resolves the resource.
/// </summary>
/// <param name="resource">The resource.</param>
/// <returns></returns>
object IUpdatable.ResolveResource(object resource)
{
// Resolve Resource always just returns the resource
// since we not using tokens or cookies to the actual objects
// the resources are always the actual CLR objects
return resource;
}
/// <summary>
/// Saves the changes.
/// </summary>
void IUpdatable.SaveChanges()
{
// All saves must be all or nothing.
using (ITransaction tx = Session.BeginTransaction())
{
try
{
// If we have anything in the object cache,
// add it to session.
if (_updateCache != null)
{
_updateCache.ForEach(o => session.SaveOrUpdate(o));
_updateCache.Clear();
}
// Push the changes to the database
session.Flush();
// Commit the Transaction
tx.Commit();
}
catch (Exception ex)
{
// If anythign goes wrong, it all gets rolled back
tx.Rollback();
// Send the error back to the user
throw new DataServiceException("Failed to save changes. See inner exception for details", ex);
}
}
}
/// <summary>
/// Sets the reference.
/// </summary>
/// <param name="targetResource">The target resource.</param>
/// <param name="propertyName">Name of the property.</param>
/// <param name="propertyValue">The property value.</param>
void IUpdatable.SetReference(object targetResource, string propertyName, object propertyValue)
{
((IUpdatable)this).SetValue(targetResource, propertyName, propertyValue);
}
/// <summary>
/// Sets the value.
/// </summary>
/// <param name="targetResource">The target resource.</param>
/// <param name="propertyName">Name of the property.</param>
/// <param name="propertyValue">The property value.</param>
void IUpdatable.SetValue(object targetResource, string propertyName, object propertyValue)
{
IClassMetadata metadata = session.SessionFactory.GetClassMetadata(targetResource.GetType().FullName);
if (metadata == null)
{
throw new DataServiceException("Type not recognized as a valid type for this Context");
}
// See if its the Key property first
if (metadata.IdentifierPropertyName == propertyName)
{
metadata.SetIdentifier(targetResource, propertyValue);
}
else // Else set the property
{
metadata.SetPropertyValue(targetResource, propertyName, propertyValue);
}
}
#endregion
#region IExpandProvider Members
IEnumerable IExpandProvider.ApplyExpansions(IQueryable queryable, ICollection<ExpandSegmentCollection> expandPaths)
{
if (queryable == null) throw new DataServiceException("Query cannot be null");
INHibernateQueryable nHibQuery = queryable as INHibernateQueryable;
if (nHibQuery == null) throw new DataServiceException("Expansion only supported on INHibernateQueryable queries");
if (expandPaths.Count == 0) throw new DataServiceException("Expansion Paths cannot be null");
foreach (ExpandSegmentCollection coll in expandPaths)
{
foreach (ExpandSegment seg in coll)
{
if (seg.HasFilter)
{
throw new DataServiceException("NHibernate does not support Expansions with Filters");
}
else
{
nHibQuery.QueryOptions.AddExpansion(seg.Name);
}
}
}
return nHibQuery as IEnumerable;
}
#endregion
}
}

View File

@ -0,0 +1,57 @@
using System;
using System.Collections;
using System.Linq;
using System.Linq.Expressions;
namespace NHibernate.Linq
{
/// <summary>
/// Provides a static method that enables LINQ syntax for NHibernate Criteria Queries.
/// </summary>
public static class NHibernateExtensions
{
/// <summary>
/// Creates a new <see cref="T:NHibernate.Linq.NHibernateQueryProvider"/> object used to evaluate an expression tree.
/// </summary>
/// <typeparam name="T">An NHibernate entity type.</typeparam>
/// <param name="session">An initialized <see cref="T:NHibernate.ISession"/> object.</param>
/// <returns>An <see cref="T:NHibernate.Linq.NHibernateQueryProvider"/> used to evaluate an expression tree.</returns>
public static INHibernateQueryable<T> Linq<T>(this ISession session)
{
QueryOptions options = new QueryOptions();
return new Query<T>(new NHibernateQueryProvider(session, options), options);
}
public static INHibernateQueryable<T> Linq<T>(this ISession session,string entityName)
{
QueryOptions options = new QueryOptions();
return new Query<T>(new NHibernateQueryProvider(session, options,entityName), options);
}
public static void List<T>(this ISession session, Expression expr, IList list)
{
var options = new QueryOptions();
var queryProvider = new NHibernateQueryProvider(session, options);
IQueryable<T> queryable = new Query<T>(queryProvider, options);
queryable = queryable.Where((Expression<Func<T, bool>>)expr);
var result = queryProvider.TranslateExpression(queryable.Expression);
var criteria = result as ICriteria;
if (criteria != null)
{
criteria.List(list);
}
else
{
var items = result as IEnumerable;
if (items != null)
{
foreach (var item in items)
{
list.Add(item);
}
}
}
}
}
}

View File

@ -0,0 +1,62 @@
using System;
using System.Linq.Expressions;
using NHibernate.Engine;
using NHibernate.Linq.Util;
using NHibernate.Linq.Visitors;
namespace NHibernate.Linq
{
public class NHibernateQueryProvider : QueryProvider
{
private readonly ISession _session;
private readonly string entityName;
public NHibernateQueryProvider(ISession session, QueryOptions queryOptions)
{
if (session == null) throw new ArgumentNullException("session");
_session = session;
this.queryOptions = queryOptions;
}
public NHibernateQueryProvider(ISession session, QueryOptions queryOptions,string entityName)
{
if (session == null) throw new ArgumentNullException("session");
_session = session;
this.entityName = entityName;
this.queryOptions = queryOptions;
}
private static object ResultsFromCriteria(ICriteria criteria, Expression expression)
{
System.Type elementType = TypeSystem.GetElementType(expression.Type);
return Activator.CreateInstance(typeof(CriteriaResultReader<>)
.MakeGenericType(elementType), criteria);
}
public object TranslateExpression(Expression expression)
{
expression = Evaluator.PartialEval(expression);
expression = new BinaryBooleanReducer().Visit(expression);
expression = new AssociationVisitor((ISessionFactoryImplementor)_session.SessionFactory).Visit(expression);
expression = new InheritanceVisitor().Visit(expression);
expression = CollectionAliasVisitor.AssignCollectionAccessAliases(expression);
expression = new PropertyToMethodVisitor().Visit(expression);
expression = new BinaryExpressionOrderer().Visit(expression);
NHibernateQueryTranslator translator = new NHibernateQueryTranslator(_session,entityName);
return translator.Translate(expression, this.queryOptions);
}
public override object Execute(Expression expression)
{
var results = TranslateExpression(expression);
var criteria = results as ICriteria;
if (criteria != null)
return ResultsFromCriteria(criteria, expression);
return results;
}
}
}

View File

@ -0,0 +1,77 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
namespace NHibernate.Linq
{
///<summary>
/// Generic IQueryable base class. See http://blogs.msdn.com/mattwar/archive/2007/07/30/linq-building-an-iqueryable-provider-part-i.aspx
/// </summary>
public class Query<T> : INHibernateQueryable<T>
{
private readonly QueryProvider provider;
private readonly Expression expression;
private readonly QueryOptions queryOptions;
public Query(QueryProvider provider, QueryOptions queryOptions)
{
if (provider == null) throw new ArgumentNullException("provider");
this.provider = provider;
this.queryOptions = queryOptions;
this.expression = Expression.Constant(this);
}
public Query(QueryProvider provider, Expression expression, QueryOptions queryOptions)
{
if (provider == null) throw new ArgumentNullException("provider");
if (expression == null) throw new ArgumentNullException("expression");
if (!typeof(IQueryable<T>).IsAssignableFrom(expression.Type))
throw new ArgumentOutOfRangeException("expression");
this.provider = provider;
this.queryOptions = queryOptions;
this.expression = expression;
}
Expression IQueryable.Expression
{
get { return this.expression; }
}
System.Type IQueryable.ElementType
{
get { return typeof(T); }
}
IQueryProvider IQueryable.Provider
{
get { return this.provider; }
}
public QueryOptions QueryOptions
{
get { return queryOptions; }
}
public IQueryable<T> Expand(string path)
{
queryOptions.AddExpansion(path);
return this;
}
public IEnumerator<T> GetEnumerator()
{
return ((IEnumerable<T>)this.provider.Execute(this.expression)).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable)this.provider.Execute(this.expression)).GetEnumerator();
}
}
}

View File

@ -0,0 +1,56 @@
using System;
namespace NHibernate.Linq
{
/// <summary>
/// It provides methods for caching the results, and some extension methods for them.
/// </summary>
public class QueryOptions
{
private Action<ICriteria> action;
public QueryOptions()
{
this.action = delegate { };
}
public QueryOptions SetCachable(bool cachable)
{
action += criteria => criteria.SetCacheable(cachable);
return this;
}
public QueryOptions SetCacheMode(CacheMode mode)
{
action += criteria => criteria.SetCacheMode(mode);
return this;
}
public QueryOptions SetCacheRegion(string cacheRegion)
{
action += criteria => criteria.SetCacheRegion(cacheRegion);
return this;
}
public QueryOptions SetComment(string comment)
{
action += criteria => criteria.SetComment(comment);
return this;
}
public QueryOptions RegisterCustomAction(Action<ICriteria> customAction)
{
action += customAction;
return this;
}
internal void Execute(ICriteria criteria)
{
action(criteria);
}
public void AddExpansion(string path)
{
action += criteria =>
{
criteria.Fetch(SelectMode.Fetch, path);
};
}
}
}

View File

@ -0,0 +1,39 @@
using System;
using System.Linq;
using System.Linq.Expressions;
using NHibernate.Linq.Util;
namespace NHibernate.Linq
{
/// <summary>
/// Generic IQueryProvider base class. See http://blogs.msdn.com/mattwar/archive/2007/07/30/linq-building-an-iqueryable-provider-part-i.aspx
/// </summary>
public abstract class QueryProvider : IQueryProvider
{
protected QueryOptions queryOptions;
IQueryable<T> IQueryProvider.CreateQuery<T>(Expression expression)
{
return new Query<T>(this, expression, queryOptions);
}
IQueryable IQueryProvider.CreateQuery(Expression expression)
{
QueryOptions options = new QueryOptions();
System.Type elementType = TypeSystem.GetElementType(expression.Type);
return (IQueryable)Activator.CreateInstance(typeof(Query<>).MakeGenericType(elementType), new object[] { this, expression, options });
}
T IQueryProvider.Execute<T>(Expression expression)
{
return (T)this.Execute(expression);
}
object IQueryProvider.Execute(Expression expression)
{
return this.Execute(expression);
}
public abstract object Execute(Expression expression);
}
}

View File

@ -0,0 +1,321 @@
using System;
namespace NHibernate.Linq.SqlClient
{
/// <summary>
/// Provides static methods that represent functionality provided by MS SQL Server.
/// </summary>
public static class SqlClientExtensions
{
#region DateTime Functions
/// <summary>
/// Returns an integer representing the day datepart of the specified date.
/// </summary>
/// <param name="methods"></param>
/// <param name="value"></param>
/// <returns></returns>
public static int Day(this IDbMethods methods, DateTime value)
{
return 0;
}
/// <summary>
/// Returns an integer representing the day datepart of the specified date.
/// </summary>
/// <param name="methods"></param>
/// <param name="value"></param>
/// <returns></returns>
public static int Day(this IDbMethods methods, DateTime? value)
{
return 0;
}
/// <summary>
/// Returns an integer that represents the month part of a specified date.
/// </summary>
/// <param name="methods"></param>
/// <param name="value"></param>
/// <returns></returns>
public static int Month(this IDbMethods methods, DateTime value)
{
return 0;
}
/// <summary>
/// Returns an integer that represents the month part of a specified date.
/// </summary>
/// <param name="methods"></param>
/// <param name="value"></param>
/// <returns></returns>
public static int Month(this IDbMethods methods, DateTime? value)
{
return 0;
}
/// <summary>
/// Returns an integer that represents the year part of a specified date.
/// </summary>
/// <param name="methods"></param>
/// <param name="value"></param>
/// <returns></returns>
public static int Year(this IDbMethods methods, DateTime value)
{
return 0;
}
/// <summary>
/// Returns an integer that represents the year part of a specified date.
/// </summary>
/// <param name="methods"></param>
/// <param name="value"></param>
/// <returns></returns>
public static int Year(this IDbMethods methods, DateTime? value)
{
return 0;
}
#endregion DateTime Functions
#region Math Functions
#endregion Math Functions
#region String Functions
/// <summary>
/// Returns the ASCII code value of the leftmost character of a character expression.
/// </summary>
/// <param name="methods"></param>
/// <param name="value"></param>
/// <returns></returns>
public static int Ascii(this IDbMethods methods, string value)
{
return 0;
}
/// <summary>
/// Returns the ASCII code value of the leftmost character of a character expression.
/// </summary>
/// <param name="methods"></param>
/// <param name="value"></param>
/// <returns></returns>
public static int Ascii(this IDbMethods methods, char value)
{
return 0;
}
/// <summary>
/// Returns the ASCII code value of the leftmost character of a character expression.
/// </summary>
/// <param name="methods"></param>
/// <param name="value"></param>
/// <returns></returns>
public static int Ascii(this IDbMethods methods, char? value)
{
return 0;
}
/// <summary>
/// Converts an int ASCII code to a character.
/// </summary>
/// <param name="methods"></param>
/// <param name="value"></param>
/// <returns></returns>
public static char Char(this IDbMethods methods, int value)
{
return char.MinValue;
}
/// <summary>
/// Converts an int ASCII code to a character.
/// </summary>
/// <param name="methods"></param>
/// <param name="value"></param>
/// <returns></returns>
public static char Char(this IDbMethods methods, int? value)
{
return char.MinValue;
}
/// <summary>
/// Returns the starting position of the specified expression in a character string.
/// </summary>
/// <param name="methods"></param>
/// <param name="value"></param>
/// <param name="search"></param>
/// <returns></returns>
public static int CharIndex(this IDbMethods methods, string value, char search)
{
return 0;
}
/// <summary>
/// Returns the starting position of the specified expression in a character string.
/// </summary>
/// <param name="methods"></param>
/// <param name="value"></param>
/// <param name="search"></param>
/// <param name="start"></param>
/// <returns></returns>
public static int CharIndex(this IDbMethods methods, string value, char search, int start)
{
return 0;
}
/// <summary>
/// Returns the starting position of the specified expression in a character string.
/// </summary>
/// <param name="methods"></param>
/// <param name="value"></param>
/// <param name="search"></param>
/// <returns></returns>
public static int CharIndex(this IDbMethods methods, string value, string search)
{
return 0;
}
/// <summary>
/// Returns the starting position of the specified expression in a character string.
/// </summary>
/// <param name="methods"></param>
/// <param name="value"></param>
/// <param name="search"></param>
/// <param name="start"></param>
/// <returns></returns>
public static int CharIndex(this IDbMethods methods, string value, string search, int start)
{
return 0;
}
/// <summary>
/// Returns the left part of a character string with the specified number of characters.
/// </summary>
/// <param name="methods"></param>
/// <param name="value"></param>
/// <param name="length"></param>
/// <returns></returns>
public static string Left(this IDbMethods methods, string value, int length)
{
return null;
}
/// <summary>
/// Returns the number of characters of the specified string expression, excluding trailing blanks.
/// </summary>
/// <param name="methods"></param>
/// <param name="value"></param>
/// <returns></returns>
public static int Len(this IDbMethods methods, string value)
{
return 0;
}
/// <summary>
/// Returns a character expression after converting uppercase character data to lowercase.
/// </summary>
/// <param name="methods"></param>
/// <param name="value"></param>
/// <returns></returns>
public static string Lower(this IDbMethods methods, string value)
{
return null;
}
/// <summary>
/// Returns a character expression after it removes leading blanks.
/// </summary>
/// <param name="methods"></param>
/// <param name="value"></param>
/// <returns></returns>
public static string LTrim(this IDbMethods methods, string value)
{
return null;
}
/// <summary>
/// Replaces all occurrences of a specified string value with another string value.
/// </summary>
/// <param name="methods"></param>
/// <param name="value"></param>
/// <param name="search"></param>
/// <param name="replace"></param>
/// <returns></returns>
public static string Replace(this IDbMethods methods, string value, string search, string replace)
{
return null;
}
/// <summary>
/// Repeats a string value a specified number of times.
/// </summary>
/// <param name="methods"></param>
/// <param name="value"></param>
/// <param name="count"></param>
/// <returns></returns>
public static string Replicate(this IDbMethods methods, string value, int count)
{
return null;
}
/// <summary>
/// Returns the reverse of a character expression.
/// </summary>
/// <param name="methods"></param>
/// <param name="value"></param>
/// <returns></returns>
public static string Reverse(this IDbMethods methods, string value)
{
return null;
}
/// <summary>
/// Returns the right part of a character string with the specified number of characters.
/// </summary>
/// <param name="methods"></param>
/// <param name="value"></param>
/// <param name="length"></param>
/// <returns></returns>
public static string Right(this IDbMethods methods, string value, int length)
{
return null;
}
/// <summary>
/// Returns a character string after truncating all trailing blanks.
/// </summary>
/// <param name="methods"></param>
/// <param name="value"></param>
/// <returns></returns>
public static string RTrim(this IDbMethods methods, string value)
{
return null;
}
/// <summary>
/// Returns part of a character, binary, text, or image expression.
/// </summary>
/// <param name="methods"></param>
/// <param name="value"></param>
/// <param name="start"></param>
/// <param name="length"></param>
/// <returns></returns>
public static string Substring(this IDbMethods methods, string value, int start, int length)
{
return null;
}
/// <summary>
/// Returns a character expression with lowercase character data converted to uppercase.
/// </summary>
/// <param name="methods"></param>
/// <param name="value"></param>
/// <returns></returns>
public static string Upper(this IDbMethods methods, string value)
{
return null;
}
#endregion String Functions
}
}

View File

@ -0,0 +1,165 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using NHibernate.Transform;
namespace NHibernate.Linq.Transform
{
/// <summary>
/// Transforms critieria query results into a collection of grouped objects.
/// </summary>
public class LinqGroupingResultTransformer : IResultTransformer
{
private readonly System.Type groupingType;
private readonly IDictionary<Object, IGrouping> groups;
private readonly String propertyName;
/// <summary>
/// Initializes a new instance of the <see cref="T:NHibernate.Linq.LinqGroupingResultTransformer"/> class.
/// </summary>
/// <param name="type">A <see cref="T:System.Type"/> representing the type of collection to transform.</param>
/// <param name="propertyName">The name of the property to be used as a key for the purpose of grouping.</param>
public LinqGroupingResultTransformer(System.Type type, String propertyName)
{
System.Type[] args = type.GetGenericArguments();
groupingType = typeof(Grouping<,>).MakeGenericType(args[0], args[1]);
groups = new Dictionary<Object, IGrouping>();
this.propertyName = propertyName;
int index = propertyName.IndexOf('.');
if (index > -1)
{
this.propertyName = propertyName.Substring(index + 1);
}
}
#region IResultTransformer Members
/// <summary>
/// Transforms the query result collection.
/// </summary>
/// <param name="collection">An <see cref="T:System.Collections.IList"/> of objects.</param>
/// <returns>A transformed <see cref="T:System.Collections.IList"/> object.</returns>
public IList TransformList(IList collection)
{
while (collection.Contains(null))
{
collection.Remove(null);
}
return collection;
}
/// <summary>
/// Transforms each query result.
/// </summary>
/// <param name="tuple">An <see cref="T:System.Object"/> array of query result values.</param>
/// <param name="aliases">A <see cref="T:System.String"/> array of column aliases.</param>
/// <returns>An <see cref="T:System.Object"/> initialized with the values from the specified tuple.</returns>
public object TransformTuple(object[] tuple, string[] aliases)
{
object value = tuple[0].GetType().GetProperty(propertyName).GetValue(tuple[0], null);
String key = String.Format("{0}", value);
if (groups.ContainsKey(key))
{
groups[key].Add(tuple[1]);
return null;
}
else
{
var group = (IGrouping)Activator.CreateInstance(groupingType, value);
group.Add(tuple[1]);
groups[key] = group;
return group;
}
}
#endregion
}
/// <summary>
/// Provides a method for adding individual objects to a collection of grouped objects.
/// </summary>
internal interface IGrouping
{
/// <summary>
/// Adds an object to the current group.
/// </summary>
/// <param name="item">The <see cref="T:System.Object"/> to add.</param>
void Add(object item);
}
/// <summary>
/// Represents a collection of objects that have a common key.
/// </summary>
/// <typeparam name="TKey"></typeparam>
/// <typeparam name="TElement"></typeparam>
internal class Grouping<TKey, TElement> : IGrouping<TKey, TElement>, IGrouping
{
private readonly TKey key;
private readonly IList<TElement> list = new List<TElement>();
/// <summary>
/// Initializes a new instance of the <see cref="T:NHibernate.Linq.Grouping"/> class.
/// </summary>
/// <param name="key"></param>
public Grouping(TKey key)
{
this.key = key;
}
#region IGrouping Members
/// <summary>
/// Adds an object to the current group.
/// </summary>
/// <param name="item">The <see cref="T:System.Object"/> to add.</param>
public void Add(object item)
{
list.Add((TElement)item);
}
#endregion
#region IGrouping<TKey,TElement> Members
/// <summary>
/// Gets the key of the <see cref="T:System.Linq.IGrouping`2"/>.
/// </summary>
public TKey Key
{
get { return key; }
}
/// <summary>
/// Returns an enumerator that iterates through the collection.
/// </summary>
/// <returns>An <see cref="T:System.Collections.Generic.IEnumerator`1"/> that can be used to iterate through the collection.</returns>
public IEnumerator<TElement> GetEnumerator()
{
return list.GetEnumerator();
}
/// <summary>
/// Returns an enumerator that iterates through the collection.
/// </summary>
/// <returns>An <see cref="T:System.Collections.IEnumerator"/> that can be used to iterate through the collection.</returns>
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#endregion
/// <summary>
/// Returns a <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
/// </summary>
/// <returns>A <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.</returns>
public override string ToString()
{
return String.Format("Key = {0}", Key);
}
}
}

View File

@ -0,0 +1,32 @@
using System.Collections;
namespace NHibernate.Linq.Transform
{
public class LinqJoinResultsTransformer : NHibernate.Transform.IResultTransformer
{
private readonly System.Type _entityType;
public LinqJoinResultsTransformer(System.Type entityType)
{
_entityType = entityType;
}
public IList TransformList(IList collection)
{
return collection;
}
public object TransformTuple(object[] tuple, string[] aliases)
{
foreach (object obj in tuple)
{
if (obj != null && obj.GetType() == _entityType)
{
return obj;
}
}
return null;
}
}
}

View File

@ -0,0 +1,150 @@
using System;
using System.Collections;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using NHibernate.Linq.Util;
namespace NHibernate.Transform
{
[Serializable]
public class TypeSafeConstructorMemberInitResultTransformer : IResultTransformer
{
private readonly Expression expression;
public TypeSafeConstructorMemberInitResultTransformer(NewExpression expression)
{
this.expression = expression;
}
public TypeSafeConstructorMemberInitResultTransformer(MemberInitExpression expression)
{
this.expression = expression;
}
public object TransformTuple(object[] tuple, string[] aliases)
{
try
{
int argumentCount;
switch (expression.NodeType)
{
case ExpressionType.New:
return InvokeConstructor((NewExpression)expression, tuple, out argumentCount);
case ExpressionType.MemberInit:
return InvokeMemberInitExpression((MemberInitExpression)expression, tuple, out argumentCount);
default:
throw new NotSupportedException();
}
}
catch (Exception e)
{
throw new QueryException(
"could not instantiate: " +
expression.Type.FullName,
e);
}
}
private object InvokeConstructor(NewExpression expression, object[] args, out int argumentCount)
{
object valueToSet;
int nestedArgumentCount;
argumentCount = 0;
ArrayList argList = new ArrayList();
int i = 0;
foreach (var arg in expression.Arguments)
{
switch (arg.NodeType)
{
case ExpressionType.New:
valueToSet = InvokeConstructor((NewExpression)arg, args.Skip(i).ToArray(), out nestedArgumentCount);
i += nestedArgumentCount;
break;
case ExpressionType.MemberInit:
valueToSet = InvokeMemberInitExpression((MemberInitExpression)arg,
args.Skip(i).ToArray(), out nestedArgumentCount);
i += nestedArgumentCount;
break;
default:
valueToSet = LinqUtil.ChangeType(args[i], arg.Type);
i++;
break;
}
argList.Add(valueToSet);
}
argumentCount = i;
return expression.Constructor.Invoke(argList.ToArray());
}
private object InvokeMemberInitExpression(MemberInitExpression expression, object[] args, out int argumentCount)
{
object valueToSet;
int nestedArgumentCount, constructorArgumentCount;
argumentCount = 0;
object instance = InvokeConstructor(expression.NewExpression, args, out constructorArgumentCount);
int i = constructorArgumentCount;
foreach (MemberAssignment binding in expression.Bindings)
{
switch (binding.Expression.NodeType)
{
case ExpressionType.New:
valueToSet = InvokeConstructor((NewExpression)binding.Expression,
args.Skip(i).ToArray(), out nestedArgumentCount);
i += nestedArgumentCount;
break;
case ExpressionType.MemberInit:
valueToSet = InvokeMemberInitExpression((MemberInitExpression)binding.Expression,
args.Skip(i).ToArray(), out nestedArgumentCount);
i += nestedArgumentCount;
break;
default:
valueToSet = args[i];
i++;
break;
}
SetValue(binding.Member, instance, valueToSet);
}
argumentCount = i;
return instance;
}
/// <summary>
/// Sets the value of the field or property represented by the specified
/// <see cref="T:System.Reflection.MemberInfo"/> for the supplied object instance.
/// </summary>
/// <param name="memberInfo">A <see cref="T:System.Reflection.MemberInfo"/> object.</param>
/// <param name="instance">An instance of an object.</param>
/// <param name="valueToSet">The value to set on the specified object.</param>
private void SetValue(MemberInfo memberInfo, object instance, object valueToSet)
{
var field = memberInfo as FieldInfo;
if (field != null)
{
field.SetValue(instance, LinqUtil.ChangeType(valueToSet, field.FieldType));
}
else
{
var prop = memberInfo as PropertyInfo;
prop.SetValue(instance, LinqUtil.ChangeType(valueToSet, prop.PropertyType), null);
}
}
public IList TransformList(IList collection)
{
return collection;
}
}
}

View File

@ -0,0 +1,122 @@
using System;
using System.Collections.Generic;
using NHibernate.Criterion;
using NHibernate.Engine;
using NHibernate.Impl;
using NHibernate.Loader.Criteria;
using NHibernate.Persister.Entity;
using NHibernate.Transform;
namespace NHibernate.Linq.Util
{
public static class CriteriaUtil
{
#region Extension Methods
public static ICriteriaQuery GenerateCriteriaQuery(this ICriteria criteria, ISessionFactory sessionFactory, string rootEntityName)
{
return new CriteriaQueryTranslator(
(ISessionFactoryImplementor)sessionFactory,
(CriteriaImpl)criteria,
rootEntityName, "this");
}
public static void SetProjectionIfNotNull(this ICriteria criteria, IProjection projection)
{
if (projection != null)
criteria.SetProjection(projection);
}
public static void SetResultTransformerIfNotNull(this ICriteria criteria, IResultTransformer transformer)
{
if (transformer != null)
criteria.SetResultTransformer(transformer);
else
criteria.SetResultTransformer(new RootEntityResultTransformer());
}
public static void Add(this ICriteria criteria, IEnumerable<ICriterion> criterion)
{
foreach (ICriterion c in criterion)
{
criteria.Add(c);
}
}
public static string GetEntityOrClassName(this ICriteria criteria)
{
if (criteria is CriteriaImpl)
{
return ((CriteriaImpl)criteria).EntityOrClassName;
}
if (criteria is DetachedCriteriaAdapter)
{
var adapter = (DetachedCriteriaAdapter)criteria;
return adapter.DetachedCriteria.EntityOrClassName;
}
throw new NotSupportedException("criteria must be of type CriteriaImpl or DetachedCriteriaAdapter.");
}
public static IProjection GetProjection(this ICriteria criteria)
{
var impl = criteria as CriteriaImpl;
if (impl != null)
{
return impl.Projection;
}
return null;
}
public static System.Type GetRootType(this ICriteria criteria)
{
if (criteria is DetachedCriteriaAdapter)
{
var adapter = (DetachedCriteriaAdapter)criteria;
return GetRootType(adapter.DetachedCriteria, adapter.Session);
}
return GetRootType(GetRootCriteria(criteria));
}
#endregion
public static ISessionImplementor GetSession(ICriteria criteria)
{
return GetRootCriteria(criteria).Session;
}
private static CriteriaImpl GetRootCriteria(ICriteria criteria)
{
var impl = criteria as CriteriaImpl;
if (impl != null)
return impl;
return GetRootCriteria(((CriteriaImpl.Subcriteria)criteria).Parent);
}
private static System.Type GetRootType(CriteriaImpl criteria)
{
if (criteria.Session == null)
throw new InvalidOperationException("Could not get root type on criteria that is not attached to a session");
ISessionFactoryImplementor factory = criteria.Session.Factory;
//TODO: need to cache the entityName meta data
var entityNames = factory.GetEntityNameMetaData();
if (!entityNames.ContainsKey(criteria.EntityOrClassName))
throw new InvalidOperationException("Could not find entity named: " + criteria.EntityOrClassName);
return entityNames[criteria.EntityOrClassName];
}
private static System.Type GetRootType(DetachedCriteria criteria, ISession session)
{
ISessionFactoryImplementor factory = (ISessionFactoryImplementor)session.SessionFactory;
IEntityPersister persister = factory.GetEntityPersister(criteria.EntityOrClassName);
if (persister == null)
throw new InvalidOperationException("Could not find entity named: " + criteria.EntityOrClassName);
return persister.MappedClass;
}
}
}

View File

@ -1,6 +1,8 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using NHibernate.Criterion;
using NHibernate.SqlCommand;
using NHibernate.Transform;
@ -151,7 +153,8 @@ namespace NHibernate.Linq.Util
throw new NotSupportedException();
}
public ICriteria SetFetchMode(string associationPath, FetchMode mode)
[Obsolete("Use Fetch instead")]
public ICriteria SetFetchMode(string associationPath, FetchMode mode)
{
return detachedCriteria.SetFetchMode(associationPath, mode).Adapt(session);
}
@ -240,11 +243,34 @@ namespace NHibernate.Linq.Util
throw new NotSupportedException();
}
#endregion
public Task<IList> ListAsync(CancellationToken cancellationToken = default(CancellationToken)) {
throw new NotSupportedException();
}
#region ICloneable Members
public Task<object> UniqueResultAsync(CancellationToken cancellationToken = default(CancellationToken)) {
throw new NotSupportedException();
}
public object Clone()
public Task ListAsync(IList results, CancellationToken cancellationToken = default(CancellationToken)) {
throw new NotSupportedException();
}
public Task<IList<T>> ListAsync<T>(CancellationToken cancellationToken = default(CancellationToken)) {
throw new NotSupportedException();
}
public Task<T> UniqueResultAsync<T>(CancellationToken cancellationToken = default(CancellationToken)) {
throw new NotSupportedException();
}
IFutureEnumerable<T> ICriteria.Future<T>() {
throw new NotSupportedException();
}
#endregion
#region ICloneable Members
public object Clone()
{
throw new NotSupportedException();
}
@ -277,5 +303,6 @@ namespace NHibernate.Linq.Util
_readOnlyInitialized = true;
return this;
}
}
}

View File

@ -0,0 +1,153 @@
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
namespace NHibernate.Linq.Util
{
/// <summary>
/// Provides static utility methods that aid in evaluating expression trees.
/// </summary>
public static class LinqUtil
{
/// <summary>
/// Creates a collection of type T by invoking a delegate method during
/// enumeration that return each item, begining with an initialValue.
/// </summary>
/// <typeparam name="T">The type of collection being created.</typeparam>
/// <param name="func">A delegate method to invoke.</param>
/// <param name="initialValue">The first item in the collection.</param>
/// <returns>An <see cref="T:System.Collections.Generic.IEnumerable`1"/> collection of type T.</returns>
public static IEnumerable<T> Iterate<T>(Func<T, T> func, T initialValue)
{
T value = initialValue;
while (true)
{
yield return value;
value = func(value);
}
}
/// <summary>
/// Returns an <see cref="T:System.Object"/> with the specified <see cref="T:System.Type"/>
/// and whose value is equivalent to the specified object.
/// </summary>
/// <param name="value">An <see cref="T:System.Object"/> that implements the <see cref="T:System.IConvertible"/> interface.</param>
/// <param name="conversionType">A <see cref="T:System.Type"/>.</param>
/// <returns>An object whose <see cref="T:System.Type"/> is conversionType and whose value is equivalent
/// to value, or null, if value is null and conversionType is not a value type.</returns>
public static object ChangeType(object value, System.Type conversionType)
{
// have to use IsAssignableFrom() due to proxy classes
if (value != null && !conversionType.IsAssignableFrom(value.GetType()))
{
if (IsNullableType(conversionType))
{
System.Type arg = conversionType.GetGenericArguments()[0];
if (arg.IsEnum)
{
if (value is string)
{
value = Activator.CreateInstance(conversionType, Enum.Parse(arg, value as string));
}
else
{
value = Activator.CreateInstance(conversionType, Enum.ToObject(arg, value));
}
}
else
{
value = Activator.CreateInstance(conversionType, Convert.ChangeType(value, arg));
}
}
else
{
if (conversionType.IsEnum)
{
if (value is string)
{
value = Enum.Parse(conversionType, value as string);
}
else
{
value = Enum.ToObject(conversionType, value);
}
}
else
{
value = Convert.ChangeType(value, conversionType);
}
}
}
return value;
}
/// <summary>
/// Determines if the specified type is a <see cref="T:System.Nullable`1"/> type.
/// </summary>
/// <param name="type">A <see cref="T:System.Type"/> to check.</param>
/// <returns>True if the type is a <see cref="T:System.Nullable`1"/> type, otherwise false.</returns>
public static bool IsNullableType(System.Type type)
{
return type != null && type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>);
}
/// <summary>
/// Determines if the specified type is an anonymous type.
/// </summary>
/// <param name="type">A <see cref="T:System.Type"/> to check.</param>
/// <returns>True if the type is an anonymous type, otherwise false.</returns>
public static bool IsAnonymousType(System.Type type)
{
return type != null && type.Name.StartsWith("<");
}
/// <summary>
/// Encodes an <see cref="T:System.Object"/> for use in SQL statements.
/// </summary>
/// <param name="value">The value to encode.</param>
/// <returns>A SQL encoded value.</returns>
public static string SqlEncode(object value)
{
if (value != null)
{
System.Type type = value.GetType();
if (IsNullableType(type))
{
value = type.GetProperty("Value").GetValue(value, null);
return SqlEncode(value);
}
switch (System.Type.GetTypeCode(type))
{
case TypeCode.Boolean:
return ((bool)value) ? "1" : "0";
case TypeCode.Byte:
case TypeCode.Decimal:
case TypeCode.Double:
case TypeCode.Int16:
case TypeCode.Int32:
case TypeCode.Int64:
case TypeCode.SByte:
case TypeCode.Single:
return value.ToString();
case TypeCode.DBNull:
case TypeCode.Empty:
return "null";
default:
return String.Format("'{0}'", value.ToString().Replace("'", "''"));
}
}
return "null";
}
public static Expression StripQuotes(Expression e)
{
while (e.NodeType == ExpressionType.Quote)
e = ((UnaryExpression)e).Operand;
return e;
}
}
}

View File

@ -0,0 +1,140 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using NHibernate.Criterion;
using NHibernate.Linq.Expressions;
using Expression = System.Linq.Expressions.Expression;
namespace NHibernate.Linq.Util
{
public class QueryUtil
{
public static object GetExpressionValue(Expression expression)
{
var constExpr = expression as ConstantExpression;
if (constExpr != null)
return constExpr.Value;
return Expression.Lambda(typeof(Func<>).MakeGenericType(expression.Type), expression)
.Compile().DynamicInvoke();
}
public static List<ParameterExpression> GetParameters(Expression expr)
{
var list = new List<ParameterExpression>();
GetParameters(expr, list);
return list;
}
public static object[] GetMethodParameterValues(MethodCallExpression call)
{
System.Type[] paramTypes = null;
return GetMethodParameterValues(call, out paramTypes);
}
public static object[] GetMethodParameterValues(MethodCallExpression call, out System.Type[] types)
{
ConstantExpression ce;
object[] values = null;
types = null;
if (call != null)
{
var valueList = new List<Object>();
var typeList = new List<System.Type>();
for (int i = 0; i < call.Arguments.Count; i++)
{
ce = call.Arguments[i] as ConstantExpression;
if (ce != null)
{
valueList.Add(ce.Value);
typeList.Add(ce.Type);
}
}
if (valueList.Count > 0)
{
values = valueList.ToArray();
types = typeList.ToArray();
}
}
return values;
}
public static SqlFunctionExpression GetFunctionCriteria(MethodCallExpression call, ICriterion criterion,
SqlFunctionExpression rightFunction)
{
System.Type[] paramTypes = null;
object[] paramValues = GetMethodParameterValues(call, out paramTypes);
int propertyPosition = 0;
string methodName = QueryUtil.GetMethodName(call, out propertyPosition);
return new SqlFunctionExpression(methodName, call.Method.ReturnType, paramValues, paramTypes, criterion,
propertyPosition, rightFunction);
}
public static void GetParameters(Expression expr, List<ParameterExpression> list)
{
if (expr is ParameterExpression)
{
var pe = expr as ParameterExpression;
if (!list.Contains(pe))
{
list.Add(pe);
}
}
else if (expr is MemberExpression)
{
GetParameters(((MemberExpression)expr).Expression, list);
}
else if (expr is UnaryExpression)
{
GetParameters(((UnaryExpression)expr).Operand, list);
}
else if (expr is BinaryExpression)
{
var be = expr as BinaryExpression;
GetParameters(be.Left, list);
GetParameters(be.Right, list);
}
}
public static string GetMethodName(MethodCallExpression call, out int propertyPosition)
{
object[] attribs = call.Method.GetCustomAttributes(typeof(SqlFunctionAttribute), false);
var attrib = attribs.FirstOrDefault() as SqlFunctionAttribute;
string methodName = call.Method.Name;
propertyPosition = 0;
if (attrib != null)
{
if (!String.IsNullOrEmpty(attrib.Owner))
methodName = String.Format("{0}.{1}", attrib.Owner, methodName);
propertyPosition = attrib.PropertyPosition;
}
else
{
// provide mapping of System methods to SQL functions
switch (methodName.ToLower())
{
case "tolower":
methodName = "lower";
break;
case "toupper":
methodName = "upper";
break;
case "indexof":
case "charindex":
methodName = "charindex";
propertyPosition = 1;
break;
}
}
return methodName;
}
}
}

View File

@ -0,0 +1,48 @@
using System.Collections.Generic;
using NHibernate.Engine;
using NHibernate.Metadata;
namespace NHibernate.Linq.Util
{
public static class SessionFactoryUtil
{
public static IDictionary<System.Type, string> GetProxyMetaData(this ISessionFactoryImplementor factory, IDictionary<string, IClassMetadata> metaData)
{
var dict = new Dictionary<System.Type, string>();
foreach (var item in metaData)
{
if (item.Value.HasProxy)
{
var proxyType = factory.GetEntityPersister(item.Key).ConcreteProxyClass;
if (proxyType != item.Value.MappedClass && !dict.ContainsKey(proxyType))
{
dict.Add(proxyType, item.Key);
}
}
}
return dict;
}
public static IDictionary<string, System.Type> GetEntityNameMetaData(this ISessionFactoryImplementor factory)
{
var metaData = factory.GetAllClassMetadata();
var dict = new Dictionary<string, System.Type>();
foreach (var item in metaData)
{
var type = item.Value.MappedClass;
dict.Add(item.Key, type);
if (item.Value.HasProxy)
{
var proxyType = factory.GetEntityPersister(item.Key).ConcreteProxyClass;
if (proxyType != type && !dict.ContainsKey(proxyType.FullName))
{
dict.Add(proxyType.FullName, proxyType);
}
}
}
return dict;
}
}
}

View File

@ -0,0 +1,54 @@
using System.Collections.Generic;
namespace NHibernate.Linq.Util
{
/// <remarks>
/// http://blogs.msdn.com/mattwar/archive/2007/07/30/linq-building-an-iqueryable-provider-part-i.aspx
/// </remarks>
public static class TypeSystem
{
public static System.Type GetElementType(System.Type seqType)
{
System.Type ienum = FindIEnumerable(seqType);
if (ienum == null) return seqType;
return ienum.GetGenericArguments()[0];
}
private static System.Type FindIEnumerable(System.Type seqType)
{
if (seqType == null || seqType == typeof(string))
return null;
if (seqType.IsArray)
return typeof(IEnumerable<>).MakeGenericType(seqType.GetElementType());
if (seqType.IsGenericType)
{
foreach (System.Type arg in seqType.GetGenericArguments())
{
System.Type ienum = typeof(IEnumerable<>).MakeGenericType(arg);
if (ienum.IsAssignableFrom(seqType))
{
return ienum;
}
}
}
System.Type[] ifaces = seqType.GetInterfaces();
if (ifaces != null && ifaces.Length > 0)
{
foreach (System.Type iface in ifaces)
{
System.Type ienum = FindIEnumerable(iface);
if (ienum != null) return ienum;
}
}
if (seqType.BaseType != null && seqType.BaseType != typeof(object))
{
return FindIEnumerable(seqType.BaseType);
}
return null;
}
}
}

View File

@ -0,0 +1,146 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using NHibernate.Engine;
using NHibernate.Linq.Expressions;
using NHibernate.Linq.Util;
using NHibernate.Metadata;
using NHibernate.Type;
namespace NHibernate.Linq.Visitors
{
/// <summary>
/// Preprocesses an expression tree replacing MemberAccessExpressions and ParameterExpressions with
/// NHibernate-specific PropertyAccessExpressions and EntityExpressions respectively.
/// </summary>
public class AssociationVisitor : ExpressionVisitor
{
private readonly ISessionFactoryImplementor _sessionFactory;
private readonly IDictionary<string, IClassMetadata> _metaData;
private readonly IDictionary<System.Type, string> _proxyTypes;
public AssociationVisitor(ISessionFactoryImplementor sessionFactory)
{
_sessionFactory = sessionFactory;
_metaData = _sessionFactory.GetAllClassMetadata();
_proxyTypes = _sessionFactory.GetProxyMetaData(_metaData);
}
private IClassMetadata GetMetaData(System.Type type)
{
if (LinqUtil.IsAnonymousType(type))
return null;
string entityName = _sessionFactory.TryGetGuessEntityName(type);
if (!String.IsNullOrEmpty(entityName) && _metaData.ContainsKey(entityName))
return _metaData[entityName];
if (_proxyTypes.ContainsKey(type))
return _metaData[_proxyTypes[type]];
return null;
}
private EntityExpression GetParentExpression(MemberExpression expr, out string memberName, out IType nhibernateType)
{
memberName = null;
nhibernateType = null;
CollectionAccessExpression collectionExpr = expr.Expression as CollectionAccessExpression;
if (collectionExpr != null)
{
return null;
}
PropertyAccessExpression propExpr = expr.Expression as PropertyAccessExpression;
if (propExpr != null)
{
memberName = propExpr.Name + "." + expr.Member.Name;
nhibernateType = propExpr.Expression.MetaData.GetPropertyType(memberName);
return propExpr.Expression;
}
EntityExpression entityExpr = expr.Expression as EntityExpression;
if (entityExpr != null)
{
memberName = expr.Member.Name;
nhibernateType = entityExpr.MetaData.GetPropertyType(memberName);
return entityExpr;
}
return null;
}
private string AssociationPathForEntity(MemberExpression expr)
{
PropertyAccessExpression propExpr = expr.Expression as PropertyAccessExpression;
if (propExpr != null)
return propExpr.Name + "." + expr.Member.Name;
EntityExpression entityExpr = expr.Expression as EntityExpression;
if (entityExpr != null && entityExpr.Expression != null)
return entityExpr.Alias + "." + expr.Member.Name;
return expr.Member.Name;
}
protected override Expression VisitMemberAccess(MemberExpression expr)
{
expr = (MemberExpression)base.VisitMemberAccess(expr);
IClassMetadata metaData = GetMetaData(expr.Type);
if (metaData != null)
{
string associationPath = AssociationPathForEntity(expr);
return new EntityExpression(associationPath, expr.Member.Name, expr.Type, metaData, expr.Expression);
}
string memberName;
IType nhibernateType;
EntityExpression parentExpression = GetParentExpression(expr, out memberName, out nhibernateType);
if (parentExpression != null)
{
if (nhibernateType.IsCollectionType)
{
CollectionType collectionType = (CollectionType)nhibernateType;
IType nhElementType = collectionType.GetElementType((ISessionFactoryImplementor)_sessionFactory);
System.Type elementType = nhElementType.ReturnedClass;
IClassMetadata elementMetaData = GetMetaData(elementType);
EntityExpression elementExpression = null;
if (elementMetaData != null)
elementExpression = new EntityExpression(null, memberName, elementType, elementMetaData, null);
return new CollectionAccessExpression(memberName, expr.Type, nhibernateType, parentExpression, elementExpression);
}
return new PropertyAccessExpression(memberName, expr.Type, nhibernateType, parentExpression);
}
return expr;
}
protected override Expression VisitParameter(ParameterExpression expr)
{
IClassMetadata metaData = GetMetaData(expr.Type);
if (metaData != null)
return new EntityExpression(null, expr.Name, expr.Type, metaData, null);
return expr;
}
protected override Expression VisitConstant(ConstantExpression expr)
{
IQueryable query = expr.Value as IQueryable;
if (query != null)
{
return new QuerySourceExpression("this", query);
}
return expr;
}
}
}

View File

@ -0,0 +1,74 @@
using System.Linq.Expressions;
namespace NHibernate.Linq.Visitors
{
/// <summary>
/// Preprocesses an expression tree replacing binary boolean expressions with unary expressions.
/// </summary>
public class BinaryBooleanReducer : ExpressionVisitor
{
//this class simplifies this:
// timesheet.Entries.Any() == true
//to this:
// timesheet.Entries.Any()
private Expression ProcessBinaryExpression(Expression exprToCompare, Expression exprToReturn, ExpressionType nodeType, Expression original)
{
BooleanConstantFinder visitor = new BooleanConstantFinder();
visitor.Visit(exprToCompare);
if (visitor.Constant.HasValue)
{
switch (nodeType)
{
case ExpressionType.Equal:
return visitor.Constant.Value ? exprToReturn : Expression.Not(exprToReturn);
case ExpressionType.NotEqual:
return visitor.Constant.Value ? Expression.Not(exprToReturn) : exprToReturn;
case ExpressionType.Or:
case ExpressionType.OrElse:
return visitor.Constant.Value ? Expression.Constant(true) : exprToReturn;
case ExpressionType.And:
case ExpressionType.AndAlso:
return visitor.Constant.Value ? exprToReturn : Expression.Constant(false);
default:
return original;
}
}
else
return original;
}
protected override Expression VisitBinary(BinaryExpression expr)
{
Expression e = ProcessBinaryExpression(expr.Left, expr.Right, expr.NodeType, expr);
if (e != expr)
return e;
e = ProcessBinaryExpression(expr.Right, expr.Left, expr.NodeType, expr);
if (e != expr)
return e;
return base.VisitBinary(expr);
}
class BooleanConstantFinder : ExpressionVisitor
{
private bool _isNestedBinaryExpression;
public bool? Constant { get; private set; }
protected override Expression VisitConstant(ConstantExpression c)
{
if (c.Type == typeof(bool) && !_isNestedBinaryExpression)
Constant = (bool)c.Value;
return c;
}
protected override Expression VisitBinary(BinaryExpression b)
{
_isNestedBinaryExpression = true;
return b;
}
}
}
}

View File

@ -0,0 +1,40 @@
using NHibernate.Criterion;
namespace NHibernate.Linq.Visitors
{
/// <summary>
/// Represents a method that returns an <see cref="T:NHibernate.Criterion.ICriterion"/>
/// object that compares one property to another property using a binary expression.
/// </summary>
/// <param name="propertyName">The name of the property to compare on the left hand side of the expression.</param>
/// <param name="otherPropertyName">The name of the property to compare on the right hand side of the expression.</param>
/// <returns>An initialized <see cref="T:NHibernate.Criterion.ICriterion"/> object.</returns>
public delegate ICriterion ComparePropToProp(string propertyName, string otherPropertyName);
/// <summary>
/// Represents a method that returns an <see cref="T:NHibernate.Criterion.ICriterion"/>
/// object that compares a property to a constant value using a binary expression.
/// </summary>
/// <param name="propertyName">The name of the property to compare on the left hand side of the expression.</param>
/// <param name="value">The constant value used for the right hand side of the expression.</param>
/// <returns>An initialized <see cref="T:NHibernate.Criterion.ICriterion"/> object.</returns>
public delegate ICriterion ComparePropToValue(string propertyName, object value);
/// <summary>
/// Represents a method that returns an <see cref="T:NHibernate.Criterion.ICriterion"/>
/// object that compares a value to a criteria using a binary expression.
/// </summary>
/// <param name="value">The value on the left hand side of the expression.</param>
/// <param name="criteria">The <see cref="T:NHibernate.Criterion.DetachedCriteria"/> used for the right hand side of the expression.</param>
/// <returns>An initialized <see cref="T:NHibernate.Criterion.ICriterion"/> object.</returns>
public delegate ICriterion CompareValueToCriteria(object value, DetachedCriteria criteria);
/// <summary>
/// Represents a method that returns an <see cref="T:NHibernate.Criterion.ICriterion"/>
/// object that compares a property to a criteria using a binary expression.
/// </summary>
/// <param name="propertyName">The name of the property to compare on the left hand side of the expression.</param>
/// <param name="criteria">The <see cref="T:NHibernate.Criterion.DetachedCriteria"/> used for the right hand side of the expression.</param>
/// <returns>An initialized <see cref="T:NHibernate.Criterion.ICriterion"/> object.</returns>
public delegate ICriterion ComparePropToCriteria(string propertyName, DetachedCriteria criteria);
}

View File

@ -0,0 +1,10 @@
namespace NHibernate.Linq.Visitors
{
public enum BinaryCriterionType
{
None,
Value,
Property,
Criteria
}
}

View File

@ -0,0 +1,154 @@
using System;
using System.Linq.Expressions;
using NHibernate.Criterion;
using NHibernate.Linq.Expressions;
using NHibernate.Linq.Util;
using Expression = System.Linq.Expressions.Expression;
namespace NHibernate.Linq.Visitors
{
/// <summary>
/// Visits a BinaryExpression providing the appropriate NHibernate ICriterion.
/// </summary>
public class BinaryCriterionVisitor : NHibernateExpressionVisitor
{
private readonly ICriteria rootCriteria;
private readonly ISession session;
public BinaryCriterionVisitor(ICriteria rootCriteria, ISession session)
{
this.rootCriteria = rootCriteria;
this.session = session;
}
public System.Type ConvertTo { get; private set; }
public BinaryCriterionType Type { get; private set; }
public object Value { get; private set; }
public string Name { get; private set; }
public DetachedCriteria Criteria { get; private set; }
protected override Expression VisitMethodCall(MethodCallExpression expr)
{
Type = BinaryCriterionType.Criteria;
//TODO: don't hardcode this alias 'sub'
Criteria = DetachedCriteria.ForEntityName(rootCriteria.GetEntityOrClassName(), "sub");
EntityExpression rootEntity = EntityExpressionVisitor.RootEntity(expr);
if (rootEntity != null)
{
string identifierName = rootEntity.MetaData.IdentifierPropertyName;
Criteria.Add(Restrictions.EqProperty(rootCriteria.Alias + "." + identifierName, "sub." + identifierName));
}
if (SelectArgumentsVisitor.SupportsMethod(expr.Method.Name))
{
var projectionVisitor = new SelectArgumentsVisitor(Criteria.Adapt(session), session);
projectionVisitor.Visit(expr);
Criteria.SetProjection(projectionVisitor.Projection);
}
return expr;
}
protected override Expression VisitMemberAccess(MemberExpression expr)
{
Type = BinaryCriterionType.Property;
Name = expr.Member.Name;
return expr;
}
protected override Expression VisitConstant(ConstantExpression expr)
{
Type = BinaryCriterionType.Value;
Value = QueryUtil.GetExpressionValue(expr);
return expr;
}
protected override Expression VisitEntity(EntityExpression expr)
{
Type = BinaryCriterionType.Property;
Name = MemberNameVisitor.GetMemberName(rootCriteria, expr);
return expr;
}
protected override Expression VisitPropertyAccess(PropertyAccessExpression expr)
{
Type = BinaryCriterionType.Property;
Name = MemberNameVisitor.GetMemberName(rootCriteria, expr);
return expr;
}
protected override Expression VisitCollectionAccess(CollectionAccessExpression expr)
{
return VisitPropertyAccess(expr);
}
protected override Expression VisitUnary(UnaryExpression expr)
{
if (expr.NodeType == ExpressionType.Convert)
{
//convert to the type of the operand, not the type of the conversion
ConvertTo = expr.Operand.Type;
Visit(expr.Operand);
}
return expr;
}
public static ICriterion GetBinaryCriteria(
ICriteria rootCriteria,
ISession session,
BinaryExpression expr,
ComparePropToValue comparePropToValue,
ComparePropToProp comparePropToProp,
CompareValueToCriteria compareValueToCriteria,
ComparePropToCriteria comparePropToCriteria)
{
var left = new BinaryCriterionVisitor(rootCriteria, session);
var right = new BinaryCriterionVisitor(rootCriteria, session);
left.Visit(expr.Left);
right.Visit(expr.Right);
//the query should have been preprocessed so that
//only the following combinations are possible:
// LEFT RIGHT
// ========================
// property value
// property property
// property criteria
// value criteria
// criteria criteria <== not supported yet
switch (left.Type)
{
case BinaryCriterionType.Property:
switch (right.Type)
{
case BinaryCriterionType.Value:
object val = right.Value;
if (left.ConvertTo != null)
val = LinqUtil.ChangeType(val, left.ConvertTo);
return comparePropToValue(left.Name, val);
case BinaryCriterionType.Property:
return comparePropToProp(left.Name, right.Name);
case BinaryCriterionType.Criteria:
return comparePropToCriteria(left.Name, right.Criteria);
}
break;
case BinaryCriterionType.Value:
return compareValueToCriteria(left.Value, right.Criteria);
}
throw new NotSupportedException("Could not understand: " + expr);
}
}
}

View File

@ -0,0 +1,140 @@
using System.Linq.Expressions;
using NHibernate.Linq.Expressions;
namespace NHibernate.Linq.Visitors
{
/// <summary>
/// Preprocesses an expression tree ordering binary expressions in accordance with the <see cref="T:NHibernate.Linq.Visitors.BinaryCriterionVisitor"/>.
/// </summary>
public class BinaryExpressionOrderer : NHibernateExpressionVisitor
{
// This class makes sure binary expressions are in one of these configurations
// so that the BinaryCriterionVisitor can correctly process it.
// LEFT RIGHT
// ========================
// property value
// property property
// property criteria
// value criteria
// criteria criteria
private ExpressionType ReflectExpressionType(ExpressionType type)
{
switch (type)
{
case ExpressionType.LessThan:
return ExpressionType.GreaterThan;
case ExpressionType.LessThanOrEqual:
return ExpressionType.GreaterThanOrEqual;
case ExpressionType.GreaterThan:
return ExpressionType.LessThan;
case ExpressionType.GreaterThanOrEqual:
return ExpressionType.LessThanOrEqual;
case ExpressionType.Equal:
return ExpressionType.Equal;
case ExpressionType.NotEqual:
return ExpressionType.NotEqual;
default:
return type;
}
}
private Expression Swap(BinaryExpression expr)
{
ExpressionType nodeType = ReflectExpressionType(expr.NodeType);
return Expression.MakeBinary(nodeType, expr.Right, expr.Left, expr.IsLiftedToNull, expr.Method, expr.Conversion);
}
protected override Expression VisitBinary(BinaryExpression expr)
{
BinaryExpressionTypeFinder left = new BinaryExpressionTypeFinder();
left.Visit(expr.Left);
if (left.Type == BinaryCriterionType.None)
return base.VisitBinary(expr);
BinaryExpressionTypeFinder right = new BinaryExpressionTypeFinder();
right.Visit(expr.Right);
if (right.Type == BinaryCriterionType.None)
return base.VisitBinary(expr);
if (right.Type == BinaryCriterionType.Property)
{
if (left.Type == BinaryCriterionType.Criteria
|| left.Type == BinaryCriterionType.Value)
{
return Swap(expr);
}
}
else if (right.Type == BinaryCriterionType.Value)
{
if (left.Type == BinaryCriterionType.Criteria)
{
return Swap(expr);
}
}
return expr;
}
class BinaryExpressionTypeFinder : NHibernateExpressionVisitor
{
private bool _isNestedBinaryExpression;
private BinaryCriterionType _type;
public BinaryCriterionType Type
{
get { return _type; }
private set
{
if (!_isNestedBinaryExpression)
_type = value;
}
}
protected override Expression VisitBinary(BinaryExpression b)
{
_isNestedBinaryExpression = true;
return b;
}
protected override Expression VisitMethodCall(MethodCallExpression expr)
{
Type = BinaryCriterionType.Criteria;
return expr;
}
protected override Expression VisitConstant(ConstantExpression expr)
{
Type = BinaryCriterionType.Value;
return expr;
}
protected override Expression VisitEntity(EntityExpression expr)
{
Type = BinaryCriterionType.Property;
return expr;
}
protected override Expression VisitPropertyAccess(PropertyAccessExpression expr)
{
Type = BinaryCriterionType.Property;
return expr;
}
protected override Expression VisitCollectionAccess(CollectionAccessExpression expr)
{
Type = BinaryCriterionType.Property;
return expr;
}
}
}
}

View File

@ -0,0 +1,66 @@
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using NHibernate.Linq.Expressions;
using NHibernate.Linq.Util;
namespace NHibernate.Linq.Visitors
{
/// <summary>
/// Assigns the appropriate aliases to a collection access.
/// </summary>
public class CollectionAliasVisitor : NHibernateExpressionVisitor
{
private IEnumerable<ParameterExpression> _currentCollectionAliases;
private string FindCollectionAlias(System.Type elementType)
{
if (_currentCollectionAliases == null) return null;
foreach (ParameterExpression p in _currentCollectionAliases)
{
if (p.Type == elementType)
{
return p.Name;
}
}
return null;
}
protected override Expression VisitCollectionAccess(CollectionAccessExpression expr)
{
EntityExpression elementExpression = expr.ElementExpression;
if (elementExpression == null)
return expr;
string alias = FindCollectionAlias(elementExpression.Type);
if (String.IsNullOrEmpty(alias))
return expr;
elementExpression = new EntityExpression(elementExpression.AssociationPath, alias, elementExpression.Type, elementExpression.MetaData, elementExpression.Expression);
return new CollectionAccessExpression(expr.Name, expr.Type, expr.NHibernateType, expr.Expression, elementExpression);
}
protected override Expression VisitMethodCall(MethodCallExpression expr)
{
if (expr.Arguments.Count > 1)
{
LambdaExpression lambda = LinqUtil.StripQuotes(expr.Arguments[1]) as LambdaExpression;
if (lambda != null)
{
_currentCollectionAliases = lambda.Parameters;
}
}
return base.VisitMethodCall(expr);
}
public static Expression AssignCollectionAccessAliases(Expression expr)
{
CollectionAliasVisitor visitor = new CollectionAliasVisitor();
return visitor.Visit(expr);
}
}
}

View File

@ -0,0 +1,50 @@
using System.Linq.Expressions;
using NHibernate.Linq.Expressions;
namespace NHibernate.Linq.Visitors
{
/// <summary>
/// Retrieves the first (or root) instance of EntityExpression found in the given Expression.
/// </summary>
public class EntityExpressionVisitor : NHibernateExpressionVisitor
{
private readonly bool _findFirstEntity;
public EntityExpression Expression { get; private set; }
public EntityExpressionVisitor(bool findFirstEntity)
{
_findFirstEntity = findFirstEntity;
}
protected override Expression VisitEntity(EntityExpression expr)
{
this.Expression = expr;
if (_findFirstEntity) return expr;
return base.VisitEntity(expr);
}
protected override Expression VisitMethodCall(MethodCallExpression expr)
{
Visit(expr.Arguments[0]);
return expr;
}
private static EntityExpression FindEntity(Expression expr, bool findFirst)
{
EntityExpressionVisitor visitor = new EntityExpressionVisitor(findFirst);
visitor.Visit(expr);
return visitor.Expression;
}
public static EntityExpression FirstEntity(Expression expr)
{
return FindEntity(expr, true);
}
public static EntityExpression RootEntity(Expression expr)
{
return FindEntity(expr, false);
}
}
}

View File

@ -0,0 +1,131 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
namespace NHibernate.Linq.Visitors
{
public static class Evaluator
{
/// <summary>
/// Performs evaluation & replacement of independent sub-trees
/// </summary>
/// <param name="expression">The root of the expression tree.</param>
/// <param name="fnCanBeEvaluated">A function that decides whether a given expression node can be part of the local function.</param>
/// <returns>A new tree with sub-trees evaluated and replaced.</returns>
public static Expression PartialEval(Expression expression, Func<Expression, bool> fnCanBeEvaluated)
{
return new SubtreeEvaluator(new Nominator(fnCanBeEvaluated).Nominate(expression)).Eval(expression);
}
/// <summary>
/// Performs evaluation & replacement of independent sub-trees
/// </summary>
/// <param name="expression">The root of the expression tree.</param>
/// <returns>A new tree with sub-trees evaluated and replaced.</returns>
public static Expression PartialEval(Expression expression)
{
return PartialEval(expression, Evaluator.CanBeEvaluatedLocally);
}
private static bool CanBeEvaluatedLocally(Expression expression)
{
if (expression.NodeType == ExpressionType.Constant)
{
return !(((ConstantExpression)expression).Value is IQueryable);
}
return expression.NodeType != ExpressionType.Parameter;
}
/// <summary>
/// Evaluates & replaces sub-trees when first candidate is reached (top-down)
/// </summary>
class SubtreeEvaluator : ExpressionVisitor
{
HashSet<Expression> candidates;
internal SubtreeEvaluator(HashSet<Expression> candidates)
{
this.candidates = candidates;
}
internal Expression Eval(Expression exp)
{
return this.Visit(exp);
}
public override Expression Visit(Expression exp)
{
if (exp == null)
{
return null;
}
if (this.candidates.Contains(exp))
{
return this.Evaluate(exp);
}
return base.Visit(exp);
}
private Expression Evaluate(Expression e)
{
if (e.NodeType == ExpressionType.Constant)
return e;
if (e.NodeType == ExpressionType.Lambda)
return e;
LambdaExpression lambda = Expression.Lambda(e);
Delegate fn = lambda.Compile();
return Expression.Constant(fn.DynamicInvoke(null), e.Type);
}
}
/// <summary>
/// Performs bottom-up analysis to determine which nodes can possibly
/// be part of an evaluated sub-tree.
/// </summary>
class Nominator : ExpressionVisitor
{
Func<Expression, bool> fnCanBeEvaluated;
HashSet<Expression> candidates;
bool cannotBeEvaluated;
internal Nominator(Func<Expression, bool> fnCanBeEvaluated)
{
this.fnCanBeEvaluated = fnCanBeEvaluated;
}
internal HashSet<Expression> Nominate(Expression expression)
{
this.candidates = new HashSet<Expression>();
this.Visit(expression);
return this.candidates;
}
public override Expression Visit(Expression expression)
{
if (expression != null)
{
bool saveCannotBeEvaluated = this.cannotBeEvaluated;
this.cannotBeEvaluated = false;
base.Visit(expression);
if (!this.cannotBeEvaluated)
{
if (this.fnCanBeEvaluated(expression))
{
this.candidates.Add(expression);
}
else
{
this.cannotBeEvaluated = true;
}
}
this.cannotBeEvaluated |= saveCannotBeEvaluated;
}
return expression;
}
}
}
}

View File

@ -0,0 +1,394 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq.Expressions;
namespace NHibernate.Linq.Visitors
{
/// <summary>
/// Provides virtual methods that can be used by subclasses to parse an expression tree.
/// </summary>
/// <remarks>
/// This class actually already exists in the System.Core assembly...as an internal class.
/// I can only speculate as to why it is internal, but it is obviously much too dangerous
/// for anyone outside of Microsoft to be using...
/// </remarks>
[DebuggerStepThrough, DebuggerNonUserCode]
public abstract class ExpressionVisitor
{
public virtual Expression Visit(Expression exp)
{
if (exp == null) return exp;
switch (exp.NodeType)
{
case ExpressionType.Negate:
case ExpressionType.NegateChecked:
case ExpressionType.Not:
case ExpressionType.Convert:
case ExpressionType.ConvertChecked:
case ExpressionType.ArrayLength:
case ExpressionType.Quote:
case ExpressionType.TypeAs:
return VisitUnary((UnaryExpression)exp);
case ExpressionType.Add:
case ExpressionType.AddChecked:
case ExpressionType.Subtract:
case ExpressionType.SubtractChecked:
case ExpressionType.Multiply:
case ExpressionType.MultiplyChecked:
case ExpressionType.Divide:
case ExpressionType.Modulo:
case ExpressionType.And:
case ExpressionType.AndAlso:
case ExpressionType.Or:
case ExpressionType.OrElse:
case ExpressionType.LessThan:
case ExpressionType.LessThanOrEqual:
case ExpressionType.GreaterThan:
case ExpressionType.GreaterThanOrEqual:
case ExpressionType.Equal:
case ExpressionType.NotEqual:
case ExpressionType.Coalesce:
case ExpressionType.ArrayIndex:
case ExpressionType.RightShift:
case ExpressionType.LeftShift:
case ExpressionType.ExclusiveOr:
return VisitBinary((BinaryExpression)exp);
case ExpressionType.TypeIs:
return VisitTypeIs((TypeBinaryExpression)exp);
case ExpressionType.Conditional:
return VisitConditional((ConditionalExpression)exp);
case ExpressionType.Constant:
return VisitConstant((ConstantExpression)exp);
case ExpressionType.Parameter:
return VisitParameter((ParameterExpression)exp);
case ExpressionType.MemberAccess:
return VisitMemberAccess((MemberExpression)exp);
case ExpressionType.Call:
return VisitMethodCall((MethodCallExpression)exp);
case ExpressionType.Lambda:
return VisitLambda((LambdaExpression)exp);
case ExpressionType.New:
return VisitNew((NewExpression)exp);
case ExpressionType.NewArrayInit:
case ExpressionType.NewArrayBounds:
return VisitNewArray((NewArrayExpression)exp);
case ExpressionType.Invoke:
return VisitInvocation((InvocationExpression)exp);
case ExpressionType.MemberInit:
return VisitMemberInit((MemberInitExpression)exp);
case ExpressionType.ListInit:
return VisitListInit((ListInitExpression)exp);
default:
throw new NotSupportedException(String.Format("Unhandled expression type: '{0}'", exp.NodeType));
}
}
protected virtual MemberBinding VisitBinding(MemberBinding binding)
{
switch (binding.BindingType)
{
case MemberBindingType.Assignment:
return VisitMemberAssignment((MemberAssignment)binding);
case MemberBindingType.MemberBinding:
return VisitMemberMemberBinding((MemberMemberBinding)binding);
case MemberBindingType.ListBinding:
return VisitMemberListBinding((MemberListBinding)binding);
default:
throw new NotSupportedException(string.Format("Unhandled binding type '{0}'", binding.BindingType));
}
}
protected virtual ElementInit VisitElementInitializer(ElementInit initializer)
{
ReadOnlyCollection<Expression> arguments = VisitList(initializer.Arguments);
if (arguments != initializer.Arguments)
{
return Expression.ElementInit(initializer.AddMethod, arguments);
}
return initializer;
}
protected virtual Expression VisitUnary(UnaryExpression u)
{
Expression operand = Visit(u.Operand);
if (operand != u.Operand)
{
return Expression.MakeUnary(u.NodeType, operand, u.Type, u.Method);
}
return u;
}
protected virtual Expression VisitBinary(BinaryExpression b)
{
Expression left = Visit(b.Left);
Expression right = Visit(b.Right);
Expression conversion = Visit(b.Conversion);
if (left != b.Left || right != b.Right || conversion != b.Conversion)
{
if (b.NodeType == ExpressionType.Coalesce && b.Conversion != null)
return Expression.Coalesce(left, right, conversion as LambdaExpression);
else
return Expression.MakeBinary(b.NodeType, left, right, b.IsLiftedToNull, b.Method);
}
return b;
}
protected virtual Expression VisitTypeIs(TypeBinaryExpression b)
{
Expression expr = Visit(b.Expression);
if (expr != b.Expression)
{
return Expression.TypeIs(expr, b.TypeOperand);
}
return b;
}
protected virtual Expression VisitConstant(ConstantExpression c)
{
return c;
}
protected virtual Expression VisitConditional(ConditionalExpression c)
{
Expression test = Visit(c.Test);
Expression ifTrue = Visit(c.IfTrue);
Expression ifFalse = Visit(c.IfFalse);
if (test != c.Test || ifTrue != c.IfTrue || ifFalse != c.IfFalse)
{
return Expression.Condition(test, ifTrue, ifFalse);
}
return c;
}
protected virtual Expression VisitParameter(ParameterExpression p)
{
return p;
}
protected virtual Expression VisitMemberAccess(MemberExpression m)
{
Expression exp = Visit(m.Expression);
if (exp != m.Expression)
{
return Expression.MakeMemberAccess(exp, m.Member);
}
return m;
}
protected virtual Expression VisitMethodCall(MethodCallExpression m)
{
Expression obj = Visit(m.Object);
IEnumerable<Expression> args = VisitList(m.Arguments);
if (obj != m.Object || args != m.Arguments)
{
return Expression.Call(obj, m.Method, args);
}
return m;
}
protected virtual ReadOnlyCollection<Expression> VisitList(ReadOnlyCollection<Expression> original)
{
List<Expression> list = null;
for (int i = 0, n = original.Count; i < n; i++)
{
Expression p = Visit(original[i]);
if (list != null)
{
list.Add(p);
}
else if (p != original[i])
{
list = new List<Expression>(n);
for (int j = 0; j < i; j++)
{
list.Add(original[j]);
}
list.Add(p);
}
}
if (list != null)
return list.AsReadOnly();
return original;
}
protected virtual MemberAssignment VisitMemberAssignment(MemberAssignment assignment)
{
Expression e = Visit(assignment.Expression);
if (e != assignment.Expression)
{
return Expression.Bind(assignment.Member, e);
}
return assignment;
}
protected virtual MemberMemberBinding VisitMemberMemberBinding(MemberMemberBinding binding)
{
IEnumerable<MemberBinding> bindings = VisitBindingList(binding.Bindings);
if (bindings != binding.Bindings)
{
return Expression.MemberBind(binding.Member, bindings);
}
return binding;
}
protected virtual MemberListBinding VisitMemberListBinding(MemberListBinding binding)
{
IEnumerable<ElementInit> initializers = VisitElementInitializerList(binding.Initializers);
if (initializers != binding.Initializers)
{
return Expression.ListBind(binding.Member, initializers);
}
return binding;
}
protected virtual IEnumerable<MemberBinding> VisitBindingList(ReadOnlyCollection<MemberBinding> original)
{
List<MemberBinding> list = null;
for (int i = 0, n = original.Count; i < n; i++)
{
MemberBinding b = VisitBinding(original[i]);
if (list != null)
{
list.Add(b);
}
else if (b != original[i])
{
list = new List<MemberBinding>(n);
for (int j = 0; j < i; j++)
{
list.Add(original[j]);
}
list.Add(b);
}
}
if (list != null)
return list;
return original;
}
protected virtual IEnumerable<ElementInit> VisitElementInitializerList(ReadOnlyCollection<ElementInit> original)
{
List<ElementInit> list = null;
for (int i = 0, n = original.Count; i < n; i++)
{
ElementInit init = VisitElementInitializer(original[i]);
if (list != null)
{
list.Add(init);
}
else if (init != original[i])
{
list = new List<ElementInit>(n);
for (int j = 0; j < i; j++)
{
list.Add(original[j]);
}
list.Add(init);
}
}
if (list != null)
return list;
return original;
}
protected virtual Expression VisitLambda(LambdaExpression lambda)
{
Expression body = Visit(lambda.Body);
if (body != lambda.Body)
{
return Expression.Lambda(lambda.Type, body, lambda.Parameters);
}
return lambda;
}
protected virtual NewExpression VisitNew(NewExpression nex)
{
IEnumerable<Expression> args = VisitList(nex.Arguments);
if (args != nex.Arguments)
{
if (nex.Members != null)
return Expression.New(nex.Constructor, args, nex.Members);
else
return Expression.New(nex.Constructor, args);
}
return nex;
}
protected virtual Expression VisitMemberInit(MemberInitExpression init)
{
NewExpression n = VisitNew(init.NewExpression);
IEnumerable<MemberBinding> bindings = VisitBindingList(init.Bindings);
if (n != init.NewExpression || bindings != init.Bindings)
{
return Expression.MemberInit(n, bindings);
}
return init;
}
protected virtual Expression VisitListInit(ListInitExpression init)
{
NewExpression n = VisitNew(init.NewExpression);
IEnumerable<ElementInit> initializers = VisitElementInitializerList(init.Initializers);
if (n != init.NewExpression || initializers != init.Initializers)
{
return Expression.ListInit(n, initializers);
}
return init;
}
protected virtual Expression VisitNewArray(NewArrayExpression na)
{
IEnumerable<Expression> exprs = VisitList(na.Expressions);
if (exprs != na.Expressions)
{
if (na.NodeType == ExpressionType.NewArrayInit)
{
return Expression.NewArrayInit(na.Type.GetElementType(), exprs);
}
else
{
return Expression.NewArrayBounds(na.Type.GetElementType(), exprs);
}
}
return na;
}
protected virtual Expression VisitInvocation(InvocationExpression iv)
{
IEnumerable<Expression> args = VisitList(iv.Arguments);
Expression expr = Visit(iv.Expression);
if (args != iv.Arguments || expr != iv.Expression)
{
return Expression.Invoke(expr, args);
}
return iv;
}
}
}

View File

@ -0,0 +1,53 @@
using System.Collections.Generic;
using NHibernate.Criterion;
using NHibernate.Linq.Expressions;
using Expression = System.Linq.Expressions.Expression;
using NHProjections = NHibernate.Criterion.Projections;
namespace NHibernate.Linq.Visitors
{
/// <summary>
/// Visits an expression tree providing the appropriate projections for grouping arguments.
/// </summary>
public class GroupingArgumentsVisitor : NHibernateExpressionVisitor
{
private readonly ICriteria _rootCriteria;
private readonly List<IProjection> _projections = new List<IProjection>();
public IProjection Projection
{
get
{
if (_projections.Count == 0)
return null;
if (_projections.Count == 1)
return _projections[0];
ProjectionList list = NHProjections.ProjectionList();
foreach (var projection in _projections)
list.Add(projection);
return list;
}
}
public GroupingArgumentsVisitor(ICriteria rootCriteria)
{
_rootCriteria = rootCriteria;
}
protected override Expression VisitPropertyAccess(PropertyAccessExpression expr)
{
string name = MemberNameVisitor.GetMemberName(_rootCriteria, expr);
_projections.Add(NHProjections.GroupProperty(name));
return expr;
}
protected override Expression VisitCollectionAccess(CollectionAccessExpression expr)
{
return VisitPropertyAccess(expr);
}
}
}

View File

@ -0,0 +1,176 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using NHibernate.Criterion;
using NHibernate.Linq.Util;
using LinqExpression = System.Linq.Expressions.Expression;
namespace NHibernate.Linq.Visitors
{
public interface IImmediateResultsVisitor
{
object GetResults(MethodCallExpression expr);
}
/// <summary>
/// Visits any expression calls that require immediate results.
/// </summary>
public class ImmediateResultsVisitor<T> : NHibernateExpressionVisitor, IImmediateResultsVisitor
{
private readonly ISession session;
private readonly ICriteria rootCriteria;
private T results;
public ImmediateResultsVisitor(ISession session, ICriteria rootCriteria)
{
this.session = session;
this.rootCriteria = rootCriteria;
}
public object GetResults(MethodCallExpression expr)
{
Visit(expr);
return results;
}
protected override LinqExpression VisitMethodCall(MethodCallExpression call)
{
switch (call.Method.Name)
{
case "First":
results = HandleFirstCall(call);
break;
case "FirstOrDefault":
results = HandleFirstOrDefaultCall(call);
break;
case "Single":
results = HandleSingleCall(call);
break;
case "SingleOrDefault":
results = HandleSingleOrDefaultCall(call);
break;
case "Aggregate":
results = HandleAggregateCallback(call);
break;
case "Average":
case "Count":
case "LongCount":
case "Max":
case "Min":
case "Sum":
rootCriteria.ClearOrders();
results = HandleAggregateCall(call);
break;
case "Any":
results = HandleAnyCall(call);
break;
default:
throw new NotImplementedException("The method " + call.Method.Name + " is not implemented.");
}
return call;
}
private T HandleFirstCall(MethodCallExpression call)
{
return GetElementList(call, 1).First();
}
private T HandleFirstOrDefaultCall(MethodCallExpression call)
{
return GetElementList(call, 1).FirstOrDefault();
}
private T HandleSingleCall(MethodCallExpression call)
{
return GetElementList(call, 2).Single();
}
private T HandleSingleOrDefaultCall(MethodCallExpression call)
{
return GetElementList(call, 2).SingleOrDefault();
}
private IList<T> GetElementList(MethodCallExpression call, int count)
{
if (call.Arguments.Count > 1)
rootCriteria.Add(WhereArgumentsVisitor.GetCriterion(rootCriteria, session, call.Arguments[1]));
return rootCriteria.SetFirstResult(0).SetMaxResults(count).List<T>();
}
private T HandleAggregateCallback(MethodCallExpression call)
{
LambdaExpression lambda = (LambdaExpression)LinqUtil.StripQuotes(call.Arguments[call.Arguments.Count - 1]);
System.Type resultType = lambda.Parameters[1].Type;
IList list = rootCriteria.List();
MethodInfo castMethod = typeof(Enumerable).GetMethod("Cast").MakeGenericMethod(resultType);
IEnumerable enumerable = (IEnumerable)castMethod.Invoke(null, new object[] { list });
if (call.Arguments.Count == 2)
{
return (T)call.Method.Invoke(null, new object[]
{
enumerable.AsQueryable(),
lambda
});
}
else if (call.Arguments.Count == 3)
{
return (T)call.Method.Invoke(null, new[]
{
enumerable.AsQueryable(),
((ConstantExpression)call.Arguments[1]).Value,
lambda
});
}
else if (call.Arguments.Count == 4)
{
return (T)call.Method.Invoke(null, new[]
{
enumerable.AsQueryable(),
((ConstantExpression)call.Arguments[1]).Value,
LinqUtil.StripQuotes(call.Arguments[2]),
lambda
});
}
throw new ArgumentException("Invalid number of arguments passed to the Aggregate method.");
}
private T HandleAggregateCall(MethodCallExpression call)
{
var visitor = new SelectArgumentsVisitor(rootCriteria, session);
visitor.Visit(call);
T value = default(T);
if (visitor.Projection != null)
{
object result = rootCriteria.SetProjection(visitor.Projection).UniqueResult();
if (result != null)
{
value = (T)LinqUtil.ChangeType(result, typeof(T));
}
}
return value;
}
private T HandleAnyCall(MethodCallExpression call)
{
rootCriteria.SetProjection(Projections.RowCount());
if (call.Arguments.Count > 1)
rootCriteria.Add(WhereArgumentsVisitor.GetCriterion(rootCriteria, session, call.Arguments[1]));
int count = (int)rootCriteria.UniqueResult();
//HACK: the Any method always returns bool - maybe need to make this class non-generic
return (T)(object)(count > 0);
}
}
}

View File

@ -0,0 +1,26 @@
using System.Linq.Expressions;
using NHibernate.Linq.Expressions;
namespace NHibernate.Linq.Visitors
{
public class InheritanceVisitor : NHibernateExpressionVisitor
{
private System.Type castedType;
protected override Expression VisitQuerySource(QuerySourceExpression expr)
{
if (castedType != null)
return new QuerySourceExpression(expr.Alias, expr.Query, castedType);
return base.VisitQuerySource(expr);
}
protected override Expression VisitMethodCall(MethodCallExpression m)
{
//this is a naive implementation and will not work for any OfType calls not called on root entity
if (m.Method.Name == "OfType")
this.castedType = m.Method.GetGenericArguments()[0];
return base.VisitMethodCall(m);
}
}
}

View File

@ -0,0 +1,153 @@
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using NHibernate.Linq.Expressions;
using NHibernate.Linq.Util;
using NHibernate.SqlCommand;
namespace NHibernate.Linq.Visitors
{
/// <summary>
/// Visits an expression providing the member name being accessed based on the EntityExpressions and
/// PropertyAccessExpressions in the expression tree. Any entity associations encountered are added
/// as subcriteria to the query.
/// </summary>
public class MemberNameVisitor : NHibernateExpressionVisitor
{
private readonly ICriteria rootCriteria;
private readonly bool createCriteriaForCollections;
private ICriteria currentCriteria;
private Expression currentExpression;
private StringBuilder memberNameBuilder;
private string currentAssociationPath;
private bool isQueringEntity;
public string MemberName
{
get
{
if (isQueringEntity || memberNameBuilder.Length < 1)
return currentAssociationPath;
string memberName = memberNameBuilder.ToString();
return memberName.Substring(0, memberName.Length - 1); //remove the last "."
}
}
public ICriteria CurrentCriteria
{
get { return currentCriteria; }
}
public Expression CurrentExpression
{
get { return currentExpression; }
}
public MemberNameVisitor(ICriteria criteria)
: this(criteria, false) { }
public MemberNameVisitor(ICriteria criteria, bool createCriteriaForCollections)
{
this.rootCriteria = this.currentCriteria = criteria;
this.createCriteriaForCollections = createCriteriaForCollections;
this.memberNameBuilder = new StringBuilder();
}
private void ResetMemberName(string name)
{
memberNameBuilder = new StringBuilder();
memberNameBuilder.Append(name);
}
private ICriteria EnsureCriteria(string associationPath, string alias)
{
ICriteria criteria;
if ((criteria = currentCriteria.GetCriteriaByAlias(alias)) == null)
{
criteria = currentCriteria.CreateCriteria(associationPath, alias, JoinType.LeftOuterJoin);
}
return criteria;
}
private bool IsRootEntity(EntityExpression expr)
{
var nhExpression = expr.Expression as NHibernateExpression;
if (nhExpression != null)
return false;
if (expr.Type != rootCriteria.GetRootType())
return false;
return true;
}
protected override Expression VisitEntity(EntityExpression expr)
{
expr = (EntityExpression)base.VisitEntity(expr);
if (currentCriteria.GetCriteriaByAlias(expr.Alias) != null || !IsRootEntity(expr))
{
if (!String.IsNullOrEmpty(expr.AssociationPath))
currentCriteria = EnsureCriteria(expr.AssociationPath, expr.Alias);
ResetMemberName(expr.Alias + ".");
}
currentAssociationPath = expr.AssociationPath;
currentExpression = expr;
isQueringEntity = true;
return expr;
}
protected override Expression VisitPropertyAccess(PropertyAccessExpression expr)
{
expr = (PropertyAccessExpression)base.VisitPropertyAccess(expr);
memberNameBuilder.Append(expr.Name + ".");
currentExpression = expr;
isQueringEntity = false;
return expr;
}
protected override Expression VisitCollectionAccess(CollectionAccessExpression expr)
{
expr = (CollectionAccessExpression)base.VisitCollectionAccess(expr);
//memberNameBuilder.Append(expr.Name + ".");
ResetMemberName(expr.Name + ".");
currentExpression = expr;
if (createCriteriaForCollections)
{
if (expr.ElementExpression != null)
{
currentCriteria = EnsureCriteria(expr.Name, expr.ElementExpression.Alias);
}
}
isQueringEntity = false;
return expr;
}
protected override Expression VisitMethodCall(MethodCallExpression expr)
{
Visit(expr.Arguments[0]);
return expr;
}
public static string GetMemberName(ICriteria rootCriteria, Expression expr)
{
MemberNameVisitor visitor = new MemberNameVisitor(rootCriteria);
visitor.Visit(expr);
return visitor.MemberName;
}
public static string GetLastMemberName(ICriteria rootCriteria, Expression expr)
{
return GetMemberName(rootCriteria, expr).Split('.').Last();
}
}
}

View File

@ -0,0 +1,68 @@
using System.Diagnostics;
using System.Linq.Expressions;
using NHibernate.Linq.Expressions;
namespace NHibernate.Linq.Visitors
{
/// <summary>
/// NHibernate-specific base expression visitor.
/// </summary>
[DebuggerStepThrough, DebuggerNonUserCode]
public class NHibernateExpressionVisitor : ExpressionVisitor
{
public override Expression Visit(Expression exp)
{
if (exp == null) return null;
switch ((NHibernateExpressionType)exp.NodeType)
{
case NHibernateExpressionType.QuerySource:
return VisitQuerySource((QuerySourceExpression)exp);
case NHibernateExpressionType.RootEntity:
case NHibernateExpressionType.Entity:
return VisitEntity((EntityExpression)exp);
case NHibernateExpressionType.PropertyAccess:
return VisitPropertyAccess((PropertyAccessExpression)exp);
case NHibernateExpressionType.CollectionAccess:
return VisitCollectionAccess((CollectionAccessExpression)exp);
default:
return base.Visit(exp);
}
}
protected virtual Expression VisitQuerySource(QuerySourceExpression expr)
{
return expr;
}
protected virtual Expression VisitEntity(EntityExpression expr)
{
Expression e = Visit(expr.Expression);
if (e != expr.Expression)
{
return new EntityExpression(expr.AssociationPath, expr.Alias, expr.Type, expr.MetaData, e);
}
return expr;
}
protected virtual Expression VisitPropertyAccess(PropertyAccessExpression expr)
{
EntityExpression e = (EntityExpression)Visit(expr.Expression);
if (e != expr.Expression)
{
return new PropertyAccessExpression(expr.Name, expr.Type, expr.NHibernateType, e);
}
return expr;
}
protected virtual Expression VisitCollectionAccess(CollectionAccessExpression expr)
{
EntityExpression e = (EntityExpression)Visit(expr.Expression);
if (e != expr.Expression)
{
return new CollectionAccessExpression(expr.Name, expr.Type, expr.NHibernateType, e, expr.ElementExpression);
}
return expr;
}
}
}

View File

@ -0,0 +1,57 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq.Expressions;
using NHibernate.Criterion;
using NHibernate.Linq.Expressions;
using NHibernate.Linq.Util;
using LinqExpression = System.Linq.Expressions.Expression;
namespace NHibernate.Linq.Visitors
{
/// <summary>
/// Translates a Linq Expression into an NHibernate ICriteria object.
/// </summary>
public class NHibernateQueryTranslator : NHibernateExpressionVisitor
{
private readonly ISession session;
private readonly string entityName;
private ICriteria rootCriteria;
private QueryOptions options;
public NHibernateQueryTranslator(ISession session)
{
this.session = session;
}
public NHibernateQueryTranslator(ISession session,string entityName)
{
this.session = session;
this.entityName = entityName;
}
public virtual object Translate(LinqExpression expression, QueryOptions queryOptions)
{
this.rootCriteria = null;
this.options = queryOptions;
Visit(expression); //ensure criteria
var visitor = new RootVisitor(rootCriteria, session, true);
visitor.Visit(expression);
return visitor.Results;
}
protected override LinqExpression VisitQuerySource(QuerySourceExpression expr)
{
if (rootCriteria == null)
{
if(!string.IsNullOrEmpty(this.entityName))
rootCriteria = session.CreateCriteria(entityName, expr.Alias);
else
rootCriteria = session.CreateCriteria(expr.ElementType, expr.Alias);
options.Execute(rootCriteria);
}
return expr;
}
}
}

View File

@ -0,0 +1,46 @@
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using NHibernate.Linq.Expressions;
namespace NHibernate.Linq.Visitors
{
/// <summary>
/// Converts calls to an IEnumerable.Count property to IEnumerable.Count() extension method.
/// </summary>
public class PropertyToMethodVisitor : NHibernateExpressionVisitor
{
private MethodInfo GetCountMethod(System.Type elementType)
{
//this should work, but it doesn't...
//System.Type genericEnumerableType = typeof(IEnumerable<>).MakeGenericType(elementType);
//return typeof(Enumerable).GetMethod("Count", new System.Type[] { genericEnumerableType });
var method = typeof(Enumerable).GetMethods()
.Where(m => m.Name == "Count" && m.GetParameters().Count() == 1).First();
return method.MakeGenericMethod(elementType);
}
private Expression CastIfNecessary(CollectionAccessExpression expr)
{
if (expr.Type.IsGenericType)
return expr;
MethodInfo method = typeof(Enumerable).GetMethod("Cast")
.MakeGenericMethod(expr.ElementExpression.Type);
return Expression.Call(method, expr);
}
protected override Expression VisitMemberAccess(MemberExpression m)
{
CollectionAccessExpression parent = m.Expression as CollectionAccessExpression;
if (parent != null && m.Member.Name == "Count")
{
MethodInfo method = GetCountMethod(parent.ElementExpression.Type);
return Expression.Call(method, CastIfNecessary(parent));
}
return base.VisitMemberAccess(m);
}
}
}

View File

@ -0,0 +1,210 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq.Expressions;
using NHibernate.Criterion;
using NHibernate.Linq.Expressions;
using NHibernate.Linq.Util;
using LinqExpression = System.Linq.Expressions.Expression;
namespace NHibernate.Linq.Visitors
{
/// <summary>
/// Translates a Linq Expression into an NHibernate ICriteria object.
/// </summary>
public class RootVisitor : NHibernateExpressionVisitor
{
private readonly ICriteria rootCriteria;
private readonly ISession session;
private readonly bool isAtRoot;
private object results;
private bool hasResults;
public object Results
{
get
{
if (hasResults)
return results;
return rootCriteria;
}
}
public RootVisitor(ICriteria rootCriteria, ISession session, bool isAtRoot)
{
this.rootCriteria = rootCriteria;
this.session = session;
this.isAtRoot = isAtRoot;
}
protected override LinqExpression VisitMethodCall(MethodCallExpression expr)
{
Visit(expr.Arguments[0]);
switch (expr.Method.Name)
{
case "Where":
HandleWhereCall(expr);
break;
case "Select":
HandleSelectCall(expr);
break;
case "OrderBy":
case "ThenBy":
HandleOrderByCall(expr);
break;
case "OrderByDescending":
case "ThenByDescending":
HandleOrderByDescendingCall(expr);
break;
case "Take":
HandleTakeCall(expr);
break;
case "Skip":
HandleSkipCall(expr);
break;
case "Distinct":
HandleDistinctCall(expr);
break;
case "GroupBy":
HandleGroupByCall(expr);
break;
case "SelectMany":
HandleSelectManyCall(expr);
break;
case "OfType":
case "Cast":
//ignore OfType calls -- handled by InheritanceVisitor
break;
case "First":
case "FirstOrDefault":
case "Single":
case "SingleOrDefault":
case "Aggregate":
case "Average":
case "Count":
case "LongCount":
case "Max":
case "Min":
case "Sum":
case "Any":
if (isAtRoot)
HandleImmediateResultsCall(expr);
else
HandleSelectMethodCall(expr);
break;
default:
throw new NotImplementedException("The method " + expr.Method.Name + " is not implemented.");
}
return expr;
}
private void HandleWhereCall(MethodCallExpression call)
{
IEnumerable<ICriterion> criterion = WhereArgumentsVisitor.GetCriterion(rootCriteria, session, call.Arguments[1]);
rootCriteria.Add(criterion);
}
private void HandleSelectCall(MethodCallExpression call)
{
var lambda = (LambdaExpression)LinqUtil.StripQuotes(call.Arguments[1]);
var visitor = new SelectArgumentsVisitor(rootCriteria, session);
visitor.Visit(lambda.Body);
rootCriteria.SetProjectionIfNotNull(visitor.Projection);
rootCriteria.SetResultTransformerIfNotNull(visitor.Transformer);
}
private void HandleOrderByCall(MethodCallExpression call)
{
LinqExpression expr = ((UnaryExpression)call.Arguments[1]).Operand;
string name = MemberNameVisitor.GetMemberName(rootCriteria, expr);
rootCriteria.AddOrder(Order.Asc(name));
}
private void HandleOrderByDescendingCall(MethodCallExpression call)
{
LinqExpression expr = ((UnaryExpression)call.Arguments[1]).Operand;
string name = MemberNameVisitor.GetMemberName(rootCriteria, expr);
rootCriteria.AddOrder(Order.Desc(name));
}
private void HandleTakeCall(MethodCallExpression call)
{
var count = (int)((ConstantExpression)call.Arguments[1]).Value;
rootCriteria.SetMaxResults(count);
}
private void HandleSkipCall(MethodCallExpression call)
{
var index = (int)((ConstantExpression)call.Arguments[1]).Value;
rootCriteria.SetFirstResult(index);
}
private void HandleDistinctCall(MethodCallExpression call)
{
var projection = rootCriteria.GetProjection() as PropertyProjection;
if (projection != null)
{
rootCriteria.SetProjection(
Projections.Distinct(
Projections.Property(projection.PropertyName)));
}
}
private void HandleGroupByCall(MethodCallExpression call)
{
var visitor = new GroupingArgumentsVisitor(rootCriteria);
visitor.Visit(call.Arguments[1]);
rootCriteria.SetProjectionIfNotNull(visitor.Projection);
}
private void HandleSelectManyCall(MethodCallExpression call)
{
//get the association path for the joined entity
var collectionSelector = (LambdaExpression)LinqUtil.StripQuotes(call.Arguments[1]);
LambdaExpression resultSelector = null;
if (call.Arguments.Count == 3)
{
resultSelector = (LambdaExpression)LinqUtil.StripQuotes(call.Arguments[2]);
string alias = resultSelector.Parameters[1].Name;
var visitor = new SelectManyVisitor(rootCriteria, alias);
visitor.Visit(collectionSelector.Body);
}
if (resultSelector != null)
{
//visit the result selector expression after the alias for the association has been created
var resultSelectorVisitor = new SelectArgumentsVisitor(rootCriteria, session);
resultSelectorVisitor.Visit(resultSelector.Body);
rootCriteria.SetProjectionIfNotNull(resultSelectorVisitor.Projection);
rootCriteria.SetResultTransformerIfNotNull(resultSelectorVisitor.Transformer);
}
}
private void HandleImmediateResultsCall(MethodCallExpression call)
{
System.Type resultType = call.Method.ReturnType;
System.Type visitorType = typeof(ImmediateResultsVisitor<>)
.MakeGenericType(resultType);
var visitor = (IImmediateResultsVisitor)Activator
.CreateInstance(visitorType, session, rootCriteria);
hasResults = true;
results = visitor.GetResults(call);
}
private void HandleSelectMethodCall(MethodCallExpression call)
{
throw new NotImplementedException();
}
}
}

View File

@ -0,0 +1,361 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using NHibernate.Criterion;
using NHibernate.Dialect.Function;
using NHibernate.Linq.Expressions;
using NHibernate.Linq.Transform;
using NHibernate.Linq.Util;
using NHibernate.Transform;
using NHibernate.Type;
using Expression = System.Linq.Expressions.Expression;
using NHProjections = NHibernate.Criterion.Projections;
namespace NHibernate.Linq.Visitors
{
/// <summary>
/// Provides the appropriate NHibernate selection projections and/or IResultTransformers
/// based on a given expression tree.
/// </summary>
public class SelectArgumentsVisitor : NHibernateExpressionVisitor
{
#region Fields & Properties
private static readonly ISQLFunction arithmaticAddition = new VarArgsSQLFunction("(", "+", ")");
private static readonly ISQLFunction arithmaticDivide = new VarArgsSQLFunction("(", "/", ")");
private static readonly ISQLFunction arithmaticMultiply = new VarArgsSQLFunction("(", "*", ")");
private static readonly ISQLFunction arithmaticSubstract = new VarArgsSQLFunction("(", "-", ")");
private readonly ICriteria _rootCriteria;
private readonly ISession _session;
private readonly List<IProjection> _projections;
private IResultTransformer _transformer;
private ICriteriaQuery _criteriaQuery;
public IProjection Projection
{
get
{
if (_projections.Count == 0)
return null;
if (_projections.Count == 1)
return _projections[0];
ProjectionList list = NHProjections.ProjectionList();
foreach (var projection in _projections)
list.Add(projection);
return list;
}
}
public IResultTransformer Transformer
{
get { return _transformer; }
}
private ICriteriaQuery CriteriaQuery
{
get
{
if (_criteriaQuery == null)
_criteriaQuery = _rootCriteria.GenerateCriteriaQuery(_session.SessionFactory, _rootCriteria.GetEntityOrClassName());
return _criteriaQuery;
}
}
#endregion
public SelectArgumentsVisitor(ICriteria rootCriteria, ISession session)
{
_rootCriteria = rootCriteria;
_session = session;
_projections = new List<IProjection>();
}
public static bool SupportsMethod(string methodName)
{
return "Average".Equals(methodName)
|| "Count".Equals(methodName)
|| "LongCount".Equals(methodName)
|| "Max".Equals(methodName)
|| "Min".Equals(methodName)
|| "Sum".Equals(methodName);
}
protected override Expression VisitMethodCall(MethodCallExpression expr)
{
if (WhereArgumentsVisitor.SupportsMethod(expr.Method.Name))
return VisitCriterionExpression(expr);
//TODO: this needs to be refactored...
//create any collection subcriteria and get the collection access expression
MemberNameVisitor memberVisitor = new MemberNameVisitor(_rootCriteria, true);
memberVisitor.Visit(expr.Arguments[0]);
CollectionAccessExpression collectionExpr = (CollectionAccessExpression)memberVisitor.CurrentExpression;
string propertyName = null;
IProjection projection = null;
PropertyProjection currentProjection;
if (expr.Arguments.Count > 1)
{
propertyName = MemberNameVisitor.GetMemberName(_rootCriteria, expr.Arguments[1]);
}
else if ((currentProjection = _rootCriteria.GetProjection() as PropertyProjection) != null)
{
propertyName = currentProjection.PropertyName;
}
switch (expr.Method.Name)
{
case "Average":
projection = NHProjections.Avg(propertyName);
break;
case "Count":
case "LongCount":
if (expr.Arguments.Count > 1)
_rootCriteria.Add(WhereArgumentsVisitor.GetCriterion(_rootCriteria, _session, expr.Arguments[1]));
if (collectionExpr != null)
{
//get count on collection element's identifier property
propertyName = memberVisitor.MemberName + "." + collectionExpr.ElementExpression.MetaData.IdentifierPropertyName;
projection = NHProjections.Count(propertyName);
}
else
{
projection = NHProjections.RowCount();
}
break;
case "Max":
projection = NHProjections.Max(propertyName);
break;
case "Min":
projection = NHProjections.Min(propertyName);
break;
case "Sum":
projection = NHProjections.Sum(propertyName);
break;
default:
throw new NotImplementedException("The method '" + expr.Method.Name + "' is not implemented.");
}
_projections.Add(projection);
return expr;
}
protected override Expression VisitConstant(ConstantExpression expr)
{
_projections.Add(new ConstantProjection(expr.Value));
return expr;
}
protected override NewExpression VisitNew(NewExpression expr)
{
NewExpression newExpr = base.VisitNew(expr);
_transformer = new TypeSafeConstructorMemberInitResultTransformer(expr);
var aggregators = expr.Arguments.Where(arg => arg is MethodCallExpression && SupportsMethod(((MethodCallExpression)arg).Method.Name));
if (aggregators.Any())
{
foreach (var exp in expr.Arguments.Except(aggregators))
{
string propertyName = MemberNameVisitor.GetMemberName(_rootCriteria, exp);
if (!String.IsNullOrEmpty(propertyName))
{
_projections.Add(NHProjections.GroupProperty(propertyName));
}
}
}
return newExpr;
}
protected override Expression VisitMemberInit(MemberInitExpression expr)
{
Expression newExpr = base.VisitMemberInit(expr);
_transformer = new TypeSafeConstructorMemberInitResultTransformer(expr);
return newExpr;
}
protected override Expression VisitConditional(ConditionalExpression expr)
{
var visitorTrue = new SelectArgumentsVisitor(_rootCriteria, _session);
visitorTrue.Visit(expr.IfTrue);
var visitorFalse = new SelectArgumentsVisitor(_rootCriteria, _session);
visitorFalse.Visit(expr.IfFalse);
var visitorCondition = new WhereArgumentsVisitor(_rootCriteria, _session);
visitorCondition.Visit(expr.Test);
Conjunction conjunction = NHibernate.Criterion.Expression.Conjunction();
foreach (var criterion in visitorCondition.CurrentCriterions)
{
conjunction.Add(criterion);
}
_projections.Add(
NHibernate.Criterion.Projections
.Conditional(conjunction,
visitorTrue.Projection,
visitorFalse.Projection)
);
return expr;
}
protected override Expression VisitBinary(BinaryExpression expr)
{
switch (expr.NodeType)
{
case ExpressionType.Add:
VisitAddExpression(expr);
break;
case ExpressionType.Divide:
VisitDivideExpression(expr);
break;
case ExpressionType.Multiply:
VisitMultiplyExpression(expr);
break;
case ExpressionType.Subtract:
VisitSubtractExpression(expr);
break;
default:
return VisitCriterionExpression(expr);
}
return expr;
}
private Expression VisitCriterionExpression(Expression expr)
{
IEnumerable<ICriterion> criterion = WhereArgumentsVisitor.GetCriterion(_rootCriteria, _session, expr);
if (criterion.Count() > 0)
{
Conjunction conjunction = new Conjunction();
foreach (ICriterion crit in criterion)
{
conjunction.Add(crit);
}
_projections.Add(Projections.Conditional(
conjunction,
Projections.Constant(true),
Projections.Constant(false))
);
}
return expr;
}
#region Arithmetic
private void VisitAddExpression(BinaryExpression expr)
{
var leftVisitor = new SelectArgumentsVisitor(_rootCriteria, _session);
var rightVisitor = new SelectArgumentsVisitor(_rootCriteria, _session);
leftVisitor.Visit(expr.Left);
rightVisitor.Visit(expr.Right);
var joinedProjections = new List<IProjection>();
joinedProjections.AddRange(leftVisitor._projections);
joinedProjections.AddRange(rightVisitor._projections);
IType[] types = joinedProjections[0].GetTypes(_rootCriteria, CriteriaQuery);
var useConcat = types[0] is AbstractStringType;
SqlFunctionProjection projection;
if (useConcat)
{
projection = new SqlFunctionProjection("concat", types[0], joinedProjections.ToArray());
}
else
{
projection = new SqlFunctionProjection(arithmaticAddition, types[0], joinedProjections.ToArray());
}
_projections.Add(projection);
}
private void VisitMultiplyExpression(BinaryExpression expr)
{
VisitAritmaticOperation(expr, arithmaticMultiply);
}
private void VisitSubtractExpression(BinaryExpression expr)
{
VisitAritmaticOperation(expr, arithmaticSubstract);
}
private void VisitDivideExpression(BinaryExpression expr)
{
VisitAritmaticOperation(expr, arithmaticDivide);
}
private void VisitAritmaticOperation(BinaryExpression expr, ISQLFunction arithmaticOperation)
{
var leftVisitor = new SelectArgumentsVisitor(_rootCriteria, _session);
var rightVisitor = new SelectArgumentsVisitor(_rootCriteria, _session);
leftVisitor.Visit(expr.Left);
rightVisitor.Visit(expr.Right);
var joinedProjections = new List<IProjection>();
joinedProjections.AddRange(leftVisitor._projections);
joinedProjections.AddRange(rightVisitor._projections);
var types = joinedProjections[0].GetTypes(_rootCriteria, CriteriaQuery);
var projection = new SqlFunctionProjection(arithmaticOperation, types[0], joinedProjections.ToArray());
_projections.Add(projection);
}
#endregion
protected override Expression VisitUnary(UnaryExpression expr)
{
if (expr.NodeType == ExpressionType.Convert)
{
var visitor = new SelectArgumentsVisitor(_rootCriteria, _session);
visitor.Visit(expr.Operand);
ProjectionList list = NHProjections.ProjectionList();
foreach (IProjection proj in visitor._projections)
list.Add(proj);
var projection = new CastProjection(NHibernateUtil.GuessType(expr.Type), list);
_projections.Add(projection);
}
return expr;
}
protected override Expression VisitEntity(EntityExpression expr)
{
if (_rootCriteria.GetCriteriaByAlias(expr.Alias) != null)
{
_transformer = new LinqJoinResultsTransformer(expr.Type);
}
return expr;
}
protected override Expression VisitMemberAccess(MemberExpression expr)
{
//this must be a grouping parameter member access
IProjection projection = _rootCriteria.GetProjection();
if (projection != null && projection.IsGrouped)
{
_projections.Add(NHProjections.Alias(projection, expr.Member.Name));
}
return expr;
}
protected override Expression VisitPropertyAccess(PropertyAccessExpression expr)
{
string memberName = MemberNameVisitor.GetMemberName(_rootCriteria, expr);
_projections.Add(NHProjections.Property(memberName));
return expr;
}
}
}

View File

@ -0,0 +1,36 @@
using System.Linq.Expressions;
using NHibernate.Linq.Expressions;
using NHibernate.SqlCommand;
namespace NHibernate.Linq.Visitors
{
/// <summary>
/// Adds the appropriate subcriteria to the query based on a SelectMany expression tree.
/// </summary>
public class SelectManyVisitor : NHibernateExpressionVisitor
{
private readonly ICriteria _rootCriteria;
private readonly string _alias;
public SelectManyVisitor(ICriteria criteria, string alias)
{
_rootCriteria = criteria;
_alias = alias;
}
protected override Expression VisitCollectionAccess(CollectionAccessExpression expr)
{
MemberNameVisitor visitor = new MemberNameVisitor(_rootCriteria, false);
visitor.Visit(expr.Expression);
visitor.CurrentCriteria.CreateCriteria(expr.Name, _alias, JoinType.LeftOuterJoin);
return expr;
}
protected override Expression VisitMethodCall(MethodCallExpression expr)
{
Visit(expr.Arguments[0]);
return expr;
}
}
}

View File

@ -0,0 +1,370 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq.Expressions;
using NHibernate.Criterion;
using NHibernate.Linq.Expressions;
using NHibernate.Linq.Util;
using NHibernate.Persister.Entity;
using Expression = System.Linq.Expressions.Expression;
namespace NHibernate.Linq.Visitors
{
/// <summary>
/// Provides ICriterion for a query given a Linq expression tree.
/// </summary>
public class WhereArgumentsVisitor : NHibernateExpressionVisitor
{
private readonly Stack<IList<ICriterion>> criterionStack = new Stack<IList<ICriterion>>();
private readonly ICriteria rootCriteria;
private readonly ISession session;
public WhereArgumentsVisitor(ICriteria rootCriteria, ISession session)
{
criterionStack.Push(new List<ICriterion>());
this.rootCriteria = rootCriteria;
this.session = session;
}
public static IEnumerable<ICriterion> GetCriterion(ICriteria rootCriteria, ISession session, Expression expression)
{
var visitor = new WhereArgumentsVisitor(rootCriteria, session);
visitor.Visit(expression);
return visitor.CurrentCriterions;
}
/// <summary>
/// Gets the current collection of <see cref="T:NHibernate.Criterion.ICriterion"/> objects.
/// </summary>
public IList<ICriterion> CurrentCriterions
{
get { return criterionStack.Peek(); }
}
public static bool SupportsMethod(string methodName)
{
return "Any".Equals(methodName)
|| "StartsWith".Equals(methodName)
|| "EndsWith".Equals(methodName)
|| "Contains".Equals(methodName)
|| "Equals".Equals(methodName);
}
protected override Expression VisitConstant(ConstantExpression expr)
{
if (expr.Type == typeof(bool))
{
bool value = (bool)expr.Value;
var falseCriteria = NHibernate.Criterion.Expression.Sql("1=0");
var trueCriteria = NHibernate.Criterion.Expression.Sql("1=1");
if (value)
{
this.CurrentCriterions.Add(trueCriteria);
}
else
{
this.CurrentCriterions.Add(falseCriteria);
}
return expr;
}
if (expr.Type.IsSubclassOf(typeof(Expression)))
return Visit(expr.Value as Expression);
return base.VisitConstant(expr);
}
protected override Expression VisitMethodCall(MethodCallExpression expr)
{
switch (expr.Method.Name)
{
case "Any":
CurrentCriterions.Add(GetExistsCriteria(expr));
break;
case "StartsWith":
CurrentCriterions.Add(GetLikeCriteria(expr, MatchMode.Start));
break;
case "EndsWith":
CurrentCriterions.Add(GetLikeCriteria(expr, MatchMode.End));
break;
case "Contains":
if (expr.Object == null)
{
if (expr.Arguments[0] is ConstantExpression)
{
CurrentCriterions.Add(GetCollectionContainsCriteria(expr.Arguments[0], expr.Arguments[1]));
}
else if (expr.Arguments[0] is CollectionAccessExpression)
{
CurrentCriterions.Add(GetCollectionContainsCriteria((CollectionAccessExpression)expr.Arguments[0], expr.Arguments[1]));
}
else
{
//myArray.Contains(user.Name)
CurrentCriterions.Add(GetCollectionContainsCriteria(expr.Arguments[0], expr.Arguments[1]));
}
}
else if (expr.Object is ConstantExpression)
{
//myList.Contains(user.Name)
CurrentCriterions.Add(GetCollectionContainsCriteria(expr.Object, expr.Arguments[0]));
}
else if (expr.Object is CollectionAccessExpression)
{
//timesheet.Entries.Contains(entry)
CurrentCriterions.Add(GetCollectionContainsCriteria((CollectionAccessExpression)expr.Object, expr.Arguments[0]));
}
else
{
//user.Name.Contains(partialName)
CurrentCriterions.Add(GetLikeCriteria(expr, MatchMode.Anywhere));
}
break;
case "Equals":
VisitBinaryCriterionExpression(Expression.Equal(expr.Object, expr.Arguments[0]));
break;
}
return expr;
}
protected override Expression VisitTypeIs(TypeBinaryExpression expr)
{
var visitor = new MemberNameVisitor(rootCriteria);
visitor.Visit(expr);
string memberName = visitor.MemberName + ".class";
var metaData = session.SessionFactory.GetClassMetadata(expr.TypeOperand);
if (metaData.HasSubclasses)
{
//make sure to include any subtypes
var disjunction = new Disjunction();
foreach (string entityName in ((IEntityPersister)metaData).EntityMetamodel.SubclassEntityNames)
{
var metadata = session.SessionFactory.GetClassMetadata(entityName);
disjunction.Add(Property.ForName(memberName).Eq(metadata.MappedClass));
}
visitor.CurrentCriteria.Add(disjunction);
}
else
{
visitor.CurrentCriteria.Add(Property.ForName(memberName).Eq(expr.TypeOperand));
}
return expr;
}
protected override Expression VisitBinary(BinaryExpression expr)
{
switch (expr.NodeType)
{
case ExpressionType.AndAlso:
VisitAndAlsoExpression(expr);
break;
case ExpressionType.OrElse:
VisitOrElseExpression(expr);
break;
default:
VisitBinaryCriterionExpression(expr);
break;
}
return expr;
}
private void VisitAndAlsoExpression(BinaryExpression expr)
{
criterionStack.Push(new List<ICriterion>());
Visit(expr.Left);
Visit(expr.Right);
var ands = criterionStack.Pop();
var conjunction = new Conjunction();
foreach (var crit in ands)
conjunction.Add(crit);
CurrentCriterions.Add(conjunction);
}
private void VisitOrElseExpression(BinaryExpression expr)
{
criterionStack.Push(new List<ICriterion>());
Visit(expr.Left);
Visit(expr.Right);
IList<ICriterion> ors = criterionStack.Pop();
var disjunction = new Disjunction();
foreach (ICriterion crit in ors)
{
disjunction.Add(crit);
}
CurrentCriterions.Add(disjunction);
}
private void VisitBinaryCriterionExpression(BinaryExpression expr)
{
ComparePropToValue comparePropToValue = null;
ComparePropToProp comparePropToProp = null;
CompareValueToCriteria compareValueToCriteria = null;
ComparePropToCriteria comparePropToCriteria = null;
switch (expr.NodeType)
{
case ExpressionType.Equal:
comparePropToValue = (n, v) => (v != null) ? Restrictions.Eq(n, v) : Restrictions.IsNull(n);
comparePropToProp = Restrictions.EqProperty;
compareValueToCriteria = Subqueries.Eq;
comparePropToCriteria = Subqueries.PropertyEq;
break;
case ExpressionType.GreaterThan:
comparePropToValue = Restrictions.Gt;
comparePropToProp = Restrictions.GtProperty;
compareValueToCriteria = Subqueries.Gt;
comparePropToCriteria = Subqueries.PropertyGt;
break;
case ExpressionType.GreaterThanOrEqual:
comparePropToValue = Restrictions.Ge;
comparePropToProp = Restrictions.GeProperty;
compareValueToCriteria = Subqueries.Ge;
comparePropToCriteria = Subqueries.PropertyGe;
break;
case ExpressionType.LessThan:
comparePropToValue = Restrictions.Lt;
comparePropToProp = Restrictions.LtProperty;
compareValueToCriteria = Subqueries.Lt;
comparePropToCriteria = Subqueries.PropertyLt;
break;
case ExpressionType.LessThanOrEqual:
comparePropToValue = Restrictions.Le;
comparePropToProp = Restrictions.LeProperty;
compareValueToCriteria = Subqueries.Le;
comparePropToCriteria = Subqueries.PropertyLe;
break;
case ExpressionType.NotEqual:
comparePropToValue = (n, v) => (v != null) ? Restrictions.Not(Restrictions.Eq(n, v)) : Restrictions.IsNotNull(n);
comparePropToProp = Restrictions.NotEqProperty;
compareValueToCriteria = Subqueries.Ne;
comparePropToCriteria = Subqueries.PropertyNe;
break;
}
CurrentCriterions.Add(
BinaryCriterionVisitor.GetBinaryCriteria(rootCriteria, session,
expr, comparePropToValue, comparePropToProp,
compareValueToCriteria, comparePropToCriteria));
}
protected override Expression VisitUnary(UnaryExpression expr)
{
switch (expr.NodeType)
{
case ExpressionType.Quote:
Visit(expr.Operand);
break;
case ExpressionType.Not:
VisitNotExpression(expr);
break;
}
return expr;
}
private void VisitNotExpression(UnaryExpression expr)
{
var criterions = GetCriterion(rootCriteria, session, expr.Operand);
Conjunction conjunction = Restrictions.Conjunction();
foreach (var criterion in criterions)
conjunction.Add(criterion);
CurrentCriterions.Add(Restrictions.Not(conjunction));
}
protected override Expression VisitPropertyAccess(PropertyAccessExpression expr)
{
if (expr.Type == typeof(bool))
{
string name = MemberNameVisitor.GetMemberName(rootCriteria, expr);
CurrentCriterions.Add(Restrictions.Eq(name, true));
}
return expr;
}
private ICriterion GetExistsCriteria(MethodCallExpression expr)
{
EntityExpression rootEntity = EntityExpressionVisitor.FirstEntity(expr);
string propertyName = MemberNameVisitor.GetMemberName(rootCriteria, expr);
DetachedCriteria query = DetachedCriteria.For(rootEntity.Type)
.SetProjection(Projections.Id())
.Add(Restrictions.IsNotEmpty(propertyName));
if (expr.Arguments.Count > 1)
{
var arg = (LambdaExpression)LinqUtil.StripQuotes(expr.Arguments[1]);
string alias = arg.Parameters[0].Name;
DetachedCriteria subquery = query.CreateCriteria(propertyName, alias);
var temp = new WhereArgumentsVisitor(subquery.Adapt(session), session);
temp.Visit(arg.Body);
foreach (ICriterion c in temp.CurrentCriterions)
{
subquery.Add(c);
}
}
string identifierName = rootEntity.GetAliasedIdentifierPropertyName();
return Subqueries.PropertyIn(identifierName, query);
}
private ICriterion GetLikeCriteria(MethodCallExpression expr, MatchMode matchMode)
{
return Restrictions.Like(MemberNameVisitor.GetMemberName(rootCriteria, expr.Object),
String.Format("{0}", QueryUtil.GetExpressionValue(expr.Arguments[0])),
matchMode);
}
private ICriterion GetCollectionContainsCriteria(CollectionAccessExpression arg, Expression containsExpression)
{
EntityExpression rootEntity = EntityExpressionVisitor.FirstEntity(arg);
DetachedCriteria query = DetachedCriteria.For(rootEntity.Type)
.SetProjection(Projections.Id());
var visitor = new MemberNameVisitor(query.Adapt(session), true);
visitor.Visit(arg);
//TODO: this won't work for collections of values
var containedEntity = QueryUtil.GetExpressionValue(containsExpression);
var collectionIdPropertyName = visitor.MemberName + "." + arg.ElementExpression.MetaData.IdentifierPropertyName;
var idValue = arg.ElementExpression.MetaData.GetIdentifier(containedEntity);
query.Add(Restrictions.Eq(collectionIdPropertyName, idValue));
string identifierName = rootEntity.MetaData.IdentifierPropertyName;
return Subqueries.PropertyIn(identifierName, query);
}
private ICriterion GetCollectionContainsCriteria(Expression list, Expression containedExpr)
{
var values = QueryUtil.GetExpressionValue(list) as ICollection;
if (values == null)
throw new InvalidOperationException("Expression argument must be of type ICollection.");
return Restrictions.In(MemberNameVisitor.GetMemberName(rootCriteria, containedExpr),
values);
}
}
}

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Antlr3.Runtime" version="3.5.1" targetFramework="net48" />
<package id="Iesi.Collections" version="4.0.4" targetFramework="net48" />
<package id="NHibernate" version="5.3.10" targetFramework="net48" />
<package id="Remotion.Linq" version="2.2.0" targetFramework="net48" />
<package id="Remotion.Linq.EagerFetching" version="2.2.0" targetFramework="net48" />
</packages>

6
src/NuGet.config Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<config>
<add key="repositoryPath" value="./packages" />
</config>
</configuration>

View File

@ -7,7 +7,7 @@
<add key="Orchard.Azure.Media.StorageConnectionString" value="UseDevelopmentStorage=true" />
</appSettings>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8" />
</startup>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
@ -21,15 +21,27 @@
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="NHibernate" publicKeyToken="aa95f207798dfdb4" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.0.4000" newVersion="4.0.0.4000" />
<bindingRedirect oldVersion="0.0.0.0-5.3.0.0" newVersion="5.3.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Iesi.Collections" publicKeyToken="aa95f207798dfdb4" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="4.0.0.0" />
<bindingRedirect oldVersion="0.0.0.0-4.0.0.4000" newVersion="4.0.0.4000" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-7.0.0.0" newVersion="7.0.0.0" />
<bindingRedirect oldVersion="0.0.0.0-13.0.0.0" newVersion="13.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Data.Edm" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.8.4.0" newVersion="5.8.4.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Data.Services.Client" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.8.4.0" newVersion="5.8.4.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Data.OData" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.8.4.0" newVersion="5.8.4.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>

View File

@ -10,7 +10,8 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Orchard.Azure.Tests</RootNamespace>
<AssemblyName>Orchard.Azure.Tests</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
<LangVersion>7.3</LangVersion>
<FileAlignment>512</FileAlignment>
<FileUpgradeFlags>
</FileUpgradeFlags>
@ -55,20 +56,23 @@
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<Reference Include="log4net, Version=2.0.12.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a, processorArchitecture=MSIL">
<HintPath>..\packages\log4net.2.0.12\lib\net45\log4net.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Azure.KeyVault.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Azure.KeyVault.Core.1.0.0\lib\net40\Microsoft.Azure.KeyVault.Core.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Microsoft.Data.Edm, Version=5.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Data.Edm.5.6.4\lib\net40\Microsoft.Data.Edm.dll</HintPath>
<Reference Include="Microsoft.Data.Edm, Version=5.8.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Data.Edm.5.8.4\lib\net40\Microsoft.Data.Edm.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Microsoft.Data.OData, Version=5.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Data.OData.5.6.4\lib\net40\Microsoft.Data.OData.dll</HintPath>
<Reference Include="Microsoft.Data.OData, Version=5.8.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Data.OData.5.8.4\lib\net40\Microsoft.Data.OData.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Microsoft.Data.Services.Client, Version=5.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Data.Services.Client.5.6.4\lib\net40\Microsoft.Data.Services.Client.dll</HintPath>
<Reference Include="Microsoft.Data.Services.Client, Version=5.8.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Data.Services.Client.5.8.4\lib\net40\Microsoft.Data.Services.Client.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Microsoft.WindowsAzure.ServiceRuntime, Version=1.8.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
@ -83,9 +87,8 @@
<HintPath>..\packages\Moq.4.2.1510.2205\lib\NET40\Moq.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Newtonsoft.Json, Version=7.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
<Private>True</Private>
<Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="nunit.framework, Version=2.5.10.11092, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL">
<HintPath>..\packages\NUnit.2.5.10.11092\lib\nunit.framework.dll</HintPath>
@ -104,8 +107,8 @@
<Reference Include="System.Core">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
<Reference Include="System.Spatial, Version=5.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\System.Spatial.5.6.4\lib\net40\System.Spatial.dll</HintPath>
<Reference Include="System.Spatial, Version=5.8.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\System.Spatial.5.8.4\lib\net40\System.Spatial.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System.Web" />
@ -128,7 +131,9 @@
<None Include="App.config">
<SubType>Designer</SubType>
</None>
<None Include="packages.config" />
<None Include="packages.config">
<SubType>Designer</SubType>
</None>
</ItemGroup>
<ItemGroup>
<BootstrapperPackage Include="Microsoft.Net.Client.3.5">

View File

@ -1,12 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Azure.KeyVault.Core" version="1.0.0" targetFramework="net452" />
<package id="Microsoft.Data.Edm" version="5.6.4" targetFramework="net452" />
<package id="Microsoft.Data.OData" version="5.6.4" targetFramework="net452" />
<package id="Microsoft.Data.Services.Client" version="5.6.4" targetFramework="net452" />
<package id="Moq" version="4.2.1510.2205" targetFramework="net452" />
<package id="Newtonsoft.Json" version="7.0.1" targetFramework="net452" />
<package id="NUnit" version="2.5.10.11092" targetFramework="net452" />
<package id="System.Spatial" version="5.6.4" targetFramework="net452" />
<package id="WindowsAzure.Storage" version="5.0.2" targetFramework="net452" />
<package id="log4net" version="2.0.12" targetFramework="net48" />
<package id="Microsoft.Azure.KeyVault.Core" version="1.0.0" targetFramework="net48" />
<package id="Microsoft.Data.Edm" version="5.8.4" targetFramework="net48" />
<package id="Microsoft.Data.OData" version="5.8.4" targetFramework="net48" />
<package id="Microsoft.Data.Services.Client" version="5.8.4" targetFramework="net48" />
<package id="Moq" version="4.2.1510.2205" targetFramework="net48" />
<package id="Newtonsoft.Json" version="13.0.1" targetFramework="net48" />
<package id="NUnit" version="2.5.10.11092" targetFramework="net48" />
<package id="System.Spatial" version="5.8.4" targetFramework="net48" />
<package id="WindowsAzure.Storage" version="5.0.2" targetFramework="net48" />
</packages>

View File

@ -4,11 +4,11 @@
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="NHibernate" publicKeyToken="aa95f207798dfdb4" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.0.4000" newVersion="4.0.0.4000" />
<bindingRedirect oldVersion="0.0.0.0-5.3.0.0" newVersion="5.3.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Iesi.Collections" publicKeyToken="aa95f207798dfdb4" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="4.0.0.0" />
<bindingRedirect oldVersion="0.0.0.0-4.0.0.4000" newVersion="4.0.0.4000" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Autofac" publicKeyToken="17863af14b0044da" culture="neutral" />
@ -16,8 +16,16 @@
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-7.0.0.0" newVersion="7.0.0.0" />
<bindingRedirect oldVersion="0.0.0.0-13.0.0.0" newVersion="13.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.Http" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.2.7.0" newVersion="5.2.7.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Net.Http.Formatting" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.2.7.0" newVersion="5.2.7.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
</configuration>

View File

@ -177,7 +177,7 @@ namespace Orchard.Core.Tests.Common.Providers {
contentManager.UpdateEditor(item.ContentItem, updateModel.Object);
}
class UpdatModelStub : IUpdateModel {
class UpdateModelStub : IUpdateModel {
ModelStateDictionary _modelState = new ModelStateDictionary();
@ -215,11 +215,11 @@ namespace Orchard.Core.Tests.Common.Providers {
var user = contentManager.New<IUser>("User");
_authn.Setup(x => x.GetAuthenticatedUser()).Returns(user);
_authz.Setup(x => x.TryCheckAccess(StandardPermissions.SiteOwner, user, item)).Returns(true);
_authz.Setup(x => x.TryCheckAccess(OwnerEditorPermissions.MayEditContentOwner, user, item)).Returns(true);
item.Owner = user;
var updater = new UpdatModelStub() { Owner = null };
var updater = new UpdateModelStub() { Owner = null };
contentManager.UpdateEditor(item.ContentItem, updater);
}
@ -232,11 +232,11 @@ namespace Orchard.Core.Tests.Common.Providers {
var user = contentManager.New<IUser>("User");
_authn.Setup(x => x.GetAuthenticatedUser()).Returns(user);
_authz.Setup(x => x.TryCheckAccess(StandardPermissions.SiteOwner, user, item)).Returns(true);
_authz.Setup(x => x.TryCheckAccess(OwnerEditorPermissions.MayEditContentOwner, user, item)).Returns(true);
item.Owner = user;
var updater = new UpdatModelStub() { Owner = "" };
var updater = new UpdateModelStub() { Owner = "" };
_container.Resolve<DefaultShapeTableManagerTests.TestShapeProvider>().Discover =
b => b.Describe("Parts_Common_Owner_Edit").From(TestFeature())
@ -255,11 +255,11 @@ namespace Orchard.Core.Tests.Common.Providers {
var user = contentManager.New<IUser>("User");
_authn.Setup(x => x.GetAuthenticatedUser()).Returns(user);
_authz.Setup(x => x.TryCheckAccess(StandardPermissions.SiteOwner, user, item)).Returns(true);
_authz.Setup(x => x.TryCheckAccess(OwnerEditorPermissions.MayEditContentOwner, user, item)).Returns(true);
item.Owner = user;
var updater = new UpdatModelStub() { Owner = "" };
var updater = new UpdateModelStub() { Owner = "" };
_container.Resolve<DefaultShapeTableManagerTests.TestShapeProvider>().Discover =
b => b.Describe("Parts_Common_Owner_Edit").From(TestFeature())
@ -384,7 +384,7 @@ namespace Orchard.Core.Tests.Common.Providers {
_clock.Advance(TimeSpan.FromMinutes(1));
var editUtc = _clock.UtcNow;
var updater = new UpdatModelStub() { Owner = "" };
var updater = new UpdateModelStub() { Owner = "" };
contentManager.UpdateEditor(item.ContentItem, updater);
Assert.That(item.CreatedUtc, Is.EqualTo(createUtc));

View File

@ -20,6 +20,7 @@ using Orchard.Core.Feeds.StandardBuilders;
using Orchard.Tests.Modules;
using Orchard.Tests.Stubs;
using Orchard.Core.Title.Models;
using Orchard.Services;
namespace Orchard.Core.Tests.Feeds.Controllers {
[TestFixture]
@ -177,6 +178,7 @@ namespace Orchard.Core.Tests.Feeds.Controllers {
builder.RegisterInstance(mockContentManager.Object).As<IContentManager>();
builder.RegisterType<RssFeedBuilder>().As<IFeedBuilderProvider>();
builder.RegisterType<CorePartsFeedItemBuilder>().As<IFeedItemBuilder>();
builder.RegisterType<HtmlFilterProcessor>().As<IHtmlFilterProcessor>();
builder.RegisterInstance(query).As<IFeedQueryProvider>();
var container = builder.Build();

View File

@ -10,7 +10,8 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Orchard.Core.Tests</RootNamespace>
<AssemblyName>Orchard.Core.Tests</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
<LangVersion>7.3</LangVersion>
<FileAlignment>512</FileAlignment>
<FileUpgradeFlags>
</FileUpgradeFlags>
@ -57,43 +58,45 @@
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<Reference Include="Antlr3.Runtime, Version=3.5.0.2, Culture=neutral, PublicKeyToken=eb42632606e9261f, processorArchitecture=MSIL">
<HintPath>..\packages\Antlr3.Runtime.3.5.1\lib\net40-client\Antlr3.Runtime.dll</HintPath>
</Reference>
<Reference Include="Autofac, Version=3.5.0.0, Culture=neutral, PublicKeyToken=17863af14b0044da, processorArchitecture=MSIL">
<HintPath>..\packages\Autofac.3.5.2\lib\net40\Autofac.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="FluentNHibernate, Version=1.4.0.0, Culture=neutral, PublicKeyToken=8aa435e3cb308880, processorArchitecture=MSIL">
<HintPath>..\packages\FluentNHibernate.1.4.0.0\lib\net35\FluentNHibernate.dll</HintPath>
<Private>True</Private>
<Reference Include="FluentNHibernate, Version=3.1.0.0, Culture=neutral, PublicKeyToken=8aa435e3cb308880, processorArchitecture=MSIL">
<HintPath>..\packages\FluentNHibernate.3.1.0\lib\net461\FluentNHibernate.dll</HintPath>
</Reference>
<Reference Include="Iesi.Collections, Version=4.0.0.0, Culture=neutral, PublicKeyToken=aa95f207798dfdb4, processorArchitecture=MSIL">
<HintPath>..\packages\Iesi.Collections.4.0.1.4000\lib\net40\Iesi.Collections.dll</HintPath>
<Private>True</Private>
<Reference Include="Iesi.Collections, Version=4.0.0.4000, Culture=neutral, PublicKeyToken=aa95f207798dfdb4, processorArchitecture=MSIL">
<HintPath>..\packages\Iesi.Collections.4.0.4\lib\net461\Iesi.Collections.dll</HintPath>
</Reference>
<Reference Include="Microsoft.CSharp" />
<Reference Include="Microsoft.Web.Infrastructure, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Moq, Version=4.2.1510.2205, Culture=neutral, PublicKeyToken=69f491c39445e920, processorArchitecture=MSIL">
<HintPath>..\packages\Moq.4.2.1510.2205\lib\NET40\Moq.dll</HintPath>
<Private>True</Private>
<HintPath>..\packages\Moq.4.2.1510.2205\lib\net40\Moq.dll</HintPath>
</Reference>
<Reference Include="NHibernate, Version=4.0.0.4000, Culture=neutral, PublicKeyToken=aa95f207798dfdb4, processorArchitecture=MSIL">
<HintPath>..\packages\NHibernate.4.0.1.4000\lib\net40\NHibernate.dll</HintPath>
<Private>True</Private>
<Reference Include="NHibernate, Version=5.3.0.0, Culture=neutral, PublicKeyToken=aa95f207798dfdb4, processorArchitecture=MSIL">
<HintPath>..\packages\NHibernate.5.3.10\lib\net461\NHibernate.dll</HintPath>
</Reference>
<Reference Include="nunit.framework, Version=2.5.10.11092, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL">
<HintPath>..\packages\NUnit.2.5.10.11092\lib\nunit.framework.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="nunit.mocks, Version=2.5.10.11092, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL">
<HintPath>..\packages\NUnit.2.5.10.11092\lib\nunit.mocks.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="pnunit.framework, Version=1.0.4109.34242, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\NUnit.2.5.10.11092\lib\pnunit.framework.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Remotion.Linq, Version=2.2.0.0, Culture=neutral, PublicKeyToken=fee00910d6e5f53b, processorArchitecture=MSIL">
<HintPath>..\packages\Remotion.Linq.2.2.0\lib\net45\Remotion.Linq.dll</HintPath>
</Reference>
<Reference Include="Remotion.Linq.EagerFetching, Version=2.2.0.0, Culture=neutral, PublicKeyToken=fee00910d6e5f53b, processorArchitecture=MSIL">
<HintPath>..\packages\Remotion.Linq.EagerFetching.2.2.0\lib\net45\Remotion.Linq.EagerFetching.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Configuration" />
<Reference Include="System.Core">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
@ -102,36 +105,32 @@
<HintPath>..\..\lib\sqlce\System.Data.SqlServerCe.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System.ServiceModel" />
<Reference Include="System.Transactions" />
<Reference Include="System.Web" />
<Reference Include="System.Web.Abstractions">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
<Reference Include="System.Web.Helpers, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.AspNet.WebPages.3.2.3\lib\net45\System.Web.Helpers.dll</HintPath>
<Private>True</Private>
<HintPath>..\packages\Microsoft.AspNet.WebPages.3.2.7\lib\net45\System.Web.Helpers.dll</HintPath>
</Reference>
<Reference Include="System.Web.Mvc, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.AspNet.Mvc.5.2.3\lib\net45\System.Web.Mvc.dll</HintPath>
<Private>True</Private>
<Reference Include="System.Web.Mvc, Version=5.2.7.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.AspNet.Mvc.5.2.7\lib\net45\System.Web.Mvc.dll</HintPath>
</Reference>
<Reference Include="System.Web.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.AspNet.Razor.3.2.3\lib\net45\System.Web.Razor.dll</HintPath>
<Private>True</Private>
<HintPath>..\packages\Microsoft.AspNet.Razor.3.2.7\lib\net45\System.Web.Razor.dll</HintPath>
</Reference>
<Reference Include="System.Web.Routing">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
<Reference Include="System.Web.WebPages, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.AspNet.WebPages.3.2.3\lib\net45\System.Web.WebPages.dll</HintPath>
<Private>True</Private>
<HintPath>..\packages\Microsoft.AspNet.WebPages.3.2.7\lib\net45\System.Web.WebPages.dll</HintPath>
</Reference>
<Reference Include="System.Web.WebPages.Deployment, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.AspNet.WebPages.3.2.3\lib\net45\System.Web.WebPages.Deployment.dll</HintPath>
<Private>True</Private>
<HintPath>..\packages\Microsoft.AspNet.WebPages.3.2.7\lib\net45\System.Web.WebPages.Deployment.dll</HintPath>
</Reference>
<Reference Include="System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.AspNet.WebPages.3.2.3\lib\net45\System.Web.WebPages.Razor.dll</HintPath>
<Private>True</Private>
<HintPath>..\packages\Microsoft.AspNet.WebPages.3.2.7\lib\net45\System.Web.WebPages.Razor.dll</HintPath>
</Reference>
<Reference Include="System.Xml" />
<Reference Include="System.Xml.Linq">

View File

@ -30,5 +30,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
[assembly: AssemblyVersion("1.10.1")]
[assembly: AssemblyFileVersion("1.10.1")]
[assembly: AssemblyVersion("1.10.3")]
[assembly: AssemblyFileVersion("1.10.3")]

View File

@ -1,13 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Autofac" version="3.5.2" targetFramework="net452" />
<package id="FluentNHibernate" version="1.4.0.0" targetFramework="net452" />
<package id="Iesi.Collections" version="4.0.1.4000" targetFramework="net452" />
<package id="Microsoft.AspNet.Mvc" version="5.2.3" targetFramework="net452" />
<package id="Microsoft.AspNet.Razor" version="3.2.3" targetFramework="net452" />
<package id="Microsoft.AspNet.WebPages" version="3.2.3" targetFramework="net452" />
<package id="Microsoft.Web.Infrastructure" version="1.0.0.0" targetFramework="net452" />
<package id="Moq" version="4.2.1510.2205" targetFramework="net452" />
<package id="NHibernate" version="4.0.1.4000" targetFramework="net452" />
<package id="NUnit" version="2.5.10.11092" targetFramework="net452" />
<package id="Antlr3.Runtime" version="3.5.1" targetFramework="net48" />
<package id="Autofac" version="3.5.2" targetFramework="net48" />
<package id="FluentNHibernate" version="3.1.0" targetFramework="net48" />
<package id="Iesi.Collections" version="4.0.4" targetFramework="net48" />
<package id="Microsoft.AspNet.Mvc" version="5.2.7" targetFramework="net48" />
<package id="Microsoft.AspNet.Razor" version="3.2.7" targetFramework="net48" />
<package id="Microsoft.AspNet.WebPages" version="3.2.7" targetFramework="net48" />
<package id="Microsoft.Web.Infrastructure" version="1.0.0.0" targetFramework="net48" />
<package id="Moq" version="4.2.1510.2205" targetFramework="net48" />
<package id="NHibernate" version="5.3.10" targetFramework="net48" />
<package id="NUnit" version="2.5.10.11092" targetFramework="net48" />
<package id="Remotion.Linq" version="2.2.0" targetFramework="net48" />
<package id="Remotion.Linq.EagerFetching" version="2.2.0" targetFramework="net48" />
</packages>

View File

@ -6,4 +6,4 @@
<specFlow>
<!-- For additional details on SpecFlow configuration options see http://go.specflow.org/doc-config -->
</specFlow>
</configuration>
</configuration>

View File

@ -10,7 +10,8 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Orchard.Profile</RootNamespace>
<AssemblyName>Orchard.Profile</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
<LangVersion>7.3</LangVersion>
<FileAlignment>512</FileAlignment>
<FileUpgradeFlags>
</FileUpgradeFlags>
@ -58,15 +59,12 @@
<ItemGroup>
<Reference Include="nunit.framework, Version=2.5.10.11092, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL">
<HintPath>..\packages\NUnit.2.5.10.11092\lib\nunit.framework.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="nunit.mocks, Version=2.5.10.11092, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL">
<HintPath>..\packages\NUnit.2.5.10.11092\lib\nunit.mocks.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="pnunit.framework, Version=1.0.4109.34242, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\NUnit.2.5.10.11092\lib\pnunit.framework.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core">
@ -83,7 +81,6 @@
<Reference Include="System.Xml" />
<Reference Include="TechTalk.SpecFlow, Version=1.9.0.77, Culture=neutral, PublicKeyToken=0778194805d6db41, processorArchitecture=MSIL">
<HintPath>..\packages\SpecFlow.1.9.0\lib\net35\TechTalk.SpecFlow.dll</HintPath>
<Private>True</Private>
</Reference>
</ItemGroup>
<ItemGroup>
@ -144,4 +141,4 @@
<Target Name="AfterBuild">
</Target>
-->
</Project>
</Project>

View File

@ -30,5 +30,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
[assembly: AssemblyVersion("1.10.1")]
[assembly: AssemblyFileVersion("1.10.1")]
[assembly: AssemblyVersion("1.10.3")]
[assembly: AssemblyFileVersion("1.10.3")]

View File

@ -1,9 +1,9 @@
// ------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by SpecFlow (http://www.specflow.org/).
// SpecFlow Version:1.9.0.0
// SpecFlow Version:1.9.0.77
// SpecFlow Generator Version:1.9.0.0
// Runtime Version:4.0.30319.34014
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
@ -16,7 +16,7 @@ namespace Orchard.Profile.Tests
using TechTalk.SpecFlow;
[System.CodeDom.Compiler.GeneratedCodeAttribute("TechTalk.SpecFlow", "1.9.0.0")]
[System.CodeDom.Compiler.GeneratedCodeAttribute("TechTalk.SpecFlow", "1.9.0.77")]
[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[NUnit.Framework.TestFixtureAttribute()]
[NUnit.Framework.DescriptionAttribute("Profiling")]

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="NUnit" version="2.5.10.11092" targetFramework="net452" />
<package id="SpecFlow" version="1.9.0" targetFramework="net452" />
</packages>
<package id="NUnit" version="2.5.10.11092" targetFramework="net48" />
<package id="SpecFlow" version="1.9.0" targetFramework="net48" />
</packages>

View File

@ -1 +1 @@
setup /SiteName:Profiling /AdminUsername:admin /AdminPassword:profiling-secret /DatabaseProvider:SQLServer /DatabaseConnectionString:"Data Source=.;Initial Catalog=Orchard;Integrated Security=True" /EnabledFeatures:Profiling,Orchard.Framework,Common,Containers,Contents,Dashboard,Feeds,Navigation,Scheduling,Settings,Shapes,Title,PackagingServices,Gallery,Orchard.PublishLater,Orchard.Blogs,Orchard.Comments,Orchard.ContentTypes,Orchard.Resources,Orchard.Lists,Orchard.MediaLibrary,Orchard.ContentPicker,Orchard.MediaPicker,Orchard.Modules,Orchard.Packaging,Orchard.Pages,Orchard.Recipes,Orchard.Roles,Orchard.Tags,Orchard.Themes,Orchard.Users,Orchard.Scripting,Orchard.Scripting.Lightweight,Orchard.Widgets,TinyMce,TheThemeMachine,Orchard.Tokens,Orchard.Alias,Orchard.Autoroute
setup /SiteName:Profiling /AdminUsername:admin /AdminPassword:profiling-secret /DatabaseProvider:SQLServer /DatabaseConnectionString:"Data Source=.;Initial Catalog=Orchard;Integrated Security=True" /EnabledFeatures:Profiling,Orchard.Framework,Common,Containers,Contents,Dashboard,Feeds,Navigation,Scheduling,Settings,Shapes,Title,PackagingServices,Gallery,Orchard.PublishLater,Orchard.Blogs,Orchard.Comments,Orchard.ContentTypes,Orchard.Resources,Orchard.Lists,Orchard.MediaLibrary,Orchard.ContentPicker,Orchard.MediaPicker,Orchard.Modules,Orchard.Packaging,Orchard.Pages,Orchard.Recipes,Orchard.Roles,Orchard.Tags,Orchard.Themes,Orchard.Users,Orchard.Scripting,Orchard.Scripting.Lightweight,Orchard.Widgets,TinyMce,TheThemeMachine,Orchard.Tokens,Orchard.Alias,Orchard.Autoroute

View File

@ -3,7 +3,7 @@
// This code was generated by SpecFlow (http://www.specflow.org/).
// SpecFlow Version:1.9.0.77
// SpecFlow Generator Version:1.9.0.0
// Runtime Version:4.0.30319.34014
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.

View File

@ -3,21 +3,20 @@
<configSections>
<section name="specFlow" type="TechTalk.SpecFlow.Configuration.ConfigurationSectionHandler, TechTalk.SpecFlow" />
</configSections>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8" />
</startup>
<specFlow>
<!-- For additional details on SpecFlow configuration options see http://go.specflow.org/doc-config -->
</specFlow>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="NHibernate" publicKeyToken="aa95f207798dfdb4" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.0.4000" newVersion="4.0.0.4000" />
<bindingRedirect oldVersion="0.0.0.0-5.3.0.0" newVersion="5.3.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Iesi.Collections" publicKeyToken="aa95f207798dfdb4" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="4.0.0.0" />
<bindingRedirect oldVersion="0.0.0.0-4.0.0.4000" newVersion="4.0.0.4000" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Autofac" publicKeyToken="17863af14b0044da" culture="neutral" />
@ -25,8 +24,26 @@
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-7.0.0.0" newVersion="7.0.0.0" />
<bindingRedirect oldVersion="0.0.0.0-13.0.0.0" newVersion="13.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.Http" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.2.7.0" newVersion="5.2.7.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Net.Http.Formatting" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.2.7.0" newVersion="5.2.7.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Owin" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.2.2.0" newVersion="4.2.2.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.2.7.0" newVersion="5.2.7.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
<specFlow>
<!-- For additional details on SpecFlow configuration options see http://go.specflow.org/doc-config -->
</specFlow></configuration>

View File

@ -9,7 +9,7 @@ Scenario: I can create and publish a new Page with international characters in i
And I fill in
| name | value |
| Title.Title | Χελλο |
And I hit "Publish Now"
And I hit "Publish"
And I go to "Χελλο"
Then I should see "<h1[^>]*>.*?Χελλο.*?</h1>"
@ -20,7 +20,7 @@ Scenario: I can create and publish a new Home Page
| name | value |
| Title.Title | Foo |
| AutoroutePart.PromoteToHomePage | True |
And I hit "Publish Now"
And I hit "Publish"
And I go to "/"
Then I should see "<h1[^>]*>.*?Foo.*?</h1>"
When I go to "/welcome-to-orchard"

View File

@ -86,7 +86,7 @@ this.ScenarioSetup(scenarioInfo);
#line 9
testRunner.And("I fill in", ((string)(null)), table1, "And ");
#line 12
testRunner.And("I hit \"Publish Now\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
testRunner.And("I hit \"Publish\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 13
testRunner.And("I go to \"Χελλο\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 14
@ -119,7 +119,7 @@ this.ScenarioSetup(scenarioInfo);
#line 19
testRunner.And("I fill in", ((string)(null)), table2, "And ");
#line 23
testRunner.And("I hit \"Publish Now\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
testRunner.And("I hit \"Publish\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 24
testRunner.And("I go to \"/\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 25

View File

@ -4,8 +4,6 @@ using Orchard.ContentManagement;
using Orchard.ContentManagement.Aspects;
using Orchard.Core.Contents;
using Orchard.Data;
using Orchard.Roles.Models;
using Orchard.Roles.Services;
using Orchard.Security;
using Orchard.Security.Permissions;
using Orchard.Specs.Hosting.Orchard.Web;

View File

@ -11,6 +11,22 @@ using TechTalk.SpecFlow;
namespace Orchard.Specs.Bindings {
[Binding]
public class OrchardSiteFactory : BindingBase {
[Given(@"I have a clean site with standard extensions")]
public void GivenIHaveACleanSiteWithStandardExtensions() {
GivenIHaveACleanSiteWithStandardExtensions("/");
}
[Given(@"I have a clean site with standard extensions at ""(.*)\""")]
public void GivenIHaveACleanSiteWithStandardExtensions(string virtualDirectory) {
Binding<WebAppHosting>().GivenIHaveACleanSiteWith(
virtualDirectory,
// This is the list of extensions which will be copied over into the temporary Orchard folder.
TableData(
new { extension = "Module", names = "Lucene, Markdown, Orchard.Alias, Orchard.AntiSpam, Orchard.ArchiveLater, Orchard.Autoroute, Orchard.Azure, Orchard.Blogs, Orchard.Caching, Orchard.CodeGeneration, Orchard.Comments, Orchard.ContentPermissions, Orchard.ContentPicker, Orchard.ContentTypes, Orchard.DesignerTools, Orchard.Email, Orchard.Fields, Orchard.Forms, Orchard.ImageEditor, Orchard.ImportExport, Orchard.Indexing, Orchard.JobsQueue, Orchard.Resources, Orchard.Layouts, Orchard.Lists, Orchard.Localization, Orchard.MediaLibrary, Orchard.MediaProcessing, Orchard.Migrations, Orchard.Modules, Orchard.MultiTenancy, Orchard.OutputCache, Orchard.Packaging, Orchard.Pages, Orchard.Projections, Orchard.PublishLater, Orchard.Recipes, Orchard.Roles, Orchard.Scripting, Orchard.Scripting.CSharp, Orchard.Scripting.Dlr, Orchard.Search, Orchard.SecureSocketsLayer, Orchard.Setup, Orchard.Tags, Orchard.Taxonomies, Orchard.Templates, Orchard.Themes, Orchard.Tokens, Orchard.Users, Orchard.Warmup, Orchard.Widgets, Orchard.Workflows, Orchard.Conditions, SysCache, TinyMce, Upgrade" },
new { extension = "Core", names = "Common, Containers, Contents, Dashboard, Feeds, Navigation, Scheduling, Settings, Shapes, Title, XmlRpc" },
new { extension = "Theme", names = "SafeMode, TheAdmin, TheThemeMachine" }));
}
[Given(@"I have installed Orchard")]
public void GivenIHaveInstalledOrchard() {
GivenIHaveInstalledOrchard("/");
@ -20,13 +36,7 @@ namespace Orchard.Specs.Bindings {
public void GivenIHaveInstalledOrchard(string virtualDirectory) {
var webApp = Binding<WebAppHosting>();
// this is the list of module which will be copied over into the temporary Orchard folder
webApp.GivenIHaveACleanSiteWith(
virtualDirectory,
TableData(
new { extension = "Module", names = "Lucene, Markdown, Orchard.Alias, Orchard.AntiSpam, Orchard.ArchiveLater, Orchard.Autoroute, Orchard.Azure, Orchard.Blogs, Orchard.Caching, Orchard.CodeGeneration, Orchard.Comments, Orchard.ContentPermissions, Orchard.ContentPicker, Orchard.ContentTypes, Orchard.DesignerTools, Orchard.Email, Orchard.Fields, Orchard.Forms, Orchard.ImageEditor, Orchard.ImportExport, Orchard.Indexing, Orchard.JobsQueue, Orchard.Resources, Orchard.Layouts, Orchard.Lists, Orchard.Localization, Orchard.MediaLibrary, Orchard.MediaProcessing, Orchard.Migrations, Orchard.Modules, Orchard.MultiTenancy, Orchard.OutputCache, Orchard.Packaging, Orchard.Pages, Orchard.Projections, Orchard.PublishLater, Orchard.Recipes, Orchard.Roles, Orchard.Scripting, Orchard.Scripting.CSharp, Orchard.Scripting.Dlr, Orchard.Search, Orchard.SecureSocketsLayer, Orchard.Setup, Orchard.Tags, Orchard.Taxonomies, Orchard.Templates, Orchard.Themes, Orchard.Tokens, Orchard.Users, Orchard.Warmup, Orchard.Widgets, Orchard.Workflows, Orchard.Conditions, SysCache, TinyMce, Upgrade" },
new { extension = "Core", names = "Common, Containers, Contents, Dashboard, Feeds, Navigation, Scheduling, Settings, Shapes, Title, XmlRpc" },
new { extension = "Theme", names = "SafeMode, TheAdmin, TheThemeMachine" }));
GivenIHaveACleanSiteWithStandardExtensions(virtualDirectory);
webApp.WhenIGoTo("Setup");

View File

@ -1,17 +1,7 @@
using System;
using NUnit.Framework;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Aspects;
using Orchard.Core.Contents;
using Orchard.Data;
using Orchard.Roles.Models;
using Orchard.Roles.Services;
using Orchard.Security;
using Orchard.Security.Permissions;
using System.Linq;
using Orchard.Localization.Services;
using Orchard.Specs.Hosting.Orchard.Web;
using TechTalk.SpecFlow;
using Orchard.Localization.Services;
using System.Linq;
namespace Orchard.Specs.Bindings {
[Binding]
@ -22,7 +12,7 @@ namespace Orchard.Specs.Bindings {
var webApp = Binding<WebAppHosting>();
webApp.Host.Execute(() => {
using ( var environment = MvcApplication.CreateStandaloneEnvironment("Default") ) {
using (var environment = MvcApplication.CreateStandaloneEnvironment("Default")) {
var orchardServices = environment.Resolve<IOrchardServices>();
var cultureManager = environment.Resolve<ICultureManager>();
@ -32,6 +22,11 @@ namespace Orchard.Specs.Bindings {
}
orchardServices.WorkContext.CurrentSite.SiteCulture = cultureName;
// Restarting the shell to reset the cache, because the cache entry storing the list of available
// cultures isn't invalidated by the signal in DefaultCultureManager.ListCultures when running
// inside the test webhost.
MvcApplication.RestartTenant("Default");
}
});
}

View File

@ -40,7 +40,7 @@ namespace Orchard.Specs.Bindings {
var memberShipService = environment.Resolve<IMembershipService>();
var roleService = environment.Resolve<IRoleService>();
var userRoleRepository = environment.Resolve<IRepository<UserRolesPartRecord>>();
var user = memberShipService.CreateUser(new CreateUserParams(username, "qwerty123!", username + "@foo.com", "", "", true));
var user = memberShipService.CreateUser(new CreateUserParams(username, "qwerty123!", username + "@foo.com", "", "", true, false));
foreach (var roleName in roles.Split(new[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries)) {
var role = roleService.GetRoleByName(roleName);

View File

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using System.Web;
using Castle.Core.Logging;
using HtmlAgilityPack;
@ -318,6 +319,10 @@ namespace Orchard.Specs.Bindings {
?? _doc.DocumentNode
.SelectSingleNode(string.Format("//a[@title='{0}']", submitText));
if (submit == null) {
throw new ArgumentException("Text not found: " + submitText);
}
urlPath = HttpUtility.HtmlDecode(submit.Attributes["href"].Value);
}
@ -364,6 +369,11 @@ namespace Orchard.Specs.Bindings {
}
}
[When(@"I wait ""(.*)""")]
public void WhenIWait(int waitMilliseconds) {
Thread.Sleep(waitMilliseconds);
}
[Then(@"the status should be (.*) ""(.*)""")]
public void ThenTheStatusShouldBe(int statusCode, string statusDescription) {
Assert.That(Details.StatusCode, Is.EqualTo(statusCode));

View File

@ -7,14 +7,14 @@ Scenario: In the admin (menu) there is a link to create a Blog
Given I have installed Orchard
When I go to "admin"
Then I should see "<a[^>]*href="/Admin/Blogs/Create"[^>]*>Blog</a>"
Scenario: I can create a new blog and blog post
Given I have installed Orchard
When I go to "admin/blogs/create"
And I fill in
| name | value |
| Title.Title | My Blog |
And I hit "Save"
And I hit "Publish"
And I go to "admin/blogs"
And I follow "My Blog"
And I follow "New Post" where class name has "primaryAction"
@ -22,7 +22,7 @@ Scenario: I can create a new blog and blog post
| name | value |
| Title.Title | My Post |
| Body.Text | Hi there. |
And I hit "Publish Now"
And I hit "Publish"
And I am redirected
Then I should see "Your Blog Post has been created."
When I go to "my-blog"
@ -38,7 +38,7 @@ Scenario: I can create a new blog with multiple blog posts each with the same ti
And I fill in
| name | value |
| Title.Title | My Blog |
And I hit "Save"
And I hit "Publish"
And I go to "admin/blogs"
And I follow "My Blog"
And I follow "New Post" where class name has "primaryAction"
@ -46,7 +46,7 @@ Scenario: I can create a new blog with multiple blog posts each with the same ti
| name | value |
| Title.Title | My Post |
| Body.Text | Hi there. |
And I hit "Publish Now"
And I hit "Publish"
And I go to "my-blog/my-post"
Then I should see "<h1[^>]*>.*?My Post.*?</h1>"
And I should see "Hi there."
@ -57,7 +57,7 @@ Scenario: I can create a new blog with multiple blog posts each with the same ti
| name | value |
| Title.Title | My Post |
| Body.Text | Hi there, again. |
And I hit "Publish Now"
And I hit "Publish"
And I go to "my-blog/my-post-2"
Then I should see "<h1[^>]*>.*?My Post.*?</h1>"
And I should see "Hi there, again."
@ -69,7 +69,7 @@ Scenario: I can create a new blog with multiple blog posts each with the same ti
| Title.Title | My Post |
| AutoroutePart.CurrentUrl | my-blog/my-post |
| Body.Text | Are you still there? |
And I hit "Publish Now"
And I hit "Publish"
And I go to "my-blog/my-post-3"
Then I should see "<h1[^>]*>.*?My Post.*?</h1>"
And I should see "Are you still there?"
@ -80,7 +80,7 @@ Scenario: When viewing a blog the user agent is given an RSS feed of the blog's
And I fill in
| name | value |
| Title.Title | My Blog |
And I hit "Save"
And I hit "Publish"
And I go to "admin/blogs"
And I follow "My Blog"
And I follow "New Post" where class name has "primaryAction"
@ -88,11 +88,11 @@ Scenario: When viewing a blog the user agent is given an RSS feed of the blog's
| name | value |
| Title.Title | My Post |
| Body.Text | Hi there. |
And I hit "Publish Now"
And I hit "Publish"
And I am redirected
And I go to "my-blog/my-post"
Then I should see "<link rel="alternate" type="application/rss\+xml" title="My Blog" href="/rss\?containerid=\d+" />"
Scenario: Enabling remote blog publishing inserts the appropriate metaweblogapi markup into the blog's page
Given I have installed Orchard
And I have enabled "XmlRpc"
@ -101,7 +101,7 @@ Scenario: Enabling remote blog publishing inserts the appropriate metaweblogapi
And I fill in
| name | value |
| Title.Title | My Blog |
And I hit "Save"
And I hit "Publish"
And I go to "my-blog"
Then I should see "<link href="[^"]*/XmlRpc/LiveWriter/Manifest" rel="wlwmanifest" type="application/wlwmanifest\+xml" />"
When I go to "/XmlRpc/LiveWriter/Manifest"
@ -116,7 +116,7 @@ Scenario: The virtual path of my installation when not at the root is reflected
When I fill in
| name | value |
| Title.Title | My Blog |
And I hit "Save"
And I hit "Publish"
And I go to "admin/blogs"
And I follow "My Blog"
And I follow "New Post" where class name has "primaryAction"
@ -129,7 +129,7 @@ Scenario: The virtual path of my installation when at the root is reflected in t
When I fill in
| name | value |
| Title.Title | My Blog |
And I hit "Save"
And I hit "Publish"
And I go to "admin/blogs"
And I follow "My Blog"
And I follow "New Post" where class name has "primaryAction"
@ -142,7 +142,7 @@ Scenario: I set my blog to be the content for the home page and the posts for th
| name | value |
| Title.Title | My Blog |
| AutoroutePart.PromoteToHomePage | true |
And I hit "Save"
And I hit "Publish"
And I go to "admin/blogs"
And I follow "My Blog"
And I follow "New Post" where class name has "primaryAction"
@ -150,7 +150,7 @@ Scenario: I set my blog to be the content for the home page and the posts for th
| name | value |
| Title.Title | My Post |
| Body.Text | Hi there. |
And I hit "Publish Now"
And I hit "Publish"
And I am redirected
And I go to "/"
Then I should see "<h1>My Blog</h1>"
@ -167,114 +167,126 @@ Scenario: I can create browse blog posts on several pages
And I fill in
| name | value |
| Title.Title | My Blog |
And I hit "Save"
And I hit "Publish"
And I go to "admin/blogs"
And I follow "My Blog"
And I follow "New Post" where class name has "primaryAction"
And I wait "1000"
And I fill in
| name | value |
| Title.Title | My Post 1 |
And I hit "Publish Now"
And I hit "Publish"
And I am redirected
And I go to "admin/blogs"
And I follow "My Blog"
And I follow "New Post" where class name has "primaryAction"
And I wait "1000"
And I fill in
| name | value |
| Title.Title | My Post 2 |
And I hit "Publish Now"
And I hit "Publish"
And I am redirected
And I go to "admin/blogs"
And I follow "My Blog"
And I follow "New Post" where class name has "primaryAction"
And I wait "1000"
And I fill in
| name | value |
| Title.Title | My Post 3 |
And I hit "Publish Now"
And I hit "Publish"
And I am redirected
And I go to "admin/blogs"
And I follow "My Blog"
And I follow "New Post" where class name has "primaryAction"
And I wait "1000"
And I fill in
| name | value |
| Title.Title | My Post 4 |
And I hit "Publish Now"
And I hit "Publish"
And I am redirected
And I go to "admin/blogs"
And I follow "My Blog"
And I follow "New Post" where class name has "primaryAction"
And I wait "1000"
And I fill in
| name | value |
| Title.Title | My Post 5 |
And I hit "Publish Now"
And I hit "Publish"
And I am redirected
And I go to "admin/blogs"
And I follow "My Blog"
And I follow "New Post" where class name has "primaryAction"
And I wait "1000"
And I fill in
| name | value |
| Title.Title | My Post 6 |
And I hit "Publish Now"
And I hit "Publish"
And I am redirected
And I go to "admin/blogs"
And I follow "My Blog"
And I follow "New Post" where class name has "primaryAction"
And I wait "1000"
And I fill in
| name | value |
| Title.Title | My Post 7 |
And I hit "Publish Now"
And I hit "Publish"
And I am redirected
And I go to "admin/blogs"
And I follow "My Blog"
And I follow "New Post" where class name has "primaryAction"
And I wait "1000"
And I fill in
| name | value |
| Title.Title | My Post 8 |
And I hit "Publish Now"
And I hit "Publish"
And I am redirected
And I go to "admin/blogs"
And I follow "My Blog"
And I follow "New Post" where class name has "primaryAction"
And I wait "1000"
And I fill in
| name | value |
| Title.Title | My Post 9 |
And I hit "Publish Now"
And I hit "Publish"
And I am redirected
And I go to "admin/blogs"
And I follow "My Blog"
And I follow "New Post" where class name has "primaryAction"
And I wait "1000"
And I fill in
| name | value |
| Title.Title | My Post 10 |
And I hit "Publish Now"
And I hit "Publish"
And I am redirected
And I go to "admin/blogs"
And I follow "My Blog"
And I follow "New Post" where class name has "primaryAction"
And I wait "1000"
And I fill in
| name | value |
| Title.Title | My Post 11 |
And I hit "Publish Now"
And I hit "Publish"
And I am redirected
And I go to "admin/blogs"
And I follow "My Blog"
And I follow "New Post" where class name has "primaryAction"
And I wait "1000"
And I fill in
| name | value |
| Title.Title | My Post 12 |
And I hit "Publish Now"
And I hit "Publish"
And I am redirected
Then I should see "Your Blog Post has been created."
When I go to "my-blog"
Then I should see "<h1[^>]*>.*?My Blog.*?</h1>"
And I should see "<h1[^>]*>.*?My Post 12.*?</h1>"
And I should see "<h1[^>]*>.*?My Post 11.*?</h1>"
And I should not see "<h1[^>]*>.*?My Post 10.*?</h1>"
And I should not see "My Post 2"
When I go to "my-blog?page=2"
Then I should see "<h1[^>]*>.*?My Blog.*?</h1>"
And I should see "<h1[^>]*>.*?My Post 1.*?</h1>"
And I should see "<h1[^>]*>.*?My Post 2.*?</h1>"
And I should not see "<h1[^>]*>.*?My Post 3.*?</h1>"
And I should not see "My Post 3"
Scenario: I can create a new blog with a percent sign in the title and it gets stripped out of the slug
Given I have installed Orchard
@ -282,7 +294,7 @@ Scenario: I can create a new blog with a percent sign in the title and it gets s
And I fill in
| name | value |
| Title.Title | My Blog |
And I hit "Save"
And I hit "Publish"
And I go to "admin/blogs"
And I follow "My Blog"
And I follow "New Post" where class name has "primaryAction"
@ -290,7 +302,7 @@ Scenario: I can create a new blog with a percent sign in the title and it gets s
| name | value |
| Title.Title | My Post with a % Sign |
| Body.Text | Hi there. |
And I hit "Publish Now"
And I hit "Publish"
And I go to "my-blog/my-post-with-a-sign"
Then I should see "<h1[^>]*>.*?My Post with a % Sign.*?</h1>"
And I should see "Hi there."

View File

@ -103,7 +103,7 @@ this.ScenarioSetup(scenarioInfo);
#line 14
testRunner.And("I fill in", ((string)(null)), table1, "And ");
#line 17
testRunner.And("I hit \"Save\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
testRunner.And("I hit \"Publish\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 18
testRunner.And("I go to \"admin/blogs\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 19
@ -123,7 +123,7 @@ this.ScenarioSetup(scenarioInfo);
#line 21
testRunner.And("I fill in", ((string)(null)), table2, "And ");
#line 25
testRunner.And("I hit \"Publish Now\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
testRunner.And("I hit \"Publish\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 26
testRunner.And("I am redirected", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 27
@ -167,7 +167,7 @@ this.ScenarioSetup(scenarioInfo);
#line 38
testRunner.And("I fill in", ((string)(null)), table3, "And ");
#line 41
testRunner.And("I hit \"Save\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
testRunner.And("I hit \"Publish\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 42
testRunner.And("I go to \"admin/blogs\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 43
@ -187,7 +187,7 @@ this.ScenarioSetup(scenarioInfo);
#line 45
testRunner.And("I fill in", ((string)(null)), table4, "And ");
#line 49
testRunner.And("I hit \"Publish Now\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
testRunner.And("I hit \"Publish\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 50
testRunner.And("I go to \"my-blog/my-post\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 51
@ -213,7 +213,7 @@ this.ScenarioSetup(scenarioInfo);
#line 56
testRunner.And("I fill in", ((string)(null)), table5, "And ");
#line 60
testRunner.And("I hit \"Publish Now\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
testRunner.And("I hit \"Publish\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 61
testRunner.And("I go to \"my-blog/my-post-2\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 62
@ -242,7 +242,7 @@ this.ScenarioSetup(scenarioInfo);
#line 67
testRunner.And("I fill in", ((string)(null)), table6, "And ");
#line 72
testRunner.And("I hit \"Publish Now\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
testRunner.And("I hit \"Publish\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 73
testRunner.And("I go to \"my-blog/my-post-3\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 74
@ -274,7 +274,7 @@ this.ScenarioSetup(scenarioInfo);
#line 80
testRunner.And("I fill in", ((string)(null)), table7, "And ");
#line 83
testRunner.And("I hit \"Save\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
testRunner.And("I hit \"Publish\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 84
testRunner.And("I go to \"admin/blogs\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 85
@ -294,7 +294,7 @@ this.ScenarioSetup(scenarioInfo);
#line 87
testRunner.And("I fill in", ((string)(null)), table8, "And ");
#line 91
testRunner.And("I hit \"Publish Now\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
testRunner.And("I hit \"Publish\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 92
testRunner.And("I am redirected", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 93
@ -333,7 +333,7 @@ this.ScenarioSetup(scenarioInfo);
#line 101
testRunner.And("I fill in", ((string)(null)), table9, "And ");
#line 104
testRunner.And("I hit \"Save\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
testRunner.And("I hit \"Publish\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 105
testRunner.And("I go to \"my-blog\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 106
@ -377,7 +377,7 @@ this.ScenarioSetup(scenarioInfo);
#line 116
testRunner.When("I fill in", ((string)(null)), table10, "When ");
#line 119
testRunner.And("I hit \"Save\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
testRunner.And("I hit \"Publish\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 120
testRunner.And("I go to \"admin/blogs\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 121
@ -415,7 +415,7 @@ this.ScenarioSetup(scenarioInfo);
#line 129
testRunner.When("I fill in", ((string)(null)), table11, "When ");
#line 132
testRunner.And("I hit \"Save\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
testRunner.And("I hit \"Publish\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 133
testRunner.And("I go to \"admin/blogs\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 134
@ -454,7 +454,7 @@ this.ScenarioSetup(scenarioInfo);
#line 141
testRunner.And("I fill in", ((string)(null)), table12, "And ");
#line 145
testRunner.And("I hit \"Save\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
testRunner.And("I hit \"Publish\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 146
testRunner.And("I go to \"admin/blogs\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 147
@ -474,7 +474,7 @@ this.ScenarioSetup(scenarioInfo);
#line 149
testRunner.And("I fill in", ((string)(null)), table13, "And ");
#line 153
testRunner.And("I hit \"Publish Now\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
testRunner.And("I hit \"Publish\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 154
testRunner.And("I am redirected", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 155
@ -518,13 +518,15 @@ this.ScenarioSetup(scenarioInfo);
#line 167
testRunner.And("I fill in", ((string)(null)), table14, "And ");
#line 170
testRunner.And("I hit \"Save\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
testRunner.And("I hit \"Publish\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 171
testRunner.And("I go to \"admin/blogs\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 172
testRunner.And("I follow \"My Blog\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 173
testRunner.And("I follow \"New Post\" where class name has \"primaryAction\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 174
testRunner.And("I wait \"1000\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line hidden
TechTalk.SpecFlow.Table table15 = new TechTalk.SpecFlow.Table(new string[] {
"name",
@ -532,18 +534,20 @@ this.ScenarioSetup(scenarioInfo);
table15.AddRow(new string[] {
"Title.Title",
"My Post 1"});
#line 174
#line 175
testRunner.And("I fill in", ((string)(null)), table15, "And ");
#line 177
testRunner.And("I hit \"Publish Now\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 178
testRunner.And("I am redirected", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
testRunner.And("I hit \"Publish\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 179
testRunner.And("I go to \"admin/blogs\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
testRunner.And("I am redirected", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 180
testRunner.And("I follow \"My Blog\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
testRunner.And("I go to \"admin/blogs\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 181
testRunner.And("I follow \"My Blog\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 182
testRunner.And("I follow \"New Post\" where class name has \"primaryAction\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 183
testRunner.And("I wait \"1000\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line hidden
TechTalk.SpecFlow.Table table16 = new TechTalk.SpecFlow.Table(new string[] {
"name",
@ -551,18 +555,20 @@ this.ScenarioSetup(scenarioInfo);
table16.AddRow(new string[] {
"Title.Title",
"My Post 2"});
#line 182
#line 184
testRunner.And("I fill in", ((string)(null)), table16, "And ");
#line 185
testRunner.And("I hit \"Publish Now\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 186
testRunner.And("I am redirected", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 187
testRunner.And("I go to \"admin/blogs\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
testRunner.And("I hit \"Publish\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 188
testRunner.And("I follow \"My Blog\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
testRunner.And("I am redirected", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 189
testRunner.And("I go to \"admin/blogs\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 190
testRunner.And("I follow \"My Blog\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 191
testRunner.And("I follow \"New Post\" where class name has \"primaryAction\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 192
testRunner.And("I wait \"1000\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line hidden
TechTalk.SpecFlow.Table table17 = new TechTalk.SpecFlow.Table(new string[] {
"name",
@ -570,18 +576,20 @@ this.ScenarioSetup(scenarioInfo);
table17.AddRow(new string[] {
"Title.Title",
"My Post 3"});
#line 190
testRunner.And("I fill in", ((string)(null)), table17, "And ");
#line 193
testRunner.And("I hit \"Publish Now\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 194
testRunner.And("I am redirected", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 195
testRunner.And("I go to \"admin/blogs\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
testRunner.And("I fill in", ((string)(null)), table17, "And ");
#line 196
testRunner.And("I follow \"My Blog\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
testRunner.And("I hit \"Publish\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 197
testRunner.And("I am redirected", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 198
testRunner.And("I go to \"admin/blogs\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 199
testRunner.And("I follow \"My Blog\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 200
testRunner.And("I follow \"New Post\" where class name has \"primaryAction\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 201
testRunner.And("I wait \"1000\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line hidden
TechTalk.SpecFlow.Table table18 = new TechTalk.SpecFlow.Table(new string[] {
"name",
@ -589,18 +597,20 @@ this.ScenarioSetup(scenarioInfo);
table18.AddRow(new string[] {
"Title.Title",
"My Post 4"});
#line 198
testRunner.And("I fill in", ((string)(null)), table18, "And ");
#line 201
testRunner.And("I hit \"Publish Now\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 202
testRunner.And("I am redirected", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 203
testRunner.And("I go to \"admin/blogs\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 204
testRunner.And("I follow \"My Blog\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
testRunner.And("I fill in", ((string)(null)), table18, "And ");
#line 205
testRunner.And("I hit \"Publish\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 206
testRunner.And("I am redirected", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 207
testRunner.And("I go to \"admin/blogs\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 208
testRunner.And("I follow \"My Blog\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 209
testRunner.And("I follow \"New Post\" where class name has \"primaryAction\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 210
testRunner.And("I wait \"1000\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line hidden
TechTalk.SpecFlow.Table table19 = new TechTalk.SpecFlow.Table(new string[] {
"name",
@ -608,18 +618,20 @@ this.ScenarioSetup(scenarioInfo);
table19.AddRow(new string[] {
"Title.Title",
"My Post 5"});
#line 206
testRunner.And("I fill in", ((string)(null)), table19, "And ");
#line 209
testRunner.And("I hit \"Publish Now\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 210
testRunner.And("I am redirected", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 211
testRunner.And("I fill in", ((string)(null)), table19, "And ");
#line 214
testRunner.And("I hit \"Publish\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 215
testRunner.And("I am redirected", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 216
testRunner.And("I go to \"admin/blogs\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 212
#line 217
testRunner.And("I follow \"My Blog\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 213
#line 218
testRunner.And("I follow \"New Post\" where class name has \"primaryAction\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 219
testRunner.And("I wait \"1000\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line hidden
TechTalk.SpecFlow.Table table20 = new TechTalk.SpecFlow.Table(new string[] {
"name",
@ -627,18 +639,20 @@ this.ScenarioSetup(scenarioInfo);
table20.AddRow(new string[] {
"Title.Title",
"My Post 6"});
#line 214
testRunner.And("I fill in", ((string)(null)), table20, "And ");
#line 217
testRunner.And("I hit \"Publish Now\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 218
testRunner.And("I am redirected", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 219
testRunner.And("I go to \"admin/blogs\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 220
testRunner.And("I fill in", ((string)(null)), table20, "And ");
#line 223
testRunner.And("I hit \"Publish\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 224
testRunner.And("I am redirected", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 225
testRunner.And("I go to \"admin/blogs\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 226
testRunner.And("I follow \"My Blog\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 221
#line 227
testRunner.And("I follow \"New Post\" where class name has \"primaryAction\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 228
testRunner.And("I wait \"1000\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line hidden
TechTalk.SpecFlow.Table table21 = new TechTalk.SpecFlow.Table(new string[] {
"name",
@ -646,18 +660,20 @@ this.ScenarioSetup(scenarioInfo);
table21.AddRow(new string[] {
"Title.Title",
"My Post 7"});
#line 222
testRunner.And("I fill in", ((string)(null)), table21, "And ");
#line 225
testRunner.And("I hit \"Publish Now\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 226
testRunner.And("I am redirected", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 227
testRunner.And("I go to \"admin/blogs\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 228
testRunner.And("I follow \"My Blog\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 229
testRunner.And("I fill in", ((string)(null)), table21, "And ");
#line 232
testRunner.And("I hit \"Publish\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 233
testRunner.And("I am redirected", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 234
testRunner.And("I go to \"admin/blogs\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 235
testRunner.And("I follow \"My Blog\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 236
testRunner.And("I follow \"New Post\" where class name has \"primaryAction\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 237
testRunner.And("I wait \"1000\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line hidden
TechTalk.SpecFlow.Table table22 = new TechTalk.SpecFlow.Table(new string[] {
"name",
@ -665,29 +681,10 @@ this.ScenarioSetup(scenarioInfo);
table22.AddRow(new string[] {
"Title.Title",
"My Post 8"});
#line 230
testRunner.And("I fill in", ((string)(null)), table22, "And ");
#line 233
testRunner.And("I hit \"Publish Now\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 234
testRunner.And("I am redirected", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 235
testRunner.And("I go to \"admin/blogs\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 236
testRunner.And("I follow \"My Blog\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 237
testRunner.And("I follow \"New Post\" where class name has \"primaryAction\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line hidden
TechTalk.SpecFlow.Table table23 = new TechTalk.SpecFlow.Table(new string[] {
"name",
"value"});
table23.AddRow(new string[] {
"Title.Title",
"My Post 9"});
#line 238
testRunner.And("I fill in", ((string)(null)), table23, "And ");
testRunner.And("I fill in", ((string)(null)), table22, "And ");
#line 241
testRunner.And("I hit \"Publish Now\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
testRunner.And("I hit \"Publish\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 242
testRunner.And("I am redirected", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 243
@ -696,6 +693,29 @@ this.ScenarioSetup(scenarioInfo);
testRunner.And("I follow \"My Blog\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 245
testRunner.And("I follow \"New Post\" where class name has \"primaryAction\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 246
testRunner.And("I wait \"1000\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line hidden
TechTalk.SpecFlow.Table table23 = new TechTalk.SpecFlow.Table(new string[] {
"name",
"value"});
table23.AddRow(new string[] {
"Title.Title",
"My Post 9"});
#line 247
testRunner.And("I fill in", ((string)(null)), table23, "And ");
#line 250
testRunner.And("I hit \"Publish\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 251
testRunner.And("I am redirected", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 252
testRunner.And("I go to \"admin/blogs\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 253
testRunner.And("I follow \"My Blog\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 254
testRunner.And("I follow \"New Post\" where class name has \"primaryAction\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 255
testRunner.And("I wait \"1000\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line hidden
TechTalk.SpecFlow.Table table24 = new TechTalk.SpecFlow.Table(new string[] {
"name",
@ -703,18 +723,20 @@ this.ScenarioSetup(scenarioInfo);
table24.AddRow(new string[] {
"Title.Title",
"My Post 10"});
#line 246
#line 256
testRunner.And("I fill in", ((string)(null)), table24, "And ");
#line 249
testRunner.And("I hit \"Publish Now\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 250
#line 259
testRunner.And("I hit \"Publish\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 260
testRunner.And("I am redirected", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 251
#line 261
testRunner.And("I go to \"admin/blogs\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 252
#line 262
testRunner.And("I follow \"My Blog\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 253
#line 263
testRunner.And("I follow \"New Post\" where class name has \"primaryAction\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 264
testRunner.And("I wait \"1000\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line hidden
TechTalk.SpecFlow.Table table25 = new TechTalk.SpecFlow.Table(new string[] {
"name",
@ -722,18 +744,20 @@ this.ScenarioSetup(scenarioInfo);
table25.AddRow(new string[] {
"Title.Title",
"My Post 11"});
#line 254
#line 265
testRunner.And("I fill in", ((string)(null)), table25, "And ");
#line 257
testRunner.And("I hit \"Publish Now\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 258
#line 268
testRunner.And("I hit \"Publish\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 269
testRunner.And("I am redirected", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 259
#line 270
testRunner.And("I go to \"admin/blogs\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 260
#line 271
testRunner.And("I follow \"My Blog\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 261
#line 272
testRunner.And("I follow \"New Post\" where class name has \"primaryAction\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 273
testRunner.And("I wait \"1000\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line hidden
TechTalk.SpecFlow.Table table26 = new TechTalk.SpecFlow.Table(new string[] {
"name",
@ -741,34 +765,34 @@ this.ScenarioSetup(scenarioInfo);
table26.AddRow(new string[] {
"Title.Title",
"My Post 12"});
#line 262
testRunner.And("I fill in", ((string)(null)), table26, "And ");
#line 265
testRunner.And("I hit \"Publish Now\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 266
testRunner.And("I am redirected", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 267
testRunner.Then("I should see \"Your Blog Post has been created.\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line 268
testRunner.When("I go to \"my-blog\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 269
testRunner.Then("I should see \"<h1[^>]*>.*?My Blog.*?</h1>\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line 270
testRunner.And("I should see \"<h1[^>]*>.*?My Post 12.*?</h1>\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 271
testRunner.And("I should see \"<h1[^>]*>.*?My Post 11.*?</h1>\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 272
testRunner.And("I should not see \"<h1[^>]*>.*?My Post 10.*?</h1>\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 273
testRunner.When("I go to \"my-blog?page=2\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 274
testRunner.Then("I should see \"<h1[^>]*>.*?My Blog.*?</h1>\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line 275
testRunner.And("I should see \"<h1[^>]*>.*?My Post 1.*?</h1>\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 276
testRunner.And("I should see \"<h1[^>]*>.*?My Post 2.*?</h1>\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
testRunner.And("I fill in", ((string)(null)), table26, "And ");
#line 277
testRunner.And("I should not see \"<h1[^>]*>.*?My Post 3.*?</h1>\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
testRunner.And("I hit \"Publish\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 278
testRunner.And("I am redirected", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 279
testRunner.Then("I should see \"Your Blog Post has been created.\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line 280
testRunner.When("I go to \"my-blog\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 281
testRunner.Then("I should see \"<h1[^>]*>.*?My Blog.*?</h1>\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line 282
testRunner.And("I should see \"<h1[^>]*>.*?My Post 12.*?</h1>\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 283
testRunner.And("I should see \"<h1[^>]*>.*?My Post 11.*?</h1>\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 284
testRunner.And("I should not see \"My Post 2\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 285
testRunner.When("I go to \"my-blog?page=2\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 286
testRunner.Then("I should see \"<h1[^>]*>.*?My Blog.*?</h1>\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line 287
testRunner.And("I should see \"<h1[^>]*>.*?My Post 1.*?</h1>\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 288
testRunner.And("I should see \"<h1[^>]*>.*?My Post 2.*?</h1>\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 289
testRunner.And("I should not see \"My Post 3\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line hidden
this.ScenarioCleanup();
}
@ -780,11 +804,11 @@ this.ScenarioSetup(scenarioInfo);
{
TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("I can create a new blog with a percent sign in the title and it gets stripped out" +
" of the slug", ((string[])(null)));
#line 279
#line 291
this.ScenarioSetup(scenarioInfo);
#line 280
#line 292
testRunner.Given("I have installed Orchard", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line 281
#line 293
testRunner.When("I go to \"admin/blogs/create\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line hidden
TechTalk.SpecFlow.Table table27 = new TechTalk.SpecFlow.Table(new string[] {
@ -793,15 +817,15 @@ this.ScenarioSetup(scenarioInfo);
table27.AddRow(new string[] {
"Title.Title",
"My Blog"});
#line 282
#line 294
testRunner.And("I fill in", ((string)(null)), table27, "And ");
#line 285
testRunner.And("I hit \"Save\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 286
#line 297
testRunner.And("I hit \"Publish\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 298
testRunner.And("I go to \"admin/blogs\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 287
#line 299
testRunner.And("I follow \"My Blog\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 288
#line 300
testRunner.And("I follow \"New Post\" where class name has \"primaryAction\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line hidden
TechTalk.SpecFlow.Table table28 = new TechTalk.SpecFlow.Table(new string[] {
@ -813,15 +837,15 @@ this.ScenarioSetup(scenarioInfo);
table28.AddRow(new string[] {
"Body.Text",
"Hi there."});
#line 289
#line 301
testRunner.And("I fill in", ((string)(null)), table28, "And ");
#line 293
testRunner.And("I hit \"Publish Now\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 294
#line 305
testRunner.And("I hit \"Publish\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 306
testRunner.And("I go to \"my-blog/my-post-with-a-sign\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 295
#line 307
testRunner.Then("I should see \"<h1[^>]*>.*?My Post with a % Sign.*?</h1>\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line 296
#line 308
testRunner.And("I should see \"Hi there.\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line hidden
this.ScenarioCleanup();

Some files were not shown because too many files have changed in this diff Show More